Deleted Added
full compact
frm_driver.c (174993) frm_driver.c (176187)
1/****************************************************************************
1/****************************************************************************
2 * Copyright (c) 1998-2006,2007 Free Software Foundation, Inc. *
2 * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc. *
3 * *
4 * Permission is hereby granted, free of charge, to any person obtaining a *
5 * copy of this software and associated documentation files (the *
6 * "Software"), to deal in the Software without restriction, including *
7 * without limitation the rights to use, copy, modify, merge, publish, *
8 * distribute, distribute with modifications, sublicense, and/or sell *
9 * copies of the Software, and to permit persons to whom the Software is *
10 * furnished to do so, subject to the following conditions: *
11 * *
12 * The above copyright notice and this permission notice shall be included *
13 * in all copies or substantial portions of the Software. *
14 * *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
22 * *
23 * Except as contained in this notice, the name(s) of the above copyright *
24 * holders shall not be used in advertising or otherwise to promote the *
25 * sale, use or other dealings in this Software without prior written *
26 * authorization. *
27 ****************************************************************************/
28
29/****************************************************************************
30 * Author: Juergen Pfeifer, 1995,1997 *
31 ****************************************************************************/
32
33#include "form.priv.h"
34
3 * *
4 * Permission is hereby granted, free of charge, to any person obtaining a *
5 * copy of this software and associated documentation files (the *
6 * "Software"), to deal in the Software without restriction, including *
7 * without limitation the rights to use, copy, modify, merge, publish, *
8 * distribute, distribute with modifications, sublicense, and/or sell *
9 * copies of the Software, and to permit persons to whom the Software is *
10 * furnished to do so, subject to the following conditions: *
11 * *
12 * The above copyright notice and this permission notice shall be included *
13 * in all copies or substantial portions of the Software. *
14 * *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
22 * *
23 * Except as contained in this notice, the name(s) of the above copyright *
24 * holders shall not be used in advertising or otherwise to promote the *
25 * sale, use or other dealings in this Software without prior written *
26 * authorization. *
27 ****************************************************************************/
28
29/****************************************************************************
30 * Author: Juergen Pfeifer, 1995,1997 *
31 ****************************************************************************/
32
33#include "form.priv.h"
34
35MODULE_ID("$Id: frm_driver.c,v 1.85 2007/11/24 21:32:53 tom Exp $")
35MODULE_ID("$Id: frm_driver.c,v 1.86 2008/01/19 20:11:03 tom Exp $")
36
37/*----------------------------------------------------------------------------
38 This is the core module of the form library. It contains the majority
39 of the driver routines as well as the form_driver function.
40
41 Essentially this module is nearly the whole library. This is because
42 all the functions in this module depends on some others in the module,
43 so it makes no sense to split them into separate files because they
44 will always be linked together. The only acceptable concern is turnaround
45 time for this module, but now we have all Pentiums or RISCs, so what!
46
47 The driver routines are grouped into nine generic categories:
48
49 a) Page Navigation ( all functions prefixed by PN_ )
50 The current page of the form is left and some new page is
51 entered.
52 b) Inter-Field Navigation ( all functions prefixed by FN_ )
53 The current field of the form is left and some new field is
54 entered.
55 c) Intra-Field Navigation ( all functions prefixed by IFN_ )
56 The current position in the current field is changed.
57 d) Vertical Scrolling ( all functions prefixed by VSC_ )
58 Essentially this is a specialization of Intra-Field navigation.
59 It has to check for a multi-line field.
60 e) Horizontal Scrolling ( all functions prefixed by HSC_ )
61 Essentially this is a specialization of Intra-Field navigation.
62 It has to check for a single-line field.
63 f) Field Editing ( all functions prefixed by FE_ )
64 The content of the current field is changed
65 g) Edit Mode requests ( all functions prefixed by EM_ )
66 Switching between insert and overlay mode
67 h) Field-Validation requests ( all functions prefixed by FV_ )
68 Perform verifications of the field.
69 i) Choice requests ( all functions prefixed by CR_ )
70 Requests to enumerate possible field values
71 --------------------------------------------------------------------------*/
72
73/*----------------------------------------------------------------------------
74 Some remarks on the placements of assert() macros :
75 I use them only on "strategic" places, i.e. top level entries where
76 I want to make sure that things are set correctly. Throughout subordinate
77 routines I omit them mostly.
78 --------------------------------------------------------------------------*/
79
80/*
81Some options that may effect compatibility in behavior to SVr4 forms,
82but they are here to allow a more intuitive and user friendly behavior of
83our form implementation. This doesn't affect the API, so we feel it is
84uncritical.
85
86The initial implementation tries to stay very close with the behavior
87of the original SVr4 implementation, although in some areas it is quite
88clear that this isn't the most appropriate way. As far as possible this
89sources will allow you to build a forms lib that behaves quite similar
90to SVr4, but now and in the future we will give you better options.
91Perhaps at some time we will make this configurable at runtime.
92*/
93
94/* Implement a more user-friendly previous/next word behavior */
95#define FRIENDLY_PREV_NEXT_WORD (1)
96/* Fix the wrong behavior for forms with all fields inactive */
97#define FIX_FORM_INACTIVE_BUG (1)
98/* Allow dynamic field growth also when navigating past the end */
99#define GROW_IF_NAVIGATE (1)
100
101#if USE_WIDEC_SUPPORT
102#define myADDNSTR(w, s, n) wadd_wchnstr(w, s, n)
103#define myINSNSTR(w, s, n) wins_wchnstr(w, s, n)
104#define myINNSTR(w, s, n) fix_wchnstr(w, s, n)
105#define myWCWIDTH(w, y, x) cell_width(w, y, x)
106#else
107#define myADDNSTR(w, s, n) waddnstr(w, s, n)
108#define myINSNSTR(w, s, n) winsnstr(w, s, n)
109#define myINNSTR(w, s, n) winnstr(w, s, n)
110#define myWCWIDTH(w, y, x) 1
111#endif
112
113/*----------------------------------------------------------------------------
114 Forward references to some internally used static functions
115 --------------------------------------------------------------------------*/
116static int Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form);
117static int FN_Next_Field(FORM *form);
118static int FN_Previous_Field(FORM *form);
119static int FE_New_Line(FORM *);
120static int FE_Delete_Previous(FORM *);
121
122/*----------------------------------------------------------------------------
123 Macro Definitions.
124
125 Some Remarks on that: I use the convention to use UPPERCASE for constants
126 defined by Macros. If I provide a macro as a kind of inline routine to
127 provide some logic, I use my Upper_Lower case style.
128 --------------------------------------------------------------------------*/
129
130/* Calculate the position of a single row in a field buffer */
131#define Position_Of_Row_In_Buffer(field,row) ((row)*(field)->dcols)
132
133/* Calculate start address for the fields buffer# N */
134#define Address_Of_Nth_Buffer(field,N) \
135 ((field)->buf + (N)*(1+Buffer_Length(field)))
136
137/* Calculate the start address of the row in the fields specified buffer# N */
138#define Address_Of_Row_In_Nth_Buffer(field,N,row) \
139 (Address_Of_Nth_Buffer(field,N) + Position_Of_Row_In_Buffer(field,row))
140
141/* Calculate the start address of the row in the fields primary buffer */
142#define Address_Of_Row_In_Buffer(field,row) \
143 Address_Of_Row_In_Nth_Buffer(field,0,row)
144
145/* Calculate the start address of the row in the forms current field
146 buffer# N */
147#define Address_Of_Current_Row_In_Nth_Buffer(form,N) \
148 Address_Of_Row_In_Nth_Buffer((form)->current,N,(form)->currow)
149
150/* Calculate the start address of the row in the forms current field
151 primary buffer */
152#define Address_Of_Current_Row_In_Buffer(form) \
153 Address_Of_Current_Row_In_Nth_Buffer(form,0)
154
155/* Calculate the address of the cursor in the forms current field
156 primary buffer */
157#define Address_Of_Current_Position_In_Nth_Buffer(form,N) \
158 (Address_Of_Current_Row_In_Nth_Buffer(form,N) + (form)->curcol)
159
160/* Calculate the address of the cursor in the forms current field
161 buffer# N */
162#define Address_Of_Current_Position_In_Buffer(form) \
163 Address_Of_Current_Position_In_Nth_Buffer(form,0)
164
165/* Logic to decide whether or not a field is actually a field with
166 vertical or horizontal scrolling */
167#define Is_Scroll_Field(field) \
168 (((field)->drows > (field)->rows) || \
169 ((field)->dcols > (field)->cols))
170
171/* Logic to decide whether or not a field needs to have an individual window
172 instead of a derived window because it contains invisible parts.
173 This is true for non-public fields and for scrollable fields. */
174#define Has_Invisible_Parts(field) \
175 (!((field)->opts & O_PUBLIC) || \
176 Is_Scroll_Field(field))
177
178/* Logic to decide whether or not a field needs justification */
179#define Justification_Allowed(field) \
180 (((field)->just != NO_JUSTIFICATION) && \
181 (Single_Line_Field(field)) && \
182 (((field)->dcols == (field)->cols) && \
183 ((field)->opts & O_STATIC)) )
184
185/* Logic to determine whether or not a dynamic field may still grow */
186#define Growable(field) ((field)->status & _MAY_GROW)
187
188/* Macro to set the attributes for a fields window */
189#define Set_Field_Window_Attributes(field,win) \
190( wbkgdset((win),(chtype)((field)->pad | (field)->back)), \
191 wattrset((win),(field)->fore) )
192
193/* Logic to decide whether or not a field really appears on the form */
194#define Field_Really_Appears(field) \
195 ((field->form) &&\
196 (field->form->status & _POSTED) &&\
197 (field->opts & O_VISIBLE) &&\
198 (field->page == field->form->curpage))
199
200/* Logic to determine whether or not we are on the first position in the
201 current field */
202#define First_Position_In_Current_Field(form) \
203 (((form)->currow==0) && ((form)->curcol==0))
204
205#define Minimum(a,b) (((a)<=(b)) ? (a) : (b))
206#define Maximum(a,b) (((a)>=(b)) ? (a) : (b))
207
208/*----------------------------------------------------------------------------
209 Useful constants
210 --------------------------------------------------------------------------*/
211static FIELD_CELL myBLANK = BLANK;
212static FIELD_CELL myZEROS;
213
214#ifdef TRACE
215static void
216check_pos(FORM *form, int lineno)
217{
218 int y, x;
219
220 if (form && form->w)
221 {
222 getyx(form->w, y, x);
223 if (y != form->currow || x != form->curcol)
224 {
225 T(("CHECKPOS %s@%d have position %d,%d vs want %d,%d",
226 __FILE__, lineno,
227 y, x,
228 form->currow, form->curcol));
229 }
230 }
231}
232#define CHECKPOS(form) check_pos(form, __LINE__)
233#else
234#define CHECKPOS(form) /* nothing */
235#endif
236
237/*----------------------------------------------------------------------------
238 Wide-character special functions
239 --------------------------------------------------------------------------*/
240#if USE_WIDEC_SUPPORT
241/* like winsnstr */
242static int
243wins_wchnstr(WINDOW *w, cchar_t *s, int n)
244{
245 int code = ERR;
246 int y, x;
247
248 while (n-- > 0)
249 {
250 getyx(w, y, x);
251 if ((code = wins_wch(w, s++)) != OK)
252 break;
253 if ((code = wmove(w, y, x + 1)) != OK)
254 break;
255 }
256 return code;
257}
258
259/* win_wchnstr is inconsistent with winnstr, since it returns OK rather than
260 * the number of items transferred.
261 */
262static int
263fix_wchnstr(WINDOW *w, cchar_t *s, int n)
264{
265 int x;
266
267 win_wchnstr(w, s, n);
268 /*
269 * This function is used to extract the text only from the window.
270 * Strip attributes and color from the string so they will not be added
271 * back when copying the string to the window.
272 */
273 for (x = 0; x < n; ++x)
274 {
275 RemAttr(s[x], A_ATTRIBUTES);
276 SetPair(s[x], 0);
277 }
278 return n;
279}
280
281/*
282 * Returns the column of the base of the given cell.
283 */
284static int
285cell_base(WINDOW *win, int y, int x)
286{
287 int result = x;
288
289 while (LEGALYX(win, y, x))
290 {
291 cchar_t *data = &(win->_line[y].text[x]);
292
293 if (isWidecBase(CHDEREF(data)) || !isWidecExt(CHDEREF(data)))
294 {
295 result = x;
296 break;
297 }
298 --x;
299 }
300 return result;
301}
302
303/*
304 * Returns the number of columns needed for the given cell in a window.
305 */
306static int
307cell_width(WINDOW *win, int y, int x)
308{
309 int result = 1;
310
311 if (LEGALYX(win, y, x))
312 {
313 cchar_t *data = &(win->_line[y].text[x]);
314
315 if (isWidecExt(CHDEREF(data)))
316 {
317 /* recur, providing the number of columns to the next character */
318 result = cell_width(win, y, x - 1);
319 }
320 else
321 {
322 result = wcwidth(CharOf(CHDEREF(data)));
323 }
324 }
325 return result;
326}
327
328/*
329 * There is no wide-character function such as wdel_wch(), so we must find
330 * all of the cells that comprise a multi-column character and delete them
331 * one-by-one.
332 */
333static void
334delete_char(FORM *form)
335{
336 int cells = cell_width(form->w, form->currow, form->curcol);
337
338 form->curcol = cell_base(form->w, form->currow, form->curcol);
339 wmove(form->w, form->currow, form->curcol);
340 while (cells-- > 0)
341 {
342 wdelch(form->w);
343 }
344}
345#define DeleteChar(form) delete_char(form)
346#else
347#define DeleteChar(form) \
348 wmove((form)->w, (form)->currow, (form)->curcol), \
349 wdelch((form)->w)
350#endif
351
352/*---------------------------------------------------------------------------
353| Facility : libnform
354| Function : static char *Get_Start_Of_Data(char * buf, int blen)
355|
356| Description : Return pointer to first non-blank position in buffer.
357| If buffer is empty return pointer to buffer itself.
358|
359| Return Values : Pointer to first non-blank position in buffer
360+--------------------------------------------------------------------------*/
361NCURSES_INLINE static FIELD_CELL *
362Get_Start_Of_Data(FIELD_CELL *buf, int blen)
363{
364 FIELD_CELL *p = buf;
365 FIELD_CELL *end = &buf[blen];
366
367 assert(buf && blen >= 0);
368 while ((p < end) && ISBLANK(*p))
369 p++;
370 return ((p == end) ? buf : p);
371}
372
373/*---------------------------------------------------------------------------
374| Facility : libnform
375| Function : static char *After_End_Of_Data(char * buf, int blen)
376|
377| Description : Return pointer after last non-blank position in buffer.
378| If buffer is empty, return pointer to buffer itself.
379|
380| Return Values : Pointer to position after last non-blank position in
381| buffer.
382+--------------------------------------------------------------------------*/
383NCURSES_INLINE static FIELD_CELL *
384After_End_Of_Data(FIELD_CELL *buf, int blen)
385{
386 FIELD_CELL *p = &buf[blen];
387
388 assert(buf && blen >= 0);
389 while ((p > buf) && ISBLANK(p[-1]))
390 p--;
391 return (p);
392}
393
394/*---------------------------------------------------------------------------
395| Facility : libnform
396| Function : static char *Get_First_Whitespace_Character(
397| char * buf, int blen)
398|
399| Description : Position to the first whitespace character.
400|
401| Return Values : Pointer to first whitespace character in buffer.
402+--------------------------------------------------------------------------*/
403NCURSES_INLINE static FIELD_CELL *
404Get_First_Whitespace_Character(FIELD_CELL *buf, int blen)
405{
406 FIELD_CELL *p = buf;
407 FIELD_CELL *end = &p[blen];
408
409 assert(buf && blen >= 0);
410 while ((p < end) && !ISBLANK(*p))
411 p++;
412 return ((p == end) ? buf : p);
413}
414
415/*---------------------------------------------------------------------------
416| Facility : libnform
417| Function : static char *After_Last_Whitespace_Character(
418| char * buf, int blen)
419|
420| Description : Get the position after the last whitespace character.
421|
422| Return Values : Pointer to position after last whitespace character in
423| buffer.
424+--------------------------------------------------------------------------*/
425NCURSES_INLINE static FIELD_CELL *
426After_Last_Whitespace_Character(FIELD_CELL *buf, int blen)
427{
428 FIELD_CELL *p = &buf[blen];
429
430 assert(buf && blen >= 0);
431 while ((p > buf) && !ISBLANK(p[-1]))
432 p--;
433 return (p);
434}
435
436/* Set this to 1 to use the div_t version. This is a good idea if your
437 compiler has an intrinsic div() support. Unfortunately GNU-C has it
438 not yet.
439 N.B.: This only works if form->curcol follows immediately form->currow
440 and both are of type int.
441*/
442#define USE_DIV_T (0)
443
444/*---------------------------------------------------------------------------
445| Facility : libnform
446| Function : static void Adjust_Cursor_Position(
447| FORM * form, const char * pos)
448|
449| Description : Set current row and column of the form to values
450| corresponding to the buffer position.
451|
452| Return Values : -
453+--------------------------------------------------------------------------*/
454NCURSES_INLINE static void
455Adjust_Cursor_Position(FORM *form, const FIELD_CELL *pos)
456{
457 FIELD *field;
458 int idx;
459
460 field = form->current;
461 assert(pos >= field->buf && field->dcols > 0);
462 idx = (int)(pos - field->buf);
463#if USE_DIV_T
464 *((div_t *) & (form->currow)) = div(idx, field->dcols);
465#else
466 form->currow = idx / field->dcols;
467 form->curcol = idx - field->cols * form->currow;
468#endif
469 if (field->drows < form->currow)
470 form->currow = 0;
471}
472
473/*---------------------------------------------------------------------------
474| Facility : libnform
475| Function : static void Buffer_To_Window(
476| const FIELD * field,
477| WINDOW * win)
478|
479| Description : Copy the buffer to the window. If it is a multi-line
480| field, the buffer is split to the lines of the
481| window without any editing.
482|
483| Return Values : -
484+--------------------------------------------------------------------------*/
485static void
486Buffer_To_Window(const FIELD *field, WINDOW *win)
487{
488 int width, height;
489 int y, x;
490 int len;
491 int row;
492 FIELD_CELL *pBuffer;
493
494 assert(win && field);
495
496 getyx(win, y, x);
497 width = getmaxx(win);
498 height = getmaxy(win);
499
500 for (row = 0, pBuffer = field->buf;
501 row < height;
502 row++, pBuffer += width)
503 {
504 if ((len = (int)(After_End_Of_Data(pBuffer, width) - pBuffer)) > 0)
505 {
506 wmove(win, row, 0);
507 myADDNSTR(win, pBuffer, len);
508 }
509 }
510 wmove(win, y, x);
511}
512
513/*---------------------------------------------------------------------------
514| Facility : libnform
515| Function : static void Window_To_Buffer(
516| WINDOW * win,
517| FIELD * field)
518|
519| Description : Copy the content of the window into the buffer.
520| The multiple lines of a window are simply
521| concatenated into the buffer. Pad characters in
522| the window will be replaced by blanks in the buffer.
523|
524| Return Values : -
525+--------------------------------------------------------------------------*/
526static void
527Window_To_Buffer(WINDOW *win, FIELD *field)
528{
529 int pad;
530 int len = 0;
531 FIELD_CELL *p;
532 int row, height;
533
534 assert(win && field && field->buf);
535
536 pad = field->pad;
537 p = field->buf;
538 height = getmaxy(win);
539
540 for (row = 0; (row < height) && (row < field->drows); row++)
541 {
542 wmove(win, row, 0);
543 len += myINNSTR(win, p + len, field->dcols);
544 }
545 p[len] = myZEROS;
546
547 /* replace visual padding character by blanks in buffer */
548 if (pad != C_BLANK)
549 {
550 int i;
551
552 for (i = 0; i < len; i++, p++)
553 {
554 if ((unsigned long)CharOf(*p) == ChCharOf(pad)
555#if USE_WIDEC_SUPPORT
556 && p->chars[1] == 0
557#endif
558 )
559 *p = myBLANK;
560 }
561 }
562}
563
564/*---------------------------------------------------------------------------
565| Facility : libnform
566| Function : static void Synchronize_Buffer(FORM * form)
567|
568| Description : If there was a change, copy the content of the
569| window into the buffer, so the buffer is synchronized
570| with the windows content. We have to indicate that the
571| buffer needs validation due to the change.
572|
573| Return Values : -
574+--------------------------------------------------------------------------*/
575NCURSES_INLINE static void
576Synchronize_Buffer(FORM *form)
577{
578 if (form->status & _WINDOW_MODIFIED)
579 {
580 form->status &= ~_WINDOW_MODIFIED;
581 form->status |= _FCHECK_REQUIRED;
582 Window_To_Buffer(form->w, form->current);
583 wmove(form->w, form->currow, form->curcol);
584 }
585}
586
587/*---------------------------------------------------------------------------
588| Facility : libnform
589| Function : static bool Field_Grown( FIELD *field, int amount)
590|
591| Description : This function is called for growable dynamic fields
592| only. It has to increase the buffers and to allocate
593| a new window for this field.
594| This function has the side effect to set a new
595| field-buffer pointer, the dcols and drows values
596| as well as a new current Window for the field.
597|
598| Return Values : TRUE - field successfully increased
599| FALSE - there was some error
600+--------------------------------------------------------------------------*/
601static bool
602Field_Grown(FIELD *field, int amount)
603{
604 bool result = FALSE;
605
606 if (field && Growable(field))
607 {
608 bool single_line_field = Single_Line_Field(field);
609 int old_buflen = Buffer_Length(field);
610 int new_buflen;
611 int old_dcols = field->dcols;
612 int old_drows = field->drows;
613 FIELD_CELL *oldbuf = field->buf;
614 FIELD_CELL *newbuf;
615
616 int growth;
617 FORM *form = field->form;
618 bool need_visual_update = ((form != (FORM *)0) &&
619 (form->status & _POSTED) &&
620 (form->current == field));
621
622 if (need_visual_update)
623 Synchronize_Buffer(form);
624
625 if (single_line_field)
626 {
627 growth = field->cols * amount;
628 if (field->maxgrow)
629 growth = Minimum(field->maxgrow - field->dcols, growth);
630 field->dcols += growth;
631 if (field->dcols == field->maxgrow)
632 field->status &= ~_MAY_GROW;
633 }
634 else
635 {
636 growth = (field->rows + field->nrow) * amount;
637 if (field->maxgrow)
638 growth = Minimum(field->maxgrow - field->drows, growth);
639 field->drows += growth;
640 if (field->drows == field->maxgrow)
641 field->status &= ~_MAY_GROW;
642 }
643 /* drows, dcols changed, so we get really the new buffer length */
644 new_buflen = Buffer_Length(field);
645 newbuf = (FIELD_CELL *)malloc(Total_Buffer_Size(field));
646 if (!newbuf)
647 {
648 /* restore to previous state */
649 field->dcols = old_dcols;
650 field->drows = old_drows;
651 if ((single_line_field && (field->dcols != field->maxgrow)) ||
652 (!single_line_field && (field->drows != field->maxgrow)))
653 field->status |= _MAY_GROW;
654 }
655 else
656 {
657 /* Copy all the buffers. This is the reason why we can't just use
658 * realloc().
659 */
660 int i, j;
661 FIELD_CELL *old_bp;
662 FIELD_CELL *new_bp;
663
664 result = TRUE; /* allow sharing of recovery on failure */
665
666 T((T_CREATE("fieldcell %p"), newbuf));
667 field->buf = newbuf;
668 for (i = 0; i <= field->nbuf; i++)
669 {
670 new_bp = Address_Of_Nth_Buffer(field, i);
671 old_bp = oldbuf + i * (1 + old_buflen);
672 for (j = 0; j < old_buflen; ++j)
673 new_bp[j] = old_bp[j];
674 while (j < new_buflen)
675 new_bp[j++] = myBLANK;
676 new_bp[new_buflen] = myZEROS;
677 }
678
36
37/*----------------------------------------------------------------------------
38 This is the core module of the form library. It contains the majority
39 of the driver routines as well as the form_driver function.
40
41 Essentially this module is nearly the whole library. This is because
42 all the functions in this module depends on some others in the module,
43 so it makes no sense to split them into separate files because they
44 will always be linked together. The only acceptable concern is turnaround
45 time for this module, but now we have all Pentiums or RISCs, so what!
46
47 The driver routines are grouped into nine generic categories:
48
49 a) Page Navigation ( all functions prefixed by PN_ )
50 The current page of the form is left and some new page is
51 entered.
52 b) Inter-Field Navigation ( all functions prefixed by FN_ )
53 The current field of the form is left and some new field is
54 entered.
55 c) Intra-Field Navigation ( all functions prefixed by IFN_ )
56 The current position in the current field is changed.
57 d) Vertical Scrolling ( all functions prefixed by VSC_ )
58 Essentially this is a specialization of Intra-Field navigation.
59 It has to check for a multi-line field.
60 e) Horizontal Scrolling ( all functions prefixed by HSC_ )
61 Essentially this is a specialization of Intra-Field navigation.
62 It has to check for a single-line field.
63 f) Field Editing ( all functions prefixed by FE_ )
64 The content of the current field is changed
65 g) Edit Mode requests ( all functions prefixed by EM_ )
66 Switching between insert and overlay mode
67 h) Field-Validation requests ( all functions prefixed by FV_ )
68 Perform verifications of the field.
69 i) Choice requests ( all functions prefixed by CR_ )
70 Requests to enumerate possible field values
71 --------------------------------------------------------------------------*/
72
73/*----------------------------------------------------------------------------
74 Some remarks on the placements of assert() macros :
75 I use them only on "strategic" places, i.e. top level entries where
76 I want to make sure that things are set correctly. Throughout subordinate
77 routines I omit them mostly.
78 --------------------------------------------------------------------------*/
79
80/*
81Some options that may effect compatibility in behavior to SVr4 forms,
82but they are here to allow a more intuitive and user friendly behavior of
83our form implementation. This doesn't affect the API, so we feel it is
84uncritical.
85
86The initial implementation tries to stay very close with the behavior
87of the original SVr4 implementation, although in some areas it is quite
88clear that this isn't the most appropriate way. As far as possible this
89sources will allow you to build a forms lib that behaves quite similar
90to SVr4, but now and in the future we will give you better options.
91Perhaps at some time we will make this configurable at runtime.
92*/
93
94/* Implement a more user-friendly previous/next word behavior */
95#define FRIENDLY_PREV_NEXT_WORD (1)
96/* Fix the wrong behavior for forms with all fields inactive */
97#define FIX_FORM_INACTIVE_BUG (1)
98/* Allow dynamic field growth also when navigating past the end */
99#define GROW_IF_NAVIGATE (1)
100
101#if USE_WIDEC_SUPPORT
102#define myADDNSTR(w, s, n) wadd_wchnstr(w, s, n)
103#define myINSNSTR(w, s, n) wins_wchnstr(w, s, n)
104#define myINNSTR(w, s, n) fix_wchnstr(w, s, n)
105#define myWCWIDTH(w, y, x) cell_width(w, y, x)
106#else
107#define myADDNSTR(w, s, n) waddnstr(w, s, n)
108#define myINSNSTR(w, s, n) winsnstr(w, s, n)
109#define myINNSTR(w, s, n) winnstr(w, s, n)
110#define myWCWIDTH(w, y, x) 1
111#endif
112
113/*----------------------------------------------------------------------------
114 Forward references to some internally used static functions
115 --------------------------------------------------------------------------*/
116static int Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form);
117static int FN_Next_Field(FORM *form);
118static int FN_Previous_Field(FORM *form);
119static int FE_New_Line(FORM *);
120static int FE_Delete_Previous(FORM *);
121
122/*----------------------------------------------------------------------------
123 Macro Definitions.
124
125 Some Remarks on that: I use the convention to use UPPERCASE for constants
126 defined by Macros. If I provide a macro as a kind of inline routine to
127 provide some logic, I use my Upper_Lower case style.
128 --------------------------------------------------------------------------*/
129
130/* Calculate the position of a single row in a field buffer */
131#define Position_Of_Row_In_Buffer(field,row) ((row)*(field)->dcols)
132
133/* Calculate start address for the fields buffer# N */
134#define Address_Of_Nth_Buffer(field,N) \
135 ((field)->buf + (N)*(1+Buffer_Length(field)))
136
137/* Calculate the start address of the row in the fields specified buffer# N */
138#define Address_Of_Row_In_Nth_Buffer(field,N,row) \
139 (Address_Of_Nth_Buffer(field,N) + Position_Of_Row_In_Buffer(field,row))
140
141/* Calculate the start address of the row in the fields primary buffer */
142#define Address_Of_Row_In_Buffer(field,row) \
143 Address_Of_Row_In_Nth_Buffer(field,0,row)
144
145/* Calculate the start address of the row in the forms current field
146 buffer# N */
147#define Address_Of_Current_Row_In_Nth_Buffer(form,N) \
148 Address_Of_Row_In_Nth_Buffer((form)->current,N,(form)->currow)
149
150/* Calculate the start address of the row in the forms current field
151 primary buffer */
152#define Address_Of_Current_Row_In_Buffer(form) \
153 Address_Of_Current_Row_In_Nth_Buffer(form,0)
154
155/* Calculate the address of the cursor in the forms current field
156 primary buffer */
157#define Address_Of_Current_Position_In_Nth_Buffer(form,N) \
158 (Address_Of_Current_Row_In_Nth_Buffer(form,N) + (form)->curcol)
159
160/* Calculate the address of the cursor in the forms current field
161 buffer# N */
162#define Address_Of_Current_Position_In_Buffer(form) \
163 Address_Of_Current_Position_In_Nth_Buffer(form,0)
164
165/* Logic to decide whether or not a field is actually a field with
166 vertical or horizontal scrolling */
167#define Is_Scroll_Field(field) \
168 (((field)->drows > (field)->rows) || \
169 ((field)->dcols > (field)->cols))
170
171/* Logic to decide whether or not a field needs to have an individual window
172 instead of a derived window because it contains invisible parts.
173 This is true for non-public fields and for scrollable fields. */
174#define Has_Invisible_Parts(field) \
175 (!((field)->opts & O_PUBLIC) || \
176 Is_Scroll_Field(field))
177
178/* Logic to decide whether or not a field needs justification */
179#define Justification_Allowed(field) \
180 (((field)->just != NO_JUSTIFICATION) && \
181 (Single_Line_Field(field)) && \
182 (((field)->dcols == (field)->cols) && \
183 ((field)->opts & O_STATIC)) )
184
185/* Logic to determine whether or not a dynamic field may still grow */
186#define Growable(field) ((field)->status & _MAY_GROW)
187
188/* Macro to set the attributes for a fields window */
189#define Set_Field_Window_Attributes(field,win) \
190( wbkgdset((win),(chtype)((field)->pad | (field)->back)), \
191 wattrset((win),(field)->fore) )
192
193/* Logic to decide whether or not a field really appears on the form */
194#define Field_Really_Appears(field) \
195 ((field->form) &&\
196 (field->form->status & _POSTED) &&\
197 (field->opts & O_VISIBLE) &&\
198 (field->page == field->form->curpage))
199
200/* Logic to determine whether or not we are on the first position in the
201 current field */
202#define First_Position_In_Current_Field(form) \
203 (((form)->currow==0) && ((form)->curcol==0))
204
205#define Minimum(a,b) (((a)<=(b)) ? (a) : (b))
206#define Maximum(a,b) (((a)>=(b)) ? (a) : (b))
207
208/*----------------------------------------------------------------------------
209 Useful constants
210 --------------------------------------------------------------------------*/
211static FIELD_CELL myBLANK = BLANK;
212static FIELD_CELL myZEROS;
213
214#ifdef TRACE
215static void
216check_pos(FORM *form, int lineno)
217{
218 int y, x;
219
220 if (form && form->w)
221 {
222 getyx(form->w, y, x);
223 if (y != form->currow || x != form->curcol)
224 {
225 T(("CHECKPOS %s@%d have position %d,%d vs want %d,%d",
226 __FILE__, lineno,
227 y, x,
228 form->currow, form->curcol));
229 }
230 }
231}
232#define CHECKPOS(form) check_pos(form, __LINE__)
233#else
234#define CHECKPOS(form) /* nothing */
235#endif
236
237/*----------------------------------------------------------------------------
238 Wide-character special functions
239 --------------------------------------------------------------------------*/
240#if USE_WIDEC_SUPPORT
241/* like winsnstr */
242static int
243wins_wchnstr(WINDOW *w, cchar_t *s, int n)
244{
245 int code = ERR;
246 int y, x;
247
248 while (n-- > 0)
249 {
250 getyx(w, y, x);
251 if ((code = wins_wch(w, s++)) != OK)
252 break;
253 if ((code = wmove(w, y, x + 1)) != OK)
254 break;
255 }
256 return code;
257}
258
259/* win_wchnstr is inconsistent with winnstr, since it returns OK rather than
260 * the number of items transferred.
261 */
262static int
263fix_wchnstr(WINDOW *w, cchar_t *s, int n)
264{
265 int x;
266
267 win_wchnstr(w, s, n);
268 /*
269 * This function is used to extract the text only from the window.
270 * Strip attributes and color from the string so they will not be added
271 * back when copying the string to the window.
272 */
273 for (x = 0; x < n; ++x)
274 {
275 RemAttr(s[x], A_ATTRIBUTES);
276 SetPair(s[x], 0);
277 }
278 return n;
279}
280
281/*
282 * Returns the column of the base of the given cell.
283 */
284static int
285cell_base(WINDOW *win, int y, int x)
286{
287 int result = x;
288
289 while (LEGALYX(win, y, x))
290 {
291 cchar_t *data = &(win->_line[y].text[x]);
292
293 if (isWidecBase(CHDEREF(data)) || !isWidecExt(CHDEREF(data)))
294 {
295 result = x;
296 break;
297 }
298 --x;
299 }
300 return result;
301}
302
303/*
304 * Returns the number of columns needed for the given cell in a window.
305 */
306static int
307cell_width(WINDOW *win, int y, int x)
308{
309 int result = 1;
310
311 if (LEGALYX(win, y, x))
312 {
313 cchar_t *data = &(win->_line[y].text[x]);
314
315 if (isWidecExt(CHDEREF(data)))
316 {
317 /* recur, providing the number of columns to the next character */
318 result = cell_width(win, y, x - 1);
319 }
320 else
321 {
322 result = wcwidth(CharOf(CHDEREF(data)));
323 }
324 }
325 return result;
326}
327
328/*
329 * There is no wide-character function such as wdel_wch(), so we must find
330 * all of the cells that comprise a multi-column character and delete them
331 * one-by-one.
332 */
333static void
334delete_char(FORM *form)
335{
336 int cells = cell_width(form->w, form->currow, form->curcol);
337
338 form->curcol = cell_base(form->w, form->currow, form->curcol);
339 wmove(form->w, form->currow, form->curcol);
340 while (cells-- > 0)
341 {
342 wdelch(form->w);
343 }
344}
345#define DeleteChar(form) delete_char(form)
346#else
347#define DeleteChar(form) \
348 wmove((form)->w, (form)->currow, (form)->curcol), \
349 wdelch((form)->w)
350#endif
351
352/*---------------------------------------------------------------------------
353| Facility : libnform
354| Function : static char *Get_Start_Of_Data(char * buf, int blen)
355|
356| Description : Return pointer to first non-blank position in buffer.
357| If buffer is empty return pointer to buffer itself.
358|
359| Return Values : Pointer to first non-blank position in buffer
360+--------------------------------------------------------------------------*/
361NCURSES_INLINE static FIELD_CELL *
362Get_Start_Of_Data(FIELD_CELL *buf, int blen)
363{
364 FIELD_CELL *p = buf;
365 FIELD_CELL *end = &buf[blen];
366
367 assert(buf && blen >= 0);
368 while ((p < end) && ISBLANK(*p))
369 p++;
370 return ((p == end) ? buf : p);
371}
372
373/*---------------------------------------------------------------------------
374| Facility : libnform
375| Function : static char *After_End_Of_Data(char * buf, int blen)
376|
377| Description : Return pointer after last non-blank position in buffer.
378| If buffer is empty, return pointer to buffer itself.
379|
380| Return Values : Pointer to position after last non-blank position in
381| buffer.
382+--------------------------------------------------------------------------*/
383NCURSES_INLINE static FIELD_CELL *
384After_End_Of_Data(FIELD_CELL *buf, int blen)
385{
386 FIELD_CELL *p = &buf[blen];
387
388 assert(buf && blen >= 0);
389 while ((p > buf) && ISBLANK(p[-1]))
390 p--;
391 return (p);
392}
393
394/*---------------------------------------------------------------------------
395| Facility : libnform
396| Function : static char *Get_First_Whitespace_Character(
397| char * buf, int blen)
398|
399| Description : Position to the first whitespace character.
400|
401| Return Values : Pointer to first whitespace character in buffer.
402+--------------------------------------------------------------------------*/
403NCURSES_INLINE static FIELD_CELL *
404Get_First_Whitespace_Character(FIELD_CELL *buf, int blen)
405{
406 FIELD_CELL *p = buf;
407 FIELD_CELL *end = &p[blen];
408
409 assert(buf && blen >= 0);
410 while ((p < end) && !ISBLANK(*p))
411 p++;
412 return ((p == end) ? buf : p);
413}
414
415/*---------------------------------------------------------------------------
416| Facility : libnform
417| Function : static char *After_Last_Whitespace_Character(
418| char * buf, int blen)
419|
420| Description : Get the position after the last whitespace character.
421|
422| Return Values : Pointer to position after last whitespace character in
423| buffer.
424+--------------------------------------------------------------------------*/
425NCURSES_INLINE static FIELD_CELL *
426After_Last_Whitespace_Character(FIELD_CELL *buf, int blen)
427{
428 FIELD_CELL *p = &buf[blen];
429
430 assert(buf && blen >= 0);
431 while ((p > buf) && !ISBLANK(p[-1]))
432 p--;
433 return (p);
434}
435
436/* Set this to 1 to use the div_t version. This is a good idea if your
437 compiler has an intrinsic div() support. Unfortunately GNU-C has it
438 not yet.
439 N.B.: This only works if form->curcol follows immediately form->currow
440 and both are of type int.
441*/
442#define USE_DIV_T (0)
443
444/*---------------------------------------------------------------------------
445| Facility : libnform
446| Function : static void Adjust_Cursor_Position(
447| FORM * form, const char * pos)
448|
449| Description : Set current row and column of the form to values
450| corresponding to the buffer position.
451|
452| Return Values : -
453+--------------------------------------------------------------------------*/
454NCURSES_INLINE static void
455Adjust_Cursor_Position(FORM *form, const FIELD_CELL *pos)
456{
457 FIELD *field;
458 int idx;
459
460 field = form->current;
461 assert(pos >= field->buf && field->dcols > 0);
462 idx = (int)(pos - field->buf);
463#if USE_DIV_T
464 *((div_t *) & (form->currow)) = div(idx, field->dcols);
465#else
466 form->currow = idx / field->dcols;
467 form->curcol = idx - field->cols * form->currow;
468#endif
469 if (field->drows < form->currow)
470 form->currow = 0;
471}
472
473/*---------------------------------------------------------------------------
474| Facility : libnform
475| Function : static void Buffer_To_Window(
476| const FIELD * field,
477| WINDOW * win)
478|
479| Description : Copy the buffer to the window. If it is a multi-line
480| field, the buffer is split to the lines of the
481| window without any editing.
482|
483| Return Values : -
484+--------------------------------------------------------------------------*/
485static void
486Buffer_To_Window(const FIELD *field, WINDOW *win)
487{
488 int width, height;
489 int y, x;
490 int len;
491 int row;
492 FIELD_CELL *pBuffer;
493
494 assert(win && field);
495
496 getyx(win, y, x);
497 width = getmaxx(win);
498 height = getmaxy(win);
499
500 for (row = 0, pBuffer = field->buf;
501 row < height;
502 row++, pBuffer += width)
503 {
504 if ((len = (int)(After_End_Of_Data(pBuffer, width) - pBuffer)) > 0)
505 {
506 wmove(win, row, 0);
507 myADDNSTR(win, pBuffer, len);
508 }
509 }
510 wmove(win, y, x);
511}
512
513/*---------------------------------------------------------------------------
514| Facility : libnform
515| Function : static void Window_To_Buffer(
516| WINDOW * win,
517| FIELD * field)
518|
519| Description : Copy the content of the window into the buffer.
520| The multiple lines of a window are simply
521| concatenated into the buffer. Pad characters in
522| the window will be replaced by blanks in the buffer.
523|
524| Return Values : -
525+--------------------------------------------------------------------------*/
526static void
527Window_To_Buffer(WINDOW *win, FIELD *field)
528{
529 int pad;
530 int len = 0;
531 FIELD_CELL *p;
532 int row, height;
533
534 assert(win && field && field->buf);
535
536 pad = field->pad;
537 p = field->buf;
538 height = getmaxy(win);
539
540 for (row = 0; (row < height) && (row < field->drows); row++)
541 {
542 wmove(win, row, 0);
543 len += myINNSTR(win, p + len, field->dcols);
544 }
545 p[len] = myZEROS;
546
547 /* replace visual padding character by blanks in buffer */
548 if (pad != C_BLANK)
549 {
550 int i;
551
552 for (i = 0; i < len; i++, p++)
553 {
554 if ((unsigned long)CharOf(*p) == ChCharOf(pad)
555#if USE_WIDEC_SUPPORT
556 && p->chars[1] == 0
557#endif
558 )
559 *p = myBLANK;
560 }
561 }
562}
563
564/*---------------------------------------------------------------------------
565| Facility : libnform
566| Function : static void Synchronize_Buffer(FORM * form)
567|
568| Description : If there was a change, copy the content of the
569| window into the buffer, so the buffer is synchronized
570| with the windows content. We have to indicate that the
571| buffer needs validation due to the change.
572|
573| Return Values : -
574+--------------------------------------------------------------------------*/
575NCURSES_INLINE static void
576Synchronize_Buffer(FORM *form)
577{
578 if (form->status & _WINDOW_MODIFIED)
579 {
580 form->status &= ~_WINDOW_MODIFIED;
581 form->status |= _FCHECK_REQUIRED;
582 Window_To_Buffer(form->w, form->current);
583 wmove(form->w, form->currow, form->curcol);
584 }
585}
586
587/*---------------------------------------------------------------------------
588| Facility : libnform
589| Function : static bool Field_Grown( FIELD *field, int amount)
590|
591| Description : This function is called for growable dynamic fields
592| only. It has to increase the buffers and to allocate
593| a new window for this field.
594| This function has the side effect to set a new
595| field-buffer pointer, the dcols and drows values
596| as well as a new current Window for the field.
597|
598| Return Values : TRUE - field successfully increased
599| FALSE - there was some error
600+--------------------------------------------------------------------------*/
601static bool
602Field_Grown(FIELD *field, int amount)
603{
604 bool result = FALSE;
605
606 if (field && Growable(field))
607 {
608 bool single_line_field = Single_Line_Field(field);
609 int old_buflen = Buffer_Length(field);
610 int new_buflen;
611 int old_dcols = field->dcols;
612 int old_drows = field->drows;
613 FIELD_CELL *oldbuf = field->buf;
614 FIELD_CELL *newbuf;
615
616 int growth;
617 FORM *form = field->form;
618 bool need_visual_update = ((form != (FORM *)0) &&
619 (form->status & _POSTED) &&
620 (form->current == field));
621
622 if (need_visual_update)
623 Synchronize_Buffer(form);
624
625 if (single_line_field)
626 {
627 growth = field->cols * amount;
628 if (field->maxgrow)
629 growth = Minimum(field->maxgrow - field->dcols, growth);
630 field->dcols += growth;
631 if (field->dcols == field->maxgrow)
632 field->status &= ~_MAY_GROW;
633 }
634 else
635 {
636 growth = (field->rows + field->nrow) * amount;
637 if (field->maxgrow)
638 growth = Minimum(field->maxgrow - field->drows, growth);
639 field->drows += growth;
640 if (field->drows == field->maxgrow)
641 field->status &= ~_MAY_GROW;
642 }
643 /* drows, dcols changed, so we get really the new buffer length */
644 new_buflen = Buffer_Length(field);
645 newbuf = (FIELD_CELL *)malloc(Total_Buffer_Size(field));
646 if (!newbuf)
647 {
648 /* restore to previous state */
649 field->dcols = old_dcols;
650 field->drows = old_drows;
651 if ((single_line_field && (field->dcols != field->maxgrow)) ||
652 (!single_line_field && (field->drows != field->maxgrow)))
653 field->status |= _MAY_GROW;
654 }
655 else
656 {
657 /* Copy all the buffers. This is the reason why we can't just use
658 * realloc().
659 */
660 int i, j;
661 FIELD_CELL *old_bp;
662 FIELD_CELL *new_bp;
663
664 result = TRUE; /* allow sharing of recovery on failure */
665
666 T((T_CREATE("fieldcell %p"), newbuf));
667 field->buf = newbuf;
668 for (i = 0; i <= field->nbuf; i++)
669 {
670 new_bp = Address_Of_Nth_Buffer(field, i);
671 old_bp = oldbuf + i * (1 + old_buflen);
672 for (j = 0; j < old_buflen; ++j)
673 new_bp[j] = old_bp[j];
674 while (j < new_buflen)
675 new_bp[j++] = myBLANK;
676 new_bp[new_buflen] = myZEROS;
677 }
678
679#if USE_WIDEC_SUPPORT
679#if USE_WIDEC_SUPPORT && NCURSES_EXT_FUNCS
680 if (wresize(field->working, 1, Buffer_Length(field) + 1) == ERR)
681 result = FALSE;
682#endif
683
684 if (need_visual_update && result)
685 {
686 WINDOW *new_window = newpad(field->drows, field->dcols);
687
688 if (new_window != 0)
689 {
690 assert(form != (FORM *)0);
691 if (form->w)
692 delwin(form->w);
693 form->w = new_window;
694 Set_Field_Window_Attributes(field, form->w);
695 werase(form->w);
696 Buffer_To_Window(field, form->w);
697 untouchwin(form->w);
698 wmove(form->w, form->currow, form->curcol);
699 }
700 else
701 result = FALSE;
702 }
703
704 if (result)
705 {
706 free(oldbuf);
707 /* reflect changes in linked fields */
708 if (field != field->link)
709 {
710 FIELD *linked_field;
711
712 for (linked_field = field->link;
713 linked_field != field;
714 linked_field = linked_field->link)
715 {
716 linked_field->buf = field->buf;
717 linked_field->drows = field->drows;
718 linked_field->dcols = field->dcols;
719 }
720 }
721 }
722 else
723 {
724 /* restore old state */
725 field->dcols = old_dcols;
726 field->drows = old_drows;
727 field->buf = oldbuf;
728 if ((single_line_field &&
729 (field->dcols != field->maxgrow)) ||
730 (!single_line_field &&
731 (field->drows != field->maxgrow)))
732 field->status |= _MAY_GROW;
733 free(newbuf);
734 }
735 }
736 }
737 return (result);
738}
739
740#ifdef NCURSES_MOUSE_VERSION
741/*---------------------------------------------------------------------------
742| Facility : libnform
743| Function : int Field_encloses(FIELD *field, int ry, int rx)
744|
745| Description : Check if the given coordinates lie within the given field.
746|
747| Return Values : E_OK - success
748| E_BAD_ARGUMENT - invalid form pointer
749| E_SYSTEM_ERROR - form has no current field or
750| field-window
751+--------------------------------------------------------------------------*/
752static int
753Field_encloses(FIELD *field, int ry, int rx)
754{
755 T((T_CALLED("Field_encloses(%p)"), field));
756 if (field != 0
757 && field->frow <= ry
758 && (field->frow + field->rows) > ry
759 && field->fcol <= rx
760 && (field->fcol + field->cols) > rx)
761 {
762 RETURN(E_OK);
763 }
764 RETURN(E_INVALID_FIELD);
765}
766#endif
767
768/*---------------------------------------------------------------------------
769| Facility : libnform
770| Function : int _nc_Position_Form_Cursor(FORM * form)
771|
772| Description : Position the cursor in the window for the current
773| field to be in sync. with the currow and curcol
774| values.
775|
776| Return Values : E_OK - success
777| E_BAD_ARGUMENT - invalid form pointer
778| E_SYSTEM_ERROR - form has no current field or
779| field-window
780+--------------------------------------------------------------------------*/
781NCURSES_EXPORT(int)
782_nc_Position_Form_Cursor(FORM *form)
783{
784 FIELD *field;
785 WINDOW *formwin;
786
787 if (!form)
788 return (E_BAD_ARGUMENT);
789
790 if (!form->w || !form->current)
791 return (E_SYSTEM_ERROR);
792
793 field = form->current;
794 formwin = Get_Form_Window(form);
795
796 wmove(form->w, form->currow, form->curcol);
797 if (Has_Invisible_Parts(field))
798 {
799 /* in this case fieldwin isn't derived from formwin, so we have
800 to move the cursor in formwin by hand... */
801 wmove(formwin,
802 field->frow + form->currow - form->toprow,
803 field->fcol + form->curcol - form->begincol);
804 wcursyncup(formwin);
805 }
806 else
807 wcursyncup(form->w);
808 return (E_OK);
809}
810
811/*---------------------------------------------------------------------------
812| Facility : libnform
813| Function : int _nc_Refresh_Current_Field(FORM * form)
814|
815| Description : Propagate the changes in the fields window to the
816| window of the form.
817|
818| Return Values : E_OK - on success
819| E_BAD_ARGUMENT - invalid form pointer
820| E_SYSTEM_ERROR - general error
821+--------------------------------------------------------------------------*/
822NCURSES_EXPORT(int)
823_nc_Refresh_Current_Field(FORM *form)
824{
825 WINDOW *formwin;
826 FIELD *field;
827
828 T((T_CALLED("_nc_Refresh_Current_Field(%p)"), form));
829
830 if (!form)
831 RETURN(E_BAD_ARGUMENT);
832
833 if (!form->w || !form->current)
834 RETURN(E_SYSTEM_ERROR);
835
836 field = form->current;
837 formwin = Get_Form_Window(form);
838
839 if (field->opts & O_PUBLIC)
840 {
841 if (Is_Scroll_Field(field))
842 {
843 /* Again, in this case the fieldwin isn't derived from formwin,
844 so we have to perform a copy operation. */
845 if (Single_Line_Field(field))
846 {
847 /* horizontal scrolling */
848 if (form->curcol < form->begincol)
849 form->begincol = form->curcol;
850 else
851 {
852 if (form->curcol >= (form->begincol + field->cols))
853 form->begincol = form->curcol - field->cols + 1;
854 }
855 copywin(form->w,
856 formwin,
857 0,
858 form->begincol,
859 field->frow,
860 field->fcol,
861 field->frow,
862 field->cols + field->fcol - 1,
863 0);
864 }
865 else
866 {
867 /* A multi-line, i.e. vertical scrolling field */
868 int row_after_bottom, first_modified_row, first_unmodified_row;
869
870 if (field->drows > field->rows)
871 {
872 row_after_bottom = form->toprow + field->rows;
873 if (form->currow < form->toprow)
874 {
875 form->toprow = form->currow;
876 field->status |= _NEWTOP;
877 }
878 if (form->currow >= row_after_bottom)
879 {
880 form->toprow = form->currow - field->rows + 1;
881 field->status |= _NEWTOP;
882 }
883 if (field->status & _NEWTOP)
884 {
885 /* means we have to copy whole range */
886 first_modified_row = form->toprow;
887 first_unmodified_row = first_modified_row + field->rows;
888 field->status &= ~_NEWTOP;
889 }
890 else
891 {
892 /* we try to optimize : finding the range of touched
893 lines */
894 first_modified_row = form->toprow;
895 while (first_modified_row < row_after_bottom)
896 {
897 if (is_linetouched(form->w, first_modified_row))
898 break;
899 first_modified_row++;
900 }
901 first_unmodified_row = first_modified_row;
902 while (first_unmodified_row < row_after_bottom)
903 {
904 if (!is_linetouched(form->w, first_unmodified_row))
905 break;
906 first_unmodified_row++;
907 }
908 }
909 }
910 else
911 {
912 first_modified_row = form->toprow;
913 first_unmodified_row = first_modified_row + field->rows;
914 }
915 if (first_unmodified_row != first_modified_row)
916 copywin(form->w,
917 formwin,
918 first_modified_row,
919 0,
920 field->frow + first_modified_row - form->toprow,
921 field->fcol,
922 field->frow + first_unmodified_row - form->toprow - 1,
923 field->cols + field->fcol - 1,
924 0);
925 }
926 wsyncup(formwin);
927 }
928 else
929 {
930 /* if the field-window is simply a derived window, i.e. contains no
931 * invisible parts, the whole thing is trivial
932 */
933 wsyncup(form->w);
934 }
935 }
936 untouchwin(form->w);
937 returnCode(_nc_Position_Form_Cursor(form));
938}
939
940/*---------------------------------------------------------------------------
941| Facility : libnform
942| Function : static void Perform_Justification(
943| FIELD * field,
944| WINDOW * win)
945|
946| Description : Output field with requested justification
947|
948| Return Values : -
949+--------------------------------------------------------------------------*/
950static void
951Perform_Justification(FIELD *field, WINDOW *win)
952{
953 FIELD_CELL *bp;
954 int len;
955 int col = 0;
956
957 bp = Get_Start_Of_Data(field->buf, Buffer_Length(field));
958 len = (int)(After_End_Of_Data(field->buf, Buffer_Length(field)) - bp);
959
960 if (len > 0)
961 {
962 assert(win && (field->drows == 1) && (field->dcols == field->cols));
963
964 switch (field->just)
965 {
966 case JUSTIFY_LEFT:
967 break;
968 case JUSTIFY_CENTER:
969 col = (field->cols - len) / 2;
970 break;
971 case JUSTIFY_RIGHT:
972 col = field->cols - len;
973 break;
974 default:
975 break;
976 }
977
978 wmove(win, 0, col);
979 myADDNSTR(win, bp, len);
980 }
981}
982
983/*---------------------------------------------------------------------------
984| Facility : libnform
985| Function : static void Undo_Justification(
986| FIELD * field,
987| WINDOW * win)
988|
989| Description : Display field without any justification, i.e.
990| left justified
991|
992| Return Values : -
993+--------------------------------------------------------------------------*/
994static void
995Undo_Justification(FIELD *field, WINDOW *win)
996{
997 FIELD_CELL *bp;
998 int len;
999
1000 bp = Get_Start_Of_Data(field->buf, Buffer_Length(field));
1001 len = (int)(After_End_Of_Data(field->buf, Buffer_Length(field)) - bp);
1002
1003 if (len > 0)
1004 {
1005 assert(win);
1006 wmove(win, 0, 0);
1007 myADDNSTR(win, bp, len);
1008 }
1009}
1010
1011/*---------------------------------------------------------------------------
1012| Facility : libnform
1013| Function : static bool Check_Char(
1014| FIELDTYPE * typ,
1015| int ch,
1016| TypeArgument *argp)
1017|
1018| Description : Perform a single character check for character ch
1019| according to the fieldtype instance.
1020|
1021| Return Values : TRUE - Character is valid
1022| FALSE - Character is invalid
1023+--------------------------------------------------------------------------*/
1024static bool
1025Check_Char(FIELDTYPE *typ, int ch, TypeArgument *argp)
1026{
1027 if (typ)
1028 {
1029 if (typ->status & _LINKED_TYPE)
1030 {
1031 assert(argp);
1032 return (
1033 Check_Char(typ->left, ch, argp->left) ||
1034 Check_Char(typ->right, ch, argp->right));
1035 }
1036 else
1037 {
1038 if (typ->ccheck)
1039 return typ->ccheck(ch, (void *)argp);
1040 }
1041 }
1042 return (!iscntrl(UChar(ch)) ? TRUE : FALSE);
1043}
1044
1045/*---------------------------------------------------------------------------
1046| Facility : libnform
1047| Function : static int Display_Or_Erase_Field(
1048| FIELD * field,
1049| bool bEraseFlag)
1050|
1051| Description : Create a subwindow for the field and display the
1052| buffer contents (apply justification if required)
1053| or simply erase the field.
1054|
1055| Return Values : E_OK - on success
1056| E_SYSTEM_ERROR - some error (typical no memory)
1057+--------------------------------------------------------------------------*/
1058static int
1059Display_Or_Erase_Field(FIELD *field, bool bEraseFlag)
1060{
1061 WINDOW *win;
1062 WINDOW *fwin;
1063
1064 if (!field)
1065 return E_SYSTEM_ERROR;
1066
1067 fwin = Get_Form_Window(field->form);
1068 win = derwin(fwin,
1069 field->rows, field->cols, field->frow, field->fcol);
1070
1071 if (!win)
1072 return E_SYSTEM_ERROR;
1073 else
1074 {
1075 if (field->opts & O_VISIBLE)
1076 Set_Field_Window_Attributes(field, win);
1077 else
1078 wattrset(win, WINDOW_ATTRS(fwin));
1079 werase(win);
1080 }
1081
1082 if (!bEraseFlag)
1083 {
1084 if (field->opts & O_PUBLIC)
1085 {
1086 if (Justification_Allowed(field))
1087 Perform_Justification(field, win);
1088 else
1089 Buffer_To_Window(field, win);
1090 }
1091 field->status &= ~_NEWTOP;
1092 }
1093 wsyncup(win);
1094 delwin(win);
1095 return E_OK;
1096}
1097
1098/* Macros to preset the bEraseFlag */
1099#define Display_Field(field) Display_Or_Erase_Field(field,FALSE)
1100#define Erase_Field(field) Display_Or_Erase_Field(field,TRUE)
1101
1102/*---------------------------------------------------------------------------
1103| Facility : libnform
1104| Function : static int Synchronize_Field(FIELD * field)
1105|
1106| Description : Synchronize the windows content with the value in
1107| the buffer.
1108|
1109| Return Values : E_OK - success
1110| E_BAD_ARGUMENT - invalid field pointer
1111| E_SYSTEM_ERROR - some severe basic error
1112+--------------------------------------------------------------------------*/
1113static int
1114Synchronize_Field(FIELD *field)
1115{
1116 FORM *form;
1117 int res = E_OK;
1118
1119 if (!field)
1120 return (E_BAD_ARGUMENT);
1121
1122 if (((form = field->form) != (FORM *)0)
1123 && Field_Really_Appears(field))
1124 {
1125 if (field == form->current)
1126 {
1127 form->currow = form->curcol = form->toprow = form->begincol = 0;
1128 werase(form->w);
1129
1130 if ((field->opts & O_PUBLIC) && Justification_Allowed(field))
1131 Undo_Justification(field, form->w);
1132 else
1133 Buffer_To_Window(field, form->w);
1134
1135 field->status |= _NEWTOP;
1136 res = _nc_Refresh_Current_Field(form);
1137 }
1138 else
1139 res = Display_Field(field);
1140 }
1141 field->status |= _CHANGED;
1142 return (res);
1143}
1144
1145/*---------------------------------------------------------------------------
1146| Facility : libnform
1147| Function : static int Synchronize_Linked_Fields(FIELD * field)
1148|
1149| Description : Propagate the Synchronize_Field function to all linked
1150| fields. The first error that occurs in the sequence
1151| of updates is the return value.
1152|
1153| Return Values : E_OK - success
1154| E_BAD_ARGUMENT - invalid field pointer
1155| E_SYSTEM_ERROR - some severe basic error
1156+--------------------------------------------------------------------------*/
1157static int
1158Synchronize_Linked_Fields(FIELD *field)
1159{
1160 FIELD *linked_field;
1161 int res = E_OK;
1162 int syncres;
1163
1164 if (!field)
1165 return (E_BAD_ARGUMENT);
1166
1167 if (!field->link)
1168 return (E_SYSTEM_ERROR);
1169
1170 for (linked_field = field->link;
1171 linked_field != field;
1172 linked_field = linked_field->link)
1173 {
1174 if (((syncres = Synchronize_Field(linked_field)) != E_OK) &&
1175 (res == E_OK))
1176 res = syncres;
1177 }
1178 return (res);
1179}
1180
1181/*---------------------------------------------------------------------------
1182| Facility : libnform
1183| Function : int _nc_Synchronize_Attributes(FIELD * field)
1184|
1185| Description : If a fields visual attributes have changed, this
1186| routine is called to propagate those changes to the
1187| screen.
1188|
1189| Return Values : E_OK - success
1190| E_BAD_ARGUMENT - invalid field pointer
1191| E_SYSTEM_ERROR - some severe basic error
1192+--------------------------------------------------------------------------*/
1193NCURSES_EXPORT(int)
1194_nc_Synchronize_Attributes(FIELD *field)
1195{
1196 FORM *form;
1197 int res = E_OK;
1198 WINDOW *formwin;
1199
1200 T((T_CALLED("_nc_Synchronize_Attributes(%p)"), field));
1201
1202 if (!field)
1203 returnCode(E_BAD_ARGUMENT);
1204
1205 CHECKPOS(field->form);
1206 if (((form = field->form) != (FORM *)0)
1207 && Field_Really_Appears(field))
1208 {
1209 if (form->current == field)
1210 {
1211 Synchronize_Buffer(form);
1212 Set_Field_Window_Attributes(field, form->w);
1213 werase(form->w);
1214 wmove(form->w, form->currow, form->curcol);
1215
1216 if (field->opts & O_PUBLIC)
1217 {
1218 if (Justification_Allowed(field))
1219 Undo_Justification(field, form->w);
1220 else
1221 Buffer_To_Window(field, form->w);
1222 }
1223 else
1224 {
1225 formwin = Get_Form_Window(form);
1226 copywin(form->w, formwin,
1227 0, 0,
1228 field->frow, field->fcol,
1229 field->rows - 1, field->cols - 1, 0);
1230 wsyncup(formwin);
1231 Buffer_To_Window(field, form->w);
1232 field->status |= _NEWTOP; /* fake refresh to paint all */
1233 _nc_Refresh_Current_Field(form);
1234 }
1235 }
1236 else
1237 {
1238 res = Display_Field(field);
1239 }
1240 }
1241 CHECKPOS(form);
1242 returnCode(res);
1243}
1244
1245/*---------------------------------------------------------------------------
1246| Facility : libnform
1247| Function : int _nc_Synchronize_Options(FIELD * field,
1248| Field_Options newopts)
1249|
1250| Description : If a fields options have changed, this routine is
1251| called to propagate these changes to the screen and
1252| to really change the behavior of the field.
1253|
1254| Return Values : E_OK - success
1255| E_BAD_ARGUMENT - invalid field pointer
1256| E_CURRENT - field is the current one
1257| E_SYSTEM_ERROR - some severe basic error
1258+--------------------------------------------------------------------------*/
1259NCURSES_EXPORT(int)
1260_nc_Synchronize_Options(FIELD *field, Field_Options newopts)
1261{
1262 Field_Options oldopts;
1263 Field_Options changed_opts;
1264 FORM *form;
1265 int res = E_OK;
1266
1267 T((T_CALLED("_nc_Synchronize_Options(%p,%#x)"), field, newopts));
1268
1269 if (!field)
1270 returnCode(E_BAD_ARGUMENT);
1271
1272 oldopts = field->opts;
1273 changed_opts = oldopts ^ newopts;
1274 field->opts = newopts;
1275 form = field->form;
1276
1277 if (form)
1278 {
1279 if (form->current == field)
1280 {
1281 field->opts = oldopts;
1282 returnCode(E_CURRENT);
1283 }
1284
1285 if (form->status & _POSTED)
1286 {
1287 if ((form->curpage == field->page))
1288 {
1289 if (changed_opts & O_VISIBLE)
1290 {
1291 if (newopts & O_VISIBLE)
1292 res = Display_Field(field);
1293 else
1294 res = Erase_Field(field);
1295 }
1296 else
1297 {
1298 if ((changed_opts & O_PUBLIC) &&
1299 (newopts & O_VISIBLE))
1300 res = Display_Field(field);
1301 }
1302 }
1303 }
1304 }
1305
1306 if (changed_opts & O_STATIC)
1307 {
1308 bool single_line_field = Single_Line_Field(field);
1309 int res2 = E_OK;
1310
1311 if (newopts & O_STATIC)
1312 {
1313 /* the field becomes now static */
1314 field->status &= ~_MAY_GROW;
1315 /* if actually we have no hidden columns, justification may
1316 occur again */
1317 if (single_line_field &&
1318 (field->cols == field->dcols) &&
1319 (field->just != NO_JUSTIFICATION) &&
1320 Field_Really_Appears(field))
1321 {
1322 res2 = Display_Field(field);
1323 }
1324 }
1325 else
1326 {
1327 /* field is no longer static */
1328 if ((field->maxgrow == 0) ||
1329 (single_line_field && (field->dcols < field->maxgrow)) ||
1330 (!single_line_field && (field->drows < field->maxgrow)))
1331 {
1332 field->status |= _MAY_GROW;
1333 /* a field with justification now changes its behavior,
1334 so we must redisplay it */
1335 if (single_line_field &&
1336 (field->just != NO_JUSTIFICATION) &&
1337 Field_Really_Appears(field))
1338 {
1339 res2 = Display_Field(field);
1340 }
1341 }
1342 }
1343 if (res2 != E_OK)
1344 res = res2;
1345 }
1346
1347 returnCode(res);
1348}
1349
1350/*---------------------------------------------------------------------------
1351| Facility : libnform
1352| Function : int _nc_Set_Current_Field(FORM * form,
1353| FIELD * newfield)
1354|
1355| Description : Make the newfield the new current field.
1356|
1357| Return Values : E_OK - success
1358| E_BAD_ARGUMENT - invalid form or field pointer
1359| E_SYSTEM_ERROR - some severe basic error
1360| E_NOT_CONNECTED - no fields are connected to the form
1361+--------------------------------------------------------------------------*/
1362NCURSES_EXPORT(int)
1363_nc_Set_Current_Field(FORM *form, FIELD *newfield)
1364{
1365 FIELD *field;
1366 WINDOW *new_window;
1367
1368 T((T_CALLED("_nc_Set_Current_Field(%p,%p)"), form, newfield));
1369
1370 if (!form || !newfield || !form->current || (newfield->form != form))
1371 returnCode(E_BAD_ARGUMENT);
1372
1373 if ((form->status & _IN_DRIVER))
1374 returnCode(E_BAD_STATE);
1375
1376 if (!(form->field))
1377 returnCode(E_NOT_CONNECTED);
1378
1379 field = form->current;
1380
1381 if ((field != newfield) ||
1382 !(form->status & _POSTED))
1383 {
1384 if ((form->w) &&
1385 (field->opts & O_VISIBLE) &&
1386 (field->form->curpage == field->page))
1387 {
1388 _nc_Refresh_Current_Field(form);
1389 if (field->opts & O_PUBLIC)
1390 {
1391 if (field->drows > field->rows)
1392 {
1393 if (form->toprow == 0)
1394 field->status &= ~_NEWTOP;
1395 else
1396 field->status |= _NEWTOP;
1397 }
1398 else
1399 {
1400 if (Justification_Allowed(field))
1401 {
1402 Window_To_Buffer(form->w, field);
1403 werase(form->w);
1404 Perform_Justification(field, form->w);
1405 wsyncup(form->w);
1406 }
1407 }
1408 }
1409 delwin(form->w);
1410 form->w = (WINDOW *)0;
1411 }
1412
1413 field = newfield;
1414
1415 if (Has_Invisible_Parts(field))
1416 new_window = newpad(field->drows, field->dcols);
1417 else
1418 new_window = derwin(Get_Form_Window(form),
1419 field->rows, field->cols, field->frow, field->fcol);
1420
1421 if (!new_window)
1422 returnCode(E_SYSTEM_ERROR);
1423
1424 form->current = field;
1425
1426 if (form->w)
1427 delwin(form->w);
1428 form->w = new_window;
1429
1430 form->status &= ~_WINDOW_MODIFIED;
1431 Set_Field_Window_Attributes(field, form->w);
1432
1433 if (Has_Invisible_Parts(field))
1434 {
1435 werase(form->w);
1436 Buffer_To_Window(field, form->w);
1437 }
1438 else
1439 {
1440 if (Justification_Allowed(field))
1441 {
1442 werase(form->w);
1443 Undo_Justification(field, form->w);
1444 wsyncup(form->w);
1445 }
1446 }
1447
1448 untouchwin(form->w);
1449 }
1450
1451 form->currow = form->curcol = form->toprow = form->begincol = 0;
1452 returnCode(E_OK);
1453}
1454
1455/*----------------------------------------------------------------------------
1456 Intra-Field Navigation routines
1457 --------------------------------------------------------------------------*/
1458
1459/*---------------------------------------------------------------------------
1460| Facility : libnform
1461| Function : static int IFN_Next_Character(FORM * form)
1462|
1463| Description : Move to the next character in the field. In a multi-line
1464| field this wraps at the end of the line.
1465|
1466| Return Values : E_OK - success
1467| E_REQUEST_DENIED - at the rightmost position
1468+--------------------------------------------------------------------------*/
1469static int
1470IFN_Next_Character(FORM *form)
1471{
1472 FIELD *field = form->current;
1473 int step = myWCWIDTH(form->w, form->currow, form->curcol);
1474
1475 T((T_CALLED("IFN_Next_Character(%p)"), form));
1476 if ((form->curcol += step) == field->dcols)
1477 {
1478 if ((++(form->currow)) == field->drows)
1479 {
1480#if GROW_IF_NAVIGATE
1481 if (!Single_Line_Field(field) && Field_Grown(field, 1))
1482 {
1483 form->curcol = 0;
1484 returnCode(E_OK);
1485 }
1486#endif
1487 form->currow--;
1488#if GROW_IF_NAVIGATE
1489 if (Single_Line_Field(field) && Field_Grown(field, 1))
1490 returnCode(E_OK);
1491#endif
1492 form->curcol -= step;
1493 returnCode(E_REQUEST_DENIED);
1494 }
1495 form->curcol = 0;
1496 }
1497 returnCode(E_OK);
1498}
1499
1500/*---------------------------------------------------------------------------
1501| Facility : libnform
1502| Function : static int IFN_Previous_Character(FORM * form)
1503|
1504| Description : Move to the previous character in the field. In a
1505| multi-line field this wraps and the beginning of the
1506| line.
1507|
1508| Return Values : E_OK - success
1509| E_REQUEST_DENIED - at the leftmost position
1510+--------------------------------------------------------------------------*/
1511static int
1512IFN_Previous_Character(FORM *form)
1513{
1514 int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1);
1515 int oldcol = form->curcol;
1516
1517 T((T_CALLED("IFN_Previous_Character(%p)"), form));
1518 if ((form->curcol -= amount) < 0)
1519 {
1520 if ((--(form->currow)) < 0)
1521 {
1522 form->currow++;
1523 form->curcol = oldcol;
1524 returnCode(E_REQUEST_DENIED);
1525 }
1526 form->curcol = form->current->dcols - 1;
1527 }
1528 returnCode(E_OK);
1529}
1530
1531/*---------------------------------------------------------------------------
1532| Facility : libnform
1533| Function : static int IFN_Next_Line(FORM * form)
1534|
1535| Description : Move to the beginning of the next line in the field
1536|
1537| Return Values : E_OK - success
1538| E_REQUEST_DENIED - at the last line
1539+--------------------------------------------------------------------------*/
1540static int
1541IFN_Next_Line(FORM *form)
1542{
1543 FIELD *field = form->current;
1544
1545 T((T_CALLED("IFN_Next_Line(%p)"), form));
1546 if ((++(form->currow)) == field->drows)
1547 {
1548#if GROW_IF_NAVIGATE
1549 if (!Single_Line_Field(field) && Field_Grown(field, 1))
1550 returnCode(E_OK);
1551#endif
1552 form->currow--;
1553 returnCode(E_REQUEST_DENIED);
1554 }
1555 form->curcol = 0;
1556 returnCode(E_OK);
1557}
1558
1559/*---------------------------------------------------------------------------
1560| Facility : libnform
1561| Function : static int IFN_Previous_Line(FORM * form)
1562|
1563| Description : Move to the beginning of the previous line in the field
1564|
1565| Return Values : E_OK - success
1566| E_REQUEST_DENIED - at the first line
1567+--------------------------------------------------------------------------*/
1568static int
1569IFN_Previous_Line(FORM *form)
1570{
1571 T((T_CALLED("IFN_Previous_Line(%p)"), form));
1572 if ((--(form->currow)) < 0)
1573 {
1574 form->currow++;
1575 returnCode(E_REQUEST_DENIED);
1576 }
1577 form->curcol = 0;
1578 returnCode(E_OK);
1579}
1580
1581/*---------------------------------------------------------------------------
1582| Facility : libnform
1583| Function : static int IFN_Next_Word(FORM * form)
1584|
1585| Description : Move to the beginning of the next word in the field.
1586|
1587| Return Values : E_OK - success
1588| E_REQUEST_DENIED - there is no next word
1589+--------------------------------------------------------------------------*/
1590static int
1591IFN_Next_Word(FORM *form)
1592{
1593 FIELD *field = form->current;
1594 FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1595 FIELD_CELL *s;
1596 FIELD_CELL *t;
1597
1598 T((T_CALLED("IFN_Next_Word(%p)"), form));
1599
1600 /* We really need access to the data, so we have to synchronize */
1601 Synchronize_Buffer(form);
1602
1603 /* Go to the first whitespace after the current position (including
1604 current position). This is then the starting point to look for the
1605 next non-blank data */
1606 s = Get_First_Whitespace_Character(bp, Buffer_Length(field) -
1607 (int)(bp - field->buf));
1608
1609 /* Find the start of the next word */
1610 t = Get_Start_Of_Data(s, Buffer_Length(field) -
1611 (int)(s - field->buf));
1612#if !FRIENDLY_PREV_NEXT_WORD
1613 if (s == t)
1614 returnCode(E_REQUEST_DENIED);
1615 else
1616#endif
1617 {
1618 Adjust_Cursor_Position(form, t);
1619 returnCode(E_OK);
1620 }
1621}
1622
1623/*---------------------------------------------------------------------------
1624| Facility : libnform
1625| Function : static int IFN_Previous_Word(FORM * form)
1626|
1627| Description : Move to the beginning of the previous word in the field.
1628|
1629| Return Values : E_OK - success
1630| E_REQUEST_DENIED - there is no previous word
1631+--------------------------------------------------------------------------*/
1632static int
1633IFN_Previous_Word(FORM *form)
1634{
1635 FIELD *field = form->current;
1636 FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1637 FIELD_CELL *s;
1638 FIELD_CELL *t;
1639 bool again = FALSE;
1640
1641 T((T_CALLED("IFN_Previous_Word(%p)"), form));
1642
1643 /* We really need access to the data, so we have to synchronize */
1644 Synchronize_Buffer(form);
1645
1646 s = After_End_Of_Data(field->buf, (int)(bp - field->buf));
1647 /* s points now right after the last non-blank in the buffer before bp.
1648 If bp was in a word, s equals bp. In this case we must find the last
1649 whitespace in the buffer before bp and repeat the game to really find
1650 the previous word! */
1651 if (s == bp)
1652 again = TRUE;
1653
1654 /* And next call now goes backward to look for the last whitespace
1655 before that, pointing right after this, so it points to the begin
1656 of the previous word.
1657 */
1658 t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf));
1659#if !FRIENDLY_PREV_NEXT_WORD
1660 if (s == t)
1661 returnCode(E_REQUEST_DENIED);
1662#endif
1663 if (again)
1664 {
1665 /* and do it again, replacing bp by t */
1666 s = After_End_Of_Data(field->buf, (int)(t - field->buf));
1667 t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf));
1668#if !FRIENDLY_PREV_NEXT_WORD
1669 if (s == t)
1670 returnCode(E_REQUEST_DENIED);
1671#endif
1672 }
1673 Adjust_Cursor_Position(form, t);
1674 returnCode(E_OK);
1675}
1676
1677/*---------------------------------------------------------------------------
1678| Facility : libnform
1679| Function : static int IFN_Beginning_Of_Field(FORM * form)
1680|
1681| Description : Place the cursor at the first non-pad character in
1682| the field.
1683|
1684| Return Values : E_OK - success
1685+--------------------------------------------------------------------------*/
1686static int
1687IFN_Beginning_Of_Field(FORM *form)
1688{
1689 FIELD *field = form->current;
1690
1691 T((T_CALLED("IFN_Beginning_Of_Field(%p)"), form));
1692 Synchronize_Buffer(form);
1693 Adjust_Cursor_Position(form,
1694 Get_Start_Of_Data(field->buf, Buffer_Length(field)));
1695 returnCode(E_OK);
1696}
1697
1698/*---------------------------------------------------------------------------
1699| Facility : libnform
1700| Function : static int IFN_End_Of_Field(FORM * form)
1701|
1702| Description : Place the cursor after the last non-pad character in
1703| the field. If the field occupies the last position in
1704| the buffer, the cursor is positioned on the last
1705| character.
1706|
1707| Return Values : E_OK - success
1708+--------------------------------------------------------------------------*/
1709static int
1710IFN_End_Of_Field(FORM *form)
1711{
1712 FIELD *field = form->current;
1713 FIELD_CELL *pos;
1714
1715 T((T_CALLED("IFN_End_Of_Field(%p)"), form));
1716 Synchronize_Buffer(form);
1717 pos = After_End_Of_Data(field->buf, Buffer_Length(field));
1718 if (pos == (field->buf + Buffer_Length(field)))
1719 pos--;
1720 Adjust_Cursor_Position(form, pos);
1721 returnCode(E_OK);
1722}
1723
1724/*---------------------------------------------------------------------------
1725| Facility : libnform
1726| Function : static int IFN_Beginning_Of_Line(FORM * form)
1727|
1728| Description : Place the cursor on the first non-pad character in
1729| the current line of the field.
1730|
1731| Return Values : E_OK - success
1732+--------------------------------------------------------------------------*/
1733static int
1734IFN_Beginning_Of_Line(FORM *form)
1735{
1736 FIELD *field = form->current;
1737
1738 T((T_CALLED("IFN_Beginning_Of_Line(%p)"), form));
1739 Synchronize_Buffer(form);
1740 Adjust_Cursor_Position(form,
1741 Get_Start_Of_Data(Address_Of_Current_Row_In_Buffer(form),
1742 field->dcols));
1743 returnCode(E_OK);
1744}
1745
1746/*---------------------------------------------------------------------------
1747| Facility : libnform
1748| Function : static int IFN_End_Of_Line(FORM * form)
1749|
1750| Description : Place the cursor after the last non-pad character in the
1751| current line of the field. If the field occupies the
1752| last column in the line, the cursor is positioned on the
1753| last character of the line.
1754|
1755| Return Values : E_OK - success
1756+--------------------------------------------------------------------------*/
1757static int
1758IFN_End_Of_Line(FORM *form)
1759{
1760 FIELD *field = form->current;
1761 FIELD_CELL *pos;
1762 FIELD_CELL *bp;
1763
1764 T((T_CALLED("IFN_End_Of_Line(%p)"), form));
1765 Synchronize_Buffer(form);
1766 bp = Address_Of_Current_Row_In_Buffer(form);
1767 pos = After_End_Of_Data(bp, field->dcols);
1768 if (pos == (bp + field->dcols))
1769 pos--;
1770 Adjust_Cursor_Position(form, pos);
1771 returnCode(E_OK);
1772}
1773
1774/*---------------------------------------------------------------------------
1775| Facility : libnform
1776| Function : static int IFN_Left_Character(FORM * form)
1777|
1778| Description : Move one character to the left in the current line.
1779| This doesn't cycle.
1780|
1781| Return Values : E_OK - success
1782| E_REQUEST_DENIED - already in first column
1783+--------------------------------------------------------------------------*/
1784static int
1785IFN_Left_Character(FORM *form)
1786{
1787 int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1);
1788 int oldcol = form->curcol;
1789
1790 T((T_CALLED("IFN_Left_Character(%p)"), form));
1791 if ((form->curcol -= amount) < 0)
1792 {
1793 form->curcol = oldcol;
1794 returnCode(E_REQUEST_DENIED);
1795 }
1796 returnCode(E_OK);
1797}
1798
1799/*---------------------------------------------------------------------------
1800| Facility : libnform
1801| Function : static int IFN_Right_Character(FORM * form)
1802|
1803| Description : Move one character to the right in the current line.
1804| This doesn't cycle.
1805|
1806| Return Values : E_OK - success
1807| E_REQUEST_DENIED - already in last column
1808+--------------------------------------------------------------------------*/
1809static int
1810IFN_Right_Character(FORM *form)
1811{
1812 int amount = myWCWIDTH(form->w, form->currow, form->curcol);
1813 int oldcol = form->curcol;
1814
1815 T((T_CALLED("IFN_Right_Character(%p)"), form));
1816 if ((form->curcol += amount) >= form->current->dcols)
1817 {
1818#if GROW_IF_NAVIGATE
1819 FIELD *field = form->current;
1820
1821 if (Single_Line_Field(field) && Field_Grown(field, 1))
1822 returnCode(E_OK);
1823#endif
1824 form->curcol = oldcol;
1825 returnCode(E_REQUEST_DENIED);
1826 }
1827 returnCode(E_OK);
1828}
1829
1830/*---------------------------------------------------------------------------
1831| Facility : libnform
1832| Function : static int IFN_Up_Character(FORM * form)
1833|
1834| Description : Move one line up. This doesn't cycle through the lines
1835| of the field.
1836|
1837| Return Values : E_OK - success
1838| E_REQUEST_DENIED - already in last column
1839+--------------------------------------------------------------------------*/
1840static int
1841IFN_Up_Character(FORM *form)
1842{
1843 T((T_CALLED("IFN_Up_Character(%p)"), form));
1844 if ((--(form->currow)) < 0)
1845 {
1846 form->currow++;
1847 returnCode(E_REQUEST_DENIED);
1848 }
1849 returnCode(E_OK);
1850}
1851
1852/*---------------------------------------------------------------------------
1853| Facility : libnform
1854| Function : static int IFN_Down_Character(FORM * form)
1855|
1856| Description : Move one line down. This doesn't cycle through the
1857| lines of the field.
1858|
1859| Return Values : E_OK - success
1860| E_REQUEST_DENIED - already in last column
1861+--------------------------------------------------------------------------*/
1862static int
1863IFN_Down_Character(FORM *form)
1864{
1865 FIELD *field = form->current;
1866
1867 T((T_CALLED("IFN_Down_Character(%p)"), form));
1868 if ((++(form->currow)) == field->drows)
1869 {
1870#if GROW_IF_NAVIGATE
1871 if (!Single_Line_Field(field) && Field_Grown(field, 1))
1872 returnCode(E_OK);
1873#endif
1874 --(form->currow);
1875 returnCode(E_REQUEST_DENIED);
1876 }
1877 returnCode(E_OK);
1878}
1879/*----------------------------------------------------------------------------
1880 END of Intra-Field Navigation routines
1881 --------------------------------------------------------------------------*/
1882
1883/*----------------------------------------------------------------------------
1884 Vertical scrolling helper routines
1885 --------------------------------------------------------------------------*/
1886
1887/*---------------------------------------------------------------------------
1888| Facility : libnform
1889| Function : static int VSC_Generic(FORM *form, int nlines)
1890|
1891| Description : Scroll multi-line field forward (nlines>0) or
1892| backward (nlines<0) this many lines.
1893|
1894| Return Values : E_OK - success
1895| E_REQUEST_DENIED - can't scroll
1896+--------------------------------------------------------------------------*/
1897static int
1898VSC_Generic(FORM *form, int nlines)
1899{
1900 FIELD *field = form->current;
1901 int res = E_REQUEST_DENIED;
1902 int rows_to_go = (nlines > 0 ? nlines : -nlines);
1903
1904 if (nlines > 0)
1905 {
1906 if ((rows_to_go + form->toprow) > (field->drows - field->rows))
1907 rows_to_go = (field->drows - field->rows - form->toprow);
1908
1909 if (rows_to_go > 0)
1910 {
1911 form->currow += rows_to_go;
1912 form->toprow += rows_to_go;
1913 res = E_OK;
1914 }
1915 }
1916 else
1917 {
1918 if (rows_to_go > form->toprow)
1919 rows_to_go = form->toprow;
1920
1921 if (rows_to_go > 0)
1922 {
1923 form->currow -= rows_to_go;
1924 form->toprow -= rows_to_go;
1925 res = E_OK;
1926 }
1927 }
1928 return (res);
1929}
1930/*----------------------------------------------------------------------------
1931 End of Vertical scrolling helper routines
1932 --------------------------------------------------------------------------*/
1933
1934/*----------------------------------------------------------------------------
1935 Vertical scrolling routines
1936 --------------------------------------------------------------------------*/
1937
1938/*---------------------------------------------------------------------------
1939| Facility : libnform
1940| Function : static int Vertical_Scrolling(
1941| int (* const fct) (FORM *),
1942| FORM * form)
1943|
1944| Description : Performs the generic vertical scrolling routines.
1945| This has to check for a multi-line field and to set
1946| the _NEWTOP flag if scrolling really occurred.
1947|
1948| Return Values : Propagated error code from low-level driver calls
1949+--------------------------------------------------------------------------*/
1950static int
1951Vertical_Scrolling(int (*const fct) (FORM *), FORM *form)
1952{
1953 int res = E_REQUEST_DENIED;
1954
1955 if (!Single_Line_Field(form->current))
1956 {
1957 res = fct(form);
1958 if (res == E_OK)
1959 form->current->status |= _NEWTOP;
1960 }
1961 return (res);
1962}
1963
1964/*---------------------------------------------------------------------------
1965| Facility : libnform
1966| Function : static int VSC_Scroll_Line_Forward(FORM * form)
1967|
1968| Description : Scroll multi-line field forward a line
1969|
1970| Return Values : E_OK - success
1971| E_REQUEST_DENIED - no data ahead
1972+--------------------------------------------------------------------------*/
1973static int
1974VSC_Scroll_Line_Forward(FORM *form)
1975{
1976 T((T_CALLED("VSC_Scroll_Line_Forward(%p)"), form));
1977 returnCode(VSC_Generic(form, 1));
1978}
1979
1980/*---------------------------------------------------------------------------
1981| Facility : libnform
1982| Function : static int VSC_Scroll_Line_Backward(FORM * form)
1983|
1984| Description : Scroll multi-line field backward a line
1985|
1986| Return Values : E_OK - success
1987| E_REQUEST_DENIED - no data behind
1988+--------------------------------------------------------------------------*/
1989static int
1990VSC_Scroll_Line_Backward(FORM *form)
1991{
1992 T((T_CALLED("VSC_Scroll_Line_Backward(%p)"), form));
1993 returnCode(VSC_Generic(form, -1));
1994}
1995
1996/*---------------------------------------------------------------------------
1997| Facility : libnform
1998| Function : static int VSC_Scroll_Page_Forward(FORM * form)
1999|
2000| Description : Scroll a multi-line field forward a page
2001|
2002| Return Values : E_OK - success
2003| E_REQUEST_DENIED - no data ahead
2004+--------------------------------------------------------------------------*/
2005static int
2006VSC_Scroll_Page_Forward(FORM *form)
2007{
2008 T((T_CALLED("VSC_Scroll_Page_Forward(%p)"), form));
2009 returnCode(VSC_Generic(form, form->current->rows));
2010}
2011
2012/*---------------------------------------------------------------------------
2013| Facility : libnform
2014| Function : static int VSC_Scroll_Half_Page_Forward(FORM * form)
2015|
2016| Description : Scroll a multi-line field forward half a page
2017|
2018| Return Values : E_OK - success
2019| E_REQUEST_DENIED - no data ahead
2020+--------------------------------------------------------------------------*/
2021static int
2022VSC_Scroll_Half_Page_Forward(FORM *form)
2023{
2024 T((T_CALLED("VSC_Scroll_Half_Page_Forward(%p)"), form));
2025 returnCode(VSC_Generic(form, (form->current->rows + 1) / 2));
2026}
2027
2028/*---------------------------------------------------------------------------
2029| Facility : libnform
2030| Function : static int VSC_Scroll_Page_Backward(FORM * form)
2031|
2032| Description : Scroll a multi-line field backward a page
2033|
2034| Return Values : E_OK - success
2035| E_REQUEST_DENIED - no data behind
2036+--------------------------------------------------------------------------*/
2037static int
2038VSC_Scroll_Page_Backward(FORM *form)
2039{
2040 T((T_CALLED("VSC_Scroll_Page_Backward(%p)"), form));
2041 returnCode(VSC_Generic(form, -(form->current->rows)));
2042}
2043
2044/*---------------------------------------------------------------------------
2045| Facility : libnform
2046| Function : static int VSC_Scroll_Half_Page_Backward(FORM * form)
2047|
2048| Description : Scroll a multi-line field backward half a page
2049|
2050| Return Values : E_OK - success
2051| E_REQUEST_DENIED - no data behind
2052+--------------------------------------------------------------------------*/
2053static int
2054VSC_Scroll_Half_Page_Backward(FORM *form)
2055{
2056 T((T_CALLED("VSC_Scroll_Half_Page_Backward(%p)"), form));
2057 returnCode(VSC_Generic(form, -((form->current->rows + 1) / 2)));
2058}
2059/*----------------------------------------------------------------------------
2060 End of Vertical scrolling routines
2061 --------------------------------------------------------------------------*/
2062
2063/*----------------------------------------------------------------------------
2064 Horizontal scrolling helper routines
2065 --------------------------------------------------------------------------*/
2066
2067/*---------------------------------------------------------------------------
2068| Facility : libnform
2069| Function : static int HSC_Generic(FORM *form, int ncolumns)
2070|
2071| Description : Scroll single-line field forward (ncolumns>0) or
2072| backward (ncolumns<0) this many columns.
2073|
2074| Return Values : E_OK - success
2075| E_REQUEST_DENIED - can't scroll
2076+--------------------------------------------------------------------------*/
2077static int
2078HSC_Generic(FORM *form, int ncolumns)
2079{
2080 FIELD *field = form->current;
2081 int res = E_REQUEST_DENIED;
2082 int cols_to_go = (ncolumns > 0 ? ncolumns : -ncolumns);
2083
2084 if (ncolumns > 0)
2085 {
2086 if ((cols_to_go + form->begincol) > (field->dcols - field->cols))
2087 cols_to_go = field->dcols - field->cols - form->begincol;
2088
2089 if (cols_to_go > 0)
2090 {
2091 form->curcol += cols_to_go;
2092 form->begincol += cols_to_go;
2093 res = E_OK;
2094 }
2095 }
2096 else
2097 {
2098 if (cols_to_go > form->begincol)
2099 cols_to_go = form->begincol;
2100
2101 if (cols_to_go > 0)
2102 {
2103 form->curcol -= cols_to_go;
2104 form->begincol -= cols_to_go;
2105 res = E_OK;
2106 }
2107 }
2108 return (res);
2109}
2110/*----------------------------------------------------------------------------
2111 End of Horizontal scrolling helper routines
2112 --------------------------------------------------------------------------*/
2113
2114/*----------------------------------------------------------------------------
2115 Horizontal scrolling routines
2116 --------------------------------------------------------------------------*/
2117
2118/*---------------------------------------------------------------------------
2119| Facility : libnform
2120| Function : static int Horizontal_Scrolling(
2121| int (* const fct) (FORM *),
2122| FORM * form)
2123|
2124| Description : Performs the generic horizontal scrolling routines.
2125| This has to check for a single-line field.
2126|
2127| Return Values : Propagated error code from low-level driver calls
2128+--------------------------------------------------------------------------*/
2129static int
2130Horizontal_Scrolling(int (*const fct) (FORM *), FORM *form)
2131{
2132 if (Single_Line_Field(form->current))
2133 return fct(form);
2134 else
2135 return (E_REQUEST_DENIED);
2136}
2137
2138/*---------------------------------------------------------------------------
2139| Facility : libnform
2140| Function : static int HSC_Scroll_Char_Forward(FORM * form)
2141|
2142| Description : Scroll single-line field forward a character
2143|
2144| Return Values : E_OK - success
2145| E_REQUEST_DENIED - no data ahead
2146+--------------------------------------------------------------------------*/
2147static int
2148HSC_Scroll_Char_Forward(FORM *form)
2149{
2150 T((T_CALLED("HSC_Scroll_Char_Forward(%p)"), form));
2151 returnCode(HSC_Generic(form, 1));
2152}
2153
2154/*---------------------------------------------------------------------------
2155| Facility : libnform
2156| Function : static int HSC_Scroll_Char_Backward(FORM * form)
2157|
2158| Description : Scroll single-line field backward a character
2159|
2160| Return Values : E_OK - success
2161| E_REQUEST_DENIED - no data behind
2162+--------------------------------------------------------------------------*/
2163static int
2164HSC_Scroll_Char_Backward(FORM *form)
2165{
2166 T((T_CALLED("HSC_Scroll_Char_Backward(%p)"), form));
2167 returnCode(HSC_Generic(form, -1));
2168}
2169
2170/*---------------------------------------------------------------------------
2171| Facility : libnform
2172| Function : static int HSC_Horizontal_Line_Forward(FORM* form)
2173|
2174| Description : Scroll single-line field forward a line
2175|
2176| Return Values : E_OK - success
2177| E_REQUEST_DENIED - no data ahead
2178+--------------------------------------------------------------------------*/
2179static int
2180HSC_Horizontal_Line_Forward(FORM *form)
2181{
2182 T((T_CALLED("HSC_Horizontal_Line_Forward(%p)"), form));
2183 returnCode(HSC_Generic(form, form->current->cols));
2184}
2185
2186/*---------------------------------------------------------------------------
2187| Facility : libnform
2188| Function : static int HSC_Horizontal_Half_Line_Forward(FORM* form)
2189|
2190| Description : Scroll single-line field forward half a line
2191|
2192| Return Values : E_OK - success
2193| E_REQUEST_DENIED - no data ahead
2194+--------------------------------------------------------------------------*/
2195static int
2196HSC_Horizontal_Half_Line_Forward(FORM *form)
2197{
2198 T((T_CALLED("HSC_Horizontal_Half_Line_Forward(%p)"), form));
2199 returnCode(HSC_Generic(form, (form->current->cols + 1) / 2));
2200}
2201
2202/*---------------------------------------------------------------------------
2203| Facility : libnform
2204| Function : static int HSC_Horizontal_Line_Backward(FORM* form)
2205|
2206| Description : Scroll single-line field backward a line
2207|
2208| Return Values : E_OK - success
2209| E_REQUEST_DENIED - no data behind
2210+--------------------------------------------------------------------------*/
2211static int
2212HSC_Horizontal_Line_Backward(FORM *form)
2213{
2214 T((T_CALLED("HSC_Horizontal_Line_Backward(%p)"), form));
2215 returnCode(HSC_Generic(form, -(form->current->cols)));
2216}
2217
2218/*---------------------------------------------------------------------------
2219| Facility : libnform
2220| Function : static int HSC_Horizontal_Half_Line_Backward(FORM* form)
2221|
2222| Description : Scroll single-line field backward half a line
2223|
2224| Return Values : E_OK - success
2225| E_REQUEST_DENIED - no data behind
2226+--------------------------------------------------------------------------*/
2227static int
2228HSC_Horizontal_Half_Line_Backward(FORM *form)
2229{
2230 T((T_CALLED("HSC_Horizontal_Half_Line_Backward(%p)"), form));
2231 returnCode(HSC_Generic(form, -((form->current->cols + 1) / 2)));
2232}
2233
2234/*----------------------------------------------------------------------------
2235 End of Horizontal scrolling routines
2236 --------------------------------------------------------------------------*/
2237
2238/*----------------------------------------------------------------------------
2239 Helper routines for Field Editing
2240 --------------------------------------------------------------------------*/
2241
2242/*---------------------------------------------------------------------------
2243| Facility : libnform
2244| Function : static bool Is_There_Room_For_A_Line(FORM * form)
2245|
2246| Description : Check whether or not there is enough room in the
2247| buffer to enter a whole line.
2248|
2249| Return Values : TRUE - there is enough space
2250| FALSE - there is not enough space
2251+--------------------------------------------------------------------------*/
2252NCURSES_INLINE static bool
2253Is_There_Room_For_A_Line(FORM *form)
2254{
2255 FIELD *field = form->current;
2256 FIELD_CELL *begin_of_last_line, *s;
2257
2258 Synchronize_Buffer(form);
2259 begin_of_last_line = Address_Of_Row_In_Buffer(field, (field->drows - 1));
2260 s = After_End_Of_Data(begin_of_last_line, field->dcols);
2261 return ((s == begin_of_last_line) ? TRUE : FALSE);
2262}
2263
2264/*---------------------------------------------------------------------------
2265| Facility : libnform
2266| Function : static bool Is_There_Room_For_A_Char_In_Line(FORM * form)
2267|
2268| Description : Checks whether or not there is room for a new character
2269| in the current line.
2270|
2271| Return Values : TRUE - there is room
2272| FALSE - there is not enough room (line full)
2273+--------------------------------------------------------------------------*/
2274NCURSES_INLINE static bool
2275Is_There_Room_For_A_Char_In_Line(FORM *form)
2276{
2277 int last_char_in_line;
2278
2279 wmove(form->w, form->currow, form->current->dcols - 1);
2280 last_char_in_line = (int)(winch(form->w) & A_CHARTEXT);
2281 wmove(form->w, form->currow, form->curcol);
2282 return (((last_char_in_line == form->current->pad) ||
2283 is_blank(last_char_in_line)) ? TRUE : FALSE);
2284}
2285
2286#define There_Is_No_Room_For_A_Char_In_Line(f) \
2287 !Is_There_Room_For_A_Char_In_Line(f)
2288
2289/*---------------------------------------------------------------------------
2290| Facility : libnform
2291| Function : static int Insert_String(
2292| FORM * form,
2293| int row,
2294| char *txt,
2295| int len )
2296|
2297| Description : Insert the 'len' characters beginning at pointer 'txt'
2298| into the 'row' of the 'form'. The insertion occurs
2299| on the beginning of the row, all other characters are
2300| moved to the right. After the text a pad character will
2301| be inserted to separate the text from the rest. If
2302| necessary the insertion moves characters on the next
2303| line to make place for the requested insertion string.
2304|
2305| Return Values : E_OK - success
2306| E_REQUEST_DENIED -
2307| E_SYSTEM_ERROR - system error
2308+--------------------------------------------------------------------------*/
2309static int
2310Insert_String(FORM *form, int row, FIELD_CELL *txt, int len)
2311{
2312 FIELD *field = form->current;
2313 FIELD_CELL *bp = Address_Of_Row_In_Buffer(field, row);
2314 int datalen = (int)(After_End_Of_Data(bp, field->dcols) - bp);
2315 int freelen = field->dcols - datalen;
2316 int requiredlen = len + 1;
2317 FIELD_CELL *split;
2318 int result = E_REQUEST_DENIED;
2319
2320 if (freelen >= requiredlen)
2321 {
2322 wmove(form->w, row, 0);
2323 myINSNSTR(form->w, txt, len);
2324 wmove(form->w, row, len);
2325 myINSNSTR(form->w, &myBLANK, 1);
2326 return E_OK;
2327 }
2328 else
2329 {
2330 /* we have to move characters on the next line. If we are on the
2331 last line this may work, if the field is growable */
2332 if ((row == (field->drows - 1)) && Growable(field))
2333 {
2334 if (!Field_Grown(field, 1))
2335 return (E_SYSTEM_ERROR);
2336 /* !!!Side-Effect : might be changed due to growth!!! */
2337 bp = Address_Of_Row_In_Buffer(field, row);
2338 }
2339
2340 if (row < (field->drows - 1))
2341 {
2342 split =
2343 After_Last_Whitespace_Character(bp,
2344 (int)(Get_Start_Of_Data(bp
2345 + field->dcols
2346 - requiredlen,
2347 requiredlen)
2348 - bp));
2349 /* split points now to the first character of the portion of the
2350 line that must be moved to the next line */
2351 datalen = (int)(split - bp); /* + freelen has to stay on this line */
2352 freelen = field->dcols - (datalen + freelen); /* for the next line */
2353
2354 if ((result = Insert_String(form, row + 1, split, freelen)) == E_OK)
2355 {
2356 wmove(form->w, row, datalen);
2357 wclrtoeol(form->w);
2358 wmove(form->w, row, 0);
2359 myINSNSTR(form->w, txt, len);
2360 wmove(form->w, row, len);
2361 myINSNSTR(form->w, &myBLANK, 1);
2362 return E_OK;
2363 }
2364 }
2365 return (result);
2366 }
2367}
2368
2369/*---------------------------------------------------------------------------
2370| Facility : libnform
2371| Function : static int Wrapping_Not_Necessary_Or_Wrapping_Ok(
2372| FORM * form)
2373|
2374| Description : If a character has been entered into a field, it may
2375| be that wrapping has to occur. This routine checks
2376| whether or not wrapping is required and if so, performs
2377| the wrapping.
2378|
2379| Return Values : E_OK - no wrapping required or wrapping
2380| was successful
2381| E_REQUEST_DENIED -
2382| E_SYSTEM_ERROR - some system error
2383+--------------------------------------------------------------------------*/
2384static int
2385Wrapping_Not_Necessary_Or_Wrapping_Ok(FORM *form)
2386{
2387 FIELD *field = form->current;
2388 int result = E_REQUEST_DENIED;
2389 bool Last_Row = ((field->drows - 1) == form->currow);
2390
2391 if ((field->opts & O_WRAP) && /* wrapping wanted */
2392 (!Single_Line_Field(field)) && /* must be multi-line */
2393 (There_Is_No_Room_For_A_Char_In_Line(form)) && /* line is full */
2394 (!Last_Row || Growable(field))) /* there are more lines */
2395 {
2396 FIELD_CELL *bp;
2397 FIELD_CELL *split;
2398 int chars_to_be_wrapped;
2399 int chars_to_remain_on_line;
2400
2401 if (Last_Row)
2402 {
2403 /* the above logic already ensures, that in this case the field
2404 is growable */
2405 if (!Field_Grown(field, 1))
2406 return E_SYSTEM_ERROR;
2407 }
2408 bp = Address_Of_Current_Row_In_Buffer(form);
2409 Window_To_Buffer(form->w, field);
2410 split = After_Last_Whitespace_Character(bp, field->dcols);
2411 /* split points to the first character of the sequence to be brought
2412 on the next line */
2413 chars_to_remain_on_line = (int)(split - bp);
2414 chars_to_be_wrapped = field->dcols - chars_to_remain_on_line;
2415 if (chars_to_remain_on_line > 0)
2416 {
2417 if ((result = Insert_String(form, form->currow + 1, split,
2418 chars_to_be_wrapped)) == E_OK)
2419 {
2420 wmove(form->w, form->currow, chars_to_remain_on_line);
2421 wclrtoeol(form->w);
2422 if (form->curcol >= chars_to_remain_on_line)
2423 {
2424 form->currow++;
2425 form->curcol -= chars_to_remain_on_line;
2426 }
2427 return E_OK;
2428 }
2429 }
2430 else
2431 return E_OK;
2432 if (result != E_OK)
2433 {
2434 DeleteChar(form);
2435 Window_To_Buffer(form->w, field);
2436 result = E_REQUEST_DENIED;
2437 }
2438 }
2439 else
2440 result = E_OK; /* wrapping was not necessary */
2441 return (result);
2442}
2443
2444/*----------------------------------------------------------------------------
2445 Field Editing routines
2446 --------------------------------------------------------------------------*/
2447
2448/*---------------------------------------------------------------------------
2449| Facility : libnform
2450| Function : static int Field_Editing(
2451| int (* const fct) (FORM *),
2452| FORM * form)
2453|
2454| Description : Generic routine for field editing requests. The driver
2455| routines are only called for editable fields, the
2456| _WINDOW_MODIFIED flag is set if editing occurred.
2457| This is somewhat special due to the overload semantics
2458| of the NEW_LINE and DEL_PREV requests.
2459|
2460| Return Values : Error code from low level drivers.
2461+--------------------------------------------------------------------------*/
2462static int
2463Field_Editing(int (*const fct) (FORM *), FORM *form)
2464{
2465 int res = E_REQUEST_DENIED;
2466
2467 /* We have to deal here with the specific case of the overloaded
2468 behavior of New_Line and Delete_Previous requests.
2469 They may end up in navigational requests if we are on the first
2470 character in a field. But navigation is also allowed on non-
2471 editable fields.
2472 */
2473 if ((fct == FE_Delete_Previous) &&
2474 (form->opts & O_BS_OVERLOAD) &&
2475 First_Position_In_Current_Field(form))
2476 {
2477 res = Inter_Field_Navigation(FN_Previous_Field, form);
2478 }
2479 else
2480 {
2481 if (fct == FE_New_Line)
2482 {
2483 if ((form->opts & O_NL_OVERLOAD) &&
2484 First_Position_In_Current_Field(form))
2485 {
2486 res = Inter_Field_Navigation(FN_Next_Field, form);
2487 }
2488 else
2489 /* FE_New_Line deals itself with the _WINDOW_MODIFIED flag */
2490 res = fct(form);
2491 }
2492 else
2493 {
2494 /* From now on, everything must be editable */
2495 if (form->current->opts & O_EDIT)
2496 {
2497 res = fct(form);
2498 if (res == E_OK)
2499 form->status |= _WINDOW_MODIFIED;
2500 }
2501 }
2502 }
2503 return res;
2504}
2505
2506/*---------------------------------------------------------------------------
2507| Facility : libnform
2508| Function : static int FE_New_Line(FORM * form)
2509|
2510| Description : Perform a new line request. This is rather complex
2511| compared to other routines in this code due to the
2512| rather difficult to understand description in the
2513| manuals.
2514|
2515| Return Values : E_OK - success
2516| E_REQUEST_DENIED - new line not allowed
2517| E_SYSTEM_ERROR - system error
2518+--------------------------------------------------------------------------*/
2519static int
2520FE_New_Line(FORM *form)
2521{
2522 FIELD *field = form->current;
2523 FIELD_CELL *bp, *t;
2524 bool Last_Row = ((field->drows - 1) == form->currow);
2525
2526 T((T_CALLED("FE_New_Line(%p)"), form));
2527 if (form->status & _OVLMODE)
2528 {
2529 if (Last_Row &&
2530 (!(Growable(field) && !Single_Line_Field(field))))
2531 {
2532 if (!(form->opts & O_NL_OVERLOAD))
2533 returnCode(E_REQUEST_DENIED);
2534 wmove(form->w, form->currow, form->curcol);
2535 wclrtoeol(form->w);
2536 /* we have to set this here, although it is also
2537 handled in the generic routine. The reason is,
2538 that FN_Next_Field may fail, but the form is
2539 definitively changed */
2540 form->status |= _WINDOW_MODIFIED;
2541 returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2542 }
2543 else
2544 {
2545 if (Last_Row && !Field_Grown(field, 1))
2546 {
2547 /* N.B.: due to the logic in the 'if', LastRow==TRUE
2548 means here that the field is growable and not
2549 a single-line field */
2550 returnCode(E_SYSTEM_ERROR);
2551 }
2552 wmove(form->w, form->currow, form->curcol);
2553 wclrtoeol(form->w);
2554 form->currow++;
2555 form->curcol = 0;
2556 form->status |= _WINDOW_MODIFIED;
2557 returnCode(E_OK);
2558 }
2559 }
2560 else
2561 {
2562 /* Insert Mode */
2563 if (Last_Row &&
2564 !(Growable(field) && !Single_Line_Field(field)))
2565 {
2566 if (!(form->opts & O_NL_OVERLOAD))
2567 returnCode(E_REQUEST_DENIED);
2568 returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2569 }
2570 else
2571 {
2572 bool May_Do_It = !Last_Row && Is_There_Room_For_A_Line(form);
2573
2574 if (!(May_Do_It || Growable(field)))
2575 returnCode(E_REQUEST_DENIED);
2576 if (!May_Do_It && !Field_Grown(field, 1))
2577 returnCode(E_SYSTEM_ERROR);
2578
2579 bp = Address_Of_Current_Position_In_Buffer(form);
2580 t = After_End_Of_Data(bp, field->dcols - form->curcol);
2581 wmove(form->w, form->currow, form->curcol);
2582 wclrtoeol(form->w);
2583 form->currow++;
2584 form->curcol = 0;
2585 wmove(form->w, form->currow, form->curcol);
2586 winsertln(form->w);
2587 myADDNSTR(form->w, bp, (int)(t - bp));
2588 form->status |= _WINDOW_MODIFIED;
2589 returnCode(E_OK);
2590 }
2591 }
2592}
2593
2594/*---------------------------------------------------------------------------
2595| Facility : libnform
2596| Function : static int FE_Insert_Character(FORM * form)
2597|
2598| Description : Insert blank character at the cursor position
2599|
2600| Return Values : E_OK
2601| E_REQUEST_DENIED
2602+--------------------------------------------------------------------------*/
2603static int
2604FE_Insert_Character(FORM *form)
2605{
2606 FIELD *field = form->current;
2607 int result = E_REQUEST_DENIED;
2608
2609 T((T_CALLED("FE_Insert_Character(%p)"), form));
2610 if (Check_Char(field->type, (int)C_BLANK, (TypeArgument *)(field->arg)))
2611 {
2612 bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
2613
2614 if (There_Is_Room ||
2615 ((Single_Line_Field(field) && Growable(field))))
2616 {
2617 if (!There_Is_Room && !Field_Grown(field, 1))
2618 result = E_SYSTEM_ERROR;
2619 else
2620 {
2621 winsch(form->w, (chtype)C_BLANK);
2622 result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form);
2623 }
2624 }
2625 }
2626 returnCode(result);
2627}
2628
2629/*---------------------------------------------------------------------------
2630| Facility : libnform
2631| Function : static int FE_Insert_Line(FORM * form)
2632|
2633| Description : Insert a blank line at the cursor position
2634|
2635| Return Values : E_OK - success
2636| E_REQUEST_DENIED - line can not be inserted
2637+--------------------------------------------------------------------------*/
2638static int
2639FE_Insert_Line(FORM *form)
2640{
2641 FIELD *field = form->current;
2642 int result = E_REQUEST_DENIED;
2643
2644 T((T_CALLED("FE_Insert_Line(%p)"), form));
2645 if (Check_Char(field->type, (int)C_BLANK, (TypeArgument *)(field->arg)))
2646 {
2647 bool Maybe_Done = (form->currow != (field->drows - 1)) &&
2648 Is_There_Room_For_A_Line(form);
2649
2650 if (!Single_Line_Field(field) &&
2651 (Maybe_Done || Growable(field)))
2652 {
2653 if (!Maybe_Done && !Field_Grown(field, 1))
2654 result = E_SYSTEM_ERROR;
2655 else
2656 {
2657 form->curcol = 0;
2658 winsertln(form->w);
2659 result = E_OK;
2660 }
2661 }
2662 }
2663 returnCode(result);
2664}
2665
2666/*---------------------------------------------------------------------------
2667| Facility : libnform
2668| Function : static int FE_Delete_Character(FORM * form)
2669|
2670| Description : Delete character at the cursor position
2671|
2672| Return Values : E_OK - success
2673+--------------------------------------------------------------------------*/
2674static int
2675FE_Delete_Character(FORM *form)
2676{
2677 T((T_CALLED("FE_Delete_Character(%p)"), form));
2678 DeleteChar(form);
2679 returnCode(E_OK);
2680}
2681
2682/*---------------------------------------------------------------------------
2683| Facility : libnform
2684| Function : static int FE_Delete_Previous(FORM * form)
2685|
2686| Description : Delete character before cursor. Again this is a rather
2687| difficult piece compared to others due to the overloading
2688| semantics of backspace.
2689| N.B.: The case of overloaded BS on first field position
2690| is already handled in the generic routine.
2691|
2692| Return Values : E_OK - success
2693| E_REQUEST_DENIED - Character can't be deleted
2694+--------------------------------------------------------------------------*/
2695static int
2696FE_Delete_Previous(FORM *form)
2697{
2698 FIELD *field = form->current;
2699
2700 T((T_CALLED("FE_Delete_Previous(%p)"), form));
2701 if (First_Position_In_Current_Field(form))
2702 returnCode(E_REQUEST_DENIED);
2703
2704 if ((--(form->curcol)) < 0)
2705 {
2706 FIELD_CELL *this_line, *prev_line, *prev_end, *this_end;
2707 int this_row = form->currow;
2708
2709 form->curcol++;
2710 if (form->status & _OVLMODE)
2711 returnCode(E_REQUEST_DENIED);
2712
2713 prev_line = Address_Of_Row_In_Buffer(field, (form->currow - 1));
2714 this_line = Address_Of_Row_In_Buffer(field, (form->currow));
2715 Synchronize_Buffer(form);
2716 prev_end = After_End_Of_Data(prev_line, field->dcols);
2717 this_end = After_End_Of_Data(this_line, field->dcols);
2718 if ((int)(this_end - this_line) >
2719 (field->cols - (int)(prev_end - prev_line)))
2720 returnCode(E_REQUEST_DENIED);
2721 wmove(form->w, form->currow, form->curcol);
2722 wdeleteln(form->w);
2723 Adjust_Cursor_Position(form, prev_end);
2724 /*
2725 * If we did not really move to the previous line, help the user a
2726 * little. It is however a little inconsistent. Normally, when
2727 * backspacing around the point where text wraps to a new line in a
2728 * multi-line form, we absorb one keystroke for the wrapping point. That
2729 * is consistent with SVr4 forms. However, SVr4 does not allow typing
2730 * into the last column of the field, and requires the user to enter a
2731 * newline to move to the next line. Therefore it can consistently eat
2732 * that keystroke. Since ncurses allows the last column, it wraps
2733 * automatically (given the proper options). But we cannot eat the
2734 * keystroke to back over the wrapping point, since that would put the
2735 * cursor past the end of the form field. In this case, just delete the
2736 * character at the end of the field.
2737 */
2738 if (form->currow == this_row && this_row > 0)
2739 {
2740 form->currow -= 1;
2741 form->curcol = field->dcols - 1;
2742 DeleteChar(form);
2743 }
2744 else
2745 {
2746 wmove(form->w, form->currow, form->curcol);
2747 myADDNSTR(form->w, this_line, (int)(this_end - this_line));
2748 }
2749 }
2750 else
2751 {
2752 DeleteChar(form);
2753 }
2754 returnCode(E_OK);
2755}
2756
2757/*---------------------------------------------------------------------------
2758| Facility : libnform
2759| Function : static int FE_Delete_Line(FORM * form)
2760|
2761| Description : Delete line at cursor position.
2762|
2763| Return Values : E_OK - success
2764+--------------------------------------------------------------------------*/
2765static int
2766FE_Delete_Line(FORM *form)
2767{
2768 T((T_CALLED("FE_Delete_Line(%p)"), form));
2769 form->curcol = 0;
2770 wdeleteln(form->w);
2771 returnCode(E_OK);
2772}
2773
2774/*---------------------------------------------------------------------------
2775| Facility : libnform
2776| Function : static int FE_Delete_Word(FORM * form)
2777|
2778| Description : Delete word at cursor position
2779|
2780| Return Values : E_OK - success
2781| E_REQUEST_DENIED - failure
2782+--------------------------------------------------------------------------*/
2783static int
2784FE_Delete_Word(FORM *form)
2785{
2786 FIELD *field = form->current;
2787 FIELD_CELL *bp = Address_Of_Current_Row_In_Buffer(form);
2788 FIELD_CELL *ep = bp + field->dcols;
2789 FIELD_CELL *cp = bp + form->curcol;
2790 FIELD_CELL *s;
2791
2792 T((T_CALLED("FE_Delete_Word(%p)"), form));
2793 Synchronize_Buffer(form);
2794 if (ISBLANK(*cp))
2795 returnCode(E_REQUEST_DENIED); /* not in word */
2796
2797 /* move cursor to begin of word and erase to end of screen-line */
2798 Adjust_Cursor_Position(form,
2799 After_Last_Whitespace_Character(bp, form->curcol));
2800 wmove(form->w, form->currow, form->curcol);
2801 wclrtoeol(form->w);
2802
2803 /* skip over word in buffer */
2804 s = Get_First_Whitespace_Character(cp, (int)(ep - cp));
2805 /* to begin of next word */
2806 s = Get_Start_Of_Data(s, (int)(ep - s));
2807 if ((s != cp) && !ISBLANK(*s))
2808 {
2809 /* copy remaining line to window */
2810 myADDNSTR(form->w, s, (int)(s - After_End_Of_Data(s, (int)(ep - s))));
2811 }
2812 returnCode(E_OK);
2813}
2814
2815/*---------------------------------------------------------------------------
2816| Facility : libnform
2817| Function : static int FE_Clear_To_End_Of_Line(FORM * form)
2818|
2819| Description : Clear to end of current line.
2820|
2821| Return Values : E_OK - success
2822+--------------------------------------------------------------------------*/
2823static int
2824FE_Clear_To_End_Of_Line(FORM *form)
2825{
2826 T((T_CALLED("FE_Clear_To_End_Of_Line(%p)"), form));
2827 wmove(form->w, form->currow, form->curcol);
2828 wclrtoeol(form->w);
2829 returnCode(E_OK);
2830}
2831
2832/*---------------------------------------------------------------------------
2833| Facility : libnform
2834| Function : static int FE_Clear_To_End_Of_Field(FORM * form)
2835|
2836| Description : Clear to end of field.
2837|
2838| Return Values : E_OK - success
2839+--------------------------------------------------------------------------*/
2840static int
2841FE_Clear_To_End_Of_Field(FORM *form)
2842{
2843 T((T_CALLED("FE_Clear_To_End_Of_Field(%p)"), form));
2844 wmove(form->w, form->currow, form->curcol);
2845 wclrtobot(form->w);
2846 returnCode(E_OK);
2847}
2848
2849/*---------------------------------------------------------------------------
2850| Facility : libnform
2851| Function : static int FE_Clear_Field(FORM * form)
2852|
2853| Description : Clear entire field.
2854|
2855| Return Values : E_OK - success
2856+--------------------------------------------------------------------------*/
2857static int
2858FE_Clear_Field(FORM *form)
2859{
2860 T((T_CALLED("FE_Clear_Field(%p)"), form));
2861 form->currow = form->curcol = 0;
2862 werase(form->w);
2863 returnCode(E_OK);
2864}
2865/*----------------------------------------------------------------------------
2866 END of Field Editing routines
2867 --------------------------------------------------------------------------*/
2868
2869/*----------------------------------------------------------------------------
2870 Edit Mode routines
2871 --------------------------------------------------------------------------*/
2872
2873/*---------------------------------------------------------------------------
2874| Facility : libnform
2875| Function : static int EM_Overlay_Mode(FORM * form)
2876|
2877| Description : Switch to overlay mode.
2878|
2879| Return Values : E_OK - success
2880+--------------------------------------------------------------------------*/
2881static int
2882EM_Overlay_Mode(FORM *form)
2883{
2884 T((T_CALLED("EM_Overlay_Mode(%p)"), form));
2885 form->status |= _OVLMODE;
2886 returnCode(E_OK);
2887}
2888
2889/*---------------------------------------------------------------------------
2890| Facility : libnform
2891| Function : static int EM_Insert_Mode(FORM * form)
2892|
2893| Description : Switch to insert mode
2894|
2895| Return Values : E_OK - success
2896+--------------------------------------------------------------------------*/
2897static int
2898EM_Insert_Mode(FORM *form)
2899{
2900 T((T_CALLED("EM_Insert_Mode(%p)"), form));
2901 form->status &= ~_OVLMODE;
2902 returnCode(E_OK);
2903}
2904
2905/*----------------------------------------------------------------------------
2906 END of Edit Mode routines
2907 --------------------------------------------------------------------------*/
2908
2909/*----------------------------------------------------------------------------
2910 Helper routines for Choice Requests
2911 --------------------------------------------------------------------------*/
2912
2913/*---------------------------------------------------------------------------
2914| Facility : libnform
2915| Function : static bool Next_Choice(
2916| FIELDTYPE * typ,
2917| FIELD * field,
2918| TypeArgument *argp)
2919|
2920| Description : Get the next field choice. For linked types this is
2921| done recursively.
2922|
2923| Return Values : TRUE - next choice successfully retrieved
2924| FALSE - couldn't retrieve next choice
2925+--------------------------------------------------------------------------*/
2926static bool
2927Next_Choice(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
2928{
2929 if (!typ || !(typ->status & _HAS_CHOICE))
2930 return FALSE;
2931
2932 if (typ->status & _LINKED_TYPE)
2933 {
2934 assert(argp);
2935 return (
2936 Next_Choice(typ->left, field, argp->left) ||
2937 Next_Choice(typ->right, field, argp->right));
2938 }
2939 else
2940 {
2941 assert(typ->next);
2942 return typ->next(field, (void *)argp);
2943 }
2944}
2945
2946/*---------------------------------------------------------------------------
2947| Facility : libnform
2948| Function : static bool Previous_Choice(
2949| FIELDTYPE * typ,
2950| FIELD * field,
2951| TypeArgument *argp)
2952|
2953| Description : Get the previous field choice. For linked types this
2954| is done recursively.
2955|
2956| Return Values : TRUE - previous choice successfully retrieved
2957| FALSE - couldn't retrieve previous choice
2958+--------------------------------------------------------------------------*/
2959static bool
2960Previous_Choice(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
2961{
2962 if (!typ || !(typ->status & _HAS_CHOICE))
2963 return FALSE;
2964
2965 if (typ->status & _LINKED_TYPE)
2966 {
2967 assert(argp);
2968 return (
2969 Previous_Choice(typ->left, field, argp->left) ||
2970 Previous_Choice(typ->right, field, argp->right));
2971 }
2972 else
2973 {
2974 assert(typ->prev);
2975 return typ->prev(field, (void *)argp);
2976 }
2977}
2978/*----------------------------------------------------------------------------
2979 End of Helper routines for Choice Requests
2980 --------------------------------------------------------------------------*/
2981
2982/*----------------------------------------------------------------------------
2983 Routines for Choice Requests
2984 --------------------------------------------------------------------------*/
2985
2986/*---------------------------------------------------------------------------
2987| Facility : libnform
2988| Function : static int CR_Next_Choice(FORM * form)
2989|
2990| Description : Get the next field choice.
2991|
2992| Return Values : E_OK - success
2993| E_REQUEST_DENIED - next choice couldn't be retrieved
2994+--------------------------------------------------------------------------*/
2995static int
2996CR_Next_Choice(FORM *form)
2997{
2998 FIELD *field = form->current;
2999
3000 T((T_CALLED("CR_Next_Choice(%p)"), form));
3001 Synchronize_Buffer(form);
3002 returnCode((Next_Choice(field->type, field, (TypeArgument *)(field->arg)))
3003 ? E_OK
3004 : E_REQUEST_DENIED);
3005}
3006
3007/*---------------------------------------------------------------------------
3008| Facility : libnform
3009| Function : static int CR_Previous_Choice(FORM * form)
3010|
3011| Description : Get the previous field choice.
3012|
3013| Return Values : E_OK - success
3014| E_REQUEST_DENIED - prev. choice couldn't be retrieved
3015+--------------------------------------------------------------------------*/
3016static int
3017CR_Previous_Choice(FORM *form)
3018{
3019 FIELD *field = form->current;
3020
3021 T((T_CALLED("CR_Previous_Choice(%p)"), form));
3022 Synchronize_Buffer(form);
3023 returnCode((Previous_Choice(field->type, field, (TypeArgument *)(field->arg)))
3024 ? E_OK
3025 : E_REQUEST_DENIED);
3026}
3027/*----------------------------------------------------------------------------
3028 End of Routines for Choice Requests
3029 --------------------------------------------------------------------------*/
3030
3031/*----------------------------------------------------------------------------
3032 Helper routines for Field Validations.
3033 --------------------------------------------------------------------------*/
3034
3035/*---------------------------------------------------------------------------
3036| Facility : libnform
3037| Function : static bool Check_Field(
3038| FIELDTYPE * typ,
3039| FIELD * field,
3040| TypeArgument * argp)
3041|
3042| Description : Check the field according to its fieldtype and its
3043| actual arguments. For linked fieldtypes this is done
3044| recursively.
3045|
3046| Return Values : TRUE - field is valid
3047| FALSE - field is invalid.
3048+--------------------------------------------------------------------------*/
3049static bool
3050Check_Field(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
3051{
3052 if (typ)
3053 {
3054 if (field->opts & O_NULLOK)
3055 {
3056 FIELD_CELL *bp = field->buf;
3057
3058 assert(bp);
3059 while (ISBLANK(*bp))
3060 {
3061 bp++;
3062 }
3063 if (CharOf(*bp) == 0)
3064 return TRUE;
3065 }
3066
3067 if (typ->status & _LINKED_TYPE)
3068 {
3069 assert(argp);
3070 return (
3071 Check_Field(typ->left, field, argp->left) ||
3072 Check_Field(typ->right, field, argp->right));
3073 }
3074 else
3075 {
3076 if (typ->fcheck)
3077 return typ->fcheck(field, (void *)argp);
3078 }
3079 }
3080 return TRUE;
3081}
3082
3083/*---------------------------------------------------------------------------
3084| Facility : libnform
3085| Function : bool _nc_Internal_Validation(FORM * form )
3086|
3087| Description : Validate the current field of the form.
3088|
3089| Return Values : TRUE - field is valid
3090| FALSE - field is invalid
3091+--------------------------------------------------------------------------*/
3092NCURSES_EXPORT(bool)
3093_nc_Internal_Validation(FORM *form)
3094{
3095 FIELD *field;
3096
3097 field = form->current;
3098
3099 Synchronize_Buffer(form);
3100 if ((form->status & _FCHECK_REQUIRED) ||
3101 (!(field->opts & O_PASSOK)))
3102 {
3103 if (!Check_Field(field->type, field, (TypeArgument *)(field->arg)))
3104 return FALSE;
3105 form->status &= ~_FCHECK_REQUIRED;
3106 field->status |= _CHANGED;
3107 Synchronize_Linked_Fields(field);
3108 }
3109 return TRUE;
3110}
3111/*----------------------------------------------------------------------------
3112 End of Helper routines for Field Validations.
3113 --------------------------------------------------------------------------*/
3114
3115/*----------------------------------------------------------------------------
3116 Routines for Field Validation.
3117 --------------------------------------------------------------------------*/
3118
3119/*---------------------------------------------------------------------------
3120| Facility : libnform
3121| Function : static int FV_Validation(FORM * form)
3122|
3123| Description : Validate the current field of the form.
3124|
3125| Return Values : E_OK - field valid
3126| E_INVALID_FIELD - field not valid
3127+--------------------------------------------------------------------------*/
3128static int
3129FV_Validation(FORM *form)
3130{
3131 T((T_CALLED("FV_Validation(%p)"), form));
3132 if (_nc_Internal_Validation(form))
3133 returnCode(E_OK);
3134 else
3135 returnCode(E_INVALID_FIELD);
3136}
3137/*----------------------------------------------------------------------------
3138 End of routines for Field Validation.
3139 --------------------------------------------------------------------------*/
3140
3141/*----------------------------------------------------------------------------
3142 Helper routines for Inter-Field Navigation
3143 --------------------------------------------------------------------------*/
3144
3145/*---------------------------------------------------------------------------
3146| Facility : libnform
3147| Function : static FIELD *Next_Field_On_Page(FIELD * field)
3148|
3149| Description : Get the next field after the given field on the current
3150| page. The order of fields is the one defined by the
3151| fields array. Only visible and active fields are
3152| counted.
3153|
3154| Return Values : Pointer to the next field.
3155+--------------------------------------------------------------------------*/
3156NCURSES_INLINE static FIELD *
3157Next_Field_On_Page(FIELD *field)
3158{
3159 FORM *form = field->form;
3160 FIELD **field_on_page = &form->field[field->index];
3161 FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3162 FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3163
3164 do
3165 {
3166 field_on_page =
3167 (field_on_page == last_on_page) ? first_on_page : field_on_page + 1;
3168 if (Field_Is_Selectable(*field_on_page))
3169 break;
3170 }
3171 while (field != (*field_on_page));
3172 return (*field_on_page);
3173}
3174
3175/*---------------------------------------------------------------------------
3176| Facility : libnform
3177| Function : FIELD* _nc_First_Active_Field(FORM * form)
3178|
3179| Description : Get the first active field on the current page,
3180| if there are such. If there are none, get the first
3181| visible field on the page. If there are also none,
3182| we return the first field on page and hope the best.
3183|
3184| Return Values : Pointer to calculated field.
3185+--------------------------------------------------------------------------*/
3186NCURSES_EXPORT(FIELD *)
3187_nc_First_Active_Field(FORM *form)
3188{
3189 FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3190 FIELD *proposed = Next_Field_On_Page(*last_on_page);
3191
3192 if (proposed == *last_on_page)
3193 {
3194 /* there might be the special situation, where there is no
3195 active and visible field on the current page. We then select
3196 the first visible field on this readonly page
3197 */
3198 if (Field_Is_Not_Selectable(proposed))
3199 {
3200 FIELD **field = &form->field[proposed->index];
3201 FIELD **first = &form->field[form->page[form->curpage].pmin];
3202
3203 do
3204 {
3205 field = (field == last_on_page) ? first : field + 1;
3206 if (((*field)->opts & O_VISIBLE))
3207 break;
3208 }
3209 while (proposed != (*field));
3210
3211 proposed = *field;
3212
3213 if ((proposed == *last_on_page) && !(proposed->opts & O_VISIBLE))
3214 {
3215 /* This means, there is also no visible field on the page.
3216 So we propose the first one and hope the very best...
3217 Some very clever user has designed a readonly and invisible
3218 page on this form.
3219 */
3220 proposed = *first;
3221 }
3222 }
3223 }
3224 return (proposed);
3225}
3226
3227/*---------------------------------------------------------------------------
3228| Facility : libnform
3229| Function : static FIELD *Previous_Field_On_Page(FIELD * field)
3230|
3231| Description : Get the previous field before the given field on the
3232| current page. The order of fields is the one defined by
3233| the fields array. Only visible and active fields are
3234| counted.
3235|
3236| Return Values : Pointer to the previous field.
3237+--------------------------------------------------------------------------*/
3238NCURSES_INLINE static FIELD *
3239Previous_Field_On_Page(FIELD *field)
3240{
3241 FORM *form = field->form;
3242 FIELD **field_on_page = &form->field[field->index];
3243 FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3244 FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3245
3246 do
3247 {
3248 field_on_page =
3249 (field_on_page == first_on_page) ? last_on_page : field_on_page - 1;
3250 if (Field_Is_Selectable(*field_on_page))
3251 break;
3252 }
3253 while (field != (*field_on_page));
3254
3255 return (*field_on_page);
3256}
3257
3258/*---------------------------------------------------------------------------
3259| Facility : libnform
3260| Function : static FIELD *Sorted_Next_Field(FIELD * field)
3261|
3262| Description : Get the next field after the given field on the current
3263| page. The order of fields is the one defined by the
3264| (row,column) geometry, rows are major.
3265|
3266| Return Values : Pointer to the next field.
3267+--------------------------------------------------------------------------*/
3268NCURSES_INLINE static FIELD *
3269Sorted_Next_Field(FIELD *field)
3270{
3271 FIELD *field_on_page = field;
3272
3273 do
3274 {
3275 field_on_page = field_on_page->snext;
3276 if (Field_Is_Selectable(field_on_page))
3277 break;
3278 }
3279 while (field_on_page != field);
3280
3281 return (field_on_page);
3282}
3283
3284/*---------------------------------------------------------------------------
3285| Facility : libnform
3286| Function : static FIELD *Sorted_Previous_Field(FIELD * field)
3287|
3288| Description : Get the previous field before the given field on the
3289| current page. The order of fields is the one defined
3290| by the (row,column) geometry, rows are major.
3291|
3292| Return Values : Pointer to the previous field.
3293+--------------------------------------------------------------------------*/
3294NCURSES_INLINE static FIELD *
3295Sorted_Previous_Field(FIELD *field)
3296{
3297 FIELD *field_on_page = field;
3298
3299 do
3300 {
3301 field_on_page = field_on_page->sprev;
3302 if (Field_Is_Selectable(field_on_page))
3303 break;
3304 }
3305 while (field_on_page != field);
3306
3307 return (field_on_page);
3308}
3309
3310/*---------------------------------------------------------------------------
3311| Facility : libnform
3312| Function : static FIELD *Left_Neighbor_Field(FIELD * field)
3313|
3314| Description : Get the left neighbor of the field on the same line
3315| and the same page. Cycles through the line.
3316|
3317| Return Values : Pointer to left neighbor field.
3318+--------------------------------------------------------------------------*/
3319NCURSES_INLINE static FIELD *
3320Left_Neighbor_Field(FIELD *field)
3321{
3322 FIELD *field_on_page = field;
3323
3324 /* For a field that has really a left neighbor, the while clause
3325 immediately fails and the loop is left, positioned at the right
3326 neighbor. Otherwise we cycle backwards through the sorted field list
3327 until we enter the same line (from the right end).
3328 */
3329 do
3330 {
3331 field_on_page = Sorted_Previous_Field(field_on_page);
3332 }
3333 while (field_on_page->frow != field->frow);
3334
3335 return (field_on_page);
3336}
3337
3338/*---------------------------------------------------------------------------
3339| Facility : libnform
3340| Function : static FIELD *Right_Neighbor_Field(FIELD * field)
3341|
3342| Description : Get the right neighbor of the field on the same line
3343| and the same page.
3344|
3345| Return Values : Pointer to right neighbor field.
3346+--------------------------------------------------------------------------*/
3347NCURSES_INLINE static FIELD *
3348Right_Neighbor_Field(FIELD *field)
3349{
3350 FIELD *field_on_page = field;
3351
3352 /* See the comments on Left_Neighbor_Field to understand how it works */
3353 do
3354 {
3355 field_on_page = Sorted_Next_Field(field_on_page);
3356 }
3357 while (field_on_page->frow != field->frow);
3358
3359 return (field_on_page);
3360}
3361
3362/*---------------------------------------------------------------------------
3363| Facility : libnform
3364| Function : static FIELD *Upper_Neighbor_Field(FIELD * field)
3365|
3366| Description : Because of the row-major nature of sorting the fields,
3367| it is more difficult to define whats the upper neighbor
3368| field really means. We define that it must be on a
3369| 'previous' line (cyclic order!) and is the rightmost
3370| field laying on the left side of the given field. If
3371| this set is empty, we take the first field on the line.
3372|
3373| Return Values : Pointer to the upper neighbor field.
3374+--------------------------------------------------------------------------*/
3375static FIELD *
3376Upper_Neighbor_Field(FIELD *field)
3377{
3378 FIELD *field_on_page = field;
3379 int frow = field->frow;
3380 int fcol = field->fcol;
3381
3382 /* Walk back to the 'previous' line. The second term in the while clause
3383 just guarantees that we stop if we cycled through the line because
3384 there might be no 'previous' line if the page has just one line.
3385 */
3386 do
3387 {
3388 field_on_page = Sorted_Previous_Field(field_on_page);
3389 }
3390 while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3391
3392 if (field_on_page->frow != frow)
3393 {
3394 /* We really found a 'previous' line. We are positioned at the
3395 rightmost field on this line */
3396 frow = field_on_page->frow;
3397
3398 /* We walk to the left as long as we are really right of the
3399 field. */
3400 while (field_on_page->frow == frow && field_on_page->fcol > fcol)
3401 field_on_page = Sorted_Previous_Field(field_on_page);
3402
3403 /* If we wrapped, just go to the right which is the first field on
3404 the row */
3405 if (field_on_page->frow != frow)
3406 field_on_page = Sorted_Next_Field(field_on_page);
3407 }
3408
3409 return (field_on_page);
3410}
3411
3412/*---------------------------------------------------------------------------
3413| Facility : libnform
3414| Function : static FIELD *Down_Neighbor_Field(FIELD * field)
3415|
3416| Description : Because of the row-major nature of sorting the fields,
3417| its more difficult to define whats the down neighbor
3418| field really means. We define that it must be on a
3419| 'next' line (cyclic order!) and is the leftmost
3420| field laying on the right side of the given field. If
3421| this set is empty, we take the last field on the line.
3422|
3423| Return Values : Pointer to the upper neighbor field.
3424+--------------------------------------------------------------------------*/
3425static FIELD *
3426Down_Neighbor_Field(FIELD *field)
3427{
3428 FIELD *field_on_page = field;
3429 int frow = field->frow;
3430 int fcol = field->fcol;
3431
3432 /* Walk forward to the 'next' line. The second term in the while clause
3433 just guarantees that we stop if we cycled through the line because
3434 there might be no 'next' line if the page has just one line.
3435 */
3436 do
3437 {
3438 field_on_page = Sorted_Next_Field(field_on_page);
3439 }
3440 while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3441
3442 if (field_on_page->frow != frow)
3443 {
3444 /* We really found a 'next' line. We are positioned at the rightmost
3445 field on this line */
3446 frow = field_on_page->frow;
3447
3448 /* We walk to the right as long as we are really left of the
3449 field. */
3450 while (field_on_page->frow == frow && field_on_page->fcol < fcol)
3451 field_on_page = Sorted_Next_Field(field_on_page);
3452
3453 /* If we wrapped, just go to the left which is the last field on
3454 the row */
3455 if (field_on_page->frow != frow)
3456 field_on_page = Sorted_Previous_Field(field_on_page);
3457 }
3458
3459 return (field_on_page);
3460}
3461
3462/*----------------------------------------------------------------------------
3463 Inter-Field Navigation routines
3464 --------------------------------------------------------------------------*/
3465
3466/*---------------------------------------------------------------------------
3467| Facility : libnform
3468| Function : static int Inter_Field_Navigation(
3469| int (* const fct) (FORM *),
3470| FORM * form)
3471|
3472| Description : Generic behavior for changing the current field, the
3473| field is left and a new field is entered. So the field
3474| must be validated and the field init/term hooks must
3475| be called.
3476|
3477| Return Values : E_OK - success
3478| E_INVALID_FIELD - field is invalid
3479| some other - error from subordinate call
3480+--------------------------------------------------------------------------*/
3481static int
3482Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form)
3483{
3484 int res;
3485
3486 if (!_nc_Internal_Validation(form))
3487 res = E_INVALID_FIELD;
3488 else
3489 {
3490 Call_Hook(form, fieldterm);
3491 res = fct(form);
3492 Call_Hook(form, fieldinit);
3493 }
3494 return res;
3495}
3496
3497/*---------------------------------------------------------------------------
3498| Facility : libnform
3499| Function : static int FN_Next_Field(FORM * form)
3500|
3501| Description : Move to the next field on the current page of the form
3502|
3503| Return Values : E_OK - success
3504| != E_OK - error from subordinate call
3505+--------------------------------------------------------------------------*/
3506static int
3507FN_Next_Field(FORM *form)
3508{
3509 T((T_CALLED("FN_Next_Field(%p)"), form));
3510 returnCode(_nc_Set_Current_Field(form,
3511 Next_Field_On_Page(form->current)));
3512}
3513
3514/*---------------------------------------------------------------------------
3515| Facility : libnform
3516| Function : static int FN_Previous_Field(FORM * form)
3517|
3518| Description : Move to the previous field on the current page of the
3519| form
3520|
3521| Return Values : E_OK - success
3522| != E_OK - error from subordinate call
3523+--------------------------------------------------------------------------*/
3524static int
3525FN_Previous_Field(FORM *form)
3526{
3527 T((T_CALLED("FN_Previous_Field(%p)"), form));
3528 returnCode(_nc_Set_Current_Field(form,
3529 Previous_Field_On_Page(form->current)));
3530}
3531
3532/*---------------------------------------------------------------------------
3533| Facility : libnform
3534| Function : static int FN_First_Field(FORM * form)
3535|
3536| Description : Move to the first field on the current page of the form
3537|
3538| Return Values : E_OK - success
3539| != E_OK - error from subordinate call
3540+--------------------------------------------------------------------------*/
3541static int
3542FN_First_Field(FORM *form)
3543{
3544 T((T_CALLED("FN_First_Field(%p)"), form));
3545 returnCode(_nc_Set_Current_Field(form,
3546 Next_Field_On_Page(form->field[form->page[form->curpage].pmax])));
3547}
3548
3549/*---------------------------------------------------------------------------
3550| Facility : libnform
3551| Function : static int FN_Last_Field(FORM * form)
3552|
3553| Description : Move to the last field on the current page of the form
3554|
3555| Return Values : E_OK - success
3556| != E_OK - error from subordinate call
3557+--------------------------------------------------------------------------*/
3558static int
3559FN_Last_Field(FORM *form)
3560{
3561 T((T_CALLED("FN_Last_Field(%p)"), form));
3562 returnCode(
3563 _nc_Set_Current_Field(form,
3564 Previous_Field_On_Page(form->field[form->page[form->curpage].pmin])));
3565}
3566
3567/*---------------------------------------------------------------------------
3568| Facility : libnform
3569| Function : static int FN_Sorted_Next_Field(FORM * form)
3570|
3571| Description : Move to the sorted next field on the current page
3572| of the form.
3573|
3574| Return Values : E_OK - success
3575| != E_OK - error from subordinate call
3576+--------------------------------------------------------------------------*/
3577static int
3578FN_Sorted_Next_Field(FORM *form)
3579{
3580 T((T_CALLED("FN_Sorted_Next_Field(%p)"), form));
3581 returnCode(_nc_Set_Current_Field(form,
3582 Sorted_Next_Field(form->current)));
3583}
3584
3585/*---------------------------------------------------------------------------
3586| Facility : libnform
3587| Function : static int FN_Sorted_Previous_Field(FORM * form)
3588|
3589| Description : Move to the sorted previous field on the current page
3590| of the form.
3591|
3592| Return Values : E_OK - success
3593| != E_OK - error from subordinate call
3594+--------------------------------------------------------------------------*/
3595static int
3596FN_Sorted_Previous_Field(FORM *form)
3597{
3598 T((T_CALLED("FN_Sorted_Previous_Field(%p)"), form));
3599 returnCode(_nc_Set_Current_Field(form,
3600 Sorted_Previous_Field(form->current)));
3601}
3602
3603/*---------------------------------------------------------------------------
3604| Facility : libnform
3605| Function : static int FN_Sorted_First_Field(FORM * form)
3606|
3607| Description : Move to the sorted first field on the current page
3608| of the form.
3609|
3610| Return Values : E_OK - success
3611| != E_OK - error from subordinate call
3612+--------------------------------------------------------------------------*/
3613static int
3614FN_Sorted_First_Field(FORM *form)
3615{
3616 T((T_CALLED("FN_Sorted_First_Field(%p)"), form));
3617 returnCode(_nc_Set_Current_Field(form,
3618 Sorted_Next_Field(form->field[form->page[form->curpage].smax])));
3619}
3620
3621/*---------------------------------------------------------------------------
3622| Facility : libnform
3623| Function : static int FN_Sorted_Last_Field(FORM * form)
3624|
3625| Description : Move to the sorted last field on the current page
3626| of the form.
3627|
3628| Return Values : E_OK - success
3629| != E_OK - error from subordinate call
3630+--------------------------------------------------------------------------*/
3631static int
3632FN_Sorted_Last_Field(FORM *form)
3633{
3634 T((T_CALLED("FN_Sorted_Last_Field(%p)"), form));
3635 returnCode(_nc_Set_Current_Field(form,
3636 Sorted_Previous_Field(form->field[form->page[form->curpage].smin])));
3637}
3638
3639/*---------------------------------------------------------------------------
3640| Facility : libnform
3641| Function : static int FN_Left_Field(FORM * form)
3642|
3643| Description : Get the field on the left of the current field on the
3644| same line and the same page. Cycles through the line.
3645|
3646| Return Values : E_OK - success
3647| != E_OK - error from subordinate call
3648+--------------------------------------------------------------------------*/
3649static int
3650FN_Left_Field(FORM *form)
3651{
3652 T((T_CALLED("FN_Left_Field(%p)"), form));
3653 returnCode(_nc_Set_Current_Field(form,
3654 Left_Neighbor_Field(form->current)));
3655}
3656
3657/*---------------------------------------------------------------------------
3658| Facility : libnform
3659| Function : static int FN_Right_Field(FORM * form)
3660|
3661| Description : Get the field on the right of the current field on the
3662| same line and the same page. Cycles through the line.
3663|
3664| Return Values : E_OK - success
3665| != E_OK - error from subordinate call
3666+--------------------------------------------------------------------------*/
3667static int
3668FN_Right_Field(FORM *form)
3669{
3670 T((T_CALLED("FN_Right_Field(%p)"), form));
3671 returnCode(_nc_Set_Current_Field(form,
3672 Right_Neighbor_Field(form->current)));
3673}
3674
3675/*---------------------------------------------------------------------------
3676| Facility : libnform
3677| Function : static int FN_Up_Field(FORM * form)
3678|
3679| Description : Get the upper neighbor of the current field. This
3680| cycles through the page. See the comments of the
3681| Upper_Neighbor_Field function to understand how
3682| 'upper' is defined.
3683|
3684| Return Values : E_OK - success
3685| != E_OK - error from subordinate call
3686+--------------------------------------------------------------------------*/
3687static int
3688FN_Up_Field(FORM *form)
3689{
3690 T((T_CALLED("FN_Up_Field(%p)"), form));
3691 returnCode(_nc_Set_Current_Field(form,
3692 Upper_Neighbor_Field(form->current)));
3693}
3694
3695/*---------------------------------------------------------------------------
3696| Facility : libnform
3697| Function : static int FN_Down_Field(FORM * form)
3698|
3699| Description : Get the down neighbor of the current field. This
3700| cycles through the page. See the comments of the
3701| Down_Neighbor_Field function to understand how
3702| 'down' is defined.
3703|
3704| Return Values : E_OK - success
3705| != E_OK - error from subordinate call
3706+--------------------------------------------------------------------------*/
3707static int
3708FN_Down_Field(FORM *form)
3709{
3710 T((T_CALLED("FN_Down_Field(%p)"), form));
3711 returnCode(_nc_Set_Current_Field(form,
3712 Down_Neighbor_Field(form->current)));
3713}
3714/*----------------------------------------------------------------------------
3715 END of Field Navigation routines
3716 --------------------------------------------------------------------------*/
3717
3718/*----------------------------------------------------------------------------
3719 Helper routines for Page Navigation
3720 --------------------------------------------------------------------------*/
3721
3722/*---------------------------------------------------------------------------
3723| Facility : libnform
3724| Function : int _nc_Set_Form_Page(FORM * form,
3725| int page,
3726| FIELD * field)
3727|
3728| Description : Make the given page number the current page and make
3729| the given field the current field on the page. If
3730| for the field NULL is given, make the first field on
3731| the page the current field. The routine acts only
3732| if the requested page is not the current page.
3733|
3734| Return Values : E_OK - success
3735| != E_OK - error from subordinate call
3736| E_BAD_ARGUMENT - invalid field pointer
3737| E_SYSTEM_ERROR - some severe basic error
3738+--------------------------------------------------------------------------*/
3739NCURSES_EXPORT(int)
3740_nc_Set_Form_Page(FORM *form, int page, FIELD *field)
3741{
3742 int res = E_OK;
3743
3744 if ((form->curpage != page))
3745 {
3746 FIELD *last_field, *field_on_page;
3747
3748 werase(Get_Form_Window(form));
3749 form->curpage = page;
3750 last_field = field_on_page = form->field[form->page[page].smin];
3751 do
3752 {
3753 if (field_on_page->opts & O_VISIBLE)
3754 if ((res = Display_Field(field_on_page)) != E_OK)
3755 return (res);
3756 field_on_page = field_on_page->snext;
3757 }
3758 while (field_on_page != last_field);
3759
3760 if (field)
3761 res = _nc_Set_Current_Field(form, field);
3762 else
3763 /* N.B.: we don't encapsulate this by Inter_Field_Navigation(),
3764 because this is already executed in a page navigation
3765 context that contains field navigation
3766 */
3767 res = FN_First_Field(form);
3768 }
3769 return (res);
3770}
3771
3772/*---------------------------------------------------------------------------
3773| Facility : libnform
3774| Function : static int Next_Page_Number(const FORM * form)
3775|
3776| Description : Calculate the page number following the current page
3777| number. This cycles if the highest page number is
3778| reached.
3779|
3780| Return Values : The next page number
3781+--------------------------------------------------------------------------*/
3782NCURSES_INLINE static int
3783Next_Page_Number(const FORM *form)
3784{
3785 return (form->curpage + 1) % form->maxpage;
3786}
3787
3788/*---------------------------------------------------------------------------
3789| Facility : libnform
3790| Function : static int Previous_Page_Number(const FORM * form)
3791|
3792| Description : Calculate the page number before the current page
3793| number. This cycles if the first page number is
3794| reached.
3795|
3796| Return Values : The previous page number
3797+--------------------------------------------------------------------------*/
3798NCURSES_INLINE static int
3799Previous_Page_Number(const FORM *form)
3800{
3801 return (form->curpage != 0 ? form->curpage - 1 : form->maxpage - 1);
3802}
3803
3804/*----------------------------------------------------------------------------
3805 Page Navigation routines
3806 --------------------------------------------------------------------------*/
3807
3808/*---------------------------------------------------------------------------
3809| Facility : libnform
3810| Function : static int Page_Navigation(
3811| int (* const fct) (FORM *),
3812| FORM * form)
3813|
3814| Description : Generic behavior for changing a page. This means
3815| that the field is left and a new field is entered.
3816| So the field must be validated and the field init/term
3817| hooks must be called. Because also the page is changed,
3818| the forms init/term hooks must be called also.
3819|
3820| Return Values : E_OK - success
3821| E_INVALID_FIELD - field is invalid
3822| some other - error from subordinate call
3823+--------------------------------------------------------------------------*/
3824static int
3825Page_Navigation(int (*const fct) (FORM *), FORM *form)
3826{
3827 int res;
3828
3829 if (!_nc_Internal_Validation(form))
3830 res = E_INVALID_FIELD;
3831 else
3832 {
3833 Call_Hook(form, fieldterm);
3834 Call_Hook(form, formterm);
3835 res = fct(form);
3836 Call_Hook(form, forminit);
3837 Call_Hook(form, fieldinit);
3838 }
3839 return res;
3840}
3841
3842/*---------------------------------------------------------------------------
3843| Facility : libnform
3844| Function : static int PN_Next_Page(FORM * form)
3845|
3846| Description : Move to the next page of the form
3847|
3848| Return Values : E_OK - success
3849| != E_OK - error from subordinate call
3850+--------------------------------------------------------------------------*/
3851static int
3852PN_Next_Page(FORM *form)
3853{
3854 T((T_CALLED("PN_Next_Page(%p)"), form));
3855 returnCode(_nc_Set_Form_Page(form, Next_Page_Number(form), (FIELD *)0));
3856}
3857
3858/*---------------------------------------------------------------------------
3859| Facility : libnform
3860| Function : static int PN_Previous_Page(FORM * form)
3861|
3862| Description : Move to the previous page of the form
3863|
3864| Return Values : E_OK - success
3865| != E_OK - error from subordinate call
3866+--------------------------------------------------------------------------*/
3867static int
3868PN_Previous_Page(FORM *form)
3869{
3870 T((T_CALLED("PN_Previous_Page(%p)"), form));
3871 returnCode(_nc_Set_Form_Page(form, Previous_Page_Number(form), (FIELD *)0));
3872}
3873
3874/*---------------------------------------------------------------------------
3875| Facility : libnform
3876| Function : static int PN_First_Page(FORM * form)
3877|
3878| Description : Move to the first page of the form
3879|
3880| Return Values : E_OK - success
3881| != E_OK - error from subordinate call
3882+--------------------------------------------------------------------------*/
3883static int
3884PN_First_Page(FORM *form)
3885{
3886 T((T_CALLED("PN_First_Page(%p)"), form));
3887 returnCode(_nc_Set_Form_Page(form, 0, (FIELD *)0));
3888}
3889
3890/*---------------------------------------------------------------------------
3891| Facility : libnform
3892| Function : static int PN_Last_Page(FORM * form)
3893|
3894| Description : Move to the last page of the form
3895|
3896| Return Values : E_OK - success
3897| != E_OK - error from subordinate call
3898+--------------------------------------------------------------------------*/
3899static int
3900PN_Last_Page(FORM *form)
3901{
3902 T((T_CALLED("PN_Last_Page(%p)"), form));
3903 returnCode(_nc_Set_Form_Page(form, form->maxpage - 1, (FIELD *)0));
3904}
3905
3906/*----------------------------------------------------------------------------
3907 END of Field Navigation routines
3908 --------------------------------------------------------------------------*/
3909
3910/*----------------------------------------------------------------------------
3911 Helper routines for the core form driver.
3912 --------------------------------------------------------------------------*/
3913
3914/*---------------------------------------------------------------------------
3915| Facility : libnform
3916| Function : static int Data_Entry(FORM * form,int c)
3917|
3918| Description : Enter character c into at the current position of the
3919| current field of the form.
3920|
3921| Return Values : E_OK - success
3922| E_REQUEST_DENIED - driver could not process the request
3923| E_SYSTEM_ERROR -
3924+--------------------------------------------------------------------------*/
3925static int
3926Data_Entry(FORM *form, int c)
3927{
3928 FIELD *field = form->current;
3929 int result = E_REQUEST_DENIED;
3930
3931 T((T_CALLED("Data_Entry(%p,%s)"), form, _tracechtype((chtype)c)));
3932 if ((field->opts & O_EDIT)
3933#if FIX_FORM_INACTIVE_BUG
3934 && (field->opts & O_ACTIVE)
3935#endif
3936 )
3937 {
3938 if ((field->opts & O_BLANK) &&
3939 First_Position_In_Current_Field(form) &&
3940 !(form->status & _FCHECK_REQUIRED) &&
3941 !(form->status & _WINDOW_MODIFIED))
3942 werase(form->w);
3943
3944 if (form->status & _OVLMODE)
3945 {
3946 waddch(form->w, (chtype)c);
3947 }
3948 else
3949 /* no _OVLMODE */
3950 {
3951 bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
3952
3953 if (!(There_Is_Room ||
3954 ((Single_Line_Field(field) && Growable(field)))))
3955 RETURN(E_REQUEST_DENIED);
3956
3957 if (!There_Is_Room && !Field_Grown(field, 1))
3958 RETURN(E_SYSTEM_ERROR);
3959
3960 winsch(form->w, (chtype)c);
3961 }
3962
3963 if ((result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form)) == E_OK)
3964 {
3965 bool End_Of_Field = (((field->drows - 1) == form->currow) &&
3966 ((field->dcols - 1) == form->curcol));
3967
3968 form->status |= _WINDOW_MODIFIED;
3969 if (End_Of_Field && !Growable(field) && (field->opts & O_AUTOSKIP))
3970 result = Inter_Field_Navigation(FN_Next_Field, form);
3971 else
3972 {
3973 if (End_Of_Field && Growable(field) && !Field_Grown(field, 1))
3974 result = E_SYSTEM_ERROR;
3975 else
3976 {
3977#if USE_WIDEC_SUPPORT
3978 /*
3979 * We have just added a byte to the form field. It may have
3980 * been part of a multibyte character. If it was, the
3981 * addch_used field is nonzero and we should not try to move
3982 * to a new column.
3983 */
3984 if (WINDOW_EXT(form->w, addch_used) == 0)
3985 IFN_Next_Character(form);
3986#else
3987 IFN_Next_Character(form);
3988#endif
3989 result = E_OK;
3990 }
3991 }
3992 }
3993 }
3994 RETURN(result);
3995}
3996
3997/* Structure to describe the binding of a request code to a function.
3998 The member keycode codes the request value as well as the generic
3999 routine to use for the request. The code for the generic routine
4000 is coded in the upper 16 Bits while the request code is coded in
4001 the lower 16 bits.
4002
4003 In terms of C++ you might think of a request as a class with a
4004 virtual method "perform". The different types of request are
4005 derived from this base class and overload (or not) the base class
4006 implementation of perform.
4007*/
4008typedef struct
4009{
4010 int keycode; /* must be at least 32 bit: hi:mode, lo: key */
4011 int (*cmd) (FORM *); /* low level driver routine for this key */
4012}
4013Binding_Info;
4014
4015/* You may see this is the class-id of the request type class */
4016#define ID_PN (0x00000000) /* Page navigation */
4017#define ID_FN (0x00010000) /* Inter-Field navigation */
4018#define ID_IFN (0x00020000) /* Intra-Field navigation */
4019#define ID_VSC (0x00030000) /* Vertical Scrolling */
4020#define ID_HSC (0x00040000) /* Horizontal Scrolling */
4021#define ID_FE (0x00050000) /* Field Editing */
4022#define ID_EM (0x00060000) /* Edit Mode */
4023#define ID_FV (0x00070000) /* Field Validation */
4024#define ID_CH (0x00080000) /* Choice */
4025#define ID_Mask (0xffff0000)
4026#define Key_Mask (0x0000ffff)
4027#define ID_Shft (16)
4028
4029/* This array holds all the Binding Infos */
4030/* *INDENT-OFF* */
4031static const Binding_Info bindings[MAX_FORM_COMMAND - MIN_FORM_COMMAND + 1] =
4032{
4033 { REQ_NEXT_PAGE |ID_PN ,PN_Next_Page},
4034 { REQ_PREV_PAGE |ID_PN ,PN_Previous_Page},
4035 { REQ_FIRST_PAGE |ID_PN ,PN_First_Page},
4036 { REQ_LAST_PAGE |ID_PN ,PN_Last_Page},
4037
4038 { REQ_NEXT_FIELD |ID_FN ,FN_Next_Field},
4039 { REQ_PREV_FIELD |ID_FN ,FN_Previous_Field},
4040 { REQ_FIRST_FIELD |ID_FN ,FN_First_Field},
4041 { REQ_LAST_FIELD |ID_FN ,FN_Last_Field},
4042 { REQ_SNEXT_FIELD |ID_FN ,FN_Sorted_Next_Field},
4043 { REQ_SPREV_FIELD |ID_FN ,FN_Sorted_Previous_Field},
4044 { REQ_SFIRST_FIELD |ID_FN ,FN_Sorted_First_Field},
4045 { REQ_SLAST_FIELD |ID_FN ,FN_Sorted_Last_Field},
4046 { REQ_LEFT_FIELD |ID_FN ,FN_Left_Field},
4047 { REQ_RIGHT_FIELD |ID_FN ,FN_Right_Field},
4048 { REQ_UP_FIELD |ID_FN ,FN_Up_Field},
4049 { REQ_DOWN_FIELD |ID_FN ,FN_Down_Field},
4050
4051 { REQ_NEXT_CHAR |ID_IFN ,IFN_Next_Character},
4052 { REQ_PREV_CHAR |ID_IFN ,IFN_Previous_Character},
4053 { REQ_NEXT_LINE |ID_IFN ,IFN_Next_Line},
4054 { REQ_PREV_LINE |ID_IFN ,IFN_Previous_Line},
4055 { REQ_NEXT_WORD |ID_IFN ,IFN_Next_Word},
4056 { REQ_PREV_WORD |ID_IFN ,IFN_Previous_Word},
4057 { REQ_BEG_FIELD |ID_IFN ,IFN_Beginning_Of_Field},
4058 { REQ_END_FIELD |ID_IFN ,IFN_End_Of_Field},
4059 { REQ_BEG_LINE |ID_IFN ,IFN_Beginning_Of_Line},
4060 { REQ_END_LINE |ID_IFN ,IFN_End_Of_Line},
4061 { REQ_LEFT_CHAR |ID_IFN ,IFN_Left_Character},
4062 { REQ_RIGHT_CHAR |ID_IFN ,IFN_Right_Character},
4063 { REQ_UP_CHAR |ID_IFN ,IFN_Up_Character},
4064 { REQ_DOWN_CHAR |ID_IFN ,IFN_Down_Character},
4065
4066 { REQ_NEW_LINE |ID_FE ,FE_New_Line},
4067 { REQ_INS_CHAR |ID_FE ,FE_Insert_Character},
4068 { REQ_INS_LINE |ID_FE ,FE_Insert_Line},
4069 { REQ_DEL_CHAR |ID_FE ,FE_Delete_Character},
4070 { REQ_DEL_PREV |ID_FE ,FE_Delete_Previous},
4071 { REQ_DEL_LINE |ID_FE ,FE_Delete_Line},
4072 { REQ_DEL_WORD |ID_FE ,FE_Delete_Word},
4073 { REQ_CLR_EOL |ID_FE ,FE_Clear_To_End_Of_Line},
4074 { REQ_CLR_EOF |ID_FE ,FE_Clear_To_End_Of_Field},
4075 { REQ_CLR_FIELD |ID_FE ,FE_Clear_Field},
4076
4077 { REQ_OVL_MODE |ID_EM ,EM_Overlay_Mode},
4078 { REQ_INS_MODE |ID_EM ,EM_Insert_Mode},
4079
4080 { REQ_SCR_FLINE |ID_VSC ,VSC_Scroll_Line_Forward},
4081 { REQ_SCR_BLINE |ID_VSC ,VSC_Scroll_Line_Backward},
4082 { REQ_SCR_FPAGE |ID_VSC ,VSC_Scroll_Page_Forward},
4083 { REQ_SCR_BPAGE |ID_VSC ,VSC_Scroll_Page_Backward},
4084 { REQ_SCR_FHPAGE |ID_VSC ,VSC_Scroll_Half_Page_Forward},
4085 { REQ_SCR_BHPAGE |ID_VSC ,VSC_Scroll_Half_Page_Backward},
4086
4087 { REQ_SCR_FCHAR |ID_HSC ,HSC_Scroll_Char_Forward},
4088 { REQ_SCR_BCHAR |ID_HSC ,HSC_Scroll_Char_Backward},
4089 { REQ_SCR_HFLINE |ID_HSC ,HSC_Horizontal_Line_Forward},
4090 { REQ_SCR_HBLINE |ID_HSC ,HSC_Horizontal_Line_Backward},
4091 { REQ_SCR_HFHALF |ID_HSC ,HSC_Horizontal_Half_Line_Forward},
4092 { REQ_SCR_HBHALF |ID_HSC ,HSC_Horizontal_Half_Line_Backward},
4093
4094 { REQ_VALIDATION |ID_FV ,FV_Validation},
4095
4096 { REQ_NEXT_CHOICE |ID_CH ,CR_Next_Choice},
4097 { REQ_PREV_CHOICE |ID_CH ,CR_Previous_Choice}
4098};
4099/* *INDENT-ON* */
4100
4101/*---------------------------------------------------------------------------
4102| Facility : libnform
4103| Function : int form_driver(FORM * form,int c)
4104|
4105| Description : This is the workhorse of the forms system. It checks
4106| to determine whether the character c is a request or
4107| data. If it is a request, the form driver executes
4108| the request and returns the result. If it is data
4109| (printable character), it enters the data into the
4110| current position in the current field. If it is not
4111| recognized, the form driver assumes it is an application
4112| defined command and returns E_UNKNOWN_COMMAND.
4113| Application defined command should be defined relative
4114| to MAX_FORM_COMMAND, the maximum value of a request.
4115|
4116| Return Values : E_OK - success
4117| E_SYSTEM_ERROR - system error
4118| E_BAD_ARGUMENT - an argument is incorrect
4119| E_NOT_POSTED - form is not posted
4120| E_INVALID_FIELD - field contents are invalid
4121| E_BAD_STATE - called from inside a hook routine
4122| E_REQUEST_DENIED - request failed
4123| E_NOT_CONNECTED - no fields are connected to the form
4124| E_UNKNOWN_COMMAND - command not known
4125+--------------------------------------------------------------------------*/
4126NCURSES_EXPORT(int)
4127form_driver(FORM *form, int c)
4128{
4129 const Binding_Info *BI = (Binding_Info *) 0;
4130 int res = E_UNKNOWN_COMMAND;
4131
4132 T((T_CALLED("form_driver(%p,%d)"), form, c));
4133
4134 if (!form)
4135 RETURN(E_BAD_ARGUMENT);
4136
4137 if (!(form->field))
4138 RETURN(E_NOT_CONNECTED);
4139
4140 assert(form->page);
4141
4142 if (c == FIRST_ACTIVE_MAGIC)
4143 {
4144 form->current = _nc_First_Active_Field(form);
4145 RETURN(E_OK);
4146 }
4147
4148 assert(form->current &&
4149 form->current->buf &&
4150 (form->current->form == form)
4151 );
4152
4153 if (form->status & _IN_DRIVER)
4154 RETURN(E_BAD_STATE);
4155
4156 if (!(form->status & _POSTED))
4157 RETURN(E_NOT_POSTED);
4158
4159 if ((c >= MIN_FORM_COMMAND && c <= MAX_FORM_COMMAND) &&
4160 ((bindings[c - MIN_FORM_COMMAND].keycode & Key_Mask) == c))
4161 BI = &(bindings[c - MIN_FORM_COMMAND]);
4162
4163 if (BI)
4164 {
4165 typedef int (*Generic_Method) (int (*const) (FORM *), FORM *);
4166 static const Generic_Method Generic_Methods[] =
4167 {
4168 Page_Navigation, /* overloaded to call field&form hooks */
4169 Inter_Field_Navigation, /* overloaded to call field hooks */
4170 NULL, /* Intra-Field is generic */
4171 Vertical_Scrolling, /* Overloaded to check multi-line */
4172 Horizontal_Scrolling, /* Overloaded to check single-line */
4173 Field_Editing, /* Overloaded to mark modification */
4174 NULL, /* Edit Mode is generic */
4175 NULL, /* Field Validation is generic */
4176 NULL /* Choice Request is generic */
4177 };
4178 size_t nMethods = (sizeof(Generic_Methods) / sizeof(Generic_Methods[0]));
4179 size_t method = ((BI->keycode & ID_Mask) >> ID_Shft) & 0xffff;
4180
4181 if ((method >= nMethods) || !(BI->cmd))
4182 res = E_SYSTEM_ERROR;
4183 else
4184 {
4185 Generic_Method fct = Generic_Methods[method];
4186
4187 if (fct)
4188 res = fct(BI->cmd, form);
4189 else
4190 res = (BI->cmd) (form);
4191 }
4192 }
4193#ifdef NCURSES_MOUSE_VERSION
4194 else if (KEY_MOUSE == c)
4195 {
4196 MEVENT event;
4197 WINDOW *win = form->win ? form->win : stdscr;
4198 WINDOW *sub = form->sub ? form->sub : win;
4199
4200 getmouse(&event);
4201 if ((event.bstate & (BUTTON1_CLICKED |
4202 BUTTON1_DOUBLE_CLICKED |
4203 BUTTON1_TRIPLE_CLICKED))
4204 && wenclose(win, event.y, event.x))
4205 { /* we react only if the click was in the userwin, that means
4206 * inside the form display area or at the decoration window.
4207 */
4208 int ry = event.y, rx = event.x; /* screen coordinates */
4209
4210 res = E_REQUEST_DENIED;
4211 if (mouse_trafo(&ry, &rx, FALSE))
4212 { /* rx, ry are now "curses" coordinates */
4213 if (ry < sub->_begy)
4214 { /* we clicked above the display region; this is
4215 * interpreted as "scroll up" request
4216 */
4217 if (event.bstate & BUTTON1_CLICKED)
4218 res = form_driver(form, REQ_PREV_FIELD);
4219 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4220 res = form_driver(form, REQ_PREV_PAGE);
4221 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4222 res = form_driver(form, REQ_FIRST_FIELD);
4223 }
4224 else if (ry > sub->_begy + sub->_maxy)
4225 { /* we clicked below the display region; this is
4226 * interpreted as "scroll down" request
4227 */
4228 if (event.bstate & BUTTON1_CLICKED)
4229 res = form_driver(form, REQ_NEXT_FIELD);
4230 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4231 res = form_driver(form, REQ_NEXT_PAGE);
4232 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4233 res = form_driver(form, REQ_LAST_FIELD);
4234 }
4235 else if (wenclose(sub, event.y, event.x))
4236 { /* Inside the area we try to find the hit item */
4237 int i;
4238
4239 ry = event.y;
4240 rx = event.x;
4241 if (wmouse_trafo(sub, &ry, &rx, FALSE))
4242 {
4243 int min_field = form->page[form->curpage].pmin;
4244 int max_field = form->page[form->curpage].pmax;
4245
4246 for (i = min_field; i <= max_field; ++i)
4247 {
4248 FIELD *field = form->field[i];
4249
4250 if (Field_Is_Selectable(field)
4251 && Field_encloses(field, ry, rx) == E_OK)
4252 {
4253 res = _nc_Set_Current_Field(form, field);
4254 if (res == E_OK)
4255 res = _nc_Position_Form_Cursor(form);
4256 if (res == E_OK
4257 && (event.bstate & BUTTON1_DOUBLE_CLICKED))
4258 res = E_UNKNOWN_COMMAND;
4259 break;
4260 }
4261 }
4262 }
4263 }
4264 }
4265 }
4266 else
4267 res = E_REQUEST_DENIED;
4268 }
4269#endif /* NCURSES_MOUSE_VERSION */
4270 else if (!(c & (~(int)MAX_REGULAR_CHARACTER)))
4271 {
4272 /*
4273 * If we're using 8-bit characters, iscntrl+isprint cover the whole set.
4274 * But with multibyte characters, there is a third possibility, i.e.,
4275 * parts of characters that build up into printable characters which are
4276 * not considered printable.
4277 *
4278 * FIXME: the wide-character branch should also use Check_Char().
4279 */
4280#if USE_WIDEC_SUPPORT
4281 if (!iscntrl(UChar(c)))
4282#else
4283 if (isprint(UChar(c)) &&
4284 Check_Char(form->current->type, c,
4285 (TypeArgument *)(form->current->arg)))
4286#endif
4287 res = Data_Entry(form, c);
4288 }
4289 _nc_Refresh_Current_Field(form);
4290 RETURN(res);
4291}
4292
4293/*----------------------------------------------------------------------------
4294 Field-Buffer manipulation routines.
4295 The effects of setting a buffer are tightly coupled to the core of the form
4296 driver logic. This is especially true in the case of growable fields.
4297 So I don't separate this into a separate module.
4298 --------------------------------------------------------------------------*/
4299
4300/*---------------------------------------------------------------------------
4301| Facility : libnform
4302| Function : int set_field_buffer(FIELD *field,
4303| int buffer, char *value)
4304|
4305| Description : Set the given buffer of the field to the given value.
4306| Buffer 0 stores the displayed content of the field.
4307| For dynamic fields this may grow the fieldbuffers if
4308| the length of the value exceeds the current buffer
4309| length. For buffer 0 only printable values are allowed.
4310| For static fields, the value needs not to be zero ter-
4311| minated. It is copied up to the length of the buffer.
4312|
4313| Return Values : E_OK - success
4314| E_BAD_ARGUMENT - invalid argument
4315| E_SYSTEM_ERROR - system error
4316+--------------------------------------------------------------------------*/
4317NCURSES_EXPORT(int)
4318set_field_buffer(FIELD *field, int buffer, const char *value)
4319{
4320 FIELD_CELL *p;
4321 int res = E_OK;
4322 unsigned int i;
4323 unsigned int len;
4324
4325#if USE_WIDEC_SUPPORT
4326 FIELD_CELL *widevalue = 0;
4327#endif
4328
4329 T((T_CALLED("set_field_buffer(%p,%d,%s)"), field, buffer, _nc_visbuf(value)));
4330
4331 if (!field || !value || ((buffer < 0) || (buffer > field->nbuf)))
4332 RETURN(E_BAD_ARGUMENT);
4333
4334 len = Buffer_Length(field);
4335
4336 if (buffer == 0)
4337 {
4338 for (i = 0; (value[i] != '\0') && (i < len); ++i)
4339 {
4340 if (iscntrl(UChar(value[i])))
4341 RETURN(E_BAD_ARGUMENT);
4342 }
4343 }
4344
4345 if (Growable(field))
4346 {
4347 /* for a growable field we must assume zero terminated strings, because
4348 somehow we have to detect the length of what should be copied.
4349 */
4350 unsigned int vlen = strlen(value);
4351
4352 if (vlen > len)
4353 {
4354 if (!Field_Grown(field,
4355 (int)(1 + (vlen - len) / ((field->rows + field->nrow)
4356 * field->cols))))
4357 RETURN(E_SYSTEM_ERROR);
4358
4359 /* in this case we also have to check, whether or not the remaining
4360 characters in value are also printable for buffer 0. */
4361 if (buffer == 0)
4362 {
4363 for (i = len; i < vlen; i++)
4364 if (iscntrl(UChar(value[i])))
4365 RETURN(E_BAD_ARGUMENT);
4366 }
4367 len = vlen;
4368 }
4369 }
4370
4371 p = Address_Of_Nth_Buffer(field, buffer);
4372
4373#if USE_WIDEC_SUPPORT
4374 /*
4375 * Use addstr's logic for converting a string to an array of cchar_t's.
4376 * There should be a better way, but this handles nonspacing characters
4377 * and other special cases that we really do not want to handle here.
4378 */
4379 wclear(field->working);
4380 mvwaddstr(field->working, 0, 0, value);
4381
4382 if ((widevalue = typeCalloc(FIELD_CELL, len + 1)) == 0)
4383 {
4384 RETURN(E_SYSTEM_ERROR);
4385 }
4386 else
4387 {
4388 mvwin_wchnstr(field->working, 0, 0, widevalue, (int)len);
4389 for (i = 0; i < len; ++i)
4390 {
4391 if (CharEq(myZEROS, widevalue[i]))
4392 {
4393 while (i < len)
4394 p[i++] = myBLANK;
4395 break;
4396 }
4397 p[i] = widevalue[i];
4398 }
4399 free(widevalue);
4400 }
4401#else
4402 for (i = 0; i < len; ++i)
4403 {
4404 if (value[i] == '\0')
4405 {
4406 while (i < len)
4407 p[i++] = myBLANK;
4408 break;
4409 }
4410 p[i] = value[i];
4411 }
4412#endif
4413
4414 if (buffer == 0)
4415 {
4416 int syncres;
4417
4418 if (((syncres = Synchronize_Field(field)) != E_OK) &&
4419 (res == E_OK))
4420 res = syncres;
4421 if (((syncres = Synchronize_Linked_Fields(field)) != E_OK) &&
4422 (res == E_OK))
4423 res = syncres;
4424 }
4425 RETURN(res);
4426}
4427
4428/*---------------------------------------------------------------------------
4429| Facility : libnform
4430| Function : char *field_buffer(const FIELD *field,int buffer)
4431|
4432| Description : Return the address of the buffer for the field.
4433|
4434| Return Values : Pointer to buffer or NULL if arguments were invalid.
4435+--------------------------------------------------------------------------*/
4436NCURSES_EXPORT(char *)
4437field_buffer(const FIELD *field, int buffer)
4438{
4439 char *result = 0;
4440
4441 T((T_CALLED("field_buffer(%p,%d)"), field, buffer));
4442
4443 if (field && (buffer >= 0) && (buffer <= field->nbuf))
4444 {
4445#if USE_WIDEC_SUPPORT
4446 FIELD_CELL *data = Address_Of_Nth_Buffer(field, buffer);
4447 unsigned need = 0;
4448 int size = Buffer_Length(field);
4449 int n;
4450
4451 /* determine the number of bytes needed to store the expanded string */
4452 for (n = 0; n < size; ++n)
4453 {
4454 if (!isWidecExt(data[n]))
4455 {
4456 mbstate_t state;
4457 size_t next;
4458
4459 init_mb(state);
4460 next = _nc_wcrtomb(0, data[n].chars[0], &state);
4461 if (!isEILSEQ(next))
4462 need += next;
4463 }
4464 }
4465
4466 /* allocate a place to store the expanded string */
4467 if (field->expanded[buffer] != 0)
4468 free(field->expanded[buffer]);
4469 field->expanded[buffer] = typeMalloc(char, need + 1);
4470
4471 /* expand the multibyte data */
4472 if ((result = field->expanded[buffer]) != 0)
4473 {
4474 wclear(field->working);
4475 mvwadd_wchnstr(field->working, 0, 0, data, size);
4476 mvwinnstr(field->working, 0, 0, result, (int)need);
4477 }
4478#else
4479 result = Address_Of_Nth_Buffer(field, buffer);
4480#endif
4481 }
4482 returnPtr(result);
4483}
4484
4485#if USE_WIDEC_SUPPORT
4486
4487/* FIXME: see lib_get_wch.c */
4488#if HAVE_MBTOWC && HAVE_MBLEN
4489#define reset_mbytes(state) mblen(NULL, 0), mbtowc(NULL, NULL, 0)
4490#define count_mbytes(buffer,length,state) mblen(buffer,length)
4491#define trans_mbytes(wch,buffer,length,state) \
4492 (int) mbtowc(&wch, buffer, length)
4493#elif HAVE_MBRTOWC && HAVE_MBRLEN
4494#define NEED_STATE
4495#define reset_mbytes(state) init_mb(state)
4496#define count_mbytes(buffer,length,state) mbrlen(buffer,length,&state)
4497#define trans_mbytes(wch,buffer,length,state) \
4498 (int) mbrtowc(&wch, buffer, length, &state)
4499#else
4500make an error
4501#endif
4502
4503/*---------------------------------------------------------------------------
4504| Convert a multibyte string to a wide-character string. The result must be
4505| freed by the caller.
4506+--------------------------------------------------------------------------*/
4507NCURSES_EXPORT(wchar_t *)
4508_nc_Widen_String(char *source, int *lengthp)
4509{
4510 wchar_t *result = 0;
4511 wchar_t wch;
4512 size_t given = strlen(source);
4513 size_t tries;
4514 int pass;
4515 int status;
4516
4517#ifdef NEED_STATE
4518 mbstate_t state;
4519#endif
4520
4521 for (pass = 0; pass < 2; ++pass)
4522 {
4523 unsigned need = 0;
4524 size_t passed = 0;
4525
4526 while (passed < given)
4527 {
4528 bool found = FALSE;
4529
4530 for (tries = 1, status = 0; tries <= (given - passed); ++tries)
4531 {
4532 int save = source[passed + tries];
4533
4534 source[passed + tries] = 0;
4535 reset_mbytes(state);
4536 status = trans_mbytes(wch, source + passed, tries, state);
4537 source[passed + tries] = save;
4538
4539 if (status > 0)
4540 {
4541 found = TRUE;
4542 break;
4543 }
4544 }
4545 if (found)
4546 {
4547 if (pass)
4548 {
4549 result[need] = wch;
4550 }
4551 passed += status;
4552 ++need;
4553 }
4554 else
4555 {
4556 if (pass)
4557 {
4558 result[need] = source[passed];
4559 }
4560 ++need;
4561 ++passed;
4562 }
4563 }
4564
4565 if (!pass)
4566 {
4567 if (!need)
4568 break;
4569 result = typeCalloc(wchar_t, need);
4570
4571 *lengthp = need;
4572 if (result == 0)
4573 break;
4574 }
4575 }
4576
4577 return result;
4578}
4579#endif
4580
4581/* frm_driver.c ends here */
680 if (wresize(field->working, 1, Buffer_Length(field) + 1) == ERR)
681 result = FALSE;
682#endif
683
684 if (need_visual_update && result)
685 {
686 WINDOW *new_window = newpad(field->drows, field->dcols);
687
688 if (new_window != 0)
689 {
690 assert(form != (FORM *)0);
691 if (form->w)
692 delwin(form->w);
693 form->w = new_window;
694 Set_Field_Window_Attributes(field, form->w);
695 werase(form->w);
696 Buffer_To_Window(field, form->w);
697 untouchwin(form->w);
698 wmove(form->w, form->currow, form->curcol);
699 }
700 else
701 result = FALSE;
702 }
703
704 if (result)
705 {
706 free(oldbuf);
707 /* reflect changes in linked fields */
708 if (field != field->link)
709 {
710 FIELD *linked_field;
711
712 for (linked_field = field->link;
713 linked_field != field;
714 linked_field = linked_field->link)
715 {
716 linked_field->buf = field->buf;
717 linked_field->drows = field->drows;
718 linked_field->dcols = field->dcols;
719 }
720 }
721 }
722 else
723 {
724 /* restore old state */
725 field->dcols = old_dcols;
726 field->drows = old_drows;
727 field->buf = oldbuf;
728 if ((single_line_field &&
729 (field->dcols != field->maxgrow)) ||
730 (!single_line_field &&
731 (field->drows != field->maxgrow)))
732 field->status |= _MAY_GROW;
733 free(newbuf);
734 }
735 }
736 }
737 return (result);
738}
739
740#ifdef NCURSES_MOUSE_VERSION
741/*---------------------------------------------------------------------------
742| Facility : libnform
743| Function : int Field_encloses(FIELD *field, int ry, int rx)
744|
745| Description : Check if the given coordinates lie within the given field.
746|
747| Return Values : E_OK - success
748| E_BAD_ARGUMENT - invalid form pointer
749| E_SYSTEM_ERROR - form has no current field or
750| field-window
751+--------------------------------------------------------------------------*/
752static int
753Field_encloses(FIELD *field, int ry, int rx)
754{
755 T((T_CALLED("Field_encloses(%p)"), field));
756 if (field != 0
757 && field->frow <= ry
758 && (field->frow + field->rows) > ry
759 && field->fcol <= rx
760 && (field->fcol + field->cols) > rx)
761 {
762 RETURN(E_OK);
763 }
764 RETURN(E_INVALID_FIELD);
765}
766#endif
767
768/*---------------------------------------------------------------------------
769| Facility : libnform
770| Function : int _nc_Position_Form_Cursor(FORM * form)
771|
772| Description : Position the cursor in the window for the current
773| field to be in sync. with the currow and curcol
774| values.
775|
776| Return Values : E_OK - success
777| E_BAD_ARGUMENT - invalid form pointer
778| E_SYSTEM_ERROR - form has no current field or
779| field-window
780+--------------------------------------------------------------------------*/
781NCURSES_EXPORT(int)
782_nc_Position_Form_Cursor(FORM *form)
783{
784 FIELD *field;
785 WINDOW *formwin;
786
787 if (!form)
788 return (E_BAD_ARGUMENT);
789
790 if (!form->w || !form->current)
791 return (E_SYSTEM_ERROR);
792
793 field = form->current;
794 formwin = Get_Form_Window(form);
795
796 wmove(form->w, form->currow, form->curcol);
797 if (Has_Invisible_Parts(field))
798 {
799 /* in this case fieldwin isn't derived from formwin, so we have
800 to move the cursor in formwin by hand... */
801 wmove(formwin,
802 field->frow + form->currow - form->toprow,
803 field->fcol + form->curcol - form->begincol);
804 wcursyncup(formwin);
805 }
806 else
807 wcursyncup(form->w);
808 return (E_OK);
809}
810
811/*---------------------------------------------------------------------------
812| Facility : libnform
813| Function : int _nc_Refresh_Current_Field(FORM * form)
814|
815| Description : Propagate the changes in the fields window to the
816| window of the form.
817|
818| Return Values : E_OK - on success
819| E_BAD_ARGUMENT - invalid form pointer
820| E_SYSTEM_ERROR - general error
821+--------------------------------------------------------------------------*/
822NCURSES_EXPORT(int)
823_nc_Refresh_Current_Field(FORM *form)
824{
825 WINDOW *formwin;
826 FIELD *field;
827
828 T((T_CALLED("_nc_Refresh_Current_Field(%p)"), form));
829
830 if (!form)
831 RETURN(E_BAD_ARGUMENT);
832
833 if (!form->w || !form->current)
834 RETURN(E_SYSTEM_ERROR);
835
836 field = form->current;
837 formwin = Get_Form_Window(form);
838
839 if (field->opts & O_PUBLIC)
840 {
841 if (Is_Scroll_Field(field))
842 {
843 /* Again, in this case the fieldwin isn't derived from formwin,
844 so we have to perform a copy operation. */
845 if (Single_Line_Field(field))
846 {
847 /* horizontal scrolling */
848 if (form->curcol < form->begincol)
849 form->begincol = form->curcol;
850 else
851 {
852 if (form->curcol >= (form->begincol + field->cols))
853 form->begincol = form->curcol - field->cols + 1;
854 }
855 copywin(form->w,
856 formwin,
857 0,
858 form->begincol,
859 field->frow,
860 field->fcol,
861 field->frow,
862 field->cols + field->fcol - 1,
863 0);
864 }
865 else
866 {
867 /* A multi-line, i.e. vertical scrolling field */
868 int row_after_bottom, first_modified_row, first_unmodified_row;
869
870 if (field->drows > field->rows)
871 {
872 row_after_bottom = form->toprow + field->rows;
873 if (form->currow < form->toprow)
874 {
875 form->toprow = form->currow;
876 field->status |= _NEWTOP;
877 }
878 if (form->currow >= row_after_bottom)
879 {
880 form->toprow = form->currow - field->rows + 1;
881 field->status |= _NEWTOP;
882 }
883 if (field->status & _NEWTOP)
884 {
885 /* means we have to copy whole range */
886 first_modified_row = form->toprow;
887 first_unmodified_row = first_modified_row + field->rows;
888 field->status &= ~_NEWTOP;
889 }
890 else
891 {
892 /* we try to optimize : finding the range of touched
893 lines */
894 first_modified_row = form->toprow;
895 while (first_modified_row < row_after_bottom)
896 {
897 if (is_linetouched(form->w, first_modified_row))
898 break;
899 first_modified_row++;
900 }
901 first_unmodified_row = first_modified_row;
902 while (first_unmodified_row < row_after_bottom)
903 {
904 if (!is_linetouched(form->w, first_unmodified_row))
905 break;
906 first_unmodified_row++;
907 }
908 }
909 }
910 else
911 {
912 first_modified_row = form->toprow;
913 first_unmodified_row = first_modified_row + field->rows;
914 }
915 if (first_unmodified_row != first_modified_row)
916 copywin(form->w,
917 formwin,
918 first_modified_row,
919 0,
920 field->frow + first_modified_row - form->toprow,
921 field->fcol,
922 field->frow + first_unmodified_row - form->toprow - 1,
923 field->cols + field->fcol - 1,
924 0);
925 }
926 wsyncup(formwin);
927 }
928 else
929 {
930 /* if the field-window is simply a derived window, i.e. contains no
931 * invisible parts, the whole thing is trivial
932 */
933 wsyncup(form->w);
934 }
935 }
936 untouchwin(form->w);
937 returnCode(_nc_Position_Form_Cursor(form));
938}
939
940/*---------------------------------------------------------------------------
941| Facility : libnform
942| Function : static void Perform_Justification(
943| FIELD * field,
944| WINDOW * win)
945|
946| Description : Output field with requested justification
947|
948| Return Values : -
949+--------------------------------------------------------------------------*/
950static void
951Perform_Justification(FIELD *field, WINDOW *win)
952{
953 FIELD_CELL *bp;
954 int len;
955 int col = 0;
956
957 bp = Get_Start_Of_Data(field->buf, Buffer_Length(field));
958 len = (int)(After_End_Of_Data(field->buf, Buffer_Length(field)) - bp);
959
960 if (len > 0)
961 {
962 assert(win && (field->drows == 1) && (field->dcols == field->cols));
963
964 switch (field->just)
965 {
966 case JUSTIFY_LEFT:
967 break;
968 case JUSTIFY_CENTER:
969 col = (field->cols - len) / 2;
970 break;
971 case JUSTIFY_RIGHT:
972 col = field->cols - len;
973 break;
974 default:
975 break;
976 }
977
978 wmove(win, 0, col);
979 myADDNSTR(win, bp, len);
980 }
981}
982
983/*---------------------------------------------------------------------------
984| Facility : libnform
985| Function : static void Undo_Justification(
986| FIELD * field,
987| WINDOW * win)
988|
989| Description : Display field without any justification, i.e.
990| left justified
991|
992| Return Values : -
993+--------------------------------------------------------------------------*/
994static void
995Undo_Justification(FIELD *field, WINDOW *win)
996{
997 FIELD_CELL *bp;
998 int len;
999
1000 bp = Get_Start_Of_Data(field->buf, Buffer_Length(field));
1001 len = (int)(After_End_Of_Data(field->buf, Buffer_Length(field)) - bp);
1002
1003 if (len > 0)
1004 {
1005 assert(win);
1006 wmove(win, 0, 0);
1007 myADDNSTR(win, bp, len);
1008 }
1009}
1010
1011/*---------------------------------------------------------------------------
1012| Facility : libnform
1013| Function : static bool Check_Char(
1014| FIELDTYPE * typ,
1015| int ch,
1016| TypeArgument *argp)
1017|
1018| Description : Perform a single character check for character ch
1019| according to the fieldtype instance.
1020|
1021| Return Values : TRUE - Character is valid
1022| FALSE - Character is invalid
1023+--------------------------------------------------------------------------*/
1024static bool
1025Check_Char(FIELDTYPE *typ, int ch, TypeArgument *argp)
1026{
1027 if (typ)
1028 {
1029 if (typ->status & _LINKED_TYPE)
1030 {
1031 assert(argp);
1032 return (
1033 Check_Char(typ->left, ch, argp->left) ||
1034 Check_Char(typ->right, ch, argp->right));
1035 }
1036 else
1037 {
1038 if (typ->ccheck)
1039 return typ->ccheck(ch, (void *)argp);
1040 }
1041 }
1042 return (!iscntrl(UChar(ch)) ? TRUE : FALSE);
1043}
1044
1045/*---------------------------------------------------------------------------
1046| Facility : libnform
1047| Function : static int Display_Or_Erase_Field(
1048| FIELD * field,
1049| bool bEraseFlag)
1050|
1051| Description : Create a subwindow for the field and display the
1052| buffer contents (apply justification if required)
1053| or simply erase the field.
1054|
1055| Return Values : E_OK - on success
1056| E_SYSTEM_ERROR - some error (typical no memory)
1057+--------------------------------------------------------------------------*/
1058static int
1059Display_Or_Erase_Field(FIELD *field, bool bEraseFlag)
1060{
1061 WINDOW *win;
1062 WINDOW *fwin;
1063
1064 if (!field)
1065 return E_SYSTEM_ERROR;
1066
1067 fwin = Get_Form_Window(field->form);
1068 win = derwin(fwin,
1069 field->rows, field->cols, field->frow, field->fcol);
1070
1071 if (!win)
1072 return E_SYSTEM_ERROR;
1073 else
1074 {
1075 if (field->opts & O_VISIBLE)
1076 Set_Field_Window_Attributes(field, win);
1077 else
1078 wattrset(win, WINDOW_ATTRS(fwin));
1079 werase(win);
1080 }
1081
1082 if (!bEraseFlag)
1083 {
1084 if (field->opts & O_PUBLIC)
1085 {
1086 if (Justification_Allowed(field))
1087 Perform_Justification(field, win);
1088 else
1089 Buffer_To_Window(field, win);
1090 }
1091 field->status &= ~_NEWTOP;
1092 }
1093 wsyncup(win);
1094 delwin(win);
1095 return E_OK;
1096}
1097
1098/* Macros to preset the bEraseFlag */
1099#define Display_Field(field) Display_Or_Erase_Field(field,FALSE)
1100#define Erase_Field(field) Display_Or_Erase_Field(field,TRUE)
1101
1102/*---------------------------------------------------------------------------
1103| Facility : libnform
1104| Function : static int Synchronize_Field(FIELD * field)
1105|
1106| Description : Synchronize the windows content with the value in
1107| the buffer.
1108|
1109| Return Values : E_OK - success
1110| E_BAD_ARGUMENT - invalid field pointer
1111| E_SYSTEM_ERROR - some severe basic error
1112+--------------------------------------------------------------------------*/
1113static int
1114Synchronize_Field(FIELD *field)
1115{
1116 FORM *form;
1117 int res = E_OK;
1118
1119 if (!field)
1120 return (E_BAD_ARGUMENT);
1121
1122 if (((form = field->form) != (FORM *)0)
1123 && Field_Really_Appears(field))
1124 {
1125 if (field == form->current)
1126 {
1127 form->currow = form->curcol = form->toprow = form->begincol = 0;
1128 werase(form->w);
1129
1130 if ((field->opts & O_PUBLIC) && Justification_Allowed(field))
1131 Undo_Justification(field, form->w);
1132 else
1133 Buffer_To_Window(field, form->w);
1134
1135 field->status |= _NEWTOP;
1136 res = _nc_Refresh_Current_Field(form);
1137 }
1138 else
1139 res = Display_Field(field);
1140 }
1141 field->status |= _CHANGED;
1142 return (res);
1143}
1144
1145/*---------------------------------------------------------------------------
1146| Facility : libnform
1147| Function : static int Synchronize_Linked_Fields(FIELD * field)
1148|
1149| Description : Propagate the Synchronize_Field function to all linked
1150| fields. The first error that occurs in the sequence
1151| of updates is the return value.
1152|
1153| Return Values : E_OK - success
1154| E_BAD_ARGUMENT - invalid field pointer
1155| E_SYSTEM_ERROR - some severe basic error
1156+--------------------------------------------------------------------------*/
1157static int
1158Synchronize_Linked_Fields(FIELD *field)
1159{
1160 FIELD *linked_field;
1161 int res = E_OK;
1162 int syncres;
1163
1164 if (!field)
1165 return (E_BAD_ARGUMENT);
1166
1167 if (!field->link)
1168 return (E_SYSTEM_ERROR);
1169
1170 for (linked_field = field->link;
1171 linked_field != field;
1172 linked_field = linked_field->link)
1173 {
1174 if (((syncres = Synchronize_Field(linked_field)) != E_OK) &&
1175 (res == E_OK))
1176 res = syncres;
1177 }
1178 return (res);
1179}
1180
1181/*---------------------------------------------------------------------------
1182| Facility : libnform
1183| Function : int _nc_Synchronize_Attributes(FIELD * field)
1184|
1185| Description : If a fields visual attributes have changed, this
1186| routine is called to propagate those changes to the
1187| screen.
1188|
1189| Return Values : E_OK - success
1190| E_BAD_ARGUMENT - invalid field pointer
1191| E_SYSTEM_ERROR - some severe basic error
1192+--------------------------------------------------------------------------*/
1193NCURSES_EXPORT(int)
1194_nc_Synchronize_Attributes(FIELD *field)
1195{
1196 FORM *form;
1197 int res = E_OK;
1198 WINDOW *formwin;
1199
1200 T((T_CALLED("_nc_Synchronize_Attributes(%p)"), field));
1201
1202 if (!field)
1203 returnCode(E_BAD_ARGUMENT);
1204
1205 CHECKPOS(field->form);
1206 if (((form = field->form) != (FORM *)0)
1207 && Field_Really_Appears(field))
1208 {
1209 if (form->current == field)
1210 {
1211 Synchronize_Buffer(form);
1212 Set_Field_Window_Attributes(field, form->w);
1213 werase(form->w);
1214 wmove(form->w, form->currow, form->curcol);
1215
1216 if (field->opts & O_PUBLIC)
1217 {
1218 if (Justification_Allowed(field))
1219 Undo_Justification(field, form->w);
1220 else
1221 Buffer_To_Window(field, form->w);
1222 }
1223 else
1224 {
1225 formwin = Get_Form_Window(form);
1226 copywin(form->w, formwin,
1227 0, 0,
1228 field->frow, field->fcol,
1229 field->rows - 1, field->cols - 1, 0);
1230 wsyncup(formwin);
1231 Buffer_To_Window(field, form->w);
1232 field->status |= _NEWTOP; /* fake refresh to paint all */
1233 _nc_Refresh_Current_Field(form);
1234 }
1235 }
1236 else
1237 {
1238 res = Display_Field(field);
1239 }
1240 }
1241 CHECKPOS(form);
1242 returnCode(res);
1243}
1244
1245/*---------------------------------------------------------------------------
1246| Facility : libnform
1247| Function : int _nc_Synchronize_Options(FIELD * field,
1248| Field_Options newopts)
1249|
1250| Description : If a fields options have changed, this routine is
1251| called to propagate these changes to the screen and
1252| to really change the behavior of the field.
1253|
1254| Return Values : E_OK - success
1255| E_BAD_ARGUMENT - invalid field pointer
1256| E_CURRENT - field is the current one
1257| E_SYSTEM_ERROR - some severe basic error
1258+--------------------------------------------------------------------------*/
1259NCURSES_EXPORT(int)
1260_nc_Synchronize_Options(FIELD *field, Field_Options newopts)
1261{
1262 Field_Options oldopts;
1263 Field_Options changed_opts;
1264 FORM *form;
1265 int res = E_OK;
1266
1267 T((T_CALLED("_nc_Synchronize_Options(%p,%#x)"), field, newopts));
1268
1269 if (!field)
1270 returnCode(E_BAD_ARGUMENT);
1271
1272 oldopts = field->opts;
1273 changed_opts = oldopts ^ newopts;
1274 field->opts = newopts;
1275 form = field->form;
1276
1277 if (form)
1278 {
1279 if (form->current == field)
1280 {
1281 field->opts = oldopts;
1282 returnCode(E_CURRENT);
1283 }
1284
1285 if (form->status & _POSTED)
1286 {
1287 if ((form->curpage == field->page))
1288 {
1289 if (changed_opts & O_VISIBLE)
1290 {
1291 if (newopts & O_VISIBLE)
1292 res = Display_Field(field);
1293 else
1294 res = Erase_Field(field);
1295 }
1296 else
1297 {
1298 if ((changed_opts & O_PUBLIC) &&
1299 (newopts & O_VISIBLE))
1300 res = Display_Field(field);
1301 }
1302 }
1303 }
1304 }
1305
1306 if (changed_opts & O_STATIC)
1307 {
1308 bool single_line_field = Single_Line_Field(field);
1309 int res2 = E_OK;
1310
1311 if (newopts & O_STATIC)
1312 {
1313 /* the field becomes now static */
1314 field->status &= ~_MAY_GROW;
1315 /* if actually we have no hidden columns, justification may
1316 occur again */
1317 if (single_line_field &&
1318 (field->cols == field->dcols) &&
1319 (field->just != NO_JUSTIFICATION) &&
1320 Field_Really_Appears(field))
1321 {
1322 res2 = Display_Field(field);
1323 }
1324 }
1325 else
1326 {
1327 /* field is no longer static */
1328 if ((field->maxgrow == 0) ||
1329 (single_line_field && (field->dcols < field->maxgrow)) ||
1330 (!single_line_field && (field->drows < field->maxgrow)))
1331 {
1332 field->status |= _MAY_GROW;
1333 /* a field with justification now changes its behavior,
1334 so we must redisplay it */
1335 if (single_line_field &&
1336 (field->just != NO_JUSTIFICATION) &&
1337 Field_Really_Appears(field))
1338 {
1339 res2 = Display_Field(field);
1340 }
1341 }
1342 }
1343 if (res2 != E_OK)
1344 res = res2;
1345 }
1346
1347 returnCode(res);
1348}
1349
1350/*---------------------------------------------------------------------------
1351| Facility : libnform
1352| Function : int _nc_Set_Current_Field(FORM * form,
1353| FIELD * newfield)
1354|
1355| Description : Make the newfield the new current field.
1356|
1357| Return Values : E_OK - success
1358| E_BAD_ARGUMENT - invalid form or field pointer
1359| E_SYSTEM_ERROR - some severe basic error
1360| E_NOT_CONNECTED - no fields are connected to the form
1361+--------------------------------------------------------------------------*/
1362NCURSES_EXPORT(int)
1363_nc_Set_Current_Field(FORM *form, FIELD *newfield)
1364{
1365 FIELD *field;
1366 WINDOW *new_window;
1367
1368 T((T_CALLED("_nc_Set_Current_Field(%p,%p)"), form, newfield));
1369
1370 if (!form || !newfield || !form->current || (newfield->form != form))
1371 returnCode(E_BAD_ARGUMENT);
1372
1373 if ((form->status & _IN_DRIVER))
1374 returnCode(E_BAD_STATE);
1375
1376 if (!(form->field))
1377 returnCode(E_NOT_CONNECTED);
1378
1379 field = form->current;
1380
1381 if ((field != newfield) ||
1382 !(form->status & _POSTED))
1383 {
1384 if ((form->w) &&
1385 (field->opts & O_VISIBLE) &&
1386 (field->form->curpage == field->page))
1387 {
1388 _nc_Refresh_Current_Field(form);
1389 if (field->opts & O_PUBLIC)
1390 {
1391 if (field->drows > field->rows)
1392 {
1393 if (form->toprow == 0)
1394 field->status &= ~_NEWTOP;
1395 else
1396 field->status |= _NEWTOP;
1397 }
1398 else
1399 {
1400 if (Justification_Allowed(field))
1401 {
1402 Window_To_Buffer(form->w, field);
1403 werase(form->w);
1404 Perform_Justification(field, form->w);
1405 wsyncup(form->w);
1406 }
1407 }
1408 }
1409 delwin(form->w);
1410 form->w = (WINDOW *)0;
1411 }
1412
1413 field = newfield;
1414
1415 if (Has_Invisible_Parts(field))
1416 new_window = newpad(field->drows, field->dcols);
1417 else
1418 new_window = derwin(Get_Form_Window(form),
1419 field->rows, field->cols, field->frow, field->fcol);
1420
1421 if (!new_window)
1422 returnCode(E_SYSTEM_ERROR);
1423
1424 form->current = field;
1425
1426 if (form->w)
1427 delwin(form->w);
1428 form->w = new_window;
1429
1430 form->status &= ~_WINDOW_MODIFIED;
1431 Set_Field_Window_Attributes(field, form->w);
1432
1433 if (Has_Invisible_Parts(field))
1434 {
1435 werase(form->w);
1436 Buffer_To_Window(field, form->w);
1437 }
1438 else
1439 {
1440 if (Justification_Allowed(field))
1441 {
1442 werase(form->w);
1443 Undo_Justification(field, form->w);
1444 wsyncup(form->w);
1445 }
1446 }
1447
1448 untouchwin(form->w);
1449 }
1450
1451 form->currow = form->curcol = form->toprow = form->begincol = 0;
1452 returnCode(E_OK);
1453}
1454
1455/*----------------------------------------------------------------------------
1456 Intra-Field Navigation routines
1457 --------------------------------------------------------------------------*/
1458
1459/*---------------------------------------------------------------------------
1460| Facility : libnform
1461| Function : static int IFN_Next_Character(FORM * form)
1462|
1463| Description : Move to the next character in the field. In a multi-line
1464| field this wraps at the end of the line.
1465|
1466| Return Values : E_OK - success
1467| E_REQUEST_DENIED - at the rightmost position
1468+--------------------------------------------------------------------------*/
1469static int
1470IFN_Next_Character(FORM *form)
1471{
1472 FIELD *field = form->current;
1473 int step = myWCWIDTH(form->w, form->currow, form->curcol);
1474
1475 T((T_CALLED("IFN_Next_Character(%p)"), form));
1476 if ((form->curcol += step) == field->dcols)
1477 {
1478 if ((++(form->currow)) == field->drows)
1479 {
1480#if GROW_IF_NAVIGATE
1481 if (!Single_Line_Field(field) && Field_Grown(field, 1))
1482 {
1483 form->curcol = 0;
1484 returnCode(E_OK);
1485 }
1486#endif
1487 form->currow--;
1488#if GROW_IF_NAVIGATE
1489 if (Single_Line_Field(field) && Field_Grown(field, 1))
1490 returnCode(E_OK);
1491#endif
1492 form->curcol -= step;
1493 returnCode(E_REQUEST_DENIED);
1494 }
1495 form->curcol = 0;
1496 }
1497 returnCode(E_OK);
1498}
1499
1500/*---------------------------------------------------------------------------
1501| Facility : libnform
1502| Function : static int IFN_Previous_Character(FORM * form)
1503|
1504| Description : Move to the previous character in the field. In a
1505| multi-line field this wraps and the beginning of the
1506| line.
1507|
1508| Return Values : E_OK - success
1509| E_REQUEST_DENIED - at the leftmost position
1510+--------------------------------------------------------------------------*/
1511static int
1512IFN_Previous_Character(FORM *form)
1513{
1514 int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1);
1515 int oldcol = form->curcol;
1516
1517 T((T_CALLED("IFN_Previous_Character(%p)"), form));
1518 if ((form->curcol -= amount) < 0)
1519 {
1520 if ((--(form->currow)) < 0)
1521 {
1522 form->currow++;
1523 form->curcol = oldcol;
1524 returnCode(E_REQUEST_DENIED);
1525 }
1526 form->curcol = form->current->dcols - 1;
1527 }
1528 returnCode(E_OK);
1529}
1530
1531/*---------------------------------------------------------------------------
1532| Facility : libnform
1533| Function : static int IFN_Next_Line(FORM * form)
1534|
1535| Description : Move to the beginning of the next line in the field
1536|
1537| Return Values : E_OK - success
1538| E_REQUEST_DENIED - at the last line
1539+--------------------------------------------------------------------------*/
1540static int
1541IFN_Next_Line(FORM *form)
1542{
1543 FIELD *field = form->current;
1544
1545 T((T_CALLED("IFN_Next_Line(%p)"), form));
1546 if ((++(form->currow)) == field->drows)
1547 {
1548#if GROW_IF_NAVIGATE
1549 if (!Single_Line_Field(field) && Field_Grown(field, 1))
1550 returnCode(E_OK);
1551#endif
1552 form->currow--;
1553 returnCode(E_REQUEST_DENIED);
1554 }
1555 form->curcol = 0;
1556 returnCode(E_OK);
1557}
1558
1559/*---------------------------------------------------------------------------
1560| Facility : libnform
1561| Function : static int IFN_Previous_Line(FORM * form)
1562|
1563| Description : Move to the beginning of the previous line in the field
1564|
1565| Return Values : E_OK - success
1566| E_REQUEST_DENIED - at the first line
1567+--------------------------------------------------------------------------*/
1568static int
1569IFN_Previous_Line(FORM *form)
1570{
1571 T((T_CALLED("IFN_Previous_Line(%p)"), form));
1572 if ((--(form->currow)) < 0)
1573 {
1574 form->currow++;
1575 returnCode(E_REQUEST_DENIED);
1576 }
1577 form->curcol = 0;
1578 returnCode(E_OK);
1579}
1580
1581/*---------------------------------------------------------------------------
1582| Facility : libnform
1583| Function : static int IFN_Next_Word(FORM * form)
1584|
1585| Description : Move to the beginning of the next word in the field.
1586|
1587| Return Values : E_OK - success
1588| E_REQUEST_DENIED - there is no next word
1589+--------------------------------------------------------------------------*/
1590static int
1591IFN_Next_Word(FORM *form)
1592{
1593 FIELD *field = form->current;
1594 FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1595 FIELD_CELL *s;
1596 FIELD_CELL *t;
1597
1598 T((T_CALLED("IFN_Next_Word(%p)"), form));
1599
1600 /* We really need access to the data, so we have to synchronize */
1601 Synchronize_Buffer(form);
1602
1603 /* Go to the first whitespace after the current position (including
1604 current position). This is then the starting point to look for the
1605 next non-blank data */
1606 s = Get_First_Whitespace_Character(bp, Buffer_Length(field) -
1607 (int)(bp - field->buf));
1608
1609 /* Find the start of the next word */
1610 t = Get_Start_Of_Data(s, Buffer_Length(field) -
1611 (int)(s - field->buf));
1612#if !FRIENDLY_PREV_NEXT_WORD
1613 if (s == t)
1614 returnCode(E_REQUEST_DENIED);
1615 else
1616#endif
1617 {
1618 Adjust_Cursor_Position(form, t);
1619 returnCode(E_OK);
1620 }
1621}
1622
1623/*---------------------------------------------------------------------------
1624| Facility : libnform
1625| Function : static int IFN_Previous_Word(FORM * form)
1626|
1627| Description : Move to the beginning of the previous word in the field.
1628|
1629| Return Values : E_OK - success
1630| E_REQUEST_DENIED - there is no previous word
1631+--------------------------------------------------------------------------*/
1632static int
1633IFN_Previous_Word(FORM *form)
1634{
1635 FIELD *field = form->current;
1636 FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1637 FIELD_CELL *s;
1638 FIELD_CELL *t;
1639 bool again = FALSE;
1640
1641 T((T_CALLED("IFN_Previous_Word(%p)"), form));
1642
1643 /* We really need access to the data, so we have to synchronize */
1644 Synchronize_Buffer(form);
1645
1646 s = After_End_Of_Data(field->buf, (int)(bp - field->buf));
1647 /* s points now right after the last non-blank in the buffer before bp.
1648 If bp was in a word, s equals bp. In this case we must find the last
1649 whitespace in the buffer before bp and repeat the game to really find
1650 the previous word! */
1651 if (s == bp)
1652 again = TRUE;
1653
1654 /* And next call now goes backward to look for the last whitespace
1655 before that, pointing right after this, so it points to the begin
1656 of the previous word.
1657 */
1658 t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf));
1659#if !FRIENDLY_PREV_NEXT_WORD
1660 if (s == t)
1661 returnCode(E_REQUEST_DENIED);
1662#endif
1663 if (again)
1664 {
1665 /* and do it again, replacing bp by t */
1666 s = After_End_Of_Data(field->buf, (int)(t - field->buf));
1667 t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf));
1668#if !FRIENDLY_PREV_NEXT_WORD
1669 if (s == t)
1670 returnCode(E_REQUEST_DENIED);
1671#endif
1672 }
1673 Adjust_Cursor_Position(form, t);
1674 returnCode(E_OK);
1675}
1676
1677/*---------------------------------------------------------------------------
1678| Facility : libnform
1679| Function : static int IFN_Beginning_Of_Field(FORM * form)
1680|
1681| Description : Place the cursor at the first non-pad character in
1682| the field.
1683|
1684| Return Values : E_OK - success
1685+--------------------------------------------------------------------------*/
1686static int
1687IFN_Beginning_Of_Field(FORM *form)
1688{
1689 FIELD *field = form->current;
1690
1691 T((T_CALLED("IFN_Beginning_Of_Field(%p)"), form));
1692 Synchronize_Buffer(form);
1693 Adjust_Cursor_Position(form,
1694 Get_Start_Of_Data(field->buf, Buffer_Length(field)));
1695 returnCode(E_OK);
1696}
1697
1698/*---------------------------------------------------------------------------
1699| Facility : libnform
1700| Function : static int IFN_End_Of_Field(FORM * form)
1701|
1702| Description : Place the cursor after the last non-pad character in
1703| the field. If the field occupies the last position in
1704| the buffer, the cursor is positioned on the last
1705| character.
1706|
1707| Return Values : E_OK - success
1708+--------------------------------------------------------------------------*/
1709static int
1710IFN_End_Of_Field(FORM *form)
1711{
1712 FIELD *field = form->current;
1713 FIELD_CELL *pos;
1714
1715 T((T_CALLED("IFN_End_Of_Field(%p)"), form));
1716 Synchronize_Buffer(form);
1717 pos = After_End_Of_Data(field->buf, Buffer_Length(field));
1718 if (pos == (field->buf + Buffer_Length(field)))
1719 pos--;
1720 Adjust_Cursor_Position(form, pos);
1721 returnCode(E_OK);
1722}
1723
1724/*---------------------------------------------------------------------------
1725| Facility : libnform
1726| Function : static int IFN_Beginning_Of_Line(FORM * form)
1727|
1728| Description : Place the cursor on the first non-pad character in
1729| the current line of the field.
1730|
1731| Return Values : E_OK - success
1732+--------------------------------------------------------------------------*/
1733static int
1734IFN_Beginning_Of_Line(FORM *form)
1735{
1736 FIELD *field = form->current;
1737
1738 T((T_CALLED("IFN_Beginning_Of_Line(%p)"), form));
1739 Synchronize_Buffer(form);
1740 Adjust_Cursor_Position(form,
1741 Get_Start_Of_Data(Address_Of_Current_Row_In_Buffer(form),
1742 field->dcols));
1743 returnCode(E_OK);
1744}
1745
1746/*---------------------------------------------------------------------------
1747| Facility : libnform
1748| Function : static int IFN_End_Of_Line(FORM * form)
1749|
1750| Description : Place the cursor after the last non-pad character in the
1751| current line of the field. If the field occupies the
1752| last column in the line, the cursor is positioned on the
1753| last character of the line.
1754|
1755| Return Values : E_OK - success
1756+--------------------------------------------------------------------------*/
1757static int
1758IFN_End_Of_Line(FORM *form)
1759{
1760 FIELD *field = form->current;
1761 FIELD_CELL *pos;
1762 FIELD_CELL *bp;
1763
1764 T((T_CALLED("IFN_End_Of_Line(%p)"), form));
1765 Synchronize_Buffer(form);
1766 bp = Address_Of_Current_Row_In_Buffer(form);
1767 pos = After_End_Of_Data(bp, field->dcols);
1768 if (pos == (bp + field->dcols))
1769 pos--;
1770 Adjust_Cursor_Position(form, pos);
1771 returnCode(E_OK);
1772}
1773
1774/*---------------------------------------------------------------------------
1775| Facility : libnform
1776| Function : static int IFN_Left_Character(FORM * form)
1777|
1778| Description : Move one character to the left in the current line.
1779| This doesn't cycle.
1780|
1781| Return Values : E_OK - success
1782| E_REQUEST_DENIED - already in first column
1783+--------------------------------------------------------------------------*/
1784static int
1785IFN_Left_Character(FORM *form)
1786{
1787 int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1);
1788 int oldcol = form->curcol;
1789
1790 T((T_CALLED("IFN_Left_Character(%p)"), form));
1791 if ((form->curcol -= amount) < 0)
1792 {
1793 form->curcol = oldcol;
1794 returnCode(E_REQUEST_DENIED);
1795 }
1796 returnCode(E_OK);
1797}
1798
1799/*---------------------------------------------------------------------------
1800| Facility : libnform
1801| Function : static int IFN_Right_Character(FORM * form)
1802|
1803| Description : Move one character to the right in the current line.
1804| This doesn't cycle.
1805|
1806| Return Values : E_OK - success
1807| E_REQUEST_DENIED - already in last column
1808+--------------------------------------------------------------------------*/
1809static int
1810IFN_Right_Character(FORM *form)
1811{
1812 int amount = myWCWIDTH(form->w, form->currow, form->curcol);
1813 int oldcol = form->curcol;
1814
1815 T((T_CALLED("IFN_Right_Character(%p)"), form));
1816 if ((form->curcol += amount) >= form->current->dcols)
1817 {
1818#if GROW_IF_NAVIGATE
1819 FIELD *field = form->current;
1820
1821 if (Single_Line_Field(field) && Field_Grown(field, 1))
1822 returnCode(E_OK);
1823#endif
1824 form->curcol = oldcol;
1825 returnCode(E_REQUEST_DENIED);
1826 }
1827 returnCode(E_OK);
1828}
1829
1830/*---------------------------------------------------------------------------
1831| Facility : libnform
1832| Function : static int IFN_Up_Character(FORM * form)
1833|
1834| Description : Move one line up. This doesn't cycle through the lines
1835| of the field.
1836|
1837| Return Values : E_OK - success
1838| E_REQUEST_DENIED - already in last column
1839+--------------------------------------------------------------------------*/
1840static int
1841IFN_Up_Character(FORM *form)
1842{
1843 T((T_CALLED("IFN_Up_Character(%p)"), form));
1844 if ((--(form->currow)) < 0)
1845 {
1846 form->currow++;
1847 returnCode(E_REQUEST_DENIED);
1848 }
1849 returnCode(E_OK);
1850}
1851
1852/*---------------------------------------------------------------------------
1853| Facility : libnform
1854| Function : static int IFN_Down_Character(FORM * form)
1855|
1856| Description : Move one line down. This doesn't cycle through the
1857| lines of the field.
1858|
1859| Return Values : E_OK - success
1860| E_REQUEST_DENIED - already in last column
1861+--------------------------------------------------------------------------*/
1862static int
1863IFN_Down_Character(FORM *form)
1864{
1865 FIELD *field = form->current;
1866
1867 T((T_CALLED("IFN_Down_Character(%p)"), form));
1868 if ((++(form->currow)) == field->drows)
1869 {
1870#if GROW_IF_NAVIGATE
1871 if (!Single_Line_Field(field) && Field_Grown(field, 1))
1872 returnCode(E_OK);
1873#endif
1874 --(form->currow);
1875 returnCode(E_REQUEST_DENIED);
1876 }
1877 returnCode(E_OK);
1878}
1879/*----------------------------------------------------------------------------
1880 END of Intra-Field Navigation routines
1881 --------------------------------------------------------------------------*/
1882
1883/*----------------------------------------------------------------------------
1884 Vertical scrolling helper routines
1885 --------------------------------------------------------------------------*/
1886
1887/*---------------------------------------------------------------------------
1888| Facility : libnform
1889| Function : static int VSC_Generic(FORM *form, int nlines)
1890|
1891| Description : Scroll multi-line field forward (nlines>0) or
1892| backward (nlines<0) this many lines.
1893|
1894| Return Values : E_OK - success
1895| E_REQUEST_DENIED - can't scroll
1896+--------------------------------------------------------------------------*/
1897static int
1898VSC_Generic(FORM *form, int nlines)
1899{
1900 FIELD *field = form->current;
1901 int res = E_REQUEST_DENIED;
1902 int rows_to_go = (nlines > 0 ? nlines : -nlines);
1903
1904 if (nlines > 0)
1905 {
1906 if ((rows_to_go + form->toprow) > (field->drows - field->rows))
1907 rows_to_go = (field->drows - field->rows - form->toprow);
1908
1909 if (rows_to_go > 0)
1910 {
1911 form->currow += rows_to_go;
1912 form->toprow += rows_to_go;
1913 res = E_OK;
1914 }
1915 }
1916 else
1917 {
1918 if (rows_to_go > form->toprow)
1919 rows_to_go = form->toprow;
1920
1921 if (rows_to_go > 0)
1922 {
1923 form->currow -= rows_to_go;
1924 form->toprow -= rows_to_go;
1925 res = E_OK;
1926 }
1927 }
1928 return (res);
1929}
1930/*----------------------------------------------------------------------------
1931 End of Vertical scrolling helper routines
1932 --------------------------------------------------------------------------*/
1933
1934/*----------------------------------------------------------------------------
1935 Vertical scrolling routines
1936 --------------------------------------------------------------------------*/
1937
1938/*---------------------------------------------------------------------------
1939| Facility : libnform
1940| Function : static int Vertical_Scrolling(
1941| int (* const fct) (FORM *),
1942| FORM * form)
1943|
1944| Description : Performs the generic vertical scrolling routines.
1945| This has to check for a multi-line field and to set
1946| the _NEWTOP flag if scrolling really occurred.
1947|
1948| Return Values : Propagated error code from low-level driver calls
1949+--------------------------------------------------------------------------*/
1950static int
1951Vertical_Scrolling(int (*const fct) (FORM *), FORM *form)
1952{
1953 int res = E_REQUEST_DENIED;
1954
1955 if (!Single_Line_Field(form->current))
1956 {
1957 res = fct(form);
1958 if (res == E_OK)
1959 form->current->status |= _NEWTOP;
1960 }
1961 return (res);
1962}
1963
1964/*---------------------------------------------------------------------------
1965| Facility : libnform
1966| Function : static int VSC_Scroll_Line_Forward(FORM * form)
1967|
1968| Description : Scroll multi-line field forward a line
1969|
1970| Return Values : E_OK - success
1971| E_REQUEST_DENIED - no data ahead
1972+--------------------------------------------------------------------------*/
1973static int
1974VSC_Scroll_Line_Forward(FORM *form)
1975{
1976 T((T_CALLED("VSC_Scroll_Line_Forward(%p)"), form));
1977 returnCode(VSC_Generic(form, 1));
1978}
1979
1980/*---------------------------------------------------------------------------
1981| Facility : libnform
1982| Function : static int VSC_Scroll_Line_Backward(FORM * form)
1983|
1984| Description : Scroll multi-line field backward a line
1985|
1986| Return Values : E_OK - success
1987| E_REQUEST_DENIED - no data behind
1988+--------------------------------------------------------------------------*/
1989static int
1990VSC_Scroll_Line_Backward(FORM *form)
1991{
1992 T((T_CALLED("VSC_Scroll_Line_Backward(%p)"), form));
1993 returnCode(VSC_Generic(form, -1));
1994}
1995
1996/*---------------------------------------------------------------------------
1997| Facility : libnform
1998| Function : static int VSC_Scroll_Page_Forward(FORM * form)
1999|
2000| Description : Scroll a multi-line field forward a page
2001|
2002| Return Values : E_OK - success
2003| E_REQUEST_DENIED - no data ahead
2004+--------------------------------------------------------------------------*/
2005static int
2006VSC_Scroll_Page_Forward(FORM *form)
2007{
2008 T((T_CALLED("VSC_Scroll_Page_Forward(%p)"), form));
2009 returnCode(VSC_Generic(form, form->current->rows));
2010}
2011
2012/*---------------------------------------------------------------------------
2013| Facility : libnform
2014| Function : static int VSC_Scroll_Half_Page_Forward(FORM * form)
2015|
2016| Description : Scroll a multi-line field forward half a page
2017|
2018| Return Values : E_OK - success
2019| E_REQUEST_DENIED - no data ahead
2020+--------------------------------------------------------------------------*/
2021static int
2022VSC_Scroll_Half_Page_Forward(FORM *form)
2023{
2024 T((T_CALLED("VSC_Scroll_Half_Page_Forward(%p)"), form));
2025 returnCode(VSC_Generic(form, (form->current->rows + 1) / 2));
2026}
2027
2028/*---------------------------------------------------------------------------
2029| Facility : libnform
2030| Function : static int VSC_Scroll_Page_Backward(FORM * form)
2031|
2032| Description : Scroll a multi-line field backward a page
2033|
2034| Return Values : E_OK - success
2035| E_REQUEST_DENIED - no data behind
2036+--------------------------------------------------------------------------*/
2037static int
2038VSC_Scroll_Page_Backward(FORM *form)
2039{
2040 T((T_CALLED("VSC_Scroll_Page_Backward(%p)"), form));
2041 returnCode(VSC_Generic(form, -(form->current->rows)));
2042}
2043
2044/*---------------------------------------------------------------------------
2045| Facility : libnform
2046| Function : static int VSC_Scroll_Half_Page_Backward(FORM * form)
2047|
2048| Description : Scroll a multi-line field backward half a page
2049|
2050| Return Values : E_OK - success
2051| E_REQUEST_DENIED - no data behind
2052+--------------------------------------------------------------------------*/
2053static int
2054VSC_Scroll_Half_Page_Backward(FORM *form)
2055{
2056 T((T_CALLED("VSC_Scroll_Half_Page_Backward(%p)"), form));
2057 returnCode(VSC_Generic(form, -((form->current->rows + 1) / 2)));
2058}
2059/*----------------------------------------------------------------------------
2060 End of Vertical scrolling routines
2061 --------------------------------------------------------------------------*/
2062
2063/*----------------------------------------------------------------------------
2064 Horizontal scrolling helper routines
2065 --------------------------------------------------------------------------*/
2066
2067/*---------------------------------------------------------------------------
2068| Facility : libnform
2069| Function : static int HSC_Generic(FORM *form, int ncolumns)
2070|
2071| Description : Scroll single-line field forward (ncolumns>0) or
2072| backward (ncolumns<0) this many columns.
2073|
2074| Return Values : E_OK - success
2075| E_REQUEST_DENIED - can't scroll
2076+--------------------------------------------------------------------------*/
2077static int
2078HSC_Generic(FORM *form, int ncolumns)
2079{
2080 FIELD *field = form->current;
2081 int res = E_REQUEST_DENIED;
2082 int cols_to_go = (ncolumns > 0 ? ncolumns : -ncolumns);
2083
2084 if (ncolumns > 0)
2085 {
2086 if ((cols_to_go + form->begincol) > (field->dcols - field->cols))
2087 cols_to_go = field->dcols - field->cols - form->begincol;
2088
2089 if (cols_to_go > 0)
2090 {
2091 form->curcol += cols_to_go;
2092 form->begincol += cols_to_go;
2093 res = E_OK;
2094 }
2095 }
2096 else
2097 {
2098 if (cols_to_go > form->begincol)
2099 cols_to_go = form->begincol;
2100
2101 if (cols_to_go > 0)
2102 {
2103 form->curcol -= cols_to_go;
2104 form->begincol -= cols_to_go;
2105 res = E_OK;
2106 }
2107 }
2108 return (res);
2109}
2110/*----------------------------------------------------------------------------
2111 End of Horizontal scrolling helper routines
2112 --------------------------------------------------------------------------*/
2113
2114/*----------------------------------------------------------------------------
2115 Horizontal scrolling routines
2116 --------------------------------------------------------------------------*/
2117
2118/*---------------------------------------------------------------------------
2119| Facility : libnform
2120| Function : static int Horizontal_Scrolling(
2121| int (* const fct) (FORM *),
2122| FORM * form)
2123|
2124| Description : Performs the generic horizontal scrolling routines.
2125| This has to check for a single-line field.
2126|
2127| Return Values : Propagated error code from low-level driver calls
2128+--------------------------------------------------------------------------*/
2129static int
2130Horizontal_Scrolling(int (*const fct) (FORM *), FORM *form)
2131{
2132 if (Single_Line_Field(form->current))
2133 return fct(form);
2134 else
2135 return (E_REQUEST_DENIED);
2136}
2137
2138/*---------------------------------------------------------------------------
2139| Facility : libnform
2140| Function : static int HSC_Scroll_Char_Forward(FORM * form)
2141|
2142| Description : Scroll single-line field forward a character
2143|
2144| Return Values : E_OK - success
2145| E_REQUEST_DENIED - no data ahead
2146+--------------------------------------------------------------------------*/
2147static int
2148HSC_Scroll_Char_Forward(FORM *form)
2149{
2150 T((T_CALLED("HSC_Scroll_Char_Forward(%p)"), form));
2151 returnCode(HSC_Generic(form, 1));
2152}
2153
2154/*---------------------------------------------------------------------------
2155| Facility : libnform
2156| Function : static int HSC_Scroll_Char_Backward(FORM * form)
2157|
2158| Description : Scroll single-line field backward a character
2159|
2160| Return Values : E_OK - success
2161| E_REQUEST_DENIED - no data behind
2162+--------------------------------------------------------------------------*/
2163static int
2164HSC_Scroll_Char_Backward(FORM *form)
2165{
2166 T((T_CALLED("HSC_Scroll_Char_Backward(%p)"), form));
2167 returnCode(HSC_Generic(form, -1));
2168}
2169
2170/*---------------------------------------------------------------------------
2171| Facility : libnform
2172| Function : static int HSC_Horizontal_Line_Forward(FORM* form)
2173|
2174| Description : Scroll single-line field forward a line
2175|
2176| Return Values : E_OK - success
2177| E_REQUEST_DENIED - no data ahead
2178+--------------------------------------------------------------------------*/
2179static int
2180HSC_Horizontal_Line_Forward(FORM *form)
2181{
2182 T((T_CALLED("HSC_Horizontal_Line_Forward(%p)"), form));
2183 returnCode(HSC_Generic(form, form->current->cols));
2184}
2185
2186/*---------------------------------------------------------------------------
2187| Facility : libnform
2188| Function : static int HSC_Horizontal_Half_Line_Forward(FORM* form)
2189|
2190| Description : Scroll single-line field forward half a line
2191|
2192| Return Values : E_OK - success
2193| E_REQUEST_DENIED - no data ahead
2194+--------------------------------------------------------------------------*/
2195static int
2196HSC_Horizontal_Half_Line_Forward(FORM *form)
2197{
2198 T((T_CALLED("HSC_Horizontal_Half_Line_Forward(%p)"), form));
2199 returnCode(HSC_Generic(form, (form->current->cols + 1) / 2));
2200}
2201
2202/*---------------------------------------------------------------------------
2203| Facility : libnform
2204| Function : static int HSC_Horizontal_Line_Backward(FORM* form)
2205|
2206| Description : Scroll single-line field backward a line
2207|
2208| Return Values : E_OK - success
2209| E_REQUEST_DENIED - no data behind
2210+--------------------------------------------------------------------------*/
2211static int
2212HSC_Horizontal_Line_Backward(FORM *form)
2213{
2214 T((T_CALLED("HSC_Horizontal_Line_Backward(%p)"), form));
2215 returnCode(HSC_Generic(form, -(form->current->cols)));
2216}
2217
2218/*---------------------------------------------------------------------------
2219| Facility : libnform
2220| Function : static int HSC_Horizontal_Half_Line_Backward(FORM* form)
2221|
2222| Description : Scroll single-line field backward half a line
2223|
2224| Return Values : E_OK - success
2225| E_REQUEST_DENIED - no data behind
2226+--------------------------------------------------------------------------*/
2227static int
2228HSC_Horizontal_Half_Line_Backward(FORM *form)
2229{
2230 T((T_CALLED("HSC_Horizontal_Half_Line_Backward(%p)"), form));
2231 returnCode(HSC_Generic(form, -((form->current->cols + 1) / 2)));
2232}
2233
2234/*----------------------------------------------------------------------------
2235 End of Horizontal scrolling routines
2236 --------------------------------------------------------------------------*/
2237
2238/*----------------------------------------------------------------------------
2239 Helper routines for Field Editing
2240 --------------------------------------------------------------------------*/
2241
2242/*---------------------------------------------------------------------------
2243| Facility : libnform
2244| Function : static bool Is_There_Room_For_A_Line(FORM * form)
2245|
2246| Description : Check whether or not there is enough room in the
2247| buffer to enter a whole line.
2248|
2249| Return Values : TRUE - there is enough space
2250| FALSE - there is not enough space
2251+--------------------------------------------------------------------------*/
2252NCURSES_INLINE static bool
2253Is_There_Room_For_A_Line(FORM *form)
2254{
2255 FIELD *field = form->current;
2256 FIELD_CELL *begin_of_last_line, *s;
2257
2258 Synchronize_Buffer(form);
2259 begin_of_last_line = Address_Of_Row_In_Buffer(field, (field->drows - 1));
2260 s = After_End_Of_Data(begin_of_last_line, field->dcols);
2261 return ((s == begin_of_last_line) ? TRUE : FALSE);
2262}
2263
2264/*---------------------------------------------------------------------------
2265| Facility : libnform
2266| Function : static bool Is_There_Room_For_A_Char_In_Line(FORM * form)
2267|
2268| Description : Checks whether or not there is room for a new character
2269| in the current line.
2270|
2271| Return Values : TRUE - there is room
2272| FALSE - there is not enough room (line full)
2273+--------------------------------------------------------------------------*/
2274NCURSES_INLINE static bool
2275Is_There_Room_For_A_Char_In_Line(FORM *form)
2276{
2277 int last_char_in_line;
2278
2279 wmove(form->w, form->currow, form->current->dcols - 1);
2280 last_char_in_line = (int)(winch(form->w) & A_CHARTEXT);
2281 wmove(form->w, form->currow, form->curcol);
2282 return (((last_char_in_line == form->current->pad) ||
2283 is_blank(last_char_in_line)) ? TRUE : FALSE);
2284}
2285
2286#define There_Is_No_Room_For_A_Char_In_Line(f) \
2287 !Is_There_Room_For_A_Char_In_Line(f)
2288
2289/*---------------------------------------------------------------------------
2290| Facility : libnform
2291| Function : static int Insert_String(
2292| FORM * form,
2293| int row,
2294| char *txt,
2295| int len )
2296|
2297| Description : Insert the 'len' characters beginning at pointer 'txt'
2298| into the 'row' of the 'form'. The insertion occurs
2299| on the beginning of the row, all other characters are
2300| moved to the right. After the text a pad character will
2301| be inserted to separate the text from the rest. If
2302| necessary the insertion moves characters on the next
2303| line to make place for the requested insertion string.
2304|
2305| Return Values : E_OK - success
2306| E_REQUEST_DENIED -
2307| E_SYSTEM_ERROR - system error
2308+--------------------------------------------------------------------------*/
2309static int
2310Insert_String(FORM *form, int row, FIELD_CELL *txt, int len)
2311{
2312 FIELD *field = form->current;
2313 FIELD_CELL *bp = Address_Of_Row_In_Buffer(field, row);
2314 int datalen = (int)(After_End_Of_Data(bp, field->dcols) - bp);
2315 int freelen = field->dcols - datalen;
2316 int requiredlen = len + 1;
2317 FIELD_CELL *split;
2318 int result = E_REQUEST_DENIED;
2319
2320 if (freelen >= requiredlen)
2321 {
2322 wmove(form->w, row, 0);
2323 myINSNSTR(form->w, txt, len);
2324 wmove(form->w, row, len);
2325 myINSNSTR(form->w, &myBLANK, 1);
2326 return E_OK;
2327 }
2328 else
2329 {
2330 /* we have to move characters on the next line. If we are on the
2331 last line this may work, if the field is growable */
2332 if ((row == (field->drows - 1)) && Growable(field))
2333 {
2334 if (!Field_Grown(field, 1))
2335 return (E_SYSTEM_ERROR);
2336 /* !!!Side-Effect : might be changed due to growth!!! */
2337 bp = Address_Of_Row_In_Buffer(field, row);
2338 }
2339
2340 if (row < (field->drows - 1))
2341 {
2342 split =
2343 After_Last_Whitespace_Character(bp,
2344 (int)(Get_Start_Of_Data(bp
2345 + field->dcols
2346 - requiredlen,
2347 requiredlen)
2348 - bp));
2349 /* split points now to the first character of the portion of the
2350 line that must be moved to the next line */
2351 datalen = (int)(split - bp); /* + freelen has to stay on this line */
2352 freelen = field->dcols - (datalen + freelen); /* for the next line */
2353
2354 if ((result = Insert_String(form, row + 1, split, freelen)) == E_OK)
2355 {
2356 wmove(form->w, row, datalen);
2357 wclrtoeol(form->w);
2358 wmove(form->w, row, 0);
2359 myINSNSTR(form->w, txt, len);
2360 wmove(form->w, row, len);
2361 myINSNSTR(form->w, &myBLANK, 1);
2362 return E_OK;
2363 }
2364 }
2365 return (result);
2366 }
2367}
2368
2369/*---------------------------------------------------------------------------
2370| Facility : libnform
2371| Function : static int Wrapping_Not_Necessary_Or_Wrapping_Ok(
2372| FORM * form)
2373|
2374| Description : If a character has been entered into a field, it may
2375| be that wrapping has to occur. This routine checks
2376| whether or not wrapping is required and if so, performs
2377| the wrapping.
2378|
2379| Return Values : E_OK - no wrapping required or wrapping
2380| was successful
2381| E_REQUEST_DENIED -
2382| E_SYSTEM_ERROR - some system error
2383+--------------------------------------------------------------------------*/
2384static int
2385Wrapping_Not_Necessary_Or_Wrapping_Ok(FORM *form)
2386{
2387 FIELD *field = form->current;
2388 int result = E_REQUEST_DENIED;
2389 bool Last_Row = ((field->drows - 1) == form->currow);
2390
2391 if ((field->opts & O_WRAP) && /* wrapping wanted */
2392 (!Single_Line_Field(field)) && /* must be multi-line */
2393 (There_Is_No_Room_For_A_Char_In_Line(form)) && /* line is full */
2394 (!Last_Row || Growable(field))) /* there are more lines */
2395 {
2396 FIELD_CELL *bp;
2397 FIELD_CELL *split;
2398 int chars_to_be_wrapped;
2399 int chars_to_remain_on_line;
2400
2401 if (Last_Row)
2402 {
2403 /* the above logic already ensures, that in this case the field
2404 is growable */
2405 if (!Field_Grown(field, 1))
2406 return E_SYSTEM_ERROR;
2407 }
2408 bp = Address_Of_Current_Row_In_Buffer(form);
2409 Window_To_Buffer(form->w, field);
2410 split = After_Last_Whitespace_Character(bp, field->dcols);
2411 /* split points to the first character of the sequence to be brought
2412 on the next line */
2413 chars_to_remain_on_line = (int)(split - bp);
2414 chars_to_be_wrapped = field->dcols - chars_to_remain_on_line;
2415 if (chars_to_remain_on_line > 0)
2416 {
2417 if ((result = Insert_String(form, form->currow + 1, split,
2418 chars_to_be_wrapped)) == E_OK)
2419 {
2420 wmove(form->w, form->currow, chars_to_remain_on_line);
2421 wclrtoeol(form->w);
2422 if (form->curcol >= chars_to_remain_on_line)
2423 {
2424 form->currow++;
2425 form->curcol -= chars_to_remain_on_line;
2426 }
2427 return E_OK;
2428 }
2429 }
2430 else
2431 return E_OK;
2432 if (result != E_OK)
2433 {
2434 DeleteChar(form);
2435 Window_To_Buffer(form->w, field);
2436 result = E_REQUEST_DENIED;
2437 }
2438 }
2439 else
2440 result = E_OK; /* wrapping was not necessary */
2441 return (result);
2442}
2443
2444/*----------------------------------------------------------------------------
2445 Field Editing routines
2446 --------------------------------------------------------------------------*/
2447
2448/*---------------------------------------------------------------------------
2449| Facility : libnform
2450| Function : static int Field_Editing(
2451| int (* const fct) (FORM *),
2452| FORM * form)
2453|
2454| Description : Generic routine for field editing requests. The driver
2455| routines are only called for editable fields, the
2456| _WINDOW_MODIFIED flag is set if editing occurred.
2457| This is somewhat special due to the overload semantics
2458| of the NEW_LINE and DEL_PREV requests.
2459|
2460| Return Values : Error code from low level drivers.
2461+--------------------------------------------------------------------------*/
2462static int
2463Field_Editing(int (*const fct) (FORM *), FORM *form)
2464{
2465 int res = E_REQUEST_DENIED;
2466
2467 /* We have to deal here with the specific case of the overloaded
2468 behavior of New_Line and Delete_Previous requests.
2469 They may end up in navigational requests if we are on the first
2470 character in a field. But navigation is also allowed on non-
2471 editable fields.
2472 */
2473 if ((fct == FE_Delete_Previous) &&
2474 (form->opts & O_BS_OVERLOAD) &&
2475 First_Position_In_Current_Field(form))
2476 {
2477 res = Inter_Field_Navigation(FN_Previous_Field, form);
2478 }
2479 else
2480 {
2481 if (fct == FE_New_Line)
2482 {
2483 if ((form->opts & O_NL_OVERLOAD) &&
2484 First_Position_In_Current_Field(form))
2485 {
2486 res = Inter_Field_Navigation(FN_Next_Field, form);
2487 }
2488 else
2489 /* FE_New_Line deals itself with the _WINDOW_MODIFIED flag */
2490 res = fct(form);
2491 }
2492 else
2493 {
2494 /* From now on, everything must be editable */
2495 if (form->current->opts & O_EDIT)
2496 {
2497 res = fct(form);
2498 if (res == E_OK)
2499 form->status |= _WINDOW_MODIFIED;
2500 }
2501 }
2502 }
2503 return res;
2504}
2505
2506/*---------------------------------------------------------------------------
2507| Facility : libnform
2508| Function : static int FE_New_Line(FORM * form)
2509|
2510| Description : Perform a new line request. This is rather complex
2511| compared to other routines in this code due to the
2512| rather difficult to understand description in the
2513| manuals.
2514|
2515| Return Values : E_OK - success
2516| E_REQUEST_DENIED - new line not allowed
2517| E_SYSTEM_ERROR - system error
2518+--------------------------------------------------------------------------*/
2519static int
2520FE_New_Line(FORM *form)
2521{
2522 FIELD *field = form->current;
2523 FIELD_CELL *bp, *t;
2524 bool Last_Row = ((field->drows - 1) == form->currow);
2525
2526 T((T_CALLED("FE_New_Line(%p)"), form));
2527 if (form->status & _OVLMODE)
2528 {
2529 if (Last_Row &&
2530 (!(Growable(field) && !Single_Line_Field(field))))
2531 {
2532 if (!(form->opts & O_NL_OVERLOAD))
2533 returnCode(E_REQUEST_DENIED);
2534 wmove(form->w, form->currow, form->curcol);
2535 wclrtoeol(form->w);
2536 /* we have to set this here, although it is also
2537 handled in the generic routine. The reason is,
2538 that FN_Next_Field may fail, but the form is
2539 definitively changed */
2540 form->status |= _WINDOW_MODIFIED;
2541 returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2542 }
2543 else
2544 {
2545 if (Last_Row && !Field_Grown(field, 1))
2546 {
2547 /* N.B.: due to the logic in the 'if', LastRow==TRUE
2548 means here that the field is growable and not
2549 a single-line field */
2550 returnCode(E_SYSTEM_ERROR);
2551 }
2552 wmove(form->w, form->currow, form->curcol);
2553 wclrtoeol(form->w);
2554 form->currow++;
2555 form->curcol = 0;
2556 form->status |= _WINDOW_MODIFIED;
2557 returnCode(E_OK);
2558 }
2559 }
2560 else
2561 {
2562 /* Insert Mode */
2563 if (Last_Row &&
2564 !(Growable(field) && !Single_Line_Field(field)))
2565 {
2566 if (!(form->opts & O_NL_OVERLOAD))
2567 returnCode(E_REQUEST_DENIED);
2568 returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2569 }
2570 else
2571 {
2572 bool May_Do_It = !Last_Row && Is_There_Room_For_A_Line(form);
2573
2574 if (!(May_Do_It || Growable(field)))
2575 returnCode(E_REQUEST_DENIED);
2576 if (!May_Do_It && !Field_Grown(field, 1))
2577 returnCode(E_SYSTEM_ERROR);
2578
2579 bp = Address_Of_Current_Position_In_Buffer(form);
2580 t = After_End_Of_Data(bp, field->dcols - form->curcol);
2581 wmove(form->w, form->currow, form->curcol);
2582 wclrtoeol(form->w);
2583 form->currow++;
2584 form->curcol = 0;
2585 wmove(form->w, form->currow, form->curcol);
2586 winsertln(form->w);
2587 myADDNSTR(form->w, bp, (int)(t - bp));
2588 form->status |= _WINDOW_MODIFIED;
2589 returnCode(E_OK);
2590 }
2591 }
2592}
2593
2594/*---------------------------------------------------------------------------
2595| Facility : libnform
2596| Function : static int FE_Insert_Character(FORM * form)
2597|
2598| Description : Insert blank character at the cursor position
2599|
2600| Return Values : E_OK
2601| E_REQUEST_DENIED
2602+--------------------------------------------------------------------------*/
2603static int
2604FE_Insert_Character(FORM *form)
2605{
2606 FIELD *field = form->current;
2607 int result = E_REQUEST_DENIED;
2608
2609 T((T_CALLED("FE_Insert_Character(%p)"), form));
2610 if (Check_Char(field->type, (int)C_BLANK, (TypeArgument *)(field->arg)))
2611 {
2612 bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
2613
2614 if (There_Is_Room ||
2615 ((Single_Line_Field(field) && Growable(field))))
2616 {
2617 if (!There_Is_Room && !Field_Grown(field, 1))
2618 result = E_SYSTEM_ERROR;
2619 else
2620 {
2621 winsch(form->w, (chtype)C_BLANK);
2622 result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form);
2623 }
2624 }
2625 }
2626 returnCode(result);
2627}
2628
2629/*---------------------------------------------------------------------------
2630| Facility : libnform
2631| Function : static int FE_Insert_Line(FORM * form)
2632|
2633| Description : Insert a blank line at the cursor position
2634|
2635| Return Values : E_OK - success
2636| E_REQUEST_DENIED - line can not be inserted
2637+--------------------------------------------------------------------------*/
2638static int
2639FE_Insert_Line(FORM *form)
2640{
2641 FIELD *field = form->current;
2642 int result = E_REQUEST_DENIED;
2643
2644 T((T_CALLED("FE_Insert_Line(%p)"), form));
2645 if (Check_Char(field->type, (int)C_BLANK, (TypeArgument *)(field->arg)))
2646 {
2647 bool Maybe_Done = (form->currow != (field->drows - 1)) &&
2648 Is_There_Room_For_A_Line(form);
2649
2650 if (!Single_Line_Field(field) &&
2651 (Maybe_Done || Growable(field)))
2652 {
2653 if (!Maybe_Done && !Field_Grown(field, 1))
2654 result = E_SYSTEM_ERROR;
2655 else
2656 {
2657 form->curcol = 0;
2658 winsertln(form->w);
2659 result = E_OK;
2660 }
2661 }
2662 }
2663 returnCode(result);
2664}
2665
2666/*---------------------------------------------------------------------------
2667| Facility : libnform
2668| Function : static int FE_Delete_Character(FORM * form)
2669|
2670| Description : Delete character at the cursor position
2671|
2672| Return Values : E_OK - success
2673+--------------------------------------------------------------------------*/
2674static int
2675FE_Delete_Character(FORM *form)
2676{
2677 T((T_CALLED("FE_Delete_Character(%p)"), form));
2678 DeleteChar(form);
2679 returnCode(E_OK);
2680}
2681
2682/*---------------------------------------------------------------------------
2683| Facility : libnform
2684| Function : static int FE_Delete_Previous(FORM * form)
2685|
2686| Description : Delete character before cursor. Again this is a rather
2687| difficult piece compared to others due to the overloading
2688| semantics of backspace.
2689| N.B.: The case of overloaded BS on first field position
2690| is already handled in the generic routine.
2691|
2692| Return Values : E_OK - success
2693| E_REQUEST_DENIED - Character can't be deleted
2694+--------------------------------------------------------------------------*/
2695static int
2696FE_Delete_Previous(FORM *form)
2697{
2698 FIELD *field = form->current;
2699
2700 T((T_CALLED("FE_Delete_Previous(%p)"), form));
2701 if (First_Position_In_Current_Field(form))
2702 returnCode(E_REQUEST_DENIED);
2703
2704 if ((--(form->curcol)) < 0)
2705 {
2706 FIELD_CELL *this_line, *prev_line, *prev_end, *this_end;
2707 int this_row = form->currow;
2708
2709 form->curcol++;
2710 if (form->status & _OVLMODE)
2711 returnCode(E_REQUEST_DENIED);
2712
2713 prev_line = Address_Of_Row_In_Buffer(field, (form->currow - 1));
2714 this_line = Address_Of_Row_In_Buffer(field, (form->currow));
2715 Synchronize_Buffer(form);
2716 prev_end = After_End_Of_Data(prev_line, field->dcols);
2717 this_end = After_End_Of_Data(this_line, field->dcols);
2718 if ((int)(this_end - this_line) >
2719 (field->cols - (int)(prev_end - prev_line)))
2720 returnCode(E_REQUEST_DENIED);
2721 wmove(form->w, form->currow, form->curcol);
2722 wdeleteln(form->w);
2723 Adjust_Cursor_Position(form, prev_end);
2724 /*
2725 * If we did not really move to the previous line, help the user a
2726 * little. It is however a little inconsistent. Normally, when
2727 * backspacing around the point where text wraps to a new line in a
2728 * multi-line form, we absorb one keystroke for the wrapping point. That
2729 * is consistent with SVr4 forms. However, SVr4 does not allow typing
2730 * into the last column of the field, and requires the user to enter a
2731 * newline to move to the next line. Therefore it can consistently eat
2732 * that keystroke. Since ncurses allows the last column, it wraps
2733 * automatically (given the proper options). But we cannot eat the
2734 * keystroke to back over the wrapping point, since that would put the
2735 * cursor past the end of the form field. In this case, just delete the
2736 * character at the end of the field.
2737 */
2738 if (form->currow == this_row && this_row > 0)
2739 {
2740 form->currow -= 1;
2741 form->curcol = field->dcols - 1;
2742 DeleteChar(form);
2743 }
2744 else
2745 {
2746 wmove(form->w, form->currow, form->curcol);
2747 myADDNSTR(form->w, this_line, (int)(this_end - this_line));
2748 }
2749 }
2750 else
2751 {
2752 DeleteChar(form);
2753 }
2754 returnCode(E_OK);
2755}
2756
2757/*---------------------------------------------------------------------------
2758| Facility : libnform
2759| Function : static int FE_Delete_Line(FORM * form)
2760|
2761| Description : Delete line at cursor position.
2762|
2763| Return Values : E_OK - success
2764+--------------------------------------------------------------------------*/
2765static int
2766FE_Delete_Line(FORM *form)
2767{
2768 T((T_CALLED("FE_Delete_Line(%p)"), form));
2769 form->curcol = 0;
2770 wdeleteln(form->w);
2771 returnCode(E_OK);
2772}
2773
2774/*---------------------------------------------------------------------------
2775| Facility : libnform
2776| Function : static int FE_Delete_Word(FORM * form)
2777|
2778| Description : Delete word at cursor position
2779|
2780| Return Values : E_OK - success
2781| E_REQUEST_DENIED - failure
2782+--------------------------------------------------------------------------*/
2783static int
2784FE_Delete_Word(FORM *form)
2785{
2786 FIELD *field = form->current;
2787 FIELD_CELL *bp = Address_Of_Current_Row_In_Buffer(form);
2788 FIELD_CELL *ep = bp + field->dcols;
2789 FIELD_CELL *cp = bp + form->curcol;
2790 FIELD_CELL *s;
2791
2792 T((T_CALLED("FE_Delete_Word(%p)"), form));
2793 Synchronize_Buffer(form);
2794 if (ISBLANK(*cp))
2795 returnCode(E_REQUEST_DENIED); /* not in word */
2796
2797 /* move cursor to begin of word and erase to end of screen-line */
2798 Adjust_Cursor_Position(form,
2799 After_Last_Whitespace_Character(bp, form->curcol));
2800 wmove(form->w, form->currow, form->curcol);
2801 wclrtoeol(form->w);
2802
2803 /* skip over word in buffer */
2804 s = Get_First_Whitespace_Character(cp, (int)(ep - cp));
2805 /* to begin of next word */
2806 s = Get_Start_Of_Data(s, (int)(ep - s));
2807 if ((s != cp) && !ISBLANK(*s))
2808 {
2809 /* copy remaining line to window */
2810 myADDNSTR(form->w, s, (int)(s - After_End_Of_Data(s, (int)(ep - s))));
2811 }
2812 returnCode(E_OK);
2813}
2814
2815/*---------------------------------------------------------------------------
2816| Facility : libnform
2817| Function : static int FE_Clear_To_End_Of_Line(FORM * form)
2818|
2819| Description : Clear to end of current line.
2820|
2821| Return Values : E_OK - success
2822+--------------------------------------------------------------------------*/
2823static int
2824FE_Clear_To_End_Of_Line(FORM *form)
2825{
2826 T((T_CALLED("FE_Clear_To_End_Of_Line(%p)"), form));
2827 wmove(form->w, form->currow, form->curcol);
2828 wclrtoeol(form->w);
2829 returnCode(E_OK);
2830}
2831
2832/*---------------------------------------------------------------------------
2833| Facility : libnform
2834| Function : static int FE_Clear_To_End_Of_Field(FORM * form)
2835|
2836| Description : Clear to end of field.
2837|
2838| Return Values : E_OK - success
2839+--------------------------------------------------------------------------*/
2840static int
2841FE_Clear_To_End_Of_Field(FORM *form)
2842{
2843 T((T_CALLED("FE_Clear_To_End_Of_Field(%p)"), form));
2844 wmove(form->w, form->currow, form->curcol);
2845 wclrtobot(form->w);
2846 returnCode(E_OK);
2847}
2848
2849/*---------------------------------------------------------------------------
2850| Facility : libnform
2851| Function : static int FE_Clear_Field(FORM * form)
2852|
2853| Description : Clear entire field.
2854|
2855| Return Values : E_OK - success
2856+--------------------------------------------------------------------------*/
2857static int
2858FE_Clear_Field(FORM *form)
2859{
2860 T((T_CALLED("FE_Clear_Field(%p)"), form));
2861 form->currow = form->curcol = 0;
2862 werase(form->w);
2863 returnCode(E_OK);
2864}
2865/*----------------------------------------------------------------------------
2866 END of Field Editing routines
2867 --------------------------------------------------------------------------*/
2868
2869/*----------------------------------------------------------------------------
2870 Edit Mode routines
2871 --------------------------------------------------------------------------*/
2872
2873/*---------------------------------------------------------------------------
2874| Facility : libnform
2875| Function : static int EM_Overlay_Mode(FORM * form)
2876|
2877| Description : Switch to overlay mode.
2878|
2879| Return Values : E_OK - success
2880+--------------------------------------------------------------------------*/
2881static int
2882EM_Overlay_Mode(FORM *form)
2883{
2884 T((T_CALLED("EM_Overlay_Mode(%p)"), form));
2885 form->status |= _OVLMODE;
2886 returnCode(E_OK);
2887}
2888
2889/*---------------------------------------------------------------------------
2890| Facility : libnform
2891| Function : static int EM_Insert_Mode(FORM * form)
2892|
2893| Description : Switch to insert mode
2894|
2895| Return Values : E_OK - success
2896+--------------------------------------------------------------------------*/
2897static int
2898EM_Insert_Mode(FORM *form)
2899{
2900 T((T_CALLED("EM_Insert_Mode(%p)"), form));
2901 form->status &= ~_OVLMODE;
2902 returnCode(E_OK);
2903}
2904
2905/*----------------------------------------------------------------------------
2906 END of Edit Mode routines
2907 --------------------------------------------------------------------------*/
2908
2909/*----------------------------------------------------------------------------
2910 Helper routines for Choice Requests
2911 --------------------------------------------------------------------------*/
2912
2913/*---------------------------------------------------------------------------
2914| Facility : libnform
2915| Function : static bool Next_Choice(
2916| FIELDTYPE * typ,
2917| FIELD * field,
2918| TypeArgument *argp)
2919|
2920| Description : Get the next field choice. For linked types this is
2921| done recursively.
2922|
2923| Return Values : TRUE - next choice successfully retrieved
2924| FALSE - couldn't retrieve next choice
2925+--------------------------------------------------------------------------*/
2926static bool
2927Next_Choice(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
2928{
2929 if (!typ || !(typ->status & _HAS_CHOICE))
2930 return FALSE;
2931
2932 if (typ->status & _LINKED_TYPE)
2933 {
2934 assert(argp);
2935 return (
2936 Next_Choice(typ->left, field, argp->left) ||
2937 Next_Choice(typ->right, field, argp->right));
2938 }
2939 else
2940 {
2941 assert(typ->next);
2942 return typ->next(field, (void *)argp);
2943 }
2944}
2945
2946/*---------------------------------------------------------------------------
2947| Facility : libnform
2948| Function : static bool Previous_Choice(
2949| FIELDTYPE * typ,
2950| FIELD * field,
2951| TypeArgument *argp)
2952|
2953| Description : Get the previous field choice. For linked types this
2954| is done recursively.
2955|
2956| Return Values : TRUE - previous choice successfully retrieved
2957| FALSE - couldn't retrieve previous choice
2958+--------------------------------------------------------------------------*/
2959static bool
2960Previous_Choice(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
2961{
2962 if (!typ || !(typ->status & _HAS_CHOICE))
2963 return FALSE;
2964
2965 if (typ->status & _LINKED_TYPE)
2966 {
2967 assert(argp);
2968 return (
2969 Previous_Choice(typ->left, field, argp->left) ||
2970 Previous_Choice(typ->right, field, argp->right));
2971 }
2972 else
2973 {
2974 assert(typ->prev);
2975 return typ->prev(field, (void *)argp);
2976 }
2977}
2978/*----------------------------------------------------------------------------
2979 End of Helper routines for Choice Requests
2980 --------------------------------------------------------------------------*/
2981
2982/*----------------------------------------------------------------------------
2983 Routines for Choice Requests
2984 --------------------------------------------------------------------------*/
2985
2986/*---------------------------------------------------------------------------
2987| Facility : libnform
2988| Function : static int CR_Next_Choice(FORM * form)
2989|
2990| Description : Get the next field choice.
2991|
2992| Return Values : E_OK - success
2993| E_REQUEST_DENIED - next choice couldn't be retrieved
2994+--------------------------------------------------------------------------*/
2995static int
2996CR_Next_Choice(FORM *form)
2997{
2998 FIELD *field = form->current;
2999
3000 T((T_CALLED("CR_Next_Choice(%p)"), form));
3001 Synchronize_Buffer(form);
3002 returnCode((Next_Choice(field->type, field, (TypeArgument *)(field->arg)))
3003 ? E_OK
3004 : E_REQUEST_DENIED);
3005}
3006
3007/*---------------------------------------------------------------------------
3008| Facility : libnform
3009| Function : static int CR_Previous_Choice(FORM * form)
3010|
3011| Description : Get the previous field choice.
3012|
3013| Return Values : E_OK - success
3014| E_REQUEST_DENIED - prev. choice couldn't be retrieved
3015+--------------------------------------------------------------------------*/
3016static int
3017CR_Previous_Choice(FORM *form)
3018{
3019 FIELD *field = form->current;
3020
3021 T((T_CALLED("CR_Previous_Choice(%p)"), form));
3022 Synchronize_Buffer(form);
3023 returnCode((Previous_Choice(field->type, field, (TypeArgument *)(field->arg)))
3024 ? E_OK
3025 : E_REQUEST_DENIED);
3026}
3027/*----------------------------------------------------------------------------
3028 End of Routines for Choice Requests
3029 --------------------------------------------------------------------------*/
3030
3031/*----------------------------------------------------------------------------
3032 Helper routines for Field Validations.
3033 --------------------------------------------------------------------------*/
3034
3035/*---------------------------------------------------------------------------
3036| Facility : libnform
3037| Function : static bool Check_Field(
3038| FIELDTYPE * typ,
3039| FIELD * field,
3040| TypeArgument * argp)
3041|
3042| Description : Check the field according to its fieldtype and its
3043| actual arguments. For linked fieldtypes this is done
3044| recursively.
3045|
3046| Return Values : TRUE - field is valid
3047| FALSE - field is invalid.
3048+--------------------------------------------------------------------------*/
3049static bool
3050Check_Field(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
3051{
3052 if (typ)
3053 {
3054 if (field->opts & O_NULLOK)
3055 {
3056 FIELD_CELL *bp = field->buf;
3057
3058 assert(bp);
3059 while (ISBLANK(*bp))
3060 {
3061 bp++;
3062 }
3063 if (CharOf(*bp) == 0)
3064 return TRUE;
3065 }
3066
3067 if (typ->status & _LINKED_TYPE)
3068 {
3069 assert(argp);
3070 return (
3071 Check_Field(typ->left, field, argp->left) ||
3072 Check_Field(typ->right, field, argp->right));
3073 }
3074 else
3075 {
3076 if (typ->fcheck)
3077 return typ->fcheck(field, (void *)argp);
3078 }
3079 }
3080 return TRUE;
3081}
3082
3083/*---------------------------------------------------------------------------
3084| Facility : libnform
3085| Function : bool _nc_Internal_Validation(FORM * form )
3086|
3087| Description : Validate the current field of the form.
3088|
3089| Return Values : TRUE - field is valid
3090| FALSE - field is invalid
3091+--------------------------------------------------------------------------*/
3092NCURSES_EXPORT(bool)
3093_nc_Internal_Validation(FORM *form)
3094{
3095 FIELD *field;
3096
3097 field = form->current;
3098
3099 Synchronize_Buffer(form);
3100 if ((form->status & _FCHECK_REQUIRED) ||
3101 (!(field->opts & O_PASSOK)))
3102 {
3103 if (!Check_Field(field->type, field, (TypeArgument *)(field->arg)))
3104 return FALSE;
3105 form->status &= ~_FCHECK_REQUIRED;
3106 field->status |= _CHANGED;
3107 Synchronize_Linked_Fields(field);
3108 }
3109 return TRUE;
3110}
3111/*----------------------------------------------------------------------------
3112 End of Helper routines for Field Validations.
3113 --------------------------------------------------------------------------*/
3114
3115/*----------------------------------------------------------------------------
3116 Routines for Field Validation.
3117 --------------------------------------------------------------------------*/
3118
3119/*---------------------------------------------------------------------------
3120| Facility : libnform
3121| Function : static int FV_Validation(FORM * form)
3122|
3123| Description : Validate the current field of the form.
3124|
3125| Return Values : E_OK - field valid
3126| E_INVALID_FIELD - field not valid
3127+--------------------------------------------------------------------------*/
3128static int
3129FV_Validation(FORM *form)
3130{
3131 T((T_CALLED("FV_Validation(%p)"), form));
3132 if (_nc_Internal_Validation(form))
3133 returnCode(E_OK);
3134 else
3135 returnCode(E_INVALID_FIELD);
3136}
3137/*----------------------------------------------------------------------------
3138 End of routines for Field Validation.
3139 --------------------------------------------------------------------------*/
3140
3141/*----------------------------------------------------------------------------
3142 Helper routines for Inter-Field Navigation
3143 --------------------------------------------------------------------------*/
3144
3145/*---------------------------------------------------------------------------
3146| Facility : libnform
3147| Function : static FIELD *Next_Field_On_Page(FIELD * field)
3148|
3149| Description : Get the next field after the given field on the current
3150| page. The order of fields is the one defined by the
3151| fields array. Only visible and active fields are
3152| counted.
3153|
3154| Return Values : Pointer to the next field.
3155+--------------------------------------------------------------------------*/
3156NCURSES_INLINE static FIELD *
3157Next_Field_On_Page(FIELD *field)
3158{
3159 FORM *form = field->form;
3160 FIELD **field_on_page = &form->field[field->index];
3161 FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3162 FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3163
3164 do
3165 {
3166 field_on_page =
3167 (field_on_page == last_on_page) ? first_on_page : field_on_page + 1;
3168 if (Field_Is_Selectable(*field_on_page))
3169 break;
3170 }
3171 while (field != (*field_on_page));
3172 return (*field_on_page);
3173}
3174
3175/*---------------------------------------------------------------------------
3176| Facility : libnform
3177| Function : FIELD* _nc_First_Active_Field(FORM * form)
3178|
3179| Description : Get the first active field on the current page,
3180| if there are such. If there are none, get the first
3181| visible field on the page. If there are also none,
3182| we return the first field on page and hope the best.
3183|
3184| Return Values : Pointer to calculated field.
3185+--------------------------------------------------------------------------*/
3186NCURSES_EXPORT(FIELD *)
3187_nc_First_Active_Field(FORM *form)
3188{
3189 FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3190 FIELD *proposed = Next_Field_On_Page(*last_on_page);
3191
3192 if (proposed == *last_on_page)
3193 {
3194 /* there might be the special situation, where there is no
3195 active and visible field on the current page. We then select
3196 the first visible field on this readonly page
3197 */
3198 if (Field_Is_Not_Selectable(proposed))
3199 {
3200 FIELD **field = &form->field[proposed->index];
3201 FIELD **first = &form->field[form->page[form->curpage].pmin];
3202
3203 do
3204 {
3205 field = (field == last_on_page) ? first : field + 1;
3206 if (((*field)->opts & O_VISIBLE))
3207 break;
3208 }
3209 while (proposed != (*field));
3210
3211 proposed = *field;
3212
3213 if ((proposed == *last_on_page) && !(proposed->opts & O_VISIBLE))
3214 {
3215 /* This means, there is also no visible field on the page.
3216 So we propose the first one and hope the very best...
3217 Some very clever user has designed a readonly and invisible
3218 page on this form.
3219 */
3220 proposed = *first;
3221 }
3222 }
3223 }
3224 return (proposed);
3225}
3226
3227/*---------------------------------------------------------------------------
3228| Facility : libnform
3229| Function : static FIELD *Previous_Field_On_Page(FIELD * field)
3230|
3231| Description : Get the previous field before the given field on the
3232| current page. The order of fields is the one defined by
3233| the fields array. Only visible and active fields are
3234| counted.
3235|
3236| Return Values : Pointer to the previous field.
3237+--------------------------------------------------------------------------*/
3238NCURSES_INLINE static FIELD *
3239Previous_Field_On_Page(FIELD *field)
3240{
3241 FORM *form = field->form;
3242 FIELD **field_on_page = &form->field[field->index];
3243 FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3244 FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3245
3246 do
3247 {
3248 field_on_page =
3249 (field_on_page == first_on_page) ? last_on_page : field_on_page - 1;
3250 if (Field_Is_Selectable(*field_on_page))
3251 break;
3252 }
3253 while (field != (*field_on_page));
3254
3255 return (*field_on_page);
3256}
3257
3258/*---------------------------------------------------------------------------
3259| Facility : libnform
3260| Function : static FIELD *Sorted_Next_Field(FIELD * field)
3261|
3262| Description : Get the next field after the given field on the current
3263| page. The order of fields is the one defined by the
3264| (row,column) geometry, rows are major.
3265|
3266| Return Values : Pointer to the next field.
3267+--------------------------------------------------------------------------*/
3268NCURSES_INLINE static FIELD *
3269Sorted_Next_Field(FIELD *field)
3270{
3271 FIELD *field_on_page = field;
3272
3273 do
3274 {
3275 field_on_page = field_on_page->snext;
3276 if (Field_Is_Selectable(field_on_page))
3277 break;
3278 }
3279 while (field_on_page != field);
3280
3281 return (field_on_page);
3282}
3283
3284/*---------------------------------------------------------------------------
3285| Facility : libnform
3286| Function : static FIELD *Sorted_Previous_Field(FIELD * field)
3287|
3288| Description : Get the previous field before the given field on the
3289| current page. The order of fields is the one defined
3290| by the (row,column) geometry, rows are major.
3291|
3292| Return Values : Pointer to the previous field.
3293+--------------------------------------------------------------------------*/
3294NCURSES_INLINE static FIELD *
3295Sorted_Previous_Field(FIELD *field)
3296{
3297 FIELD *field_on_page = field;
3298
3299 do
3300 {
3301 field_on_page = field_on_page->sprev;
3302 if (Field_Is_Selectable(field_on_page))
3303 break;
3304 }
3305 while (field_on_page != field);
3306
3307 return (field_on_page);
3308}
3309
3310/*---------------------------------------------------------------------------
3311| Facility : libnform
3312| Function : static FIELD *Left_Neighbor_Field(FIELD * field)
3313|
3314| Description : Get the left neighbor of the field on the same line
3315| and the same page. Cycles through the line.
3316|
3317| Return Values : Pointer to left neighbor field.
3318+--------------------------------------------------------------------------*/
3319NCURSES_INLINE static FIELD *
3320Left_Neighbor_Field(FIELD *field)
3321{
3322 FIELD *field_on_page = field;
3323
3324 /* For a field that has really a left neighbor, the while clause
3325 immediately fails and the loop is left, positioned at the right
3326 neighbor. Otherwise we cycle backwards through the sorted field list
3327 until we enter the same line (from the right end).
3328 */
3329 do
3330 {
3331 field_on_page = Sorted_Previous_Field(field_on_page);
3332 }
3333 while (field_on_page->frow != field->frow);
3334
3335 return (field_on_page);
3336}
3337
3338/*---------------------------------------------------------------------------
3339| Facility : libnform
3340| Function : static FIELD *Right_Neighbor_Field(FIELD * field)
3341|
3342| Description : Get the right neighbor of the field on the same line
3343| and the same page.
3344|
3345| Return Values : Pointer to right neighbor field.
3346+--------------------------------------------------------------------------*/
3347NCURSES_INLINE static FIELD *
3348Right_Neighbor_Field(FIELD *field)
3349{
3350 FIELD *field_on_page = field;
3351
3352 /* See the comments on Left_Neighbor_Field to understand how it works */
3353 do
3354 {
3355 field_on_page = Sorted_Next_Field(field_on_page);
3356 }
3357 while (field_on_page->frow != field->frow);
3358
3359 return (field_on_page);
3360}
3361
3362/*---------------------------------------------------------------------------
3363| Facility : libnform
3364| Function : static FIELD *Upper_Neighbor_Field(FIELD * field)
3365|
3366| Description : Because of the row-major nature of sorting the fields,
3367| it is more difficult to define whats the upper neighbor
3368| field really means. We define that it must be on a
3369| 'previous' line (cyclic order!) and is the rightmost
3370| field laying on the left side of the given field. If
3371| this set is empty, we take the first field on the line.
3372|
3373| Return Values : Pointer to the upper neighbor field.
3374+--------------------------------------------------------------------------*/
3375static FIELD *
3376Upper_Neighbor_Field(FIELD *field)
3377{
3378 FIELD *field_on_page = field;
3379 int frow = field->frow;
3380 int fcol = field->fcol;
3381
3382 /* Walk back to the 'previous' line. The second term in the while clause
3383 just guarantees that we stop if we cycled through the line because
3384 there might be no 'previous' line if the page has just one line.
3385 */
3386 do
3387 {
3388 field_on_page = Sorted_Previous_Field(field_on_page);
3389 }
3390 while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3391
3392 if (field_on_page->frow != frow)
3393 {
3394 /* We really found a 'previous' line. We are positioned at the
3395 rightmost field on this line */
3396 frow = field_on_page->frow;
3397
3398 /* We walk to the left as long as we are really right of the
3399 field. */
3400 while (field_on_page->frow == frow && field_on_page->fcol > fcol)
3401 field_on_page = Sorted_Previous_Field(field_on_page);
3402
3403 /* If we wrapped, just go to the right which is the first field on
3404 the row */
3405 if (field_on_page->frow != frow)
3406 field_on_page = Sorted_Next_Field(field_on_page);
3407 }
3408
3409 return (field_on_page);
3410}
3411
3412/*---------------------------------------------------------------------------
3413| Facility : libnform
3414| Function : static FIELD *Down_Neighbor_Field(FIELD * field)
3415|
3416| Description : Because of the row-major nature of sorting the fields,
3417| its more difficult to define whats the down neighbor
3418| field really means. We define that it must be on a
3419| 'next' line (cyclic order!) and is the leftmost
3420| field laying on the right side of the given field. If
3421| this set is empty, we take the last field on the line.
3422|
3423| Return Values : Pointer to the upper neighbor field.
3424+--------------------------------------------------------------------------*/
3425static FIELD *
3426Down_Neighbor_Field(FIELD *field)
3427{
3428 FIELD *field_on_page = field;
3429 int frow = field->frow;
3430 int fcol = field->fcol;
3431
3432 /* Walk forward to the 'next' line. The second term in the while clause
3433 just guarantees that we stop if we cycled through the line because
3434 there might be no 'next' line if the page has just one line.
3435 */
3436 do
3437 {
3438 field_on_page = Sorted_Next_Field(field_on_page);
3439 }
3440 while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3441
3442 if (field_on_page->frow != frow)
3443 {
3444 /* We really found a 'next' line. We are positioned at the rightmost
3445 field on this line */
3446 frow = field_on_page->frow;
3447
3448 /* We walk to the right as long as we are really left of the
3449 field. */
3450 while (field_on_page->frow == frow && field_on_page->fcol < fcol)
3451 field_on_page = Sorted_Next_Field(field_on_page);
3452
3453 /* If we wrapped, just go to the left which is the last field on
3454 the row */
3455 if (field_on_page->frow != frow)
3456 field_on_page = Sorted_Previous_Field(field_on_page);
3457 }
3458
3459 return (field_on_page);
3460}
3461
3462/*----------------------------------------------------------------------------
3463 Inter-Field Navigation routines
3464 --------------------------------------------------------------------------*/
3465
3466/*---------------------------------------------------------------------------
3467| Facility : libnform
3468| Function : static int Inter_Field_Navigation(
3469| int (* const fct) (FORM *),
3470| FORM * form)
3471|
3472| Description : Generic behavior for changing the current field, the
3473| field is left and a new field is entered. So the field
3474| must be validated and the field init/term hooks must
3475| be called.
3476|
3477| Return Values : E_OK - success
3478| E_INVALID_FIELD - field is invalid
3479| some other - error from subordinate call
3480+--------------------------------------------------------------------------*/
3481static int
3482Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form)
3483{
3484 int res;
3485
3486 if (!_nc_Internal_Validation(form))
3487 res = E_INVALID_FIELD;
3488 else
3489 {
3490 Call_Hook(form, fieldterm);
3491 res = fct(form);
3492 Call_Hook(form, fieldinit);
3493 }
3494 return res;
3495}
3496
3497/*---------------------------------------------------------------------------
3498| Facility : libnform
3499| Function : static int FN_Next_Field(FORM * form)
3500|
3501| Description : Move to the next field on the current page of the form
3502|
3503| Return Values : E_OK - success
3504| != E_OK - error from subordinate call
3505+--------------------------------------------------------------------------*/
3506static int
3507FN_Next_Field(FORM *form)
3508{
3509 T((T_CALLED("FN_Next_Field(%p)"), form));
3510 returnCode(_nc_Set_Current_Field(form,
3511 Next_Field_On_Page(form->current)));
3512}
3513
3514/*---------------------------------------------------------------------------
3515| Facility : libnform
3516| Function : static int FN_Previous_Field(FORM * form)
3517|
3518| Description : Move to the previous field on the current page of the
3519| form
3520|
3521| Return Values : E_OK - success
3522| != E_OK - error from subordinate call
3523+--------------------------------------------------------------------------*/
3524static int
3525FN_Previous_Field(FORM *form)
3526{
3527 T((T_CALLED("FN_Previous_Field(%p)"), form));
3528 returnCode(_nc_Set_Current_Field(form,
3529 Previous_Field_On_Page(form->current)));
3530}
3531
3532/*---------------------------------------------------------------------------
3533| Facility : libnform
3534| Function : static int FN_First_Field(FORM * form)
3535|
3536| Description : Move to the first field on the current page of the form
3537|
3538| Return Values : E_OK - success
3539| != E_OK - error from subordinate call
3540+--------------------------------------------------------------------------*/
3541static int
3542FN_First_Field(FORM *form)
3543{
3544 T((T_CALLED("FN_First_Field(%p)"), form));
3545 returnCode(_nc_Set_Current_Field(form,
3546 Next_Field_On_Page(form->field[form->page[form->curpage].pmax])));
3547}
3548
3549/*---------------------------------------------------------------------------
3550| Facility : libnform
3551| Function : static int FN_Last_Field(FORM * form)
3552|
3553| Description : Move to the last field on the current page of the form
3554|
3555| Return Values : E_OK - success
3556| != E_OK - error from subordinate call
3557+--------------------------------------------------------------------------*/
3558static int
3559FN_Last_Field(FORM *form)
3560{
3561 T((T_CALLED("FN_Last_Field(%p)"), form));
3562 returnCode(
3563 _nc_Set_Current_Field(form,
3564 Previous_Field_On_Page(form->field[form->page[form->curpage].pmin])));
3565}
3566
3567/*---------------------------------------------------------------------------
3568| Facility : libnform
3569| Function : static int FN_Sorted_Next_Field(FORM * form)
3570|
3571| Description : Move to the sorted next field on the current page
3572| of the form.
3573|
3574| Return Values : E_OK - success
3575| != E_OK - error from subordinate call
3576+--------------------------------------------------------------------------*/
3577static int
3578FN_Sorted_Next_Field(FORM *form)
3579{
3580 T((T_CALLED("FN_Sorted_Next_Field(%p)"), form));
3581 returnCode(_nc_Set_Current_Field(form,
3582 Sorted_Next_Field(form->current)));
3583}
3584
3585/*---------------------------------------------------------------------------
3586| Facility : libnform
3587| Function : static int FN_Sorted_Previous_Field(FORM * form)
3588|
3589| Description : Move to the sorted previous field on the current page
3590| of the form.
3591|
3592| Return Values : E_OK - success
3593| != E_OK - error from subordinate call
3594+--------------------------------------------------------------------------*/
3595static int
3596FN_Sorted_Previous_Field(FORM *form)
3597{
3598 T((T_CALLED("FN_Sorted_Previous_Field(%p)"), form));
3599 returnCode(_nc_Set_Current_Field(form,
3600 Sorted_Previous_Field(form->current)));
3601}
3602
3603/*---------------------------------------------------------------------------
3604| Facility : libnform
3605| Function : static int FN_Sorted_First_Field(FORM * form)
3606|
3607| Description : Move to the sorted first field on the current page
3608| of the form.
3609|
3610| Return Values : E_OK - success
3611| != E_OK - error from subordinate call
3612+--------------------------------------------------------------------------*/
3613static int
3614FN_Sorted_First_Field(FORM *form)
3615{
3616 T((T_CALLED("FN_Sorted_First_Field(%p)"), form));
3617 returnCode(_nc_Set_Current_Field(form,
3618 Sorted_Next_Field(form->field[form->page[form->curpage].smax])));
3619}
3620
3621/*---------------------------------------------------------------------------
3622| Facility : libnform
3623| Function : static int FN_Sorted_Last_Field(FORM * form)
3624|
3625| Description : Move to the sorted last field on the current page
3626| of the form.
3627|
3628| Return Values : E_OK - success
3629| != E_OK - error from subordinate call
3630+--------------------------------------------------------------------------*/
3631static int
3632FN_Sorted_Last_Field(FORM *form)
3633{
3634 T((T_CALLED("FN_Sorted_Last_Field(%p)"), form));
3635 returnCode(_nc_Set_Current_Field(form,
3636 Sorted_Previous_Field(form->field[form->page[form->curpage].smin])));
3637}
3638
3639/*---------------------------------------------------------------------------
3640| Facility : libnform
3641| Function : static int FN_Left_Field(FORM * form)
3642|
3643| Description : Get the field on the left of the current field on the
3644| same line and the same page. Cycles through the line.
3645|
3646| Return Values : E_OK - success
3647| != E_OK - error from subordinate call
3648+--------------------------------------------------------------------------*/
3649static int
3650FN_Left_Field(FORM *form)
3651{
3652 T((T_CALLED("FN_Left_Field(%p)"), form));
3653 returnCode(_nc_Set_Current_Field(form,
3654 Left_Neighbor_Field(form->current)));
3655}
3656
3657/*---------------------------------------------------------------------------
3658| Facility : libnform
3659| Function : static int FN_Right_Field(FORM * form)
3660|
3661| Description : Get the field on the right of the current field on the
3662| same line and the same page. Cycles through the line.
3663|
3664| Return Values : E_OK - success
3665| != E_OK - error from subordinate call
3666+--------------------------------------------------------------------------*/
3667static int
3668FN_Right_Field(FORM *form)
3669{
3670 T((T_CALLED("FN_Right_Field(%p)"), form));
3671 returnCode(_nc_Set_Current_Field(form,
3672 Right_Neighbor_Field(form->current)));
3673}
3674
3675/*---------------------------------------------------------------------------
3676| Facility : libnform
3677| Function : static int FN_Up_Field(FORM * form)
3678|
3679| Description : Get the upper neighbor of the current field. This
3680| cycles through the page. See the comments of the
3681| Upper_Neighbor_Field function to understand how
3682| 'upper' is defined.
3683|
3684| Return Values : E_OK - success
3685| != E_OK - error from subordinate call
3686+--------------------------------------------------------------------------*/
3687static int
3688FN_Up_Field(FORM *form)
3689{
3690 T((T_CALLED("FN_Up_Field(%p)"), form));
3691 returnCode(_nc_Set_Current_Field(form,
3692 Upper_Neighbor_Field(form->current)));
3693}
3694
3695/*---------------------------------------------------------------------------
3696| Facility : libnform
3697| Function : static int FN_Down_Field(FORM * form)
3698|
3699| Description : Get the down neighbor of the current field. This
3700| cycles through the page. See the comments of the
3701| Down_Neighbor_Field function to understand how
3702| 'down' is defined.
3703|
3704| Return Values : E_OK - success
3705| != E_OK - error from subordinate call
3706+--------------------------------------------------------------------------*/
3707static int
3708FN_Down_Field(FORM *form)
3709{
3710 T((T_CALLED("FN_Down_Field(%p)"), form));
3711 returnCode(_nc_Set_Current_Field(form,
3712 Down_Neighbor_Field(form->current)));
3713}
3714/*----------------------------------------------------------------------------
3715 END of Field Navigation routines
3716 --------------------------------------------------------------------------*/
3717
3718/*----------------------------------------------------------------------------
3719 Helper routines for Page Navigation
3720 --------------------------------------------------------------------------*/
3721
3722/*---------------------------------------------------------------------------
3723| Facility : libnform
3724| Function : int _nc_Set_Form_Page(FORM * form,
3725| int page,
3726| FIELD * field)
3727|
3728| Description : Make the given page number the current page and make
3729| the given field the current field on the page. If
3730| for the field NULL is given, make the first field on
3731| the page the current field. The routine acts only
3732| if the requested page is not the current page.
3733|
3734| Return Values : E_OK - success
3735| != E_OK - error from subordinate call
3736| E_BAD_ARGUMENT - invalid field pointer
3737| E_SYSTEM_ERROR - some severe basic error
3738+--------------------------------------------------------------------------*/
3739NCURSES_EXPORT(int)
3740_nc_Set_Form_Page(FORM *form, int page, FIELD *field)
3741{
3742 int res = E_OK;
3743
3744 if ((form->curpage != page))
3745 {
3746 FIELD *last_field, *field_on_page;
3747
3748 werase(Get_Form_Window(form));
3749 form->curpage = page;
3750 last_field = field_on_page = form->field[form->page[page].smin];
3751 do
3752 {
3753 if (field_on_page->opts & O_VISIBLE)
3754 if ((res = Display_Field(field_on_page)) != E_OK)
3755 return (res);
3756 field_on_page = field_on_page->snext;
3757 }
3758 while (field_on_page != last_field);
3759
3760 if (field)
3761 res = _nc_Set_Current_Field(form, field);
3762 else
3763 /* N.B.: we don't encapsulate this by Inter_Field_Navigation(),
3764 because this is already executed in a page navigation
3765 context that contains field navigation
3766 */
3767 res = FN_First_Field(form);
3768 }
3769 return (res);
3770}
3771
3772/*---------------------------------------------------------------------------
3773| Facility : libnform
3774| Function : static int Next_Page_Number(const FORM * form)
3775|
3776| Description : Calculate the page number following the current page
3777| number. This cycles if the highest page number is
3778| reached.
3779|
3780| Return Values : The next page number
3781+--------------------------------------------------------------------------*/
3782NCURSES_INLINE static int
3783Next_Page_Number(const FORM *form)
3784{
3785 return (form->curpage + 1) % form->maxpage;
3786}
3787
3788/*---------------------------------------------------------------------------
3789| Facility : libnform
3790| Function : static int Previous_Page_Number(const FORM * form)
3791|
3792| Description : Calculate the page number before the current page
3793| number. This cycles if the first page number is
3794| reached.
3795|
3796| Return Values : The previous page number
3797+--------------------------------------------------------------------------*/
3798NCURSES_INLINE static int
3799Previous_Page_Number(const FORM *form)
3800{
3801 return (form->curpage != 0 ? form->curpage - 1 : form->maxpage - 1);
3802}
3803
3804/*----------------------------------------------------------------------------
3805 Page Navigation routines
3806 --------------------------------------------------------------------------*/
3807
3808/*---------------------------------------------------------------------------
3809| Facility : libnform
3810| Function : static int Page_Navigation(
3811| int (* const fct) (FORM *),
3812| FORM * form)
3813|
3814| Description : Generic behavior for changing a page. This means
3815| that the field is left and a new field is entered.
3816| So the field must be validated and the field init/term
3817| hooks must be called. Because also the page is changed,
3818| the forms init/term hooks must be called also.
3819|
3820| Return Values : E_OK - success
3821| E_INVALID_FIELD - field is invalid
3822| some other - error from subordinate call
3823+--------------------------------------------------------------------------*/
3824static int
3825Page_Navigation(int (*const fct) (FORM *), FORM *form)
3826{
3827 int res;
3828
3829 if (!_nc_Internal_Validation(form))
3830 res = E_INVALID_FIELD;
3831 else
3832 {
3833 Call_Hook(form, fieldterm);
3834 Call_Hook(form, formterm);
3835 res = fct(form);
3836 Call_Hook(form, forminit);
3837 Call_Hook(form, fieldinit);
3838 }
3839 return res;
3840}
3841
3842/*---------------------------------------------------------------------------
3843| Facility : libnform
3844| Function : static int PN_Next_Page(FORM * form)
3845|
3846| Description : Move to the next page of the form
3847|
3848| Return Values : E_OK - success
3849| != E_OK - error from subordinate call
3850+--------------------------------------------------------------------------*/
3851static int
3852PN_Next_Page(FORM *form)
3853{
3854 T((T_CALLED("PN_Next_Page(%p)"), form));
3855 returnCode(_nc_Set_Form_Page(form, Next_Page_Number(form), (FIELD *)0));
3856}
3857
3858/*---------------------------------------------------------------------------
3859| Facility : libnform
3860| Function : static int PN_Previous_Page(FORM * form)
3861|
3862| Description : Move to the previous page of the form
3863|
3864| Return Values : E_OK - success
3865| != E_OK - error from subordinate call
3866+--------------------------------------------------------------------------*/
3867static int
3868PN_Previous_Page(FORM *form)
3869{
3870 T((T_CALLED("PN_Previous_Page(%p)"), form));
3871 returnCode(_nc_Set_Form_Page(form, Previous_Page_Number(form), (FIELD *)0));
3872}
3873
3874/*---------------------------------------------------------------------------
3875| Facility : libnform
3876| Function : static int PN_First_Page(FORM * form)
3877|
3878| Description : Move to the first page of the form
3879|
3880| Return Values : E_OK - success
3881| != E_OK - error from subordinate call
3882+--------------------------------------------------------------------------*/
3883static int
3884PN_First_Page(FORM *form)
3885{
3886 T((T_CALLED("PN_First_Page(%p)"), form));
3887 returnCode(_nc_Set_Form_Page(form, 0, (FIELD *)0));
3888}
3889
3890/*---------------------------------------------------------------------------
3891| Facility : libnform
3892| Function : static int PN_Last_Page(FORM * form)
3893|
3894| Description : Move to the last page of the form
3895|
3896| Return Values : E_OK - success
3897| != E_OK - error from subordinate call
3898+--------------------------------------------------------------------------*/
3899static int
3900PN_Last_Page(FORM *form)
3901{
3902 T((T_CALLED("PN_Last_Page(%p)"), form));
3903 returnCode(_nc_Set_Form_Page(form, form->maxpage - 1, (FIELD *)0));
3904}
3905
3906/*----------------------------------------------------------------------------
3907 END of Field Navigation routines
3908 --------------------------------------------------------------------------*/
3909
3910/*----------------------------------------------------------------------------
3911 Helper routines for the core form driver.
3912 --------------------------------------------------------------------------*/
3913
3914/*---------------------------------------------------------------------------
3915| Facility : libnform
3916| Function : static int Data_Entry(FORM * form,int c)
3917|
3918| Description : Enter character c into at the current position of the
3919| current field of the form.
3920|
3921| Return Values : E_OK - success
3922| E_REQUEST_DENIED - driver could not process the request
3923| E_SYSTEM_ERROR -
3924+--------------------------------------------------------------------------*/
3925static int
3926Data_Entry(FORM *form, int c)
3927{
3928 FIELD *field = form->current;
3929 int result = E_REQUEST_DENIED;
3930
3931 T((T_CALLED("Data_Entry(%p,%s)"), form, _tracechtype((chtype)c)));
3932 if ((field->opts & O_EDIT)
3933#if FIX_FORM_INACTIVE_BUG
3934 && (field->opts & O_ACTIVE)
3935#endif
3936 )
3937 {
3938 if ((field->opts & O_BLANK) &&
3939 First_Position_In_Current_Field(form) &&
3940 !(form->status & _FCHECK_REQUIRED) &&
3941 !(form->status & _WINDOW_MODIFIED))
3942 werase(form->w);
3943
3944 if (form->status & _OVLMODE)
3945 {
3946 waddch(form->w, (chtype)c);
3947 }
3948 else
3949 /* no _OVLMODE */
3950 {
3951 bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
3952
3953 if (!(There_Is_Room ||
3954 ((Single_Line_Field(field) && Growable(field)))))
3955 RETURN(E_REQUEST_DENIED);
3956
3957 if (!There_Is_Room && !Field_Grown(field, 1))
3958 RETURN(E_SYSTEM_ERROR);
3959
3960 winsch(form->w, (chtype)c);
3961 }
3962
3963 if ((result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form)) == E_OK)
3964 {
3965 bool End_Of_Field = (((field->drows - 1) == form->currow) &&
3966 ((field->dcols - 1) == form->curcol));
3967
3968 form->status |= _WINDOW_MODIFIED;
3969 if (End_Of_Field && !Growable(field) && (field->opts & O_AUTOSKIP))
3970 result = Inter_Field_Navigation(FN_Next_Field, form);
3971 else
3972 {
3973 if (End_Of_Field && Growable(field) && !Field_Grown(field, 1))
3974 result = E_SYSTEM_ERROR;
3975 else
3976 {
3977#if USE_WIDEC_SUPPORT
3978 /*
3979 * We have just added a byte to the form field. It may have
3980 * been part of a multibyte character. If it was, the
3981 * addch_used field is nonzero and we should not try to move
3982 * to a new column.
3983 */
3984 if (WINDOW_EXT(form->w, addch_used) == 0)
3985 IFN_Next_Character(form);
3986#else
3987 IFN_Next_Character(form);
3988#endif
3989 result = E_OK;
3990 }
3991 }
3992 }
3993 }
3994 RETURN(result);
3995}
3996
3997/* Structure to describe the binding of a request code to a function.
3998 The member keycode codes the request value as well as the generic
3999 routine to use for the request. The code for the generic routine
4000 is coded in the upper 16 Bits while the request code is coded in
4001 the lower 16 bits.
4002
4003 In terms of C++ you might think of a request as a class with a
4004 virtual method "perform". The different types of request are
4005 derived from this base class and overload (or not) the base class
4006 implementation of perform.
4007*/
4008typedef struct
4009{
4010 int keycode; /* must be at least 32 bit: hi:mode, lo: key */
4011 int (*cmd) (FORM *); /* low level driver routine for this key */
4012}
4013Binding_Info;
4014
4015/* You may see this is the class-id of the request type class */
4016#define ID_PN (0x00000000) /* Page navigation */
4017#define ID_FN (0x00010000) /* Inter-Field navigation */
4018#define ID_IFN (0x00020000) /* Intra-Field navigation */
4019#define ID_VSC (0x00030000) /* Vertical Scrolling */
4020#define ID_HSC (0x00040000) /* Horizontal Scrolling */
4021#define ID_FE (0x00050000) /* Field Editing */
4022#define ID_EM (0x00060000) /* Edit Mode */
4023#define ID_FV (0x00070000) /* Field Validation */
4024#define ID_CH (0x00080000) /* Choice */
4025#define ID_Mask (0xffff0000)
4026#define Key_Mask (0x0000ffff)
4027#define ID_Shft (16)
4028
4029/* This array holds all the Binding Infos */
4030/* *INDENT-OFF* */
4031static const Binding_Info bindings[MAX_FORM_COMMAND - MIN_FORM_COMMAND + 1] =
4032{
4033 { REQ_NEXT_PAGE |ID_PN ,PN_Next_Page},
4034 { REQ_PREV_PAGE |ID_PN ,PN_Previous_Page},
4035 { REQ_FIRST_PAGE |ID_PN ,PN_First_Page},
4036 { REQ_LAST_PAGE |ID_PN ,PN_Last_Page},
4037
4038 { REQ_NEXT_FIELD |ID_FN ,FN_Next_Field},
4039 { REQ_PREV_FIELD |ID_FN ,FN_Previous_Field},
4040 { REQ_FIRST_FIELD |ID_FN ,FN_First_Field},
4041 { REQ_LAST_FIELD |ID_FN ,FN_Last_Field},
4042 { REQ_SNEXT_FIELD |ID_FN ,FN_Sorted_Next_Field},
4043 { REQ_SPREV_FIELD |ID_FN ,FN_Sorted_Previous_Field},
4044 { REQ_SFIRST_FIELD |ID_FN ,FN_Sorted_First_Field},
4045 { REQ_SLAST_FIELD |ID_FN ,FN_Sorted_Last_Field},
4046 { REQ_LEFT_FIELD |ID_FN ,FN_Left_Field},
4047 { REQ_RIGHT_FIELD |ID_FN ,FN_Right_Field},
4048 { REQ_UP_FIELD |ID_FN ,FN_Up_Field},
4049 { REQ_DOWN_FIELD |ID_FN ,FN_Down_Field},
4050
4051 { REQ_NEXT_CHAR |ID_IFN ,IFN_Next_Character},
4052 { REQ_PREV_CHAR |ID_IFN ,IFN_Previous_Character},
4053 { REQ_NEXT_LINE |ID_IFN ,IFN_Next_Line},
4054 { REQ_PREV_LINE |ID_IFN ,IFN_Previous_Line},
4055 { REQ_NEXT_WORD |ID_IFN ,IFN_Next_Word},
4056 { REQ_PREV_WORD |ID_IFN ,IFN_Previous_Word},
4057 { REQ_BEG_FIELD |ID_IFN ,IFN_Beginning_Of_Field},
4058 { REQ_END_FIELD |ID_IFN ,IFN_End_Of_Field},
4059 { REQ_BEG_LINE |ID_IFN ,IFN_Beginning_Of_Line},
4060 { REQ_END_LINE |ID_IFN ,IFN_End_Of_Line},
4061 { REQ_LEFT_CHAR |ID_IFN ,IFN_Left_Character},
4062 { REQ_RIGHT_CHAR |ID_IFN ,IFN_Right_Character},
4063 { REQ_UP_CHAR |ID_IFN ,IFN_Up_Character},
4064 { REQ_DOWN_CHAR |ID_IFN ,IFN_Down_Character},
4065
4066 { REQ_NEW_LINE |ID_FE ,FE_New_Line},
4067 { REQ_INS_CHAR |ID_FE ,FE_Insert_Character},
4068 { REQ_INS_LINE |ID_FE ,FE_Insert_Line},
4069 { REQ_DEL_CHAR |ID_FE ,FE_Delete_Character},
4070 { REQ_DEL_PREV |ID_FE ,FE_Delete_Previous},
4071 { REQ_DEL_LINE |ID_FE ,FE_Delete_Line},
4072 { REQ_DEL_WORD |ID_FE ,FE_Delete_Word},
4073 { REQ_CLR_EOL |ID_FE ,FE_Clear_To_End_Of_Line},
4074 { REQ_CLR_EOF |ID_FE ,FE_Clear_To_End_Of_Field},
4075 { REQ_CLR_FIELD |ID_FE ,FE_Clear_Field},
4076
4077 { REQ_OVL_MODE |ID_EM ,EM_Overlay_Mode},
4078 { REQ_INS_MODE |ID_EM ,EM_Insert_Mode},
4079
4080 { REQ_SCR_FLINE |ID_VSC ,VSC_Scroll_Line_Forward},
4081 { REQ_SCR_BLINE |ID_VSC ,VSC_Scroll_Line_Backward},
4082 { REQ_SCR_FPAGE |ID_VSC ,VSC_Scroll_Page_Forward},
4083 { REQ_SCR_BPAGE |ID_VSC ,VSC_Scroll_Page_Backward},
4084 { REQ_SCR_FHPAGE |ID_VSC ,VSC_Scroll_Half_Page_Forward},
4085 { REQ_SCR_BHPAGE |ID_VSC ,VSC_Scroll_Half_Page_Backward},
4086
4087 { REQ_SCR_FCHAR |ID_HSC ,HSC_Scroll_Char_Forward},
4088 { REQ_SCR_BCHAR |ID_HSC ,HSC_Scroll_Char_Backward},
4089 { REQ_SCR_HFLINE |ID_HSC ,HSC_Horizontal_Line_Forward},
4090 { REQ_SCR_HBLINE |ID_HSC ,HSC_Horizontal_Line_Backward},
4091 { REQ_SCR_HFHALF |ID_HSC ,HSC_Horizontal_Half_Line_Forward},
4092 { REQ_SCR_HBHALF |ID_HSC ,HSC_Horizontal_Half_Line_Backward},
4093
4094 { REQ_VALIDATION |ID_FV ,FV_Validation},
4095
4096 { REQ_NEXT_CHOICE |ID_CH ,CR_Next_Choice},
4097 { REQ_PREV_CHOICE |ID_CH ,CR_Previous_Choice}
4098};
4099/* *INDENT-ON* */
4100
4101/*---------------------------------------------------------------------------
4102| Facility : libnform
4103| Function : int form_driver(FORM * form,int c)
4104|
4105| Description : This is the workhorse of the forms system. It checks
4106| to determine whether the character c is a request or
4107| data. If it is a request, the form driver executes
4108| the request and returns the result. If it is data
4109| (printable character), it enters the data into the
4110| current position in the current field. If it is not
4111| recognized, the form driver assumes it is an application
4112| defined command and returns E_UNKNOWN_COMMAND.
4113| Application defined command should be defined relative
4114| to MAX_FORM_COMMAND, the maximum value of a request.
4115|
4116| Return Values : E_OK - success
4117| E_SYSTEM_ERROR - system error
4118| E_BAD_ARGUMENT - an argument is incorrect
4119| E_NOT_POSTED - form is not posted
4120| E_INVALID_FIELD - field contents are invalid
4121| E_BAD_STATE - called from inside a hook routine
4122| E_REQUEST_DENIED - request failed
4123| E_NOT_CONNECTED - no fields are connected to the form
4124| E_UNKNOWN_COMMAND - command not known
4125+--------------------------------------------------------------------------*/
4126NCURSES_EXPORT(int)
4127form_driver(FORM *form, int c)
4128{
4129 const Binding_Info *BI = (Binding_Info *) 0;
4130 int res = E_UNKNOWN_COMMAND;
4131
4132 T((T_CALLED("form_driver(%p,%d)"), form, c));
4133
4134 if (!form)
4135 RETURN(E_BAD_ARGUMENT);
4136
4137 if (!(form->field))
4138 RETURN(E_NOT_CONNECTED);
4139
4140 assert(form->page);
4141
4142 if (c == FIRST_ACTIVE_MAGIC)
4143 {
4144 form->current = _nc_First_Active_Field(form);
4145 RETURN(E_OK);
4146 }
4147
4148 assert(form->current &&
4149 form->current->buf &&
4150 (form->current->form == form)
4151 );
4152
4153 if (form->status & _IN_DRIVER)
4154 RETURN(E_BAD_STATE);
4155
4156 if (!(form->status & _POSTED))
4157 RETURN(E_NOT_POSTED);
4158
4159 if ((c >= MIN_FORM_COMMAND && c <= MAX_FORM_COMMAND) &&
4160 ((bindings[c - MIN_FORM_COMMAND].keycode & Key_Mask) == c))
4161 BI = &(bindings[c - MIN_FORM_COMMAND]);
4162
4163 if (BI)
4164 {
4165 typedef int (*Generic_Method) (int (*const) (FORM *), FORM *);
4166 static const Generic_Method Generic_Methods[] =
4167 {
4168 Page_Navigation, /* overloaded to call field&form hooks */
4169 Inter_Field_Navigation, /* overloaded to call field hooks */
4170 NULL, /* Intra-Field is generic */
4171 Vertical_Scrolling, /* Overloaded to check multi-line */
4172 Horizontal_Scrolling, /* Overloaded to check single-line */
4173 Field_Editing, /* Overloaded to mark modification */
4174 NULL, /* Edit Mode is generic */
4175 NULL, /* Field Validation is generic */
4176 NULL /* Choice Request is generic */
4177 };
4178 size_t nMethods = (sizeof(Generic_Methods) / sizeof(Generic_Methods[0]));
4179 size_t method = ((BI->keycode & ID_Mask) >> ID_Shft) & 0xffff;
4180
4181 if ((method >= nMethods) || !(BI->cmd))
4182 res = E_SYSTEM_ERROR;
4183 else
4184 {
4185 Generic_Method fct = Generic_Methods[method];
4186
4187 if (fct)
4188 res = fct(BI->cmd, form);
4189 else
4190 res = (BI->cmd) (form);
4191 }
4192 }
4193#ifdef NCURSES_MOUSE_VERSION
4194 else if (KEY_MOUSE == c)
4195 {
4196 MEVENT event;
4197 WINDOW *win = form->win ? form->win : stdscr;
4198 WINDOW *sub = form->sub ? form->sub : win;
4199
4200 getmouse(&event);
4201 if ((event.bstate & (BUTTON1_CLICKED |
4202 BUTTON1_DOUBLE_CLICKED |
4203 BUTTON1_TRIPLE_CLICKED))
4204 && wenclose(win, event.y, event.x))
4205 { /* we react only if the click was in the userwin, that means
4206 * inside the form display area or at the decoration window.
4207 */
4208 int ry = event.y, rx = event.x; /* screen coordinates */
4209
4210 res = E_REQUEST_DENIED;
4211 if (mouse_trafo(&ry, &rx, FALSE))
4212 { /* rx, ry are now "curses" coordinates */
4213 if (ry < sub->_begy)
4214 { /* we clicked above the display region; this is
4215 * interpreted as "scroll up" request
4216 */
4217 if (event.bstate & BUTTON1_CLICKED)
4218 res = form_driver(form, REQ_PREV_FIELD);
4219 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4220 res = form_driver(form, REQ_PREV_PAGE);
4221 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4222 res = form_driver(form, REQ_FIRST_FIELD);
4223 }
4224 else if (ry > sub->_begy + sub->_maxy)
4225 { /* we clicked below the display region; this is
4226 * interpreted as "scroll down" request
4227 */
4228 if (event.bstate & BUTTON1_CLICKED)
4229 res = form_driver(form, REQ_NEXT_FIELD);
4230 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4231 res = form_driver(form, REQ_NEXT_PAGE);
4232 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4233 res = form_driver(form, REQ_LAST_FIELD);
4234 }
4235 else if (wenclose(sub, event.y, event.x))
4236 { /* Inside the area we try to find the hit item */
4237 int i;
4238
4239 ry = event.y;
4240 rx = event.x;
4241 if (wmouse_trafo(sub, &ry, &rx, FALSE))
4242 {
4243 int min_field = form->page[form->curpage].pmin;
4244 int max_field = form->page[form->curpage].pmax;
4245
4246 for (i = min_field; i <= max_field; ++i)
4247 {
4248 FIELD *field = form->field[i];
4249
4250 if (Field_Is_Selectable(field)
4251 && Field_encloses(field, ry, rx) == E_OK)
4252 {
4253 res = _nc_Set_Current_Field(form, field);
4254 if (res == E_OK)
4255 res = _nc_Position_Form_Cursor(form);
4256 if (res == E_OK
4257 && (event.bstate & BUTTON1_DOUBLE_CLICKED))
4258 res = E_UNKNOWN_COMMAND;
4259 break;
4260 }
4261 }
4262 }
4263 }
4264 }
4265 }
4266 else
4267 res = E_REQUEST_DENIED;
4268 }
4269#endif /* NCURSES_MOUSE_VERSION */
4270 else if (!(c & (~(int)MAX_REGULAR_CHARACTER)))
4271 {
4272 /*
4273 * If we're using 8-bit characters, iscntrl+isprint cover the whole set.
4274 * But with multibyte characters, there is a third possibility, i.e.,
4275 * parts of characters that build up into printable characters which are
4276 * not considered printable.
4277 *
4278 * FIXME: the wide-character branch should also use Check_Char().
4279 */
4280#if USE_WIDEC_SUPPORT
4281 if (!iscntrl(UChar(c)))
4282#else
4283 if (isprint(UChar(c)) &&
4284 Check_Char(form->current->type, c,
4285 (TypeArgument *)(form->current->arg)))
4286#endif
4287 res = Data_Entry(form, c);
4288 }
4289 _nc_Refresh_Current_Field(form);
4290 RETURN(res);
4291}
4292
4293/*----------------------------------------------------------------------------
4294 Field-Buffer manipulation routines.
4295 The effects of setting a buffer are tightly coupled to the core of the form
4296 driver logic. This is especially true in the case of growable fields.
4297 So I don't separate this into a separate module.
4298 --------------------------------------------------------------------------*/
4299
4300/*---------------------------------------------------------------------------
4301| Facility : libnform
4302| Function : int set_field_buffer(FIELD *field,
4303| int buffer, char *value)
4304|
4305| Description : Set the given buffer of the field to the given value.
4306| Buffer 0 stores the displayed content of the field.
4307| For dynamic fields this may grow the fieldbuffers if
4308| the length of the value exceeds the current buffer
4309| length. For buffer 0 only printable values are allowed.
4310| For static fields, the value needs not to be zero ter-
4311| minated. It is copied up to the length of the buffer.
4312|
4313| Return Values : E_OK - success
4314| E_BAD_ARGUMENT - invalid argument
4315| E_SYSTEM_ERROR - system error
4316+--------------------------------------------------------------------------*/
4317NCURSES_EXPORT(int)
4318set_field_buffer(FIELD *field, int buffer, const char *value)
4319{
4320 FIELD_CELL *p;
4321 int res = E_OK;
4322 unsigned int i;
4323 unsigned int len;
4324
4325#if USE_WIDEC_SUPPORT
4326 FIELD_CELL *widevalue = 0;
4327#endif
4328
4329 T((T_CALLED("set_field_buffer(%p,%d,%s)"), field, buffer, _nc_visbuf(value)));
4330
4331 if (!field || !value || ((buffer < 0) || (buffer > field->nbuf)))
4332 RETURN(E_BAD_ARGUMENT);
4333
4334 len = Buffer_Length(field);
4335
4336 if (buffer == 0)
4337 {
4338 for (i = 0; (value[i] != '\0') && (i < len); ++i)
4339 {
4340 if (iscntrl(UChar(value[i])))
4341 RETURN(E_BAD_ARGUMENT);
4342 }
4343 }
4344
4345 if (Growable(field))
4346 {
4347 /* for a growable field we must assume zero terminated strings, because
4348 somehow we have to detect the length of what should be copied.
4349 */
4350 unsigned int vlen = strlen(value);
4351
4352 if (vlen > len)
4353 {
4354 if (!Field_Grown(field,
4355 (int)(1 + (vlen - len) / ((field->rows + field->nrow)
4356 * field->cols))))
4357 RETURN(E_SYSTEM_ERROR);
4358
4359 /* in this case we also have to check, whether or not the remaining
4360 characters in value are also printable for buffer 0. */
4361 if (buffer == 0)
4362 {
4363 for (i = len; i < vlen; i++)
4364 if (iscntrl(UChar(value[i])))
4365 RETURN(E_BAD_ARGUMENT);
4366 }
4367 len = vlen;
4368 }
4369 }
4370
4371 p = Address_Of_Nth_Buffer(field, buffer);
4372
4373#if USE_WIDEC_SUPPORT
4374 /*
4375 * Use addstr's logic for converting a string to an array of cchar_t's.
4376 * There should be a better way, but this handles nonspacing characters
4377 * and other special cases that we really do not want to handle here.
4378 */
4379 wclear(field->working);
4380 mvwaddstr(field->working, 0, 0, value);
4381
4382 if ((widevalue = typeCalloc(FIELD_CELL, len + 1)) == 0)
4383 {
4384 RETURN(E_SYSTEM_ERROR);
4385 }
4386 else
4387 {
4388 mvwin_wchnstr(field->working, 0, 0, widevalue, (int)len);
4389 for (i = 0; i < len; ++i)
4390 {
4391 if (CharEq(myZEROS, widevalue[i]))
4392 {
4393 while (i < len)
4394 p[i++] = myBLANK;
4395 break;
4396 }
4397 p[i] = widevalue[i];
4398 }
4399 free(widevalue);
4400 }
4401#else
4402 for (i = 0; i < len; ++i)
4403 {
4404 if (value[i] == '\0')
4405 {
4406 while (i < len)
4407 p[i++] = myBLANK;
4408 break;
4409 }
4410 p[i] = value[i];
4411 }
4412#endif
4413
4414 if (buffer == 0)
4415 {
4416 int syncres;
4417
4418 if (((syncres = Synchronize_Field(field)) != E_OK) &&
4419 (res == E_OK))
4420 res = syncres;
4421 if (((syncres = Synchronize_Linked_Fields(field)) != E_OK) &&
4422 (res == E_OK))
4423 res = syncres;
4424 }
4425 RETURN(res);
4426}
4427
4428/*---------------------------------------------------------------------------
4429| Facility : libnform
4430| Function : char *field_buffer(const FIELD *field,int buffer)
4431|
4432| Description : Return the address of the buffer for the field.
4433|
4434| Return Values : Pointer to buffer or NULL if arguments were invalid.
4435+--------------------------------------------------------------------------*/
4436NCURSES_EXPORT(char *)
4437field_buffer(const FIELD *field, int buffer)
4438{
4439 char *result = 0;
4440
4441 T((T_CALLED("field_buffer(%p,%d)"), field, buffer));
4442
4443 if (field && (buffer >= 0) && (buffer <= field->nbuf))
4444 {
4445#if USE_WIDEC_SUPPORT
4446 FIELD_CELL *data = Address_Of_Nth_Buffer(field, buffer);
4447 unsigned need = 0;
4448 int size = Buffer_Length(field);
4449 int n;
4450
4451 /* determine the number of bytes needed to store the expanded string */
4452 for (n = 0; n < size; ++n)
4453 {
4454 if (!isWidecExt(data[n]))
4455 {
4456 mbstate_t state;
4457 size_t next;
4458
4459 init_mb(state);
4460 next = _nc_wcrtomb(0, data[n].chars[0], &state);
4461 if (!isEILSEQ(next))
4462 need += next;
4463 }
4464 }
4465
4466 /* allocate a place to store the expanded string */
4467 if (field->expanded[buffer] != 0)
4468 free(field->expanded[buffer]);
4469 field->expanded[buffer] = typeMalloc(char, need + 1);
4470
4471 /* expand the multibyte data */
4472 if ((result = field->expanded[buffer]) != 0)
4473 {
4474 wclear(field->working);
4475 mvwadd_wchnstr(field->working, 0, 0, data, size);
4476 mvwinnstr(field->working, 0, 0, result, (int)need);
4477 }
4478#else
4479 result = Address_Of_Nth_Buffer(field, buffer);
4480#endif
4481 }
4482 returnPtr(result);
4483}
4484
4485#if USE_WIDEC_SUPPORT
4486
4487/* FIXME: see lib_get_wch.c */
4488#if HAVE_MBTOWC && HAVE_MBLEN
4489#define reset_mbytes(state) mblen(NULL, 0), mbtowc(NULL, NULL, 0)
4490#define count_mbytes(buffer,length,state) mblen(buffer,length)
4491#define trans_mbytes(wch,buffer,length,state) \
4492 (int) mbtowc(&wch, buffer, length)
4493#elif HAVE_MBRTOWC && HAVE_MBRLEN
4494#define NEED_STATE
4495#define reset_mbytes(state) init_mb(state)
4496#define count_mbytes(buffer,length,state) mbrlen(buffer,length,&state)
4497#define trans_mbytes(wch,buffer,length,state) \
4498 (int) mbrtowc(&wch, buffer, length, &state)
4499#else
4500make an error
4501#endif
4502
4503/*---------------------------------------------------------------------------
4504| Convert a multibyte string to a wide-character string. The result must be
4505| freed by the caller.
4506+--------------------------------------------------------------------------*/
4507NCURSES_EXPORT(wchar_t *)
4508_nc_Widen_String(char *source, int *lengthp)
4509{
4510 wchar_t *result = 0;
4511 wchar_t wch;
4512 size_t given = strlen(source);
4513 size_t tries;
4514 int pass;
4515 int status;
4516
4517#ifdef NEED_STATE
4518 mbstate_t state;
4519#endif
4520
4521 for (pass = 0; pass < 2; ++pass)
4522 {
4523 unsigned need = 0;
4524 size_t passed = 0;
4525
4526 while (passed < given)
4527 {
4528 bool found = FALSE;
4529
4530 for (tries = 1, status = 0; tries <= (given - passed); ++tries)
4531 {
4532 int save = source[passed + tries];
4533
4534 source[passed + tries] = 0;
4535 reset_mbytes(state);
4536 status = trans_mbytes(wch, source + passed, tries, state);
4537 source[passed + tries] = save;
4538
4539 if (status > 0)
4540 {
4541 found = TRUE;
4542 break;
4543 }
4544 }
4545 if (found)
4546 {
4547 if (pass)
4548 {
4549 result[need] = wch;
4550 }
4551 passed += status;
4552 ++need;
4553 }
4554 else
4555 {
4556 if (pass)
4557 {
4558 result[need] = source[passed];
4559 }
4560 ++need;
4561 ++passed;
4562 }
4563 }
4564
4565 if (!pass)
4566 {
4567 if (!need)
4568 break;
4569 result = typeCalloc(wchar_t, need);
4570
4571 *lengthp = need;
4572 if (result == 0)
4573 break;
4574 }
4575 }
4576
4577 return result;
4578}
4579#endif
4580
4581/* frm_driver.c ends here */