1 /****************************************************************************
2 * Copyright 2018-2020,2021 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.135 2021/09/01 23:34:01 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 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 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 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 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 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 != 0)
738 {
739 assert(form != (FORM *)0);
740 if (form->w)
741 delwin(form->w);
742 form->w = new_window;
743 Set_Field_Window_Attributes(field, form->w);
744 werase(form->w);
745 Buffer_To_Window(field, form->w);
746 untouchwin(form->w);
747 wmove(form->w, form->currow, form->curcol);
748 }
749 else
750 result = FALSE;
751 }
752
753 if (result)
754 {
755 free(oldbuf);
756 /* reflect changes in linked fields */
757 if (field != field->link)
758 {
759 FIELD *linked_field;
760
761 for (linked_field = field->link;
762 linked_field != field;
763 linked_field = linked_field->link)
764 {
765 linked_field->buf = field->buf;
766 linked_field->drows = field->drows;
767 linked_field->dcols = field->dcols;
768 }
769 }
770 }
771 else
772 {
773 /* restore old state */
774 field->dcols = old_dcols;
775 field->drows = old_drows;
776 field->buf = oldbuf;
777 if ((single_line_field &&
778 (field->dcols != field->maxgrow)) ||
779 (!single_line_field &&
780 (field->drows != field->maxgrow)))
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 != 0
806 && field->frow <= ry
807 && (field->frow + field->rows) > ry
808 && field->fcol <= rx
809 && (field->fcol + field->cols) > rx)
810 {
811 RETURN(E_OK);
812 }
813 RETURN(E_INVALID_FIELD);
814 }
815 #endif
816
817 /*---------------------------------------------------------------------------
818 | Facility : libnform
819 | Function : int _nc_Position_Form_Cursor(FORM * form)
820 |
821 | Description : Position the cursor in the window for the current
822 | field to be in sync. with the currow and curcol
823 | values.
824 |
825 | Return Values : E_OK - success
826 | E_BAD_ARGUMENT - invalid form pointer
827 | E_SYSTEM_ERROR - form has no current field or
828 | field-window
829 +--------------------------------------------------------------------------*/
830 FORM_EXPORT(int)
_nc_Position_Form_Cursor(FORM * form)831 _nc_Position_Form_Cursor(FORM *form)
832 {
833 FIELD *field;
834 WINDOW *formwin;
835
836 if (!form)
837 return (E_BAD_ARGUMENT);
838
839 if (!form->w || !form->current)
840 return (E_SYSTEM_ERROR);
841
842 field = form->current;
843 formwin = Get_Form_Window(form);
844
845 wmove(form->w, form->currow, form->curcol);
846 if (Has_Invisible_Parts(field))
847 {
848 /* in this case fieldwin isn't derived from formwin, so we have
849 to move the cursor in formwin by hand... */
850 wmove(formwin,
851 field->frow + form->currow - form->toprow,
852 field->fcol + form->curcol - form->begincol);
853 wcursyncup(formwin);
854 }
855 else
856 wcursyncup(form->w);
857 return (E_OK);
858 }
859
860 /*---------------------------------------------------------------------------
861 | Facility : libnform
862 | Function : int _nc_Refresh_Current_Field(FORM * form)
863 |
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 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 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 != 0);
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 == 0))
1462 {
1463 copywin(form->w,
1464 Get_Form_Window(form),
1465 0,
1466 0,
1467 field->frow,
1468 field->fcol,
1469 field->frow,
1470 field->cols + field->fcol - 1,
1471 0);
1472 wsyncup(Get_Form_Window(form));
1473 }
1474 else
1475 {
1476 wsyncup(form->w);
1477 }
1478 }
1479 }
1480 }
1481 delwin(form->w);
1482 form->w = (WINDOW *)0;
1483 form->current = 0;
1484 }
1485
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 FIELD *field = form->current;
1706 FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1707 FIELD_CELL *s;
1708 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 FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1749 FIELD_CELL *s;
1750 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 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 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 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 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, *s;
2369
2370 Synchronize_Buffer(form);
2371 begin_of_last_line = Address_Of_Row_In_Buffer(field, (field->drows - 1));
2372 s = After_End_Of_Data(begin_of_last_line, field->dcols);
2373 return ((s == begin_of_last_line) ? TRUE : FALSE);
2374 }
2375
2376 /*---------------------------------------------------------------------------
2377 | Facility : libnform
2378 | Function : static bool Is_There_Room_For_A_Char_In_Line(FORM * form)
2379 |
2380 | Description : Checks whether or not there is room for a new character
2381 | in the current line.
2382 |
2383 | Return Values : TRUE - there is room
2384 | FALSE - there is not enough room (line full)
2385 +--------------------------------------------------------------------------*/
2386 NCURSES_INLINE static bool
Is_There_Room_For_A_Char_In_Line(FORM * form)2387 Is_There_Room_For_A_Char_In_Line(FORM *form)
2388 {
2389 int last_char_in_line;
2390
2391 wmove(form->w, form->currow, form->current->dcols - 1);
2392 last_char_in_line = (int)(winch(form->w) & A_CHARTEXT);
2393 wmove(form->w, form->currow, form->curcol);
2394 return (((last_char_in_line == form->current->pad) ||
2395 is_blank(last_char_in_line)) ? TRUE : FALSE);
2396 }
2397
2398 #define There_Is_No_Room_For_A_Char_In_Line(f) \
2399 !Is_There_Room_For_A_Char_In_Line(f)
2400
2401 /*---------------------------------------------------------------------------
2402 | Facility : libnform
2403 | Function : static int Insert_String(
2404 | FORM * form,
2405 | int row,
2406 | char *txt,
2407 | int len )
2408 |
2409 | Description : Insert the 'len' characters beginning at pointer 'txt'
2410 | into the 'row' of the 'form'. The insertion occurs
2411 | on the beginning of the row, all other characters are
2412 | moved to the right. After the text a pad character will
2413 | be inserted to separate the text from the rest. If
2414 | necessary the insertion moves characters on the next
2415 | line to make place for the requested insertion string.
2416 |
2417 | Return Values : E_OK - success
2418 | E_REQUEST_DENIED -
2419 | E_SYSTEM_ERROR - system error
2420 +--------------------------------------------------------------------------*/
2421 static int
Insert_String(FORM * form,int row,FIELD_CELL * txt,int len)2422 Insert_String(FORM *form, int row, FIELD_CELL *txt, int len)
2423 {
2424 FIELD *field = form->current;
2425 FIELD_CELL *bp = Address_Of_Row_In_Buffer(field, row);
2426 int datalen = (int)(After_End_Of_Data(bp, field->dcols) - bp);
2427 int freelen = field->dcols - datalen;
2428 int requiredlen = len + 1;
2429 int result = E_REQUEST_DENIED;
2430
2431 if (freelen >= requiredlen)
2432 {
2433 wmove(form->w, row, 0);
2434 myINSNSTR(form->w, txt, len);
2435 wmove(form->w, row, len);
2436 myINSNSTR(form->w, &myBLANK, 1);
2437 result = E_OK;
2438 }
2439 else
2440 {
2441 /* we have to move characters on the next line. If we are on the
2442 last line this may work, if the field is growable */
2443 if ((row == (field->drows - 1)) && Growable(field))
2444 {
2445 if (!Field_Grown(field, 1))
2446 return (E_SYSTEM_ERROR);
2447 /* !!!Side-Effect : might be changed due to growth!!! */
2448 bp = Address_Of_Row_In_Buffer(field, row);
2449 }
2450
2451 if (row < (field->drows - 1))
2452 {
2453 FIELD_CELL *split;
2454
2455 split =
2456 After_Last_Whitespace_Character(bp,
2457 (int)(Get_Start_Of_Data(bp
2458 + field->dcols
2459 - requiredlen,
2460 requiredlen)
2461 - bp));
2462 /* split points now to the first character of the portion of the
2463 line that must be moved to the next line */
2464 datalen = (int)(split - bp); /* + freelen has to stay on this line */
2465 freelen = field->dcols - (datalen + freelen); /* for the next line */
2466
2467 if ((result = Insert_String(form, row + 1, split, freelen)) == E_OK)
2468 {
2469 wmove(form->w, row, datalen);
2470 wclrtoeol(form->w);
2471 wmove(form->w, row, 0);
2472 myINSNSTR(form->w, txt, len);
2473 wmove(form->w, row, len);
2474 myINSNSTR(form->w, &myBLANK, 1);
2475 return E_OK;
2476 }
2477 }
2478 }
2479 return (result);
2480 }
2481
2482 /*---------------------------------------------------------------------------
2483 | Facility : libnform
2484 | Function : static int Wrapping_Not_Necessary_Or_Wrapping_Ok(
2485 | FORM * form)
2486 |
2487 | Description : If a character has been entered into a field, it may
2488 | be that wrapping has to occur. This routine checks
2489 | whether or not wrapping is required and if so, performs
2490 | the wrapping.
2491 |
2492 | Return Values : E_OK - no wrapping required or wrapping
2493 | was successful
2494 | E_REQUEST_DENIED -
2495 | E_SYSTEM_ERROR - some system error
2496 +--------------------------------------------------------------------------*/
2497 static int
Wrapping_Not_Necessary_Or_Wrapping_Ok(FORM * form)2498 Wrapping_Not_Necessary_Or_Wrapping_Ok(FORM *form)
2499 {
2500 FIELD *field = form->current;
2501 int result = E_REQUEST_DENIED;
2502 bool Last_Row = ((field->drows - 1) == form->currow);
2503
2504 if ((Field_Has_Option(field, O_WRAP)) && /* wrapping wanted */
2505 (!Single_Line_Field(field)) && /* must be multi-line */
2506 (There_Is_No_Room_For_A_Char_In_Line(form)) && /* line is full */
2507 (!Last_Row || Growable(field))) /* there are more lines */
2508 {
2509 FIELD_CELL *bp;
2510 FIELD_CELL *split;
2511 int chars_to_be_wrapped;
2512 int chars_to_remain_on_line;
2513
2514 if (Last_Row)
2515 {
2516 /* the above logic already ensures, that in this case the field
2517 is growable */
2518 if (!Field_Grown(field, 1))
2519 return E_SYSTEM_ERROR;
2520 }
2521 bp = Address_Of_Current_Row_In_Buffer(form);
2522 Window_To_Buffer(form, field);
2523 split = After_Last_Whitespace_Character(bp, field->dcols);
2524 /* split points to the first character of the sequence to be brought
2525 on the next line */
2526 chars_to_remain_on_line = (int)(split - bp);
2527 chars_to_be_wrapped = field->dcols - chars_to_remain_on_line;
2528 if (chars_to_remain_on_line > 0)
2529 {
2530 if ((result = Insert_String(form, form->currow + 1, split,
2531 chars_to_be_wrapped)) == E_OK)
2532 {
2533 wmove(form->w, form->currow, chars_to_remain_on_line);
2534 wclrtoeol(form->w);
2535 if (form->curcol >= chars_to_remain_on_line)
2536 {
2537 form->currow++;
2538 form->curcol -= chars_to_remain_on_line;
2539 }
2540 return E_OK;
2541 }
2542 }
2543 else
2544 return E_OK;
2545 if (result != E_OK)
2546 {
2547 DeleteChar(form);
2548 Window_To_Buffer(form, field);
2549 result = E_REQUEST_DENIED;
2550 }
2551 }
2552 else
2553 result = E_OK; /* wrapping was not necessary */
2554 return (result);
2555 }
2556
2557 /*----------------------------------------------------------------------------
2558 Field Editing routines
2559 --------------------------------------------------------------------------*/
2560
2561 /*---------------------------------------------------------------------------
2562 | Facility : libnform
2563 | Function : static int Field_Editing(
2564 | int (* const fct) (FORM *),
2565 | FORM * form)
2566 |
2567 | Description : Generic routine for field editing requests. The driver
2568 | routines are only called for editable fields, the
2569 | _WINDOW_MODIFIED flag is set if editing occurred.
2570 | This is somewhat special due to the overload semantics
2571 | of the NEW_LINE and DEL_PREV requests.
2572 |
2573 | Return Values : Error code from low level drivers.
2574 +--------------------------------------------------------------------------*/
2575 static int
Field_Editing(int (* const fct)(FORM *),FORM * form)2576 Field_Editing(int (*const fct) (FORM *), FORM *form)
2577 {
2578 int res = E_REQUEST_DENIED;
2579
2580 /* We have to deal here with the specific case of the overloaded
2581 behavior of New_Line and Delete_Previous requests.
2582 They may end up in navigational requests if we are on the first
2583 character in a field. But navigation is also allowed on non-
2584 editable fields.
2585 */
2586 if ((fct == FE_Delete_Previous) &&
2587 ((unsigned)form->opts & O_BS_OVERLOAD) &&
2588 First_Position_In_Current_Field(form))
2589 {
2590 res = Inter_Field_Navigation(FN_Previous_Field, form);
2591 }
2592 else
2593 {
2594 if (fct == FE_New_Line)
2595 {
2596 if (((unsigned)form->opts & O_NL_OVERLOAD) &&
2597 First_Position_In_Current_Field(form))
2598 {
2599 res = Inter_Field_Navigation(FN_Next_Field, form);
2600 }
2601 else
2602 /* FE_New_Line deals itself with the _WINDOW_MODIFIED flag */
2603 res = fct(form);
2604 }
2605 else
2606 {
2607 /* From now on, everything must be editable */
2608 if ((unsigned)form->current->opts & O_EDIT)
2609 {
2610 res = fct(form);
2611 if (res == E_OK)
2612 SetStatus(form, _WINDOW_MODIFIED);
2613 }
2614 }
2615 }
2616 return res;
2617 }
2618
2619 /*---------------------------------------------------------------------------
2620 | Facility : libnform
2621 | Function : static int FE_New_Line(FORM * form)
2622 |
2623 | Description : Perform a new line request. This is rather complex
2624 | compared to other routines in this code due to the
2625 | rather difficult to understand description in the
2626 | manuals.
2627 |
2628 | Return Values : E_OK - success
2629 | E_REQUEST_DENIED - new line not allowed
2630 | E_SYSTEM_ERROR - system error
2631 +--------------------------------------------------------------------------*/
2632 static int
FE_New_Line(FORM * form)2633 FE_New_Line(FORM *form)
2634 {
2635 FIELD *field = form->current;
2636 FIELD_CELL *bp, *t;
2637 bool Last_Row = ((field->drows - 1) == form->currow);
2638
2639 T((T_CALLED("FE_New_Line(%p)"), (void *)form));
2640 if (form->status & _OVLMODE)
2641 {
2642 if (Last_Row &&
2643 (!(Growable(field) && !Single_Line_Field(field))))
2644 {
2645 if (!((unsigned)form->opts & O_NL_OVERLOAD))
2646 returnCode(E_REQUEST_DENIED);
2647 wmove(form->w, form->currow, form->curcol);
2648 wclrtoeol(form->w);
2649 /* we have to set this here, although it is also
2650 handled in the generic routine. The reason is,
2651 that FN_Next_Field may fail, but the form is
2652 definitively changed */
2653 SetStatus(form, _WINDOW_MODIFIED);
2654 returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2655 }
2656 else
2657 {
2658 if (Last_Row && !Field_Grown(field, 1))
2659 {
2660 /* N.B.: due to the logic in the 'if', LastRow==TRUE
2661 means here that the field is growable and not
2662 a single-line field */
2663 returnCode(E_SYSTEM_ERROR);
2664 }
2665 wmove(form->w, form->currow, form->curcol);
2666 wclrtoeol(form->w);
2667 form->currow++;
2668 form->curcol = 0;
2669 SetStatus(form, _WINDOW_MODIFIED);
2670 returnCode(E_OK);
2671 }
2672 }
2673 else
2674 {
2675 /* Insert Mode */
2676 if (Last_Row &&
2677 !(Growable(field) && !Single_Line_Field(field)))
2678 {
2679 if (!((unsigned)form->opts & O_NL_OVERLOAD))
2680 returnCode(E_REQUEST_DENIED);
2681 returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2682 }
2683 else
2684 {
2685 bool May_Do_It = !Last_Row && Is_There_Room_For_A_Line(form);
2686
2687 if (!(May_Do_It || Growable(field)))
2688 returnCode(E_REQUEST_DENIED);
2689 if (!May_Do_It && !Field_Grown(field, 1))
2690 returnCode(E_SYSTEM_ERROR);
2691
2692 bp = Address_Of_Current_Position_In_Buffer(form);
2693 t = After_End_Of_Data(bp, field->dcols - form->curcol);
2694 wmove(form->w, form->currow, form->curcol);
2695 wclrtoeol(form->w);
2696 form->currow++;
2697 form->curcol = 0;
2698 wmove(form->w, form->currow, form->curcol);
2699 winsertln(form->w);
2700 myADDNSTR(form->w, bp, (int)(t - bp));
2701 SetStatus(form, _WINDOW_MODIFIED);
2702 returnCode(E_OK);
2703 }
2704 }
2705 }
2706
2707 /*---------------------------------------------------------------------------
2708 | Facility : libnform
2709 | Function : static int FE_Insert_Character(FORM * form)
2710 |
2711 | Description : Insert blank character at the cursor position
2712 |
2713 | Return Values : E_OK
2714 | E_REQUEST_DENIED
2715 +--------------------------------------------------------------------------*/
2716 static int
FE_Insert_Character(FORM * form)2717 FE_Insert_Character(FORM *form)
2718 {
2719 FIELD *field = form->current;
2720 int result = E_REQUEST_DENIED;
2721
2722 T((T_CALLED("FE_Insert_Character(%p)"), (void *)form));
2723 if (Check_Char(form, field, field->type, (int)C_BLANK,
2724 (TypeArgument *)(field->arg)))
2725 {
2726 bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
2727
2728 if (There_Is_Room ||
2729 ((Single_Line_Field(field) && Growable(field))))
2730 {
2731 if (!There_Is_Room && !Field_Grown(field, 1))
2732 result = E_SYSTEM_ERROR;
2733 else
2734 {
2735 winsch(form->w, (chtype)C_BLANK);
2736 result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form);
2737 }
2738 }
2739 }
2740 returnCode(result);
2741 }
2742
2743 /*---------------------------------------------------------------------------
2744 | Facility : libnform
2745 | Function : static int FE_Insert_Line(FORM * form)
2746 |
2747 | Description : Insert a blank line at the cursor position
2748 |
2749 | Return Values : E_OK - success
2750 | E_REQUEST_DENIED - line can not be inserted
2751 +--------------------------------------------------------------------------*/
2752 static int
FE_Insert_Line(FORM * form)2753 FE_Insert_Line(FORM *form)
2754 {
2755 FIELD *field = form->current;
2756 int result = E_REQUEST_DENIED;
2757
2758 T((T_CALLED("FE_Insert_Line(%p)"), (void *)form));
2759 if (Check_Char(form, field,
2760 field->type, (int)C_BLANK, (TypeArgument *)(field->arg)))
2761 {
2762 bool Maybe_Done = (form->currow != (field->drows - 1)) &&
2763 Is_There_Room_For_A_Line(form);
2764
2765 if (!Single_Line_Field(field) &&
2766 (Maybe_Done || Growable(field)))
2767 {
2768 if (!Maybe_Done && !Field_Grown(field, 1))
2769 result = E_SYSTEM_ERROR;
2770 else
2771 {
2772 form->curcol = 0;
2773 winsertln(form->w);
2774 result = E_OK;
2775 }
2776 }
2777 }
2778 returnCode(result);
2779 }
2780
2781 /*---------------------------------------------------------------------------
2782 | Facility : libnform
2783 | Function : static int FE_Delete_Character(FORM * form)
2784 |
2785 | Description : Delete character at the cursor position
2786 |
2787 | Return Values : E_OK - success
2788 +--------------------------------------------------------------------------*/
2789 static int
FE_Delete_Character(FORM * form)2790 FE_Delete_Character(FORM *form)
2791 {
2792 T((T_CALLED("FE_Delete_Character(%p)"), (void *)form));
2793 DeleteChar(form);
2794 returnCode(E_OK);
2795 }
2796
2797 /*---------------------------------------------------------------------------
2798 | Facility : libnform
2799 | Function : static int FE_Delete_Previous(FORM * form)
2800 |
2801 | Description : Delete character before cursor. Again this is a rather
2802 | difficult piece compared to others due to the overloading
2803 | semantics of backspace.
2804 | N.B.: The case of overloaded BS on first field position
2805 | is already handled in the generic routine.
2806 |
2807 | Return Values : E_OK - success
2808 | E_REQUEST_DENIED - Character can't be deleted
2809 +--------------------------------------------------------------------------*/
2810 static int
FE_Delete_Previous(FORM * form)2811 FE_Delete_Previous(FORM *form)
2812 {
2813 FIELD *field = form->current;
2814
2815 T((T_CALLED("FE_Delete_Previous(%p)"), (void *)form));
2816 if (First_Position_In_Current_Field(form))
2817 returnCode(E_REQUEST_DENIED);
2818
2819 if ((--(form->curcol)) < 0)
2820 {
2821 FIELD_CELL *this_line, *prev_line, *prev_end, *this_end;
2822 int this_row = form->currow;
2823
2824 form->curcol++;
2825 if (form->status & _OVLMODE)
2826 returnCode(E_REQUEST_DENIED);
2827
2828 prev_line = Address_Of_Row_In_Buffer(field, (form->currow - 1));
2829 this_line = Address_Of_Row_In_Buffer(field, (form->currow));
2830 Synchronize_Buffer(form);
2831 prev_end = After_End_Of_Data(prev_line, field->dcols);
2832 this_end = After_End_Of_Data(this_line, field->dcols);
2833 if ((int)(this_end - this_line) >
2834 (field->cols - (int)(prev_end - prev_line)))
2835 returnCode(E_REQUEST_DENIED);
2836 wmove(form->w, form->currow, form->curcol);
2837 wdeleteln(form->w);
2838 Adjust_Cursor_Position(form, prev_end);
2839 /*
2840 * If we did not really move to the previous line, help the user a
2841 * little. It is however a little inconsistent. Normally, when
2842 * backspacing around the point where text wraps to a new line in a
2843 * multi-line form, we absorb one keystroke for the wrapping point. That
2844 * is consistent with SVr4 forms. However, SVr4 does not allow typing
2845 * into the last column of the field, and requires the user to enter a
2846 * newline to move to the next line. Therefore it can consistently eat
2847 * that keystroke. Since ncurses allows the last column, it wraps
2848 * automatically (given the proper options). But we cannot eat the
2849 * keystroke to back over the wrapping point, since that would put the
2850 * cursor past the end of the form field. In this case, just delete the
2851 * character at the end of the field.
2852 */
2853 if (form->currow == this_row && this_row > 0)
2854 {
2855 form->currow -= 1;
2856 form->curcol = field->dcols - 1;
2857 DeleteChar(form);
2858 }
2859 else
2860 {
2861 wmove(form->w, form->currow, form->curcol);
2862 myADDNSTR(form->w, this_line, (int)(this_end - this_line));
2863 }
2864 }
2865 else
2866 {
2867 DeleteChar(form);
2868 }
2869 returnCode(E_OK);
2870 }
2871
2872 /*---------------------------------------------------------------------------
2873 | Facility : libnform
2874 | Function : static int FE_Delete_Line(FORM * form)
2875 |
2876 | Description : Delete line at cursor position.
2877 |
2878 | Return Values : E_OK - success
2879 +--------------------------------------------------------------------------*/
2880 static int
FE_Delete_Line(FORM * form)2881 FE_Delete_Line(FORM *form)
2882 {
2883 T((T_CALLED("FE_Delete_Line(%p)"), (void *)form));
2884 form->curcol = 0;
2885 wdeleteln(form->w);
2886 returnCode(E_OK);
2887 }
2888
2889 /*---------------------------------------------------------------------------
2890 | Facility : libnform
2891 | Function : static int FE_Delete_Word(FORM * form)
2892 |
2893 | Description : Delete word at cursor position
2894 |
2895 | Return Values : E_OK - success
2896 | E_REQUEST_DENIED - failure
2897 +--------------------------------------------------------------------------*/
2898 static int
FE_Delete_Word(FORM * form)2899 FE_Delete_Word(FORM *form)
2900 {
2901 FIELD *field = form->current;
2902 FIELD_CELL *bp = Address_Of_Current_Row_In_Buffer(form);
2903 FIELD_CELL *ep = bp + field->dcols;
2904 FIELD_CELL *cp = bp + form->curcol;
2905 FIELD_CELL *s;
2906
2907 T((T_CALLED("FE_Delete_Word(%p)"), (void *)form));
2908 Synchronize_Buffer(form);
2909 if (ISBLANK(*cp))
2910 returnCode(E_REQUEST_DENIED); /* not in word */
2911
2912 /* move cursor to begin of word and erase to end of screen-line */
2913 Adjust_Cursor_Position(form,
2914 After_Last_Whitespace_Character(bp, form->curcol));
2915 wmove(form->w, form->currow, form->curcol);
2916 wclrtoeol(form->w);
2917
2918 /* skip over word in buffer */
2919 s = Get_First_Whitespace_Character(cp, (int)(ep - cp));
2920 /* to begin of next word */
2921 s = Get_Start_Of_Data(s, (int)(ep - s));
2922 if ((s != cp) && !ISBLANK(*s))
2923 {
2924 /* copy remaining line to window */
2925 myADDNSTR(form->w, s, (int)(s - After_End_Of_Data(s, (int)(ep - s))));
2926 }
2927 returnCode(E_OK);
2928 }
2929
2930 /*---------------------------------------------------------------------------
2931 | Facility : libnform
2932 | Function : static int FE_Clear_To_End_Of_Line(FORM * form)
2933 |
2934 | Description : Clear to end of current line.
2935 |
2936 | Return Values : E_OK - success
2937 +--------------------------------------------------------------------------*/
2938 static int
FE_Clear_To_End_Of_Line(FORM * form)2939 FE_Clear_To_End_Of_Line(FORM *form)
2940 {
2941 T((T_CALLED("FE_Clear_To_End_Of_Line(%p)"), (void *)form));
2942 wmove(form->w, form->currow, form->curcol);
2943 wclrtoeol(form->w);
2944 returnCode(E_OK);
2945 }
2946
2947 /*---------------------------------------------------------------------------
2948 | Facility : libnform
2949 | Function : static int FE_Clear_To_End_Of_Field(FORM * form)
2950 |
2951 | Description : Clear to end of field.
2952 |
2953 | Return Values : E_OK - success
2954 +--------------------------------------------------------------------------*/
2955 static int
FE_Clear_To_End_Of_Field(FORM * form)2956 FE_Clear_To_End_Of_Field(FORM *form)
2957 {
2958 T((T_CALLED("FE_Clear_To_End_Of_Field(%p)"), (void *)form));
2959 wmove(form->w, form->currow, form->curcol);
2960 wclrtobot(form->w);
2961 returnCode(E_OK);
2962 }
2963
2964 /*---------------------------------------------------------------------------
2965 | Facility : libnform
2966 | Function : static int FE_Clear_Field(FORM * form)
2967 |
2968 | Description : Clear entire field.
2969 |
2970 | Return Values : E_OK - success
2971 +--------------------------------------------------------------------------*/
2972 static int
FE_Clear_Field(FORM * form)2973 FE_Clear_Field(FORM *form)
2974 {
2975 T((T_CALLED("FE_Clear_Field(%p)"), (void *)form));
2976 form->currow = form->curcol = 0;
2977 werase(form->w);
2978 returnCode(E_OK);
2979 }
2980 /*----------------------------------------------------------------------------
2981 END of Field Editing routines
2982 --------------------------------------------------------------------------*/
2983
2984 /*----------------------------------------------------------------------------
2985 Edit Mode routines
2986 --------------------------------------------------------------------------*/
2987
2988 /*---------------------------------------------------------------------------
2989 | Facility : libnform
2990 | Function : static int EM_Overlay_Mode(FORM * form)
2991 |
2992 | Description : Switch to overlay mode.
2993 |
2994 | Return Values : E_OK - success
2995 +--------------------------------------------------------------------------*/
2996 static int
EM_Overlay_Mode(FORM * form)2997 EM_Overlay_Mode(FORM *form)
2998 {
2999 T((T_CALLED("EM_Overlay_Mode(%p)"), (void *)form));
3000 SetStatus(form, _OVLMODE);
3001 returnCode(E_OK);
3002 }
3003
3004 /*---------------------------------------------------------------------------
3005 | Facility : libnform
3006 | Function : static int EM_Insert_Mode(FORM * form)
3007 |
3008 | Description : Switch to insert mode
3009 |
3010 | Return Values : E_OK - success
3011 +--------------------------------------------------------------------------*/
3012 static int
EM_Insert_Mode(FORM * form)3013 EM_Insert_Mode(FORM *form)
3014 {
3015 T((T_CALLED("EM_Insert_Mode(%p)"), (void *)form));
3016 ClrStatus(form, _OVLMODE);
3017 returnCode(E_OK);
3018 }
3019
3020 /*----------------------------------------------------------------------------
3021 END of Edit Mode routines
3022 --------------------------------------------------------------------------*/
3023
3024 /*----------------------------------------------------------------------------
3025 Helper routines for Choice Requests
3026 --------------------------------------------------------------------------*/
3027
3028 /*---------------------------------------------------------------------------
3029 | Facility : libnform
3030 | Function : static bool Next_Choice(FORM * form,
3031 | FIELDTYPE * typ,
3032 | FIELD * field,
3033 | TypeArgument *argp)
3034 |
3035 | Description : Get the next field choice. For linked types this is
3036 | done recursively.
3037 |
3038 | Return Values : TRUE - next choice successfully retrieved
3039 | FALSE - couldn't retrieve next choice
3040 +--------------------------------------------------------------------------*/
3041 static bool
Next_Choice(FORM * form,FIELDTYPE * typ,FIELD * field,TypeArgument * argp)3042 Next_Choice(FORM *form, FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
3043 {
3044 if (!typ || !(typ->status & _HAS_CHOICE))
3045 return FALSE;
3046
3047 if (typ->status & _LINKED_TYPE)
3048 {
3049 assert(argp);
3050 return (
3051 Next_Choice(form, typ->left, field, argp->left) ||
3052 Next_Choice(form, typ->right, field, argp->right));
3053 }
3054 else
3055 {
3056 #if NCURSES_INTEROP_FUNCS
3057 assert(typ->enum_next.onext);
3058 if (typ->status & _GENERIC)
3059 return typ->enum_next.gnext(form, field, (void *)argp);
3060 else
3061 return typ->enum_next.onext(field, (void *)argp);
3062 #else
3063 assert(typ->next);
3064 return typ->next(field, (void *)argp);
3065 #endif
3066 }
3067 }
3068
3069 /*---------------------------------------------------------------------------
3070 | Facility : libnform
3071 | Function : static bool Previous_Choice(FORM * form,
3072 | FIELDTYPE * typ,
3073 | FIELD * field,
3074 | TypeArgument *argp)
3075 |
3076 | Description : Get the previous field choice. For linked types this
3077 | is done recursively.
3078 |
3079 | Return Values : TRUE - previous choice successfully retrieved
3080 | FALSE - couldn't retrieve previous choice
3081 +--------------------------------------------------------------------------*/
3082 static bool
Previous_Choice(FORM * form,FIELDTYPE * typ,FIELD * field,TypeArgument * argp)3083 Previous_Choice(FORM *form, FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
3084 {
3085 if (!typ || !(typ->status & _HAS_CHOICE))
3086 return FALSE;
3087
3088 if (typ->status & _LINKED_TYPE)
3089 {
3090 assert(argp);
3091 return (
3092 Previous_Choice(form, typ->left, field, argp->left) ||
3093 Previous_Choice(form, typ->right, field, argp->right));
3094 }
3095 else
3096 {
3097 #if NCURSES_INTEROP_FUNCS
3098 assert(typ->enum_prev.oprev);
3099 if (typ->status & _GENERIC)
3100 return typ->enum_prev.gprev(form, field, (void *)argp);
3101 else
3102 return typ->enum_prev.oprev(field, (void *)argp);
3103 #else
3104 assert(typ->prev);
3105 return typ->prev(field, (void *)argp);
3106 #endif
3107 }
3108 }
3109 /*----------------------------------------------------------------------------
3110 End of Helper routines for Choice Requests
3111 --------------------------------------------------------------------------*/
3112
3113 /*----------------------------------------------------------------------------
3114 Routines for Choice Requests
3115 --------------------------------------------------------------------------*/
3116
3117 /*---------------------------------------------------------------------------
3118 | Facility : libnform
3119 | Function : static int CR_Next_Choice(FORM * form)
3120 |
3121 | Description : Get the next field choice.
3122 |
3123 | Return Values : E_OK - success
3124 | E_REQUEST_DENIED - next choice couldn't be retrieved
3125 +--------------------------------------------------------------------------*/
3126 static int
CR_Next_Choice(FORM * form)3127 CR_Next_Choice(FORM *form)
3128 {
3129 FIELD *field = form->current;
3130
3131 T((T_CALLED("CR_Next_Choice(%p)"), (void *)form));
3132 Synchronize_Buffer(form);
3133 returnCode((Next_Choice(form, field->type, field, (TypeArgument *)(field->arg)))
3134 ? E_OK
3135 : E_REQUEST_DENIED);
3136 }
3137
3138 /*---------------------------------------------------------------------------
3139 | Facility : libnform
3140 | Function : static int CR_Previous_Choice(FORM * form)
3141 |
3142 | Description : Get the previous field choice.
3143 |
3144 | Return Values : E_OK - success
3145 | E_REQUEST_DENIED - prev. choice couldn't be retrieved
3146 +--------------------------------------------------------------------------*/
3147 static int
CR_Previous_Choice(FORM * form)3148 CR_Previous_Choice(FORM *form)
3149 {
3150 FIELD *field = form->current;
3151
3152 T((T_CALLED("CR_Previous_Choice(%p)"), (void *)form));
3153 Synchronize_Buffer(form);
3154 returnCode((Previous_Choice(form, field->type, field, (TypeArgument *)(field->arg)))
3155 ? E_OK
3156 : E_REQUEST_DENIED);
3157 }
3158 /*----------------------------------------------------------------------------
3159 End of Routines for Choice Requests
3160 --------------------------------------------------------------------------*/
3161
3162 /*----------------------------------------------------------------------------
3163 Helper routines for Field Validations.
3164 --------------------------------------------------------------------------*/
3165
3166 /*---------------------------------------------------------------------------
3167 | Facility : libnform
3168 | Function : static bool Check_Field(FORM* form,
3169 | FIELDTYPE * typ,
3170 | FIELD * field,
3171 | TypeArgument * argp)
3172 |
3173 | Description : Check the field according to its fieldtype and its
3174 | actual arguments. For linked fieldtypes this is done
3175 | recursively.
3176 |
3177 | Return Values : TRUE - field is valid
3178 | FALSE - field is invalid.
3179 +--------------------------------------------------------------------------*/
3180 static bool
Check_Field(FORM * form,FIELDTYPE * typ,FIELD * field,TypeArgument * argp)3181 Check_Field(FORM *form, FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
3182 {
3183 if (typ)
3184 {
3185 if (Field_Has_Option(field, O_NULLOK))
3186 {
3187 FIELD_CELL *bp = field->buf;
3188
3189 assert(bp);
3190 while (ISBLANK(*bp))
3191 {
3192 bp++;
3193 }
3194 if (CharOf(*bp) == 0)
3195 return TRUE;
3196 }
3197
3198 if (typ->status & _LINKED_TYPE)
3199 {
3200 assert(argp);
3201 return (
3202 Check_Field(form, typ->left, field, argp->left) ||
3203 Check_Field(form, typ->right, field, argp->right));
3204 }
3205 else
3206 {
3207 #if NCURSES_INTEROP_FUNCS
3208 if (typ->fieldcheck.ofcheck)
3209 {
3210 if (typ->status & _GENERIC)
3211 return typ->fieldcheck.gfcheck(form, field, (void *)argp);
3212 else
3213 return typ->fieldcheck.ofcheck(field, (void *)argp);
3214 }
3215 #else
3216 if (typ->fcheck)
3217 return typ->fcheck(field, (void *)argp);
3218 #endif
3219 }
3220 }
3221 return TRUE;
3222 }
3223
3224 /*---------------------------------------------------------------------------
3225 | Facility : libnform
3226 | Function : bool _nc_Internal_Validation(FORM * form )
3227 |
3228 | Description : Validate the current field of the form.
3229 |
3230 | Return Values : TRUE - field is valid
3231 | FALSE - field is invalid
3232 +--------------------------------------------------------------------------*/
3233 FORM_EXPORT(bool)
_nc_Internal_Validation(FORM * form)3234 _nc_Internal_Validation(FORM *form)
3235 {
3236 FIELD *field;
3237
3238 field = form->current;
3239
3240 Synchronize_Buffer(form);
3241 if ((form->status & _FCHECK_REQUIRED) ||
3242 (!(Field_Has_Option(field, O_PASSOK))))
3243 {
3244 if (!Check_Field(form, field->type, field, (TypeArgument *)(field->arg)))
3245 return FALSE;
3246 ClrStatus(form, _FCHECK_REQUIRED);
3247 SetStatus(field, _CHANGED);
3248 Synchronize_Linked_Fields(field);
3249 }
3250 return TRUE;
3251 }
3252 /*----------------------------------------------------------------------------
3253 End of Helper routines for Field Validations.
3254 --------------------------------------------------------------------------*/
3255
3256 /*----------------------------------------------------------------------------
3257 Routines for Field Validation.
3258 --------------------------------------------------------------------------*/
3259
3260 /*---------------------------------------------------------------------------
3261 | Facility : libnform
3262 | Function : static int FV_Validation(FORM * form)
3263 |
3264 | Description : Validate the current field of the form.
3265 |
3266 | Return Values : E_OK - field valid
3267 | E_INVALID_FIELD - field not valid
3268 +--------------------------------------------------------------------------*/
3269 static int
FV_Validation(FORM * form)3270 FV_Validation(FORM *form)
3271 {
3272 T((T_CALLED("FV_Validation(%p)"), (void *)form));
3273 if (_nc_Internal_Validation(form))
3274 returnCode(E_OK);
3275 else
3276 returnCode(E_INVALID_FIELD);
3277 }
3278 /*----------------------------------------------------------------------------
3279 End of routines for Field Validation.
3280 --------------------------------------------------------------------------*/
3281
3282 /*----------------------------------------------------------------------------
3283 Helper routines for Inter-Field Navigation
3284 --------------------------------------------------------------------------*/
3285
3286 /*---------------------------------------------------------------------------
3287 | Facility : libnform
3288 | Function : static FIELD *Next_Field_On_Page(FIELD * field)
3289 |
3290 | Description : Get the next field after the given field on the current
3291 | page. The order of fields is the one defined by the
3292 | field's array. Only visible and active fields are
3293 | counted.
3294 |
3295 | Return Values : Pointer to the next field.
3296 +--------------------------------------------------------------------------*/
3297 NCURSES_INLINE static FIELD *
Next_Field_On_Page(FIELD * field)3298 Next_Field_On_Page(FIELD *field)
3299 {
3300 FORM *form = field->form;
3301 FIELD **field_on_page = &form->field[field->index];
3302 FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3303 FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3304
3305 do
3306 {
3307 field_on_page =
3308 (field_on_page == last_on_page) ? first_on_page : field_on_page + 1;
3309 if (Field_Is_Selectable(*field_on_page))
3310 break;
3311 }
3312 while (field != (*field_on_page));
3313 return (*field_on_page);
3314 }
3315
3316 /*---------------------------------------------------------------------------
3317 | Facility : libnform
3318 | Function : FIELD* _nc_First_Active_Field(FORM * form)
3319 |
3320 | Description : Get the first active field on the current page,
3321 | if there are such. If there are none, get the first
3322 | visible field on the page. If there are also none,
3323 | we return the first field on page and hope the best.
3324 |
3325 | Return Values : Pointer to calculated field.
3326 +--------------------------------------------------------------------------*/
3327 FORM_EXPORT(FIELD *)
_nc_First_Active_Field(FORM * form)3328 _nc_First_Active_Field(FORM *form)
3329 {
3330 FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3331 FIELD *proposed = Next_Field_On_Page(*last_on_page);
3332
3333 if (proposed == *last_on_page)
3334 {
3335 /* there might be the special situation, where there is no
3336 active and visible field on the current page. We then select
3337 the first visible field on this readonly page
3338 */
3339 if (Field_Is_Not_Selectable(proposed))
3340 {
3341 FIELD **field = &form->field[proposed->index];
3342 FIELD **first = &form->field[form->page[form->curpage].pmin];
3343
3344 do
3345 {
3346 field = (field == last_on_page) ? first : field + 1;
3347 if (Field_Has_Option(*field, O_VISIBLE))
3348 break;
3349 }
3350 while (proposed != (*field));
3351
3352 proposed = *field;
3353
3354 if ((proposed == *last_on_page) &&
3355 !((unsigned)proposed->opts & O_VISIBLE))
3356 {
3357 /* This means, there is also no visible field on the page.
3358 So we propose the first one and hope the very best...
3359 Some very clever user has designed a readonly and invisible
3360 page on this form.
3361 */
3362 proposed = *first;
3363 }
3364 }
3365 }
3366 return (proposed);
3367 }
3368
3369 /*---------------------------------------------------------------------------
3370 | Facility : libnform
3371 | Function : static FIELD *Previous_Field_On_Page(FIELD * field)
3372 |
3373 | Description : Get the previous field before the given field on the
3374 | current page. The order of fields is the one defined by
3375 | the field's array. Only visible and active fields are
3376 | counted.
3377 |
3378 | Return Values : Pointer to the previous field.
3379 +--------------------------------------------------------------------------*/
3380 NCURSES_INLINE static FIELD *
Previous_Field_On_Page(FIELD * field)3381 Previous_Field_On_Page(FIELD *field)
3382 {
3383 FORM *form = field->form;
3384 FIELD **field_on_page = &form->field[field->index];
3385 FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3386 FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3387
3388 do
3389 {
3390 field_on_page =
3391 (field_on_page == first_on_page) ? last_on_page : field_on_page - 1;
3392 if (Field_Is_Selectable(*field_on_page))
3393 break;
3394 }
3395 while (field != (*field_on_page));
3396
3397 return (*field_on_page);
3398 }
3399
3400 /*---------------------------------------------------------------------------
3401 | Facility : libnform
3402 | Function : static FIELD *Sorted_Next_Field(FIELD * field)
3403 |
3404 | Description : Get the next field after the given field on the current
3405 | page. The order of fields is the one defined by the
3406 | (row,column) geometry, rows are major.
3407 |
3408 | Return Values : Pointer to the next field.
3409 +--------------------------------------------------------------------------*/
3410 NCURSES_INLINE static FIELD *
Sorted_Next_Field(FIELD * field)3411 Sorted_Next_Field(FIELD *field)
3412 {
3413 FIELD *field_on_page = field;
3414
3415 do
3416 {
3417 field_on_page = field_on_page->snext;
3418 if (Field_Is_Selectable(field_on_page))
3419 break;
3420 }
3421 while (field_on_page != field);
3422
3423 return (field_on_page);
3424 }
3425
3426 /*---------------------------------------------------------------------------
3427 | Facility : libnform
3428 | Function : static FIELD *Sorted_Previous_Field(FIELD * field)
3429 |
3430 | Description : Get the previous field before the given field on the
3431 | current page. The order of fields is the one defined
3432 | by the (row,column) geometry, rows are major.
3433 |
3434 | Return Values : Pointer to the previous field.
3435 +--------------------------------------------------------------------------*/
3436 NCURSES_INLINE static FIELD *
Sorted_Previous_Field(FIELD * field)3437 Sorted_Previous_Field(FIELD *field)
3438 {
3439 FIELD *field_on_page = field;
3440
3441 do
3442 {
3443 field_on_page = field_on_page->sprev;
3444 if (Field_Is_Selectable(field_on_page))
3445 break;
3446 }
3447 while (field_on_page != field);
3448
3449 return (field_on_page);
3450 }
3451
3452 /*---------------------------------------------------------------------------
3453 | Facility : libnform
3454 | Function : static FIELD *Left_Neighbor_Field(FIELD * field)
3455 |
3456 | Description : Get the left neighbor of the field on the same line
3457 | and the same page. Cycles through the line.
3458 |
3459 | Return Values : Pointer to left neighbor field.
3460 +--------------------------------------------------------------------------*/
3461 NCURSES_INLINE static FIELD *
Left_Neighbor_Field(FIELD * field)3462 Left_Neighbor_Field(FIELD *field)
3463 {
3464 FIELD *field_on_page = field;
3465
3466 /* For a field that has really a left neighbor, the while clause
3467 immediately fails and the loop is left, positioned at the right
3468 neighbor. Otherwise we cycle backwards through the sorted field list
3469 until we enter the same line (from the right end).
3470 */
3471 do
3472 {
3473 field_on_page = Sorted_Previous_Field(field_on_page);
3474 }
3475 while (field_on_page->frow != field->frow);
3476
3477 return (field_on_page);
3478 }
3479
3480 /*---------------------------------------------------------------------------
3481 | Facility : libnform
3482 | Function : static FIELD *Right_Neighbor_Field(FIELD * field)
3483 |
3484 | Description : Get the right neighbor of the field on the same line
3485 | and the same page.
3486 |
3487 | Return Values : Pointer to right neighbor field.
3488 +--------------------------------------------------------------------------*/
3489 NCURSES_INLINE static FIELD *
Right_Neighbor_Field(FIELD * field)3490 Right_Neighbor_Field(FIELD *field)
3491 {
3492 FIELD *field_on_page = field;
3493
3494 /* See the comments on Left_Neighbor_Field to understand how it works */
3495 do
3496 {
3497 field_on_page = Sorted_Next_Field(field_on_page);
3498 }
3499 while (field_on_page->frow != field->frow);
3500
3501 return (field_on_page);
3502 }
3503
3504 /*---------------------------------------------------------------------------
3505 | Facility : libnform
3506 | Function : static FIELD *Upper_Neighbor_Field(FIELD * field)
3507 |
3508 | Description : Because of the row-major nature of sorting the fields,
3509 | it is more difficult to define what the upper neighbor
3510 | field really means. We define that it must be on a
3511 | 'previous' line (cyclic order!) and is the rightmost
3512 | field lying on the left side of the given field. If
3513 | this set is empty, we take the first field on the line.
3514 |
3515 | Return Values : Pointer to the upper neighbor field.
3516 +--------------------------------------------------------------------------*/
3517 static FIELD *
Upper_Neighbor_Field(FIELD * field)3518 Upper_Neighbor_Field(FIELD *field)
3519 {
3520 FIELD *field_on_page = field;
3521 int frow = field->frow;
3522 int fcol = field->fcol;
3523
3524 /* Walk back to the 'previous' line. The second term in the while clause
3525 just guarantees that we stop if we cycled through the line because
3526 there might be no 'previous' line if the page has just one line.
3527 */
3528 do
3529 {
3530 field_on_page = Sorted_Previous_Field(field_on_page);
3531 }
3532 while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3533
3534 if (field_on_page->frow != frow)
3535 {
3536 /* We really found a 'previous' line. We are positioned at the
3537 rightmost field on this line */
3538 frow = field_on_page->frow;
3539
3540 /* We walk to the left as long as we are really right of the
3541 field. */
3542 while (field_on_page->frow == frow && field_on_page->fcol > fcol)
3543 field_on_page = Sorted_Previous_Field(field_on_page);
3544
3545 /* If we wrapped, just go to the right which is the first field on
3546 the row */
3547 if (field_on_page->frow != frow)
3548 field_on_page = Sorted_Next_Field(field_on_page);
3549 }
3550
3551 return (field_on_page);
3552 }
3553
3554 /*---------------------------------------------------------------------------
3555 | Facility : libnform
3556 | Function : static FIELD *Down_Neighbor_Field(FIELD * field)
3557 |
3558 | Description : Because of the row-major nature of sorting the fields,
3559 | it is more difficult to define what the down neighbor
3560 | field really means. We define that it must be on a
3561 | 'next' line (cyclic order!) and is the leftmost
3562 | field laying on the right side of the given field. If
3563 | this set is empty, we take the last field on the line.
3564 |
3565 | Return Values : Pointer to the upper neighbor field.
3566 +--------------------------------------------------------------------------*/
3567 static FIELD *
Down_Neighbor_Field(FIELD * field)3568 Down_Neighbor_Field(FIELD *field)
3569 {
3570 FIELD *field_on_page = field;
3571 int frow = field->frow;
3572 int fcol = field->fcol;
3573
3574 /* Walk forward to the 'next' line. The second term in the while clause
3575 just guarantees that we stop if we cycled through the line because
3576 there might be no 'next' line if the page has just one line.
3577 */
3578 do
3579 {
3580 field_on_page = Sorted_Next_Field(field_on_page);
3581 }
3582 while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3583
3584 if (field_on_page->frow != frow)
3585 {
3586 /* We really found a 'next' line. We are positioned at the rightmost
3587 field on this line */
3588 frow = field_on_page->frow;
3589
3590 /* We walk to the right as long as we are really left of the
3591 field. */
3592 while (field_on_page->frow == frow && field_on_page->fcol < fcol)
3593 field_on_page = Sorted_Next_Field(field_on_page);
3594
3595 /* If we wrapped, just go to the left which is the last field on
3596 the row */
3597 if (field_on_page->frow != frow)
3598 field_on_page = Sorted_Previous_Field(field_on_page);
3599 }
3600
3601 return (field_on_page);
3602 }
3603
3604 /*----------------------------------------------------------------------------
3605 Inter-Field Navigation routines
3606 --------------------------------------------------------------------------*/
3607
3608 /*---------------------------------------------------------------------------
3609 | Facility : libnform
3610 | Function : static int Inter_Field_Navigation(
3611 | int (* const fct) (FORM *),
3612 | FORM * form)
3613 |
3614 | Description : Generic behavior for changing the current field, the
3615 | field is left and a new field is entered. So the field
3616 | must be validated and the field init/term hooks must
3617 | be called.
3618 |
3619 | Return Values : E_OK - success
3620 | E_INVALID_FIELD - field is invalid
3621 | some other - error from subordinate call
3622 +--------------------------------------------------------------------------*/
3623 static int
Inter_Field_Navigation(int (* const fct)(FORM *),FORM * form)3624 Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form)
3625 {
3626 int res;
3627
3628 if (!_nc_Internal_Validation(form))
3629 res = E_INVALID_FIELD;
3630 else
3631 {
3632 Call_Hook(form, fieldterm);
3633 res = fct(form);
3634 Call_Hook(form, fieldinit);
3635 }
3636 return res;
3637 }
3638
3639 /*---------------------------------------------------------------------------
3640 | Facility : libnform
3641 | Function : static int FN_Next_Field(FORM * form)
3642 |
3643 | Description : Move to the next field on the current page of the form
3644 |
3645 | Return Values : E_OK - success
3646 | != E_OK - error from subordinate call
3647 +--------------------------------------------------------------------------*/
3648 static int
FN_Next_Field(FORM * form)3649 FN_Next_Field(FORM *form)
3650 {
3651 T((T_CALLED("FN_Next_Field(%p)"), (void *)form));
3652 returnCode(_nc_Set_Current_Field(form,
3653 Next_Field_On_Page(form->current)));
3654 }
3655
3656 /*---------------------------------------------------------------------------
3657 | Facility : libnform
3658 | Function : static int FN_Previous_Field(FORM * form)
3659 |
3660 | Description : Move to the previous field on the current page of the
3661 | form
3662 |
3663 | Return Values : E_OK - success
3664 | != E_OK - error from subordinate call
3665 +--------------------------------------------------------------------------*/
3666 static int
FN_Previous_Field(FORM * form)3667 FN_Previous_Field(FORM *form)
3668 {
3669 T((T_CALLED("FN_Previous_Field(%p)"), (void *)form));
3670 returnCode(_nc_Set_Current_Field(form,
3671 Previous_Field_On_Page(form->current)));
3672 }
3673
3674 /*---------------------------------------------------------------------------
3675 | Facility : libnform
3676 | Function : static int FN_First_Field(FORM * form)
3677 |
3678 | Description : Move to the first field on the current page of the form
3679 |
3680 | Return Values : E_OK - success
3681 | != E_OK - error from subordinate call
3682 +--------------------------------------------------------------------------*/
3683 static int
FN_First_Field(FORM * form)3684 FN_First_Field(FORM *form)
3685 {
3686 T((T_CALLED("FN_First_Field(%p)"), (void *)form));
3687 returnCode(_nc_Set_Current_Field(form,
3688 Next_Field_On_Page(form->field[form->page[form->curpage].pmax])));
3689 }
3690
3691 /*---------------------------------------------------------------------------
3692 | Facility : libnform
3693 | Function : static int FN_Last_Field(FORM * form)
3694 |
3695 | Description : Move to the last field on the current page of the form
3696 |
3697 | Return Values : E_OK - success
3698 | != E_OK - error from subordinate call
3699 +--------------------------------------------------------------------------*/
3700 static int
FN_Last_Field(FORM * form)3701 FN_Last_Field(FORM *form)
3702 {
3703 T((T_CALLED("FN_Last_Field(%p)"), (void *)form));
3704 returnCode(
3705 _nc_Set_Current_Field(form,
3706 Previous_Field_On_Page(form->field[form->page[form->curpage].pmin])));
3707 }
3708
3709 /*---------------------------------------------------------------------------
3710 | Facility : libnform
3711 | Function : static int FN_Sorted_Next_Field(FORM * form)
3712 |
3713 | Description : Move to the sorted next field on the current page
3714 | of the form.
3715 |
3716 | Return Values : E_OK - success
3717 | != E_OK - error from subordinate call
3718 +--------------------------------------------------------------------------*/
3719 static int
FN_Sorted_Next_Field(FORM * form)3720 FN_Sorted_Next_Field(FORM *form)
3721 {
3722 T((T_CALLED("FN_Sorted_Next_Field(%p)"), (void *)form));
3723 returnCode(_nc_Set_Current_Field(form,
3724 Sorted_Next_Field(form->current)));
3725 }
3726
3727 /*---------------------------------------------------------------------------
3728 | Facility : libnform
3729 | Function : static int FN_Sorted_Previous_Field(FORM * form)
3730 |
3731 | Description : Move to the sorted previous field on the current page
3732 | of the form.
3733 |
3734 | Return Values : E_OK - success
3735 | != E_OK - error from subordinate call
3736 +--------------------------------------------------------------------------*/
3737 static int
FN_Sorted_Previous_Field(FORM * form)3738 FN_Sorted_Previous_Field(FORM *form)
3739 {
3740 T((T_CALLED("FN_Sorted_Previous_Field(%p)"), (void *)form));
3741 returnCode(_nc_Set_Current_Field(form,
3742 Sorted_Previous_Field(form->current)));
3743 }
3744
3745 /*---------------------------------------------------------------------------
3746 | Facility : libnform
3747 | Function : static int FN_Sorted_First_Field(FORM * form)
3748 |
3749 | Description : Move to the sorted first field on the current page
3750 | of the form.
3751 |
3752 | Return Values : E_OK - success
3753 | != E_OK - error from subordinate call
3754 +--------------------------------------------------------------------------*/
3755 static int
FN_Sorted_First_Field(FORM * form)3756 FN_Sorted_First_Field(FORM *form)
3757 {
3758 T((T_CALLED("FN_Sorted_First_Field(%p)"), (void *)form));
3759 returnCode(_nc_Set_Current_Field(form,
3760 Sorted_Next_Field(form->field[form->page[form->curpage].smax])));
3761 }
3762
3763 /*---------------------------------------------------------------------------
3764 | Facility : libnform
3765 | Function : static int FN_Sorted_Last_Field(FORM * form)
3766 |
3767 | Description : Move to the sorted last field on the current page
3768 | of the form.
3769 |
3770 | Return Values : E_OK - success
3771 | != E_OK - error from subordinate call
3772 +--------------------------------------------------------------------------*/
3773 static int
FN_Sorted_Last_Field(FORM * form)3774 FN_Sorted_Last_Field(FORM *form)
3775 {
3776 T((T_CALLED("FN_Sorted_Last_Field(%p)"), (void *)form));
3777 returnCode(_nc_Set_Current_Field(form,
3778 Sorted_Previous_Field(form->field[form->page[form->curpage].smin])));
3779 }
3780
3781 /*---------------------------------------------------------------------------
3782 | Facility : libnform
3783 | Function : static int FN_Left_Field(FORM * form)
3784 |
3785 | Description : Get the field on the left of the current field on the
3786 | same line and the same page. Cycles through the line.
3787 |
3788 | Return Values : E_OK - success
3789 | != E_OK - error from subordinate call
3790 +--------------------------------------------------------------------------*/
3791 static int
FN_Left_Field(FORM * form)3792 FN_Left_Field(FORM *form)
3793 {
3794 T((T_CALLED("FN_Left_Field(%p)"), (void *)form));
3795 returnCode(_nc_Set_Current_Field(form,
3796 Left_Neighbor_Field(form->current)));
3797 }
3798
3799 /*---------------------------------------------------------------------------
3800 | Facility : libnform
3801 | Function : static int FN_Right_Field(FORM * form)
3802 |
3803 | Description : Get the field on the right of the current field on the
3804 | same line and the same page. Cycles through the line.
3805 |
3806 | Return Values : E_OK - success
3807 | != E_OK - error from subordinate call
3808 +--------------------------------------------------------------------------*/
3809 static int
FN_Right_Field(FORM * form)3810 FN_Right_Field(FORM *form)
3811 {
3812 T((T_CALLED("FN_Right_Field(%p)"), (void *)form));
3813 returnCode(_nc_Set_Current_Field(form,
3814 Right_Neighbor_Field(form->current)));
3815 }
3816
3817 /*---------------------------------------------------------------------------
3818 | Facility : libnform
3819 | Function : static int FN_Up_Field(FORM * form)
3820 |
3821 | Description : Get the upper neighbor of the current field. This
3822 | cycles through the page. See the comments of the
3823 | Upper_Neighbor_Field function to understand how
3824 | 'upper' is defined.
3825 |
3826 | Return Values : E_OK - success
3827 | != E_OK - error from subordinate call
3828 +--------------------------------------------------------------------------*/
3829 static int
FN_Up_Field(FORM * form)3830 FN_Up_Field(FORM *form)
3831 {
3832 T((T_CALLED("FN_Up_Field(%p)"), (void *)form));
3833 returnCode(_nc_Set_Current_Field(form,
3834 Upper_Neighbor_Field(form->current)));
3835 }
3836
3837 /*---------------------------------------------------------------------------
3838 | Facility : libnform
3839 | Function : static int FN_Down_Field(FORM * form)
3840 |
3841 | Description : Get the down neighbor of the current field. This
3842 | cycles through the page. See the comments of the
3843 | Down_Neighbor_Field function to understand how
3844 | 'down' is defined.
3845 |
3846 | Return Values : E_OK - success
3847 | != E_OK - error from subordinate call
3848 +--------------------------------------------------------------------------*/
3849 static int
FN_Down_Field(FORM * form)3850 FN_Down_Field(FORM *form)
3851 {
3852 T((T_CALLED("FN_Down_Field(%p)"), (void *)form));
3853 returnCode(_nc_Set_Current_Field(form,
3854 Down_Neighbor_Field(form->current)));
3855 }
3856 /*----------------------------------------------------------------------------
3857 END of Field Navigation routines
3858 --------------------------------------------------------------------------*/
3859
3860 /*----------------------------------------------------------------------------
3861 Helper routines for Page Navigation
3862 --------------------------------------------------------------------------*/
3863
3864 /*---------------------------------------------------------------------------
3865 | Facility : libnform
3866 | Function : int _nc_Set_Form_Page(FORM * form,
3867 | int page,
3868 | FIELD * field)
3869 |
3870 | Description : Make the given page number the current page and make
3871 | the given field the current field on the page. If
3872 | for the field NULL is given, make the first field on
3873 | the page the current field. The routine acts only
3874 | if the requested page is not the current page.
3875 |
3876 | Return Values : E_OK - success
3877 | != E_OK - error from subordinate call
3878 | E_BAD_ARGUMENT - invalid field pointer
3879 | E_SYSTEM_ERROR - some severe basic error
3880 +--------------------------------------------------------------------------*/
3881 FORM_EXPORT(int)
_nc_Set_Form_Page(FORM * form,int page,FIELD * field)3882 _nc_Set_Form_Page(FORM *form, int page, FIELD *field)
3883 {
3884 int res = E_OK;
3885
3886 if ((form->curpage != page))
3887 {
3888 FIELD *last_field, *field_on_page;
3889
3890 werase(Get_Form_Window(form));
3891 form->curpage = (short)page;
3892 last_field = field_on_page = form->field[form->page[page].smin];
3893 do
3894 {
3895 if ((unsigned)field_on_page->opts & O_VISIBLE)
3896 if ((res = Display_Field(field_on_page)) != E_OK)
3897 return (res);
3898 field_on_page = field_on_page->snext;
3899 }
3900 while (field_on_page != last_field);
3901
3902 if (field)
3903 res = _nc_Set_Current_Field(form, field);
3904 else
3905 /* N.B.: we don't encapsulate this by Inter_Field_Navigation(),
3906 because this is already executed in a page navigation
3907 context that contains field navigation
3908 */
3909 res = FN_First_Field(form);
3910 }
3911 return (res);
3912 }
3913
3914 /*---------------------------------------------------------------------------
3915 | Facility : libnform
3916 | Function : static int Next_Page_Number(const FORM * form)
3917 |
3918 | Description : Calculate the page number following the current page
3919 | number. This cycles if the highest page number is
3920 | reached.
3921 |
3922 | Return Values : The next page number
3923 +--------------------------------------------------------------------------*/
3924 NCURSES_INLINE static int
Next_Page_Number(const FORM * form)3925 Next_Page_Number(const FORM *form)
3926 {
3927 return (form->curpage + 1) % form->maxpage;
3928 }
3929
3930 /*---------------------------------------------------------------------------
3931 | Facility : libnform
3932 | Function : static int Previous_Page_Number(const FORM * form)
3933 |
3934 | Description : Calculate the page number before the current page
3935 | number. This cycles if the first page number is
3936 | reached.
3937 |
3938 | Return Values : The previous page number
3939 +--------------------------------------------------------------------------*/
3940 NCURSES_INLINE static int
Previous_Page_Number(const FORM * form)3941 Previous_Page_Number(const FORM *form)
3942 {
3943 return (form->curpage != 0 ? form->curpage - 1 : form->maxpage - 1);
3944 }
3945
3946 /*----------------------------------------------------------------------------
3947 Page Navigation routines
3948 --------------------------------------------------------------------------*/
3949
3950 /*---------------------------------------------------------------------------
3951 | Facility : libnform
3952 | Function : static int Page_Navigation(
3953 | int (* const fct) (FORM *),
3954 | FORM * form)
3955 |
3956 | Description : Generic behavior for changing a page. This means
3957 | that the field is left and a new field is entered.
3958 | So the field must be validated and the field init/term
3959 | hooks must be called. Because also the page is changed,
3960 | the form's init/term hooks must be called also.
3961 |
3962 | Return Values : E_OK - success
3963 | E_INVALID_FIELD - field is invalid
3964 | some other - error from subordinate call
3965 +--------------------------------------------------------------------------*/
3966 static int
Page_Navigation(int (* const fct)(FORM *),FORM * form)3967 Page_Navigation(int (*const fct) (FORM *), FORM *form)
3968 {
3969 int res;
3970
3971 if (!_nc_Internal_Validation(form))
3972 res = E_INVALID_FIELD;
3973 else
3974 {
3975 Call_Hook(form, fieldterm);
3976 Call_Hook(form, formterm);
3977 res = fct(form);
3978 Call_Hook(form, forminit);
3979 Call_Hook(form, fieldinit);
3980 }
3981 return res;
3982 }
3983
3984 /*---------------------------------------------------------------------------
3985 | Facility : libnform
3986 | Function : static int PN_Next_Page(FORM * form)
3987 |
3988 | Description : Move to the next page of the form
3989 |
3990 | Return Values : E_OK - success
3991 | != E_OK - error from subordinate call
3992 +--------------------------------------------------------------------------*/
3993 static int
PN_Next_Page(FORM * form)3994 PN_Next_Page(FORM *form)
3995 {
3996 T((T_CALLED("PN_Next_Page(%p)"), (void *)form));
3997 returnCode(_nc_Set_Form_Page(form, Next_Page_Number(form), (FIELD *)0));
3998 }
3999
4000 /*---------------------------------------------------------------------------
4001 | Facility : libnform
4002 | Function : static int PN_Previous_Page(FORM * form)
4003 |
4004 | Description : Move to the previous page of the form
4005 |
4006 | Return Values : E_OK - success
4007 | != E_OK - error from subordinate call
4008 +--------------------------------------------------------------------------*/
4009 static int
PN_Previous_Page(FORM * form)4010 PN_Previous_Page(FORM *form)
4011 {
4012 T((T_CALLED("PN_Previous_Page(%p)"), (void *)form));
4013 returnCode(_nc_Set_Form_Page(form, Previous_Page_Number(form), (FIELD *)0));
4014 }
4015
4016 /*---------------------------------------------------------------------------
4017 | Facility : libnform
4018 | Function : static int PN_First_Page(FORM * form)
4019 |
4020 | Description : Move to the first page of the form
4021 |
4022 | Return Values : E_OK - success
4023 | != E_OK - error from subordinate call
4024 +--------------------------------------------------------------------------*/
4025 static int
PN_First_Page(FORM * form)4026 PN_First_Page(FORM *form)
4027 {
4028 T((T_CALLED("PN_First_Page(%p)"), (void *)form));
4029 returnCode(_nc_Set_Form_Page(form, 0, (FIELD *)0));
4030 }
4031
4032 /*---------------------------------------------------------------------------
4033 | Facility : libnform
4034 | Function : static int PN_Last_Page(FORM * form)
4035 |
4036 | Description : Move to the last page of the form
4037 |
4038 | Return Values : E_OK - success
4039 | != E_OK - error from subordinate call
4040 +--------------------------------------------------------------------------*/
4041 static int
PN_Last_Page(FORM * form)4042 PN_Last_Page(FORM *form)
4043 {
4044 T((T_CALLED("PN_Last_Page(%p)"), (void *)form));
4045 returnCode(_nc_Set_Form_Page(form, form->maxpage - 1, (FIELD *)0));
4046 }
4047
4048 /*----------------------------------------------------------------------------
4049 END of Field Navigation routines
4050 --------------------------------------------------------------------------*/
4051
4052 /*----------------------------------------------------------------------------
4053 Helper routines for the core form driver.
4054 --------------------------------------------------------------------------*/
4055
4056 # if USE_WIDEC_SUPPORT
4057 /*---------------------------------------------------------------------------
4058 | Facility : libnform
4059 | Function : static int Data_Entry_w(FORM * form, wchar_t c)
4060 |
4061 | Description : Enter the wide character c into at the current
4062 | position of the current field of the form.
4063 |
4064 | Return Values : E_OK - success
4065 | E_REQUEST_DENIED - driver could not process the request
4066 | E_SYSTEM_ERROR -
4067 +--------------------------------------------------------------------------*/
4068 static int
Data_Entry_w(FORM * form,wchar_t c)4069 Data_Entry_w(FORM *form, wchar_t c)
4070 {
4071 FIELD *field = form->current;
4072 int result = E_REQUEST_DENIED;
4073
4074 T((T_CALLED("Data_Entry(%p,%s)"), (void *)form, _tracechtype((chtype)c)));
4075 if ((Field_Has_Option(field, O_EDIT))
4076 #if FIX_FORM_INACTIVE_BUG
4077 && (Field_Has_Option(field, O_ACTIVE))
4078 #endif
4079 )
4080 {
4081 wchar_t given[2];
4082 cchar_t temp_ch;
4083
4084 given[0] = c;
4085 given[1] = 0;
4086 setcchar(&temp_ch, given, 0, 0, (void *)0);
4087 if ((Field_Has_Option(field, O_BLANK)) &&
4088 First_Position_In_Current_Field(form) &&
4089 !(form->status & _FCHECK_REQUIRED) &&
4090 !(form->status & _WINDOW_MODIFIED))
4091 werase(form->w);
4092
4093 if (form->status & _OVLMODE)
4094 {
4095 wadd_wch(form->w, &temp_ch);
4096 }
4097 else
4098 /* no _OVLMODE */
4099 {
4100 bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
4101
4102 if (!(There_Is_Room ||
4103 ((Single_Line_Field(field) && Growable(field)))))
4104 RETURN(E_REQUEST_DENIED);
4105
4106 if (!There_Is_Room && !Field_Grown(field, 1))
4107 RETURN(E_SYSTEM_ERROR);
4108
4109 wins_wch(form->w, &temp_ch);
4110 }
4111
4112 if ((result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form)) == E_OK)
4113 {
4114 bool End_Of_Field = (((field->drows - 1) == form->currow) &&
4115 ((field->dcols - 1) == form->curcol));
4116
4117 form->status |= _WINDOW_MODIFIED;
4118 if (End_Of_Field && !Growable(field) && (Field_Has_Option(field, O_AUTOSKIP)))
4119 result = Inter_Field_Navigation(FN_Next_Field, form);
4120 else
4121 {
4122 if (End_Of_Field && Growable(field) && !Field_Grown(field, 1))
4123 result = E_SYSTEM_ERROR;
4124 else
4125 {
4126 /*
4127 * We have just added a byte to the form field. It may have
4128 * been part of a multibyte character. If it was, the
4129 * addch_used field is nonzero and we should not try to move
4130 * to a new column.
4131 */
4132 if (WINDOW_EXT(form->w, addch_used) == 0)
4133 IFN_Next_Character(form);
4134
4135 result = E_OK;
4136 }
4137 }
4138 }
4139 }
4140 RETURN(result);
4141 }
4142 # endif
4143
4144 /*---------------------------------------------------------------------------
4145 | Facility : libnform
4146 | Function : static int Data_Entry(FORM * form,int c)
4147 |
4148 | Description : Enter character c into at the current position of the
4149 | current field of the form.
4150 |
4151 | Return Values : E_OK - success
4152 | E_REQUEST_DENIED - driver could not process the request
4153 | E_SYSTEM_ERROR -
4154 +--------------------------------------------------------------------------*/
4155 static int
Data_Entry(FORM * form,int c)4156 Data_Entry(FORM *form, int c)
4157 {
4158 FIELD *field = form->current;
4159 int result = E_REQUEST_DENIED;
4160
4161 T((T_CALLED("Data_Entry(%p,%s)"), (void *)form, _tracechtype((chtype)c)));
4162 if ((Field_Has_Option(field, O_EDIT))
4163 #if FIX_FORM_INACTIVE_BUG
4164 && (Field_Has_Option(field, O_ACTIVE))
4165 #endif
4166 )
4167 {
4168 if ((Field_Has_Option(field, O_BLANK)) &&
4169 First_Position_In_Current_Field(form) &&
4170 !(form->status & _FCHECK_REQUIRED) &&
4171 !(form->status & _WINDOW_MODIFIED))
4172 werase(form->w);
4173
4174 if (form->status & _OVLMODE)
4175 {
4176 waddch(form->w, (chtype)c);
4177 }
4178 else
4179 /* no _OVLMODE */
4180 {
4181 bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
4182
4183 if (!(There_Is_Room ||
4184 ((Single_Line_Field(field) && Growable(field)))))
4185 RETURN(E_REQUEST_DENIED);
4186
4187 if (!There_Is_Room && !Field_Grown(field, 1))
4188 RETURN(E_SYSTEM_ERROR);
4189
4190 winsch(form->w, (chtype)c);
4191 }
4192
4193 if ((result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form)) == E_OK)
4194 {
4195 bool End_Of_Field = (((field->drows - 1) == form->currow) &&
4196 ((field->dcols - 1) == form->curcol));
4197
4198 if (Field_Has_Option(field, O_EDGE_INSERT_STAY))
4199 move_after_insert = !!(form->curcol
4200 - form->begincol
4201 - field->cols
4202 + 1);
4203
4204 SetStatus(form, _WINDOW_MODIFIED);
4205 if (End_Of_Field && !Growable(field) && (Field_Has_Option(field, O_AUTOSKIP)))
4206 result = Inter_Field_Navigation(FN_Next_Field, form);
4207 else
4208 {
4209 if (End_Of_Field && Growable(field) && !Field_Grown(field, 1))
4210 result = E_SYSTEM_ERROR;
4211 else
4212 {
4213 #if USE_WIDEC_SUPPORT
4214 /*
4215 * We have just added a byte to the form field. It may have
4216 * been part of a multibyte character. If it was, the
4217 * addch_used field is nonzero and we should not try to move
4218 * to a new column.
4219 */
4220 if (WINDOW_EXT(form->w, addch_used) == 0)
4221 IFN_Next_Character(form);
4222 #else
4223 IFN_Next_Character(form);
4224 #endif
4225 result = E_OK;
4226 }
4227 }
4228 }
4229 }
4230 RETURN(result);
4231 }
4232
4233 /* Structure to describe the binding of a request code to a function.
4234 The member keycode codes the request value as well as the generic
4235 routine to use for the request. The code for the generic routine
4236 is coded in the upper 16 Bits while the request code is coded in
4237 the lower 16 bits.
4238
4239 In terms of C++ you might think of a request as a class with a
4240 virtual method "perform". The different types of request are
4241 derived from this base class and overload (or not) the base class
4242 implementation of perform.
4243 */
4244 typedef struct
4245 {
4246 int keycode; /* must be at least 32 bit: hi:mode, lo: key */
4247 int (*cmd) (FORM *); /* low level driver routine for this key */
4248 }
4249 Binding_Info;
4250
4251 /* You may see this is the class-id of the request type class */
4252 #define ID_PN (0x00000000) /* Page navigation */
4253 #define ID_FN (0x00010000) /* Inter-Field navigation */
4254 #define ID_IFN (0x00020000) /* Intra-Field navigation */
4255 #define ID_VSC (0x00030000) /* Vertical Scrolling */
4256 #define ID_HSC (0x00040000) /* Horizontal Scrolling */
4257 #define ID_FE (0x00050000) /* Field Editing */
4258 #define ID_EM (0x00060000) /* Edit Mode */
4259 #define ID_FV (0x00070000) /* Field Validation */
4260 #define ID_CH (0x00080000) /* Choice */
4261 #define ID_Mask (0xffff0000)
4262 #define Key_Mask (0x0000ffff)
4263 #define ID_Shft (16)
4264
4265 /* This array holds all the Binding Infos */
4266 /* *INDENT-OFF* */
4267 static const Binding_Info bindings[MAX_FORM_COMMAND - MIN_FORM_COMMAND + 1] =
4268 {
4269 { REQ_NEXT_PAGE |ID_PN ,PN_Next_Page},
4270 { REQ_PREV_PAGE |ID_PN ,PN_Previous_Page},
4271 { REQ_FIRST_PAGE |ID_PN ,PN_First_Page},
4272 { REQ_LAST_PAGE |ID_PN ,PN_Last_Page},
4273
4274 { REQ_NEXT_FIELD |ID_FN ,FN_Next_Field},
4275 { REQ_PREV_FIELD |ID_FN ,FN_Previous_Field},
4276 { REQ_FIRST_FIELD |ID_FN ,FN_First_Field},
4277 { REQ_LAST_FIELD |ID_FN ,FN_Last_Field},
4278 { REQ_SNEXT_FIELD |ID_FN ,FN_Sorted_Next_Field},
4279 { REQ_SPREV_FIELD |ID_FN ,FN_Sorted_Previous_Field},
4280 { REQ_SFIRST_FIELD |ID_FN ,FN_Sorted_First_Field},
4281 { REQ_SLAST_FIELD |ID_FN ,FN_Sorted_Last_Field},
4282 { REQ_LEFT_FIELD |ID_FN ,FN_Left_Field},
4283 { REQ_RIGHT_FIELD |ID_FN ,FN_Right_Field},
4284 { REQ_UP_FIELD |ID_FN ,FN_Up_Field},
4285 { REQ_DOWN_FIELD |ID_FN ,FN_Down_Field},
4286
4287 { REQ_NEXT_CHAR |ID_IFN ,IFN_Next_Character},
4288 { REQ_PREV_CHAR |ID_IFN ,IFN_Previous_Character},
4289 { REQ_NEXT_LINE |ID_IFN ,IFN_Next_Line},
4290 { REQ_PREV_LINE |ID_IFN ,IFN_Previous_Line},
4291 { REQ_NEXT_WORD |ID_IFN ,IFN_Next_Word},
4292 { REQ_PREV_WORD |ID_IFN ,IFN_Previous_Word},
4293 { REQ_BEG_FIELD |ID_IFN ,IFN_Beginning_Of_Field},
4294 { REQ_END_FIELD |ID_IFN ,IFN_End_Of_Field},
4295 { REQ_BEG_LINE |ID_IFN ,IFN_Beginning_Of_Line},
4296 { REQ_END_LINE |ID_IFN ,IFN_End_Of_Line},
4297 { REQ_LEFT_CHAR |ID_IFN ,IFN_Left_Character},
4298 { REQ_RIGHT_CHAR |ID_IFN ,IFN_Right_Character},
4299 { REQ_UP_CHAR |ID_IFN ,IFN_Up_Character},
4300 { REQ_DOWN_CHAR |ID_IFN ,IFN_Down_Character},
4301
4302 { REQ_NEW_LINE |ID_FE ,FE_New_Line},
4303 { REQ_INS_CHAR |ID_FE ,FE_Insert_Character},
4304 { REQ_INS_LINE |ID_FE ,FE_Insert_Line},
4305 { REQ_DEL_CHAR |ID_FE ,FE_Delete_Character},
4306 { REQ_DEL_PREV |ID_FE ,FE_Delete_Previous},
4307 { REQ_DEL_LINE |ID_FE ,FE_Delete_Line},
4308 { REQ_DEL_WORD |ID_FE ,FE_Delete_Word},
4309 { REQ_CLR_EOL |ID_FE ,FE_Clear_To_End_Of_Line},
4310 { REQ_CLR_EOF |ID_FE ,FE_Clear_To_End_Of_Field},
4311 { REQ_CLR_FIELD |ID_FE ,FE_Clear_Field},
4312
4313 { REQ_OVL_MODE |ID_EM ,EM_Overlay_Mode},
4314 { REQ_INS_MODE |ID_EM ,EM_Insert_Mode},
4315
4316 { REQ_SCR_FLINE |ID_VSC ,VSC_Scroll_Line_Forward},
4317 { REQ_SCR_BLINE |ID_VSC ,VSC_Scroll_Line_Backward},
4318 { REQ_SCR_FPAGE |ID_VSC ,VSC_Scroll_Page_Forward},
4319 { REQ_SCR_BPAGE |ID_VSC ,VSC_Scroll_Page_Backward},
4320 { REQ_SCR_FHPAGE |ID_VSC ,VSC_Scroll_Half_Page_Forward},
4321 { REQ_SCR_BHPAGE |ID_VSC ,VSC_Scroll_Half_Page_Backward},
4322
4323 { REQ_SCR_FCHAR |ID_HSC ,HSC_Scroll_Char_Forward},
4324 { REQ_SCR_BCHAR |ID_HSC ,HSC_Scroll_Char_Backward},
4325 { REQ_SCR_HFLINE |ID_HSC ,HSC_Horizontal_Line_Forward},
4326 { REQ_SCR_HBLINE |ID_HSC ,HSC_Horizontal_Line_Backward},
4327 { REQ_SCR_HFHALF |ID_HSC ,HSC_Horizontal_Half_Line_Forward},
4328 { REQ_SCR_HBHALF |ID_HSC ,HSC_Horizontal_Half_Line_Backward},
4329
4330 { REQ_VALIDATION |ID_FV ,FV_Validation},
4331
4332 { REQ_NEXT_CHOICE |ID_CH ,CR_Next_Choice},
4333 { REQ_PREV_CHOICE |ID_CH ,CR_Previous_Choice}
4334 };
4335 /* *INDENT-ON* */
4336
4337 /*---------------------------------------------------------------------------
4338 | Facility : libnform
4339 | Function : int form_driver(FORM * form,int c)
4340 |
4341 | Description : This is the workhorse of the forms system. It checks
4342 | to determine whether the character c is a request or
4343 | data. If it is a request, the form driver executes
4344 | the request and returns the result. If it is data
4345 | (printable character), it enters the data into the
4346 | current position in the current field. If it is not
4347 | recognized, the form driver assumes it is an application
4348 | defined command and returns E_UNKNOWN_COMMAND.
4349 | Application defined command should be defined relative
4350 | to MAX_FORM_COMMAND, the maximum value of a request.
4351 |
4352 | Return Values : E_OK - success
4353 | E_SYSTEM_ERROR - system error
4354 | E_BAD_ARGUMENT - an argument is incorrect
4355 | E_NOT_POSTED - form is not posted
4356 | E_INVALID_FIELD - field contents are invalid
4357 | E_BAD_STATE - called from inside a hook routine
4358 | E_REQUEST_DENIED - request failed
4359 | E_NOT_CONNECTED - no fields are connected to the form
4360 | E_UNKNOWN_COMMAND - command not known
4361 +--------------------------------------------------------------------------*/
4362 FORM_EXPORT(int)
form_driver(FORM * form,int c)4363 form_driver(FORM *form, int c)
4364 {
4365 const Binding_Info *BI = (Binding_Info *)0;
4366 int res = E_UNKNOWN_COMMAND;
4367
4368 move_after_insert = TRUE;
4369
4370 T((T_CALLED("form_driver(%p,%d)"), (void *)form, c));
4371
4372 if (!form)
4373 RETURN(E_BAD_ARGUMENT);
4374
4375 if (!(form->field) || !(form->current))
4376 RETURN(E_NOT_CONNECTED);
4377
4378 assert(form->page);
4379
4380 if (c == FIRST_ACTIVE_MAGIC)
4381 {
4382 form->current = _nc_First_Active_Field(form);
4383 RETURN(E_OK);
4384 }
4385
4386 assert(form->current &&
4387 form->current->buf &&
4388 (form->current->form == form)
4389 );
4390
4391 if (form->status & _IN_DRIVER)
4392 RETURN(E_BAD_STATE);
4393
4394 if (!(form->status & _POSTED))
4395 RETURN(E_NOT_POSTED);
4396
4397 if ((c >= MIN_FORM_COMMAND && c <= MAX_FORM_COMMAND) &&
4398 ((bindings[c - MIN_FORM_COMMAND].keycode & Key_Mask) == c))
4399 {
4400 TR(TRACE_CALLS, ("form_request %s", form_request_name(c)));
4401 BI = &(bindings[c - MIN_FORM_COMMAND]);
4402 }
4403
4404 if (BI)
4405 {
4406 typedef int (*Generic_Method) (int (*const) (FORM *), FORM *);
4407 static const Generic_Method Generic_Methods[] =
4408 {
4409 Page_Navigation, /* overloaded to call field&form hooks */
4410 Inter_Field_Navigation, /* overloaded to call field hooks */
4411 NULL, /* Intra-Field is generic */
4412 Vertical_Scrolling, /* Overloaded to check multi-line */
4413 Horizontal_Scrolling, /* Overloaded to check single-line */
4414 Field_Editing, /* Overloaded to mark modification */
4415 NULL, /* Edit Mode is generic */
4416 NULL, /* Field Validation is generic */
4417 NULL /* Choice Request is generic */
4418 };
4419 size_t nMethods = (sizeof(Generic_Methods) / sizeof(Generic_Methods[0]));
4420 size_t method = (size_t)((BI->keycode >> ID_Shft) & 0xffff); /* see ID_Mask */
4421
4422 if ((method >= nMethods) || !(BI->cmd))
4423 res = E_SYSTEM_ERROR;
4424 else
4425 {
4426 Generic_Method fct = Generic_Methods[method];
4427
4428 if (fct)
4429 {
4430 res = fct(BI->cmd, form);
4431 }
4432 else
4433 {
4434 res = (BI->cmd) (form);
4435 }
4436 }
4437 }
4438 #ifdef NCURSES_MOUSE_VERSION
4439 else if (KEY_MOUSE == c)
4440 {
4441 MEVENT event;
4442 WINDOW *win = form->win ? form->win : StdScreen(Get_Form_Screen(form));
4443 WINDOW *sub = form->sub ? form->sub : win;
4444
4445 getmouse(&event);
4446 if ((event.bstate & (BUTTON1_CLICKED |
4447 BUTTON1_DOUBLE_CLICKED |
4448 BUTTON1_TRIPLE_CLICKED))
4449 && wenclose(win, event.y, event.x))
4450 { /* we react only if the click was in the userwin, that means
4451 * inside the form display area or at the decoration window.
4452 */
4453 int ry = event.y, rx = event.x; /* screen coordinates */
4454
4455 res = E_REQUEST_DENIED;
4456 if (mouse_trafo(&ry, &rx, FALSE))
4457 { /* rx, ry are now "curses" coordinates */
4458 if (ry < sub->_begy)
4459 { /* we clicked above the display region; this is
4460 * interpreted as "scroll up" request
4461 */
4462 if (event.bstate & BUTTON1_CLICKED)
4463 res = form_driver(form, REQ_PREV_FIELD);
4464 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4465 res = form_driver(form, REQ_PREV_PAGE);
4466 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4467 res = form_driver(form, REQ_FIRST_FIELD);
4468 }
4469 else if (ry > sub->_begy + sub->_maxy)
4470 { /* we clicked below the display region; this is
4471 * interpreted as "scroll down" request
4472 */
4473 if (event.bstate & BUTTON1_CLICKED)
4474 res = form_driver(form, REQ_NEXT_FIELD);
4475 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4476 res = form_driver(form, REQ_NEXT_PAGE);
4477 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4478 res = form_driver(form, REQ_LAST_FIELD);
4479 }
4480 else if (wenclose(sub, event.y, event.x))
4481 { /* Inside the area we try to find the hit item */
4482 ry = event.y;
4483 rx = event.x;
4484 if (wmouse_trafo(sub, &ry, &rx, FALSE))
4485 {
4486 int min_field = form->page[form->curpage].pmin;
4487 int max_field = form->page[form->curpage].pmax;
4488 int i;
4489
4490 for (i = min_field; i <= max_field; ++i)
4491 {
4492 FIELD *field = form->field[i];
4493
4494 if (Field_Is_Selectable(field)
4495 && Field_encloses(field, ry, rx) == E_OK)
4496 {
4497 res = _nc_Set_Current_Field(form, field);
4498 if (res == E_OK)
4499 res = _nc_Position_Form_Cursor(form);
4500 if (res == E_OK
4501 && (event.bstate & BUTTON1_DOUBLE_CLICKED))
4502 res = E_UNKNOWN_COMMAND;
4503 break;
4504 }
4505 }
4506 }
4507 }
4508 }
4509 }
4510 else
4511 res = E_REQUEST_DENIED;
4512 }
4513 #endif /* NCURSES_MOUSE_VERSION */
4514 else if (!(c & (~(int)MAX_REGULAR_CHARACTER)))
4515 {
4516 /*
4517 * If we're using 8-bit characters, iscntrl+isprint cover the whole set.
4518 * But with multibyte characters, there is a third possibility, i.e.,
4519 * parts of characters that build up into printable characters which are
4520 * not considered printable.
4521 *
4522 * FIXME: the wide-character branch should also use Check_Char().
4523 */
4524 #if USE_WIDEC_SUPPORT
4525 if (!iscntrl(UChar(c)))
4526 #else
4527 if (isprint(UChar(c)) &&
4528 Check_Char(form, form->current, form->current->type, c,
4529 (TypeArgument *)(form->current->arg)))
4530 #endif
4531 res = Data_Entry(form, c);
4532 }
4533 _nc_Refresh_Current_Field(form);
4534 RETURN(res);
4535 }
4536
4537 # if USE_WIDEC_SUPPORT
4538 /*---------------------------------------------------------------------------
4539 | Facility : libnform
4540 | Function : int form_driver_w(FORM * form,int type,wchar_t c)
4541 |
4542 | Description : This is the workhorse of the forms system.
4543 |
4544 | Input is either a key code (request) or a wide char
4545 | returned by e.g. get_wch (). The type must be passed
4546 | as well,so that we are able to determine whether the char
4547 | is a multibyte char or a request.
4548
4549 | If it is a request, the form driver executes
4550 | the request and returns the result. If it is data
4551 | (printable character), it enters the data into the
4552 | current position in the current field. If it is not
4553 | recognized, the form driver assumes it is an application
4554 | defined command and returns E_UNKNOWN_COMMAND.
4555 | Application defined command should be defined relative
4556 | to MAX_FORM_COMMAND, the maximum value of a request.
4557 |
4558 | Return Values : E_OK - success
4559 | E_SYSTEM_ERROR - system error
4560 | E_BAD_ARGUMENT - an argument is incorrect
4561 | E_NOT_POSTED - form is not posted
4562 | E_INVALID_FIELD - field contents are invalid
4563 | E_BAD_STATE - called from inside a hook routine
4564 | E_REQUEST_DENIED - request failed
4565 | E_NOT_CONNECTED - no fields are connected to the form
4566 | E_UNKNOWN_COMMAND - command not known
4567 +--------------------------------------------------------------------------*/
4568 FORM_EXPORT(int)
form_driver_w(FORM * form,int type,wchar_t c)4569 form_driver_w(FORM *form, int type, wchar_t c)
4570 {
4571 const Binding_Info *BI = (Binding_Info *)0;
4572 int res = E_UNKNOWN_COMMAND;
4573
4574 T((T_CALLED("form_driver(%p,%d)"), (void *)form, (int)c));
4575
4576 if (!form)
4577 RETURN(E_BAD_ARGUMENT);
4578
4579 if (!(form->field))
4580 RETURN(E_NOT_CONNECTED);
4581
4582 assert(form->page);
4583
4584 if (c == (wchar_t)FIRST_ACTIVE_MAGIC)
4585 {
4586 form->current = _nc_First_Active_Field(form);
4587 RETURN(E_OK);
4588 }
4589
4590 assert(form->current &&
4591 form->current->buf &&
4592 (form->current->form == form)
4593 );
4594
4595 if (form->status & _IN_DRIVER)
4596 RETURN(E_BAD_STATE);
4597
4598 if (!(form->status & _POSTED))
4599 RETURN(E_NOT_POSTED);
4600
4601 /* check if this is a keycode or a (wide) char */
4602 if (type == KEY_CODE_YES)
4603 {
4604 if ((c >= MIN_FORM_COMMAND && c <= MAX_FORM_COMMAND) &&
4605 ((bindings[c - MIN_FORM_COMMAND].keycode & Key_Mask) == c))
4606 BI = &(bindings[c - MIN_FORM_COMMAND]);
4607 }
4608
4609 if (BI)
4610 {
4611 typedef int (*Generic_Method) (int (*const) (FORM *), FORM *);
4612 static const Generic_Method Generic_Methods[] =
4613 {
4614 Page_Navigation, /* overloaded to call field&form hooks */
4615 Inter_Field_Navigation, /* overloaded to call field hooks */
4616 NULL, /* Intra-Field is generic */
4617 Vertical_Scrolling, /* Overloaded to check multi-line */
4618 Horizontal_Scrolling, /* Overloaded to check single-line */
4619 Field_Editing, /* Overloaded to mark modification */
4620 NULL, /* Edit Mode is generic */
4621 NULL, /* Field Validation is generic */
4622 NULL /* Choice Request is generic */
4623 };
4624 size_t nMethods = (sizeof(Generic_Methods) / sizeof(Generic_Methods[0]));
4625 size_t method = (size_t)(BI->keycode >> ID_Shft) & 0xffff; /* see ID_Mask */
4626
4627 if ((method >= nMethods) || !(BI->cmd))
4628 res = E_SYSTEM_ERROR;
4629 else
4630 {
4631 Generic_Method fct = Generic_Methods[method];
4632
4633 if (fct)
4634 res = fct(BI->cmd, form);
4635 else
4636 res = (BI->cmd) (form);
4637 }
4638 }
4639 #ifdef NCURSES_MOUSE_VERSION
4640 else if (KEY_MOUSE == c)
4641 {
4642 MEVENT event;
4643 WINDOW *win = form->win ? form->win : StdScreen(Get_Form_Screen(form));
4644 WINDOW *sub = form->sub ? form->sub : win;
4645
4646 getmouse(&event);
4647 if ((event.bstate & (BUTTON1_CLICKED |
4648 BUTTON1_DOUBLE_CLICKED |
4649 BUTTON1_TRIPLE_CLICKED))
4650 && wenclose(win, event.y, event.x))
4651 { /* we react only if the click was in the userwin, that means
4652 * inside the form display area or at the decoration window.
4653 */
4654 int ry = event.y, rx = event.x; /* screen coordinates */
4655
4656 res = E_REQUEST_DENIED;
4657 if (mouse_trafo(&ry, &rx, FALSE))
4658 { /* rx, ry are now "curses" coordinates */
4659 if (ry < sub->_begy)
4660 { /* we clicked above the display region; this is
4661 * interpreted as "scroll up" request
4662 */
4663 if (event.bstate & BUTTON1_CLICKED)
4664 res = form_driver(form, REQ_PREV_FIELD);
4665 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4666 res = form_driver(form, REQ_PREV_PAGE);
4667 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4668 res = form_driver(form, REQ_FIRST_FIELD);
4669 }
4670 else if (ry > sub->_begy + sub->_maxy)
4671 { /* we clicked below the display region; this is
4672 * interpreted as "scroll down" request
4673 */
4674 if (event.bstate & BUTTON1_CLICKED)
4675 res = form_driver(form, REQ_NEXT_FIELD);
4676 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4677 res = form_driver(form, REQ_NEXT_PAGE);
4678 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4679 res = form_driver(form, REQ_LAST_FIELD);
4680 }
4681 else if (wenclose(sub, event.y, event.x))
4682 { /* Inside the area we try to find the hit item */
4683 ry = event.y;
4684 rx = event.x;
4685 if (wmouse_trafo(sub, &ry, &rx, FALSE))
4686 {
4687 int min_field = form->page[form->curpage].pmin;
4688 int max_field = form->page[form->curpage].pmax;
4689 int i;
4690
4691 for (i = min_field; i <= max_field; ++i)
4692 {
4693 FIELD *field = form->field[i];
4694
4695 if (Field_Is_Selectable(field)
4696 && Field_encloses(field, ry, rx) == E_OK)
4697 {
4698 res = _nc_Set_Current_Field(form, field);
4699 if (res == E_OK)
4700 res = _nc_Position_Form_Cursor(form);
4701 if (res == E_OK
4702 && (event.bstate & BUTTON1_DOUBLE_CLICKED))
4703 res = E_UNKNOWN_COMMAND;
4704 break;
4705 }
4706 }
4707 }
4708 }
4709 }
4710 }
4711 else
4712 res = E_REQUEST_DENIED;
4713 }
4714 #endif /* NCURSES_MOUSE_VERSION */
4715 else if (type == OK)
4716 {
4717 res = Data_Entry_w(form, c);
4718 }
4719
4720 _nc_Refresh_Current_Field(form);
4721 RETURN(res);
4722 }
4723 # endif /* USE_WIDEC_SUPPORT */
4724
4725 /*----------------------------------------------------------------------------
4726 Field-Buffer manipulation routines.
4727 The effects of setting a buffer are tightly coupled to the core of the form
4728 driver logic. This is especially true in the case of growable fields.
4729 So I don't separate this into a separate module.
4730 --------------------------------------------------------------------------*/
4731
4732 /*---------------------------------------------------------------------------
4733 | Facility : libnform
4734 | Function : int set_field_buffer(FIELD *field,
4735 | int buffer, char *value)
4736 |
4737 | Description : Set the given buffer of the field to the given value.
4738 | Buffer 0 stores the displayed content of the field.
4739 | For dynamic fields this may grow the fieldbuffers if
4740 | the length of the value exceeds the current buffer
4741 | length. For buffer 0 only printable values are allowed.
4742 | For static fields, the value must not be zero terminated.
4743 | It is copied up to the length of the buffer.
4744 |
4745 | Return Values : E_OK - success
4746 | E_BAD_ARGUMENT - invalid argument
4747 | E_SYSTEM_ERROR - system error
4748 +--------------------------------------------------------------------------*/
4749 FORM_EXPORT(int)
set_field_buffer(FIELD * field,int buffer,const char * value)4750 set_field_buffer(FIELD *field, int buffer, const char *value)
4751 {
4752 FIELD_CELL *p;
4753 int res = E_OK;
4754 int i;
4755 int len;
4756
4757 #if USE_WIDEC_SUPPORT
4758 FIELD_CELL *widevalue = 0;
4759 #endif
4760
4761 T((T_CALLED("set_field_buffer(%p,%d,%s)"), (void *)field, buffer, _nc_visbuf(value)));
4762
4763 if (!field || !value || ((buffer < 0) || (buffer > field->nbuf)))
4764 RETURN(E_BAD_ARGUMENT);
4765
4766 len = Buffer_Length(field);
4767
4768 if (Growable(field))
4769 {
4770 /* for a growable field we must assume zero terminated strings, because
4771 somehow we have to detect the length of what should be copied.
4772 */
4773 int vlen = (int)strlen(value);
4774
4775 if (vlen > len)
4776 {
4777 if (!Field_Grown(field,
4778 (int)(1 + (vlen - len) / ((field->rows + field->nrow)
4779 * field->cols))))
4780 RETURN(E_SYSTEM_ERROR);
4781
4782 #if !USE_WIDEC_SUPPORT
4783 len = vlen;
4784 #endif
4785 }
4786 }
4787
4788 p = Address_Of_Nth_Buffer(field, buffer);
4789
4790 #if USE_WIDEC_SUPPORT
4791 /*
4792 * Use addstr's logic for converting a string to an array of cchar_t's.
4793 * There should be a better way, but this handles nonspacing characters
4794 * and other special cases that we really do not want to handle here.
4795 */
4796 #if NCURSES_EXT_FUNCS
4797 if (wresize(field->working, 1, Buffer_Length(field) + 1) == ERR)
4798 #endif
4799 {
4800 delwin(field->working);
4801 field->working = newpad(1, Buffer_Length(field) + 1);
4802 }
4803 len = Buffer_Length(field);
4804 wclear(field->working);
4805 (void)mvwaddstr(field->working, 0, 0, value);
4806
4807 if ((widevalue = typeCalloc(FIELD_CELL, len + 1)) == 0)
4808 {
4809 RETURN(E_SYSTEM_ERROR);
4810 }
4811 else
4812 {
4813 for (i = 0; i < field->drows; ++i)
4814 {
4815 (void)mvwin_wchnstr(field->working, 0, (int)i * field->dcols,
4816 widevalue + ((int)i * field->dcols),
4817 field->dcols);
4818 }
4819 for (i = 0; i < len; ++i)
4820 {
4821 if (CharEq(myZEROS, widevalue[i]))
4822 {
4823 while (i < len)
4824 p[i++] = myBLANK;
4825 break;
4826 }
4827 p[i] = widevalue[i];
4828 }
4829 free(widevalue);
4830 }
4831 #else
4832 for (i = 0; i < len; ++i)
4833 {
4834 if (value[i] == '\0')
4835 {
4836 while (i < len)
4837 p[i++] = myBLANK;
4838 break;
4839 }
4840 p[i] = value[i];
4841 }
4842 #endif
4843
4844 if (buffer == 0)
4845 {
4846 int syncres;
4847
4848 if (((syncres = Synchronize_Field(field)) != E_OK) &&
4849 (res == E_OK))
4850 res = syncres;
4851 if (((syncres = Synchronize_Linked_Fields(field)) != E_OK) &&
4852 (res == E_OK))
4853 res = syncres;
4854 }
4855 RETURN(res);
4856 }
4857
4858 /*---------------------------------------------------------------------------
4859 | Facility : libnform
4860 | Function : char *field_buffer(const FIELD *field,int buffer)
4861 |
4862 | Description : Return the address of the buffer for the field.
4863 |
4864 | Return Values : Pointer to buffer or NULL if arguments were invalid.
4865 +--------------------------------------------------------------------------*/
4866 FORM_EXPORT(char *)
field_buffer(const FIELD * field,int buffer)4867 field_buffer(const FIELD *field, int buffer)
4868 {
4869 char *result = 0;
4870
4871 T((T_CALLED("field_buffer(%p,%d)"), (const void *)field, buffer));
4872
4873 if (field && (buffer >= 0) && (buffer <= field->nbuf))
4874 {
4875 #if USE_WIDEC_SUPPORT
4876 FIELD_CELL *data = Address_Of_Nth_Buffer(field, buffer);
4877 size_t need = 0;
4878 int size = Buffer_Length(field);
4879 int n;
4880
4881 /* determine the number of bytes needed to store the expanded string */
4882 for (n = 0; n < size; ++n)
4883 {
4884 if (!isWidecExt(data[n]) && data[n].chars[0] != L'\0')
4885 {
4886 mbstate_t state;
4887 size_t next;
4888
4889 init_mb(state);
4890 next = _nc_wcrtomb(0, data[n].chars[0], &state);
4891 if (next > 0)
4892 need += next;
4893 }
4894 }
4895
4896 /* allocate a place to store the expanded string */
4897 if (field->expanded[buffer] != 0)
4898 free(field->expanded[buffer]);
4899 field->expanded[buffer] = typeMalloc(char, need + 1);
4900
4901 /*
4902 * Expand the multibyte data.
4903 *
4904 * It may also be multi-column data. In that case, the data for a row
4905 * may be null-padded to align to the dcols/drows layout (or it may
4906 * contain embedded wide-character extensions). Change the null-padding
4907 * to blanks as needed.
4908 */
4909 if ((result = field->expanded[buffer]) != 0)
4910 {
4911 wclear(field->working);
4912 wmove(field->working, 0, 0);
4913 for (n = 0; n < size; ++n)
4914 {
4915 if (!isWidecExt(data[n]) && data[n].chars[0] != L'\0')
4916 wadd_wch(field->working, &data[n]);
4917 }
4918 wmove(field->working, 0, 0);
4919 winnstr(field->working, result, (int)need);
4920 }
4921 #else
4922 result = Address_Of_Nth_Buffer(field, buffer);
4923 #endif
4924 }
4925 returnPtr(result);
4926 }
4927
4928 #if USE_WIDEC_SUPPORT
4929
4930 /*---------------------------------------------------------------------------
4931 | Convert a multibyte string to a wide-character string. The result must be
4932 | freed by the caller.
4933 +--------------------------------------------------------------------------*/
4934 FORM_EXPORT(wchar_t *)
_nc_Widen_String(char * source,int * lengthp)4935 _nc_Widen_String(char *source, int *lengthp)
4936 {
4937 wchar_t *result = 0;
4938 wchar_t wch;
4939 size_t given = strlen(source);
4940 size_t tries;
4941 int pass;
4942 int status;
4943
4944 #ifndef state_unused
4945 mbstate_t state;
4946 #endif
4947
4948 for (pass = 0; pass < 2; ++pass)
4949 {
4950 unsigned need = 0;
4951 size_t passed = 0;
4952
4953 while (passed < given)
4954 {
4955 bool found = FALSE;
4956
4957 for (tries = 1, status = 0; tries <= (given - passed); ++tries)
4958 {
4959 int save = source[passed + tries];
4960
4961 source[passed + tries] = 0;
4962 reset_mbytes(state);
4963 status = check_mbytes(wch, source + passed, tries, state);
4964 source[passed + tries] = (char)save;
4965
4966 if (status > 0)
4967 {
4968 found = TRUE;
4969 break;
4970 }
4971 }
4972 if (found)
4973 {
4974 if (pass)
4975 {
4976 result[need] = wch;
4977 }
4978 passed += (size_t)status;
4979 ++need;
4980 }
4981 else
4982 {
4983 if (pass)
4984 {
4985 result[need] = (wchar_t)source[passed];
4986 }
4987 ++need;
4988 ++passed;
4989 }
4990 }
4991
4992 if (!pass)
4993 {
4994 if (!need)
4995 break;
4996 result = typeCalloc(wchar_t, need);
4997
4998 *lengthp = (int)need;
4999 if (result == 0)
5000 break;
5001 }
5002 }
5003
5004 return result;
5005 }
5006 #endif
5007
5008 /* frm_driver.c ends here */
5009