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