internals.c revision 1.8 1 /* $NetBSD: internals.c,v 1.8 2001/01/30 06:44:42 blymn Exp $ */
2
3 /*-
4 * Copyright (c) 1998-1999 Brett Lymn
5 * (blymn (at) baea.com.au, brett_lymn (at) yahoo.com.au)
6 * All rights reserved.
7 *
8 * This code has been donated to The NetBSD Foundation by the Author.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. The name of the author may not be used to endorse or promote products
16 * derived from this software withough specific prior written permission
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 *
30 */
31
32 #include <ctype.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <strings.h>
36 #include "internals.h"
37 #include "form.h"
38
39 #ifdef DEBUG
40 /*
41 * file handle to write debug info to, this will be initialised when
42 * the form is first posted.
43 */
44 FILE *dbg = NULL;
45 #endif
46
47 /* define our own min function - this is not generic but will do here
48 * (don't believe me? think about what value you would get
49 * from min(x++, y++)
50 */
51 #define min(a,b) (((a) > (b))? (b) : (a))
52
53 /* for the line joining function... */
54 #define JOIN_NEXT 1
55 #define JOIN_NEXT_NW 2 /* next join, don't wrap the joined line */
56 #define JOIN_PREV 3
57 #define JOIN_PREV_NW 4 /* previous join, don't wrap the joined line */
58
59 static void
60 _formi_do_char_validation(FIELD *field, FIELDTYPE *type, char c, int *ret_val);
61 static void
62 _formi_do_validation(FIELD *field, FIELDTYPE *type, int *ret_val);
63 static int
64 _formi_join_line(FIELD *field, char *str, unsigned int pos, int direction);
65 static int
66 _formi_wrap_field(FIELD *field, unsigned int pos);
67 static void
68 _formi_redraw_field(FORM *form, int field);
69 void
70 _formi_hscroll_back(FIELD *field, unsigned int amt);
71 void
72 _formi_hscroll_fwd(FIELD *field, unsigned int amt);
73 static void
74 _formi_scroll_back(FIELD *field, unsigned int amt);
75 static void
76 _formi_scroll_fwd(FIELD *field, unsigned int amt);
77 static int
78 find_sow(char *str, unsigned int offset);
79 static int
80 find_cur_line(FIELD *cur);
81
82 /*
83 * Open the debug file if it is not already open....
84 */
85 #ifdef DEBUG
86 int
87 _formi_create_dbg_file(void)
88 {
89 if (dbg == NULL) {
90 dbg = fopen("___form_dbg.out", "w");
91 if (dbg == NULL) {
92 fprintf(stderr, "Cannot open debug file!\n");
93 return E_SYSTEM_ERROR;
94 }
95 }
96
97 return E_OK;
98 }
99 #endif
100
101 /*
102 * Set the form's current field to the first valid field on the page.
103 * Assume the fields have been sorted and stitched.
104 */
105 int
106 _formi_pos_first_field(FORM *form)
107 {
108 FIELD *cur;
109 int old_page;
110
111 old_page = form->page;
112
113 /* scan forward for an active page....*/
114 while (form->page_starts[form->page].in_use == 0) {
115 form->page++;
116 if (form->page > form->max_page) {
117 form->page = old_page;
118 return E_REQUEST_DENIED;
119 }
120 }
121
122 cur = form->fields[form->page_starts[form->page].first];
123 while ((cur->opts & (O_VISIBLE | O_ACTIVE))
124 != (O_VISIBLE | O_ACTIVE)) {
125 cur = CIRCLEQ_NEXT(cur, glue);
126 if (cur == (void *) &form->sorted_fields) {
127 form->page = old_page;
128 return E_REQUEST_DENIED;
129 }
130 }
131
132 form->cur_field = cur->index;
133 return E_OK;
134 }
135
136 /*
137 * Set the field to the next active and visible field, the fields are
138 * traversed in index order in the direction given. If the parameter
139 * use_sorted is TRUE then the sorted field list will be traversed instead
140 * of using the field index.
141 */
142 int
143 _formi_pos_new_field(FORM *form, unsigned direction, unsigned use_sorted)
144 {
145 FIELD *cur;
146 int i;
147
148 i = form->cur_field;
149 cur = form->fields[i];
150
151 do {
152 if (direction == _FORMI_FORWARD) {
153 if (use_sorted == TRUE) {
154 if ((form->wrap == FALSE) &&
155 (cur == CIRCLEQ_LAST(&form->sorted_fields)))
156 return E_REQUEST_DENIED;
157 cur = CIRCLEQ_NEXT(cur, glue);
158 i = cur->index;
159 } else {
160 if ((form->wrap == FALSE) &&
161 ((i + 1) >= form->field_count))
162 return E_REQUEST_DENIED;
163 i++;
164 if (i >= form->field_count)
165 i = 0;
166 }
167 } else {
168 if (use_sorted == TRUE) {
169 if ((form->wrap == FALSE) &&
170 (cur == CIRCLEQ_FIRST(&form->sorted_fields)))
171 return E_REQUEST_DENIED;
172 cur = CIRCLEQ_PREV(cur, glue);
173 i = cur->index;
174 } else {
175 if ((form->wrap == FALSE) && (i <= 0))
176 return E_REQUEST_DENIED;
177 i--;
178 if (i < 0)
179 i = form->field_count - 1;
180 }
181 }
182
183 if ((form->fields[i]->opts & (O_VISIBLE | O_ACTIVE))
184 == (O_VISIBLE | O_ACTIVE)) {
185 form->cur_field = i;
186 return E_OK;
187 }
188 }
189 while (i != form->cur_field);
190
191 return E_REQUEST_DENIED;
192 }
193
194 /*
195 * Find the line in a field that the cursor is currently on.
196 */
197 static int
198 find_cur_line(FIELD *cur)
199 {
200 unsigned start, end, pos, row;
201 const char *str;
202
203 str = cur->buffers[0].string;
204 pos = cur->start_char + cur->hscroll + cur->cursor_xpos;
205
206 start = 0;
207 end = 0;
208
209 for (row = 0; row < cur->row_count; row++) {
210 start = _formi_find_bol(str, start);
211 end = _formi_find_eol(str, end);
212 if ((pos >= start) && (pos <= end))
213 return row;
214 }
215
216 return 0;
217 }
218
219
220 /*
221 * Word wrap the contents of the field's buffer 0 if this is allowed.
222 * If the wrap is successful, that is, the row count nor the buffer
223 * size is exceeded then the function will return E_OK, otherwise it
224 * will return E_REQUEST_DENIED.
225 */
226 static int
227 _formi_wrap_field(FIELD *field, unsigned int pos)
228 {
229 char *str, *new;
230 int width, length, allocated, row_count, sol, eol, wrapped;
231 size_t new_size;
232
233 if ((field->opts & O_WRAP) != O_WRAP)
234 return E_REQUEST_DENIED;
235
236 wrapped = FALSE;
237 row_count = 0;
238 allocated = field->buffers[0].allocated;
239 length = field->buffers[0].length;
240 if ((str = (char *) malloc(sizeof(char) * allocated)) == NULL)
241 return E_SYSTEM_ERROR;
242
243 strcpy(str,field->buffers[0].string);
244
245 if ((field->opts & O_STATIC) == O_STATIC)
246 width = field->cols;
247 else
248 width = field->dcols;
249
250 while (str[pos] != '\0') {
251 row_count++;
252 sol = _formi_find_bol(str, pos);
253 eol = _formi_find_eol(str, pos);
254 if ((eol - sol) <= width) {
255 /* line may be too short, try joining some lines */
256 pos = eol;
257 if ((eol - sol) == width) {
258 /* if line is just right then don't wrap */
259 pos++;
260 continue;
261 }
262
263 if (_formi_join_line(field, str, pos, JOIN_NEXT_NW)
264 == E_OK) {
265 row_count--; /* cuz we just joined a line */
266 wrapped = TRUE;
267 } else
268 break;
269 } else {
270 /* line is too long, split it - maybe */
271 /* split on first whitespace before current word */
272 pos = sol + width;
273 if (!isblank(str[pos]))
274 pos = find_sow(str, pos);
275
276 if (pos != sol) {
277 if (length + 1 >= allocated) {
278 new_size = allocated + 64
279 - (allocated % 64);
280
281 if ((new = (char *) realloc(str,
282 sizeof(char) * new_size)
283 ) == NULL) {
284 free(str);
285 return E_SYSTEM_ERROR;
286 }
287 str = new;
288 allocated = new_size;
289 }
290
291 bcopy(&str[pos], &str[pos + 1],
292 (unsigned) length - pos - 1);
293 str[pos] = '\n';
294 pos = pos + 1;
295 length++;
296 wrapped = TRUE;
297 } else
298 break;
299 }
300 }
301
302 if (row_count > field->rows) {
303 free(str);
304 return E_REQUEST_DENIED;
305 }
306
307 if (wrapped == TRUE) {
308 field->buffers[0].length = length;
309 field->buffers[0].allocated = allocated;
310 free(field->buffers[0].string);
311 field->buffers[0].string = str;
312 } else /* all that work was in vain.... */
313 free(str);
314
315 return E_OK;
316 }
317
318 /*
319 * Join the two lines that surround the location pos, the type
320 * variable indicates the direction of the join. Note that pos is
321 * assumed to be at either the end of the line for a JOIN_NEXT or at
322 * the beginning of the line for a JOIN_PREV. We need to check the
323 * field options to ensure the join does not overflow the line limit
324 * (if wrap is off) or wrap the field buffer again. Returns E_OK if
325 * the join was successful or E_REQUEST_DENIED if the join cannot
326 * happen.
327 */
328 static int
329 _formi_join_line(FIELD *field, char *str, unsigned int pos, int direction)
330 {
331 unsigned int len, sol, eol, npos, start, dest;
332
333 npos = pos;
334
335 if ((direction == JOIN_NEXT) || (direction == JOIN_NEXT_NW)) {
336 sol = _formi_find_bol(str, pos);
337 npos++;
338 /* see if there is another line following... */
339 if (str[npos] == '\0')
340 return E_REQUEST_DENIED;
341 eol = _formi_find_eol(str, npos);
342
343 start = npos;
344 dest = pos;
345 len = eol - npos;
346 } else {
347 if (pos == 0)
348 return E_REQUEST_DENIED;
349 eol = _formi_find_eol(str, pos);
350 npos--;
351 sol = _formi_find_bol(str, npos);
352
353 start = pos;
354 dest = npos;
355 len = eol - pos;
356 }
357
358
359 /* if we cannot wrap and the length of the resultant line
360 * is bigger than our field width we shall deny the request.
361 */
362 if (((field->opts & O_WRAP) != O_WRAP) && /* XXXXX check for dynamic field */
363 ((sol + eol - 1) > field->cols))
364 return E_REQUEST_DENIED;
365
366 bcopy(&str[start], &str[dest], (unsigned) len);
367
368 /* wrap the field if required, if this fails undo the change */
369 if ((direction == JOIN_NEXT) || (direction == JOIN_PREV)) {
370 if (_formi_wrap_field(field, (unsigned int) pos) != E_OK) {
371 bcopy(&str[dest], &str[start], (unsigned) len);
372 str[dest] = '\n';
373 return E_REQUEST_DENIED;
374 }
375 }
376
377 return E_OK;
378 }
379
380 /*
381 * skip the blanks in the given string, start at the index start and
382 * continue forward until either the end of the string or a non-blank
383 * character is found. Return the index of either the end of the string or
384 * the first non-blank character.
385 */
386 unsigned
387 _formi_skip_blanks(char *string, unsigned int start)
388 {
389 unsigned int i;
390
391 i = start;
392
393 while ((string[i] != '\0') && isblank(string[i]))
394 i++;
395
396 return i;
397 }
398
399 /*
400 * Return the index of the top left most field of the two given fields.
401 */
402 static int
403 _formi_top_left(FORM *form, int a, int b)
404 {
405 /* lower row numbers always win here.... */
406 if (form->fields[a]->form_row < form->fields[b]->form_row)
407 return a;
408
409 if (form->fields[a]->form_row > form->fields[b]->form_row)
410 return b;
411
412 /* rows must be equal, check columns */
413 if (form->fields[a]->form_col < form->fields[b]->form_col)
414 return a;
415
416 if (form->fields[a]->form_col > form->fields[b]->form_col)
417 return b;
418
419 /* if we get here fields must be in exactly the same place, punt */
420 return a;
421 }
422
423 /*
424 * Return the index to the field that is the bottom-right-most of the
425 * two given fields.
426 */
427 static int
428 _formi_bottom_right(FORM *form, int a, int b)
429 {
430 /* check the rows first, biggest row wins */
431 if (form->fields[a]->form_row > form->fields[b]->form_row)
432 return a;
433 if (form->fields[a]->form_row < form->fields[b]->form_row)
434 return b;
435
436 /* rows must be equal, check cols, biggest wins */
437 if (form->fields[a]->form_col > form->fields[b]->form_col)
438 return a;
439 if (form->fields[a]->form_col < form->fields[b]->form_col)
440 return b;
441
442 /* fields in the same place, punt */
443 return a;
444 }
445
446 /*
447 * Find the next '\n' character in the given string starting at offset
448 * if there are no newlines found then return the index to the end of the
449 * string.
450 */
451 int
452 _formi_find_eol(const char *string, unsigned int offset)
453 {
454 char *location;
455 int eol;
456
457 if ((location = index(&string[offset], '\n')) != NULL)
458 eol = location - string;
459 else
460 eol = strlen(string);
461
462 if (eol > 0)
463 eol--;
464
465 return eol;
466 }
467
468 /*
469 * Find the previous '\n' character in the given string starting at offset
470 * if there are no newlines found then return 0.
471 */
472 int
473 _formi_find_bol(const char *string, unsigned int offset)
474 {
475 int cnt;
476
477 cnt = offset;
478 while ((cnt > 0) && (string[cnt] != '\n'))
479 cnt--;
480
481 /* if we moved and found a newline go forward one to point at the
482 * actual start of the line....
483 */
484 if ((cnt != offset) && (string[cnt] == '\n'))
485 cnt++;
486
487 return cnt;
488 }
489
490 /*
491 * Find the end of the current word in the string str, starting at
492 * offset - the end includes any trailing whitespace. If the end of
493 * the string is found before a new word then just return the offset
494 * to the end of the string.
495 */
496 static int
497 find_eow(char *str, unsigned int offset)
498 {
499 int start;
500
501 start = offset;
502 /* first skip any non-whitespace */
503 while ((str[start] != '\0') && !isblank(str[start]))
504 start++;
505
506 /* see if we hit the end of the string */
507 if (str[start] == '\0')
508 return start;
509
510 /* otherwise skip the whitespace.... */
511 while ((str[start] != '\0') && isblank(str[start]))
512 start++;
513
514 return start;
515 }
516
517 /*
518 * Find the beginning of the current word in the string str, starting
519 * at offset.
520 */
521 static int
522 find_sow(char *str, unsigned int offset)
523 {
524 int start;
525
526 start = offset;
527
528 if (start > 0) {
529 if (isblank(str[start]) || isblank(str[start - 1])) {
530 if (isblank(str[start - 1]))
531 start--;
532 /* skip the whitespace.... */
533 while ((start >= 0) && isblank(str[start]))
534 start--;
535 }
536 }
537
538 /* see if we hit the start of the string */
539 if (start < 0)
540 return 0;
541
542 /* now skip any non-whitespace */
543 while ((start >= 0) && !isblank(str[start]))
544 start--;
545
546 if (start > 0)
547 start++; /* last loop has us pointing at a space, adjust */
548
549 if (start < 0)
550 start = 0;
551
552 return start;
553 }
554
555 /*
556 * Scroll the field forward the given number of lines.
557 */
558 static void
559 _formi_scroll_fwd(FIELD *field, unsigned int amt)
560 {
561 /* check if we have lines to scroll */
562 if (field->row_count < (field->start_line + field->rows))
563 return;
564
565 field->start_line += min(amt,
566 field->row_count - field->start_line
567 - field->rows);
568 }
569
570 /*
571 * Scroll the field backward the given number of lines.
572 */
573 static void
574 _formi_scroll_back(FIELD *field, unsigned int amt)
575 {
576 if (field->start_line == 0)
577 return;
578
579 field->start_line -= min(field->start_line, amt);
580 }
581
582 /*
583 * Scroll the field forward the given number of characters.
584 */
585 void
586 _formi_hscroll_fwd(FIELD *field, int unsigned amt)
587 {
588 int end, scroll_amt;
589
590 end = _formi_find_eol(field->buffers[0].string,
591 field->start_char + field->hscroll
592 + field->cursor_xpos) - field->start_char
593 - field->hscroll - field->cursor_xpos;
594
595 scroll_amt = min(amt, end);
596 if (scroll_amt < 0)
597 scroll_amt = 0;
598
599 field->hscroll += scroll_amt;
600 if (amt > field->cursor_xpos)
601 field->cursor_xpos = 0;
602 else
603 field->cursor_xpos -= scroll_amt;
604 }
605
606 /*
607 * Scroll the field backward the given number of characters.
608 */
609 void
610 _formi_hscroll_back(FIELD *field, unsigned int amt)
611 {
612 int flen, sa;
613
614 sa = min(field->hscroll, amt);
615 field->hscroll -= sa;
616 field->cursor_xpos += sa;
617 flen = field->cols;
618 if (field->start_char > 0)
619 flen--;
620 if (field->cursor_xpos > flen)
621 field->cursor_xpos = flen;
622 }
623
624 /*
625 * Find the different pages in the form fields and assign the form
626 * page_starts array with the information to find them.
627 */
628 int
629 _formi_find_pages(FORM *form)
630 {
631 int i, cur_page = 0;
632
633 if ((form->page_starts = (_FORMI_PAGE_START *)
634 malloc((form->max_page + 1) * sizeof(_FORMI_PAGE_START))) == NULL)
635 return E_SYSTEM_ERROR;
636
637 /* initialise the page starts array */
638 memset(form->page_starts, 0,
639 (form->max_page + 1) * sizeof(_FORMI_PAGE_START));
640
641 for (i =0; i < form->field_count; i++) {
642 if (form->fields[i]->page_break == 1)
643 cur_page++;
644 if (form->page_starts[cur_page].in_use == 0) {
645 form->page_starts[cur_page].in_use = 1;
646 form->page_starts[cur_page].first = i;
647 form->page_starts[cur_page].last = i;
648 form->page_starts[cur_page].top_left = i;
649 form->page_starts[cur_page].bottom_right = i;
650 } else {
651 form->page_starts[cur_page].last = i;
652 form->page_starts[cur_page].top_left =
653 _formi_top_left(form,
654 form->page_starts[cur_page].top_left,
655 i);
656 form->page_starts[cur_page].bottom_right =
657 _formi_bottom_right(form,
658 form->page_starts[cur_page].bottom_right,
659 i);
660 }
661 }
662
663 return E_OK;
664 }
665
666 /*
667 * Completely redraw the field of the given form.
668 */
669 static void
670 _formi_redraw_field(FORM *form, int field)
671 {
672 unsigned int pre, post, flen, slen, i, row, start, end, offset;
673 char *str;
674 FIELD *cur;
675 #ifdef DEBUG
676 char buffer[100];
677 #endif
678
679 cur = form->fields[field];
680 str = cur->buffers[0].string;
681 flen = cur->cols;
682 slen = 0;
683 start = 0;
684 end = 0;
685
686 wmove(form->subwin, (int) cur->form_row, (int) cur->form_col);
687 for (row = 0; row <= cur->row_count; row++) {
688 if (str == NULL) {
689 start = end = 0;
690 } else {
691 if ((str[end] == '\0') || (str[end + 1] == '\0')
692 || (row == 0))
693 start = end;
694 else
695 start = end + 1;
696 }
697
698 if (cur->buffers[0].length > 0) {
699 end = _formi_find_eol(str, start);
700 slen = end - start + 1;
701 } else
702 slen = 0;
703
704 switch (cur->justification) {
705 case JUSTIFY_RIGHT:
706 post = 0;
707 if (flen < slen)
708 pre = 0;
709 else
710 pre = flen - slen;
711
712 break;
713
714 case JUSTIFY_CENTER:
715 if (flen < slen) {
716 pre = 0;
717 post = 0;
718 } else {
719 pre = flen - slen;
720 post = pre = pre / 2;
721 /* get padding right if centring is not even */
722 if ((post + pre + slen) < flen)
723 post++;
724 }
725 break;
726
727 case NO_JUSTIFICATION:
728 case JUSTIFY_LEFT:
729 default:
730 pre = 0;
731 if (flen <= slen)
732 post = 0;
733 else {
734 post = flen - slen;
735 if (post > flen)
736 post = flen;
737 }
738 break;
739 }
740
741 if (pre > cur->hscroll - start)
742 pre = pre - cur->hscroll + start;
743 else
744 pre = 0;
745
746 if (slen > cur->hscroll) {
747 slen -= cur->hscroll;
748 post += cur->hscroll;
749 if (post > flen)
750 post = flen;
751 } else {
752 slen = 0;
753 post = flen - pre;
754 }
755
756 if (form->cur_field == field)
757 wattrset(form->subwin, cur->fore);
758 else
759 wattrset(form->subwin, cur->back);
760
761 #ifdef DEBUG
762 fprintf(dbg, "redraw_field: start=%d, pre=%d, slen=%d, flen=%d, post=%d, hscroll=%d\n",
763 start, pre, slen, flen, post, cur->hscroll);
764 if (str != NULL) {
765 strncpy(buffer, &str[cur->start_char], flen);
766 } else {
767 strcpy(buffer, "(null)");
768 }
769 buffer[flen] = '\0';
770 fprintf(dbg, "redraw_field: %s\n", buffer);
771 #endif
772
773 for (i = start + cur->hscroll; i < pre; i++)
774 waddch(form->subwin, cur->pad);
775
776 offset = cur->hscroll;
777 if (cur->start_char > 0)
778 offset += cur->start_char - 1;
779
780 if (flen > cur->hscroll + 1) {
781 if (flen > slen)
782 flen -= cur->hscroll + 1;
783 } else
784 flen = 0;
785
786 #ifdef DEBUG
787 fprintf(dbg, "redraw_field: will add %d chars, offset is %d\n",
788 min(slen, flen), offset);
789 #endif
790 for (i = 0;
791 i < min(slen, flen); i++)
792 {
793 #ifdef DEBUG
794 fprintf(dbg, "adding char str[%d]=%c\n",
795 i + offset, str[i + offset]);
796 #endif
797 waddch(form->subwin,
798 ((cur->opts & O_PUBLIC) == O_PUBLIC)?
799 str[i + offset] : cur->pad);
800 }
801
802 for (i = 0; i < post; i++)
803 waddch(form->subwin, cur->pad);
804 }
805
806 return;
807 }
808
809 /*
810 * Display the fields attached to the form that are on the current page
811 * on the screen.
812 *
813 */
814 int
815 _formi_draw_page(FORM *form)
816 {
817 int i;
818
819 if (form->page_starts[form->page].in_use == 0)
820 return E_BAD_ARGUMENT;
821
822 wclear(form->subwin);
823
824 for (i = form->page_starts[form->page].first;
825 i <= form->page_starts[form->page].last; i++)
826 _formi_redraw_field(form, i);
827
828 return E_OK;
829 }
830
831 /*
832 * Add the character c at the position pos in buffer 0 of the given field
833 */
834 int
835 _formi_add_char(FIELD *field, unsigned int pos, char c)
836 {
837 char *new;
838 unsigned int new_size;
839 int status;
840
841 /*
842 * If buffer has not had a string before, set it to a blank
843 * string. Everything should flow from there....
844 */
845 if (field->buffers[0].string == NULL) {
846 set_field_buffer(field, 0, "");
847 }
848
849 if (_formi_validate_char(field, c) != E_OK) {
850 #ifdef DEBUG
851 fprintf(dbg, "add_char: char %c failed char validation\n", c);
852 #endif
853 return E_INVALID_FIELD;
854 }
855
856 #ifdef DEBUG
857 fprintf(dbg, "add_char: pos=%d, char=%c\n", pos, c);
858 fprintf(dbg,
859 "add_char enter: xpos=%d, start=%d, length=%d(%d), allocated=%d\n",
860 field->cursor_xpos, field->start_char,
861 field->buffers[0].length, strlen(field->buffers[0].string),
862 field->buffers[0].allocated);
863 fprintf(dbg, "add_char enter: %s\n", field->buffers[0].string);
864 #endif
865 if (((field->opts & O_BLANK) == O_BLANK) &&
866 (field->buf0_status == FALSE)) {
867 field->buffers[0].length = 0;
868 field->buffers[0].string[0] = '\0';
869 pos = 0;
870 field->start_char = 0;
871 field->start_line = 0;
872 field->hscroll = 0;
873 field->row_count = 0;
874 field->cursor_xpos = 0;
875 field->cursor_ypos = 0;
876 }
877
878
879 if ((field->overlay == 0)
880 || ((field->overlay == 1) && (pos >= field->buffers[0].length))) {
881 if (field->buffers[0].length + 1
882 >= field->buffers[0].allocated) {
883 new_size = field->buffers[0].allocated + 64
884 - (field->buffers[0].allocated % 64);
885 if ((new = (char *) realloc(field->buffers[0].string,
886 new_size )) == NULL)
887 return E_SYSTEM_ERROR;
888 field->buffers[0].allocated = new_size;
889 field->buffers[0].string = new;
890 }
891 }
892
893 if ((field->overlay == 0) && (field->buffers[0].length > pos)) {
894 bcopy(&field->buffers[0].string[pos],
895 &field->buffers[0].string[pos + 1],
896 field->buffers[0].length - pos + 1);
897 }
898
899 field->buffers[0].string[pos] = c;
900 if (pos >= field->buffers[0].length) {
901 /* make sure the string is terminated if we are at the
902 * end of the string, the terminator would be missing
903 * if we are are at the end of the field.
904 */
905 field->buffers[0].string[pos + 1] = '\0';
906 }
907
908 /* only increment the length if we are inserting characters
909 * OR if we are at the end of the field in overlay mode.
910 */
911 if ((field->overlay == 0)
912 || ((field->overlay == 1) && (pos >= field->buffers[0].length)))
913 field->buffers[0].length++;
914
915 /* wrap the field, if needed */
916 status = _formi_wrap_field(field, pos);
917 if (status != E_OK) {
918 /* wrap failed for some reason, back out the char insert */
919 bcopy(&field->buffers[0].string[pos + 1],
920 &field->buffers[0].string[pos],
921 field->buffers[0].length - pos);
922 field->buffers[0].length--;
923 } else {
924 field->buf0_status = TRUE;
925 field->cursor_xpos++;
926 if (field->cursor_xpos > field->cols - 1) {
927 field->start_char++;
928 field->cursor_xpos = field->cols - 1;
929 }
930 }
931
932 #ifdef DEBUG
933 fprintf(dbg,
934 "add_char exit: xpos=%d, start=%d, length=%d(%d), allocated=%d\n",
935 field->cursor_xpos, field->start_char,
936 field->buffers[0].length, strlen(field->buffers[0].string),
937 field->buffers[0].allocated);
938 fprintf(dbg,"add_char exit: %s\n", field->buffers[0].string);
939 fprintf(dbg, "add_char exit: status = %s\n",
940 (status == E_OK)? "OK" : "FAILED");
941 #endif
942 return (status == E_OK);
943 }
944
945 /*
946 * Manipulate the text in a field, this takes the given form and performs
947 * the passed driver command on the current text field. Returns 1 if the
948 * text field was modified.
949 */
950 int
951 _formi_manipulate_field(FORM *form, int c)
952 {
953 FIELD *cur;
954 char *str;
955 unsigned int i, start, end, pos;
956
957 cur = form->fields[form->cur_field];
958
959 #ifdef DEBUG
960 fprintf(dbg,
961 "entry: xpos=%d, start_char=%d, length=%d, allocated=%d\n",
962 cur->cursor_xpos, cur->start_char, cur->buffers[0].length,
963 cur->buffers[0].allocated);
964 fprintf(dbg, "entry: string=");
965 if (cur->buffers[0].string == NULL)
966 fprintf(dbg, "(null)\n");
967 else
968 fprintf(dbg, "\"%s\"\n", cur->buffers[0].string);
969 #endif
970
971 /* Cannot manipulate a null string! */
972 if (cur->buffers[0].string == NULL)
973 return E_REQUEST_DENIED;
974
975 switch (c) {
976 case REQ_NEXT_CHAR:
977 if ((cur->cursor_xpos + cur->start_char
978 - ((cur->start_char > 0)? 1 : 0) + cur->hscroll + 1)
979 > cur->buffers[0].length) {
980 return E_REQUEST_DENIED;
981 }
982 cur->cursor_xpos++;
983 if (cur->cursor_xpos >= cur->cols - cur->hscroll - 1) {
984 if (cur->cols < (cur->hscroll + 1))
985 cur->cursor_xpos = 0;
986 else
987 cur->cursor_xpos = cur->cols
988 - cur->hscroll - 1;
989 cur->start_char++;
990 }
991 break;
992
993 case REQ_PREV_CHAR:
994 if (cur->cursor_xpos == 0) {
995 if (cur->start_char > 0)
996 cur->start_char--;
997 else if (cur->hscroll > 0)
998 cur->hscroll--;
999 else
1000 return E_REQUEST_DENIED;
1001 } else
1002 cur->cursor_xpos--;
1003 break;
1004
1005 case REQ_NEXT_LINE:
1006 cur->cursor_ypos++;
1007 if (cur->cursor_ypos > cur->rows) {
1008 if ((cur->opts & O_STATIC) == O_STATIC) {
1009 if (cur->start_line + cur->cursor_ypos
1010 > cur->drows) {
1011 cur->cursor_ypos--;
1012 return E_REQUEST_DENIED;
1013 }
1014 } else {
1015 if (cur->start_line + cur->cursor_ypos
1016 > cur->nrows + cur->rows) {
1017 cur->cursor_ypos--;
1018 return E_REQUEST_DENIED;
1019 }
1020 }
1021 cur->start_line++;
1022 }
1023 break;
1024
1025 case REQ_PREV_LINE:
1026 if (cur->cursor_ypos == 0) {
1027 if (cur->start_line == 0)
1028 return E_REQUEST_DENIED;
1029 cur->start_line--;
1030 } else
1031 cur->cursor_ypos--;
1032 break;
1033
1034 case REQ_NEXT_WORD:
1035 start = cur->start_char + cur->cursor_xpos;
1036 str = cur->buffers[0].string;
1037
1038 start = find_eow(str, start);
1039
1040 /* check if we hit the end */
1041 if (str[start] == '\0')
1042 return E_REQUEST_DENIED;
1043
1044 /* otherwise we must have found the start of a word...*/
1045 if (start - cur->start_char < cur->cols) {
1046 cur->cursor_xpos = start;
1047 } else {
1048 cur->start_char = start;
1049 cur->cursor_xpos = 0;
1050 }
1051 break;
1052
1053 case REQ_PREV_WORD:
1054 start = cur->start_char + cur->cursor_xpos;
1055 if (cur->start_char > 0)
1056 start--;
1057
1058 if (start == 0)
1059 return E_REQUEST_DENIED;
1060
1061 str = cur->buffers[0].string;
1062
1063 start = find_sow(str, start);
1064
1065 if (start - cur->start_char > 0) {
1066 cur->cursor_xpos = start;
1067 } else {
1068 cur->start_char = start;
1069 cur->cursor_xpos = 0;
1070 }
1071 break;
1072
1073 case REQ_BEG_FIELD:
1074 cur->start_char = 0;
1075 cur->start_line = 0;
1076 cur->cursor_xpos = 0;
1077 cur->cursor_ypos = 0;
1078 break;
1079
1080 case REQ_END_FIELD:
1081 if (cur->row_count > cur->rows) {
1082 cur->start_line = cur->row_count - cur->rows;
1083 cur->cursor_ypos = cur->rows - 1;
1084 } else {
1085 cur->start_line = 0;
1086 cur->cursor_ypos = cur->row_count - 1;
1087 }
1088
1089 if ((str = rindex(cur->buffers[0].string, '\n')) == NULL) {
1090 cur->cursor_xpos = cur->cols - 1;
1091 if (cur->start_char < (cur->buffers[0].length +
1092 cur->cols)) {
1093 cur->start_char = 0;
1094 cur->cursor_xpos = cur->buffers[0].length;
1095 } else {
1096 cur->start_char = cur->buffers[0].length -
1097 cur->cols;
1098 }
1099 } else {
1100 cur->start_char = (str - cur->buffers[0].string);
1101 if (strlen(str) > cur->cols)
1102 cur->cursor_xpos = cur->cols;
1103 else
1104 cur->cursor_xpos = strlen(str);
1105 }
1106 break;
1107
1108 case REQ_BEG_LINE:
1109 start = cur->start_char + cur->cursor_xpos;
1110 if (cur->buffers[0].string[start] == '\n') {
1111 if (start > 0)
1112 start--;
1113 else
1114 return E_REQUEST_DENIED;
1115 }
1116
1117 while ((start > 0)
1118 && (cur->buffers[0].string[start] != '\n'))
1119 start--;
1120
1121 if (start > 0)
1122 start++;
1123
1124 cur->start_char = start;
1125 cur->cursor_xpos = 0;
1126 break;
1127
1128 case REQ_END_LINE:
1129 start = cur->start_char + cur->cursor_xpos;
1130 end = _formi_find_eol(cur->buffers[0].string, start);
1131 start = _formi_find_bol(cur->buffers[0].string, start);
1132
1133 if (end - start > cur->cols - 1) {
1134 cur->cursor_xpos = cur->cols - 1;
1135 cur->start_char = end - cur->cols + 3;
1136 } else {
1137 cur->cursor_xpos = end - start + 1;
1138 cur->start_char = start;
1139 }
1140 break;
1141
1142 case REQ_LEFT_CHAR:
1143 if ((cur->cursor_xpos == 0) && (cur->start_char == 0))
1144 return E_REQUEST_DENIED;
1145
1146 if (cur->cursor_xpos == 0) {
1147 cur->start_char--;
1148 if (cur->buffers[0].string[cur->start_char] == '\n') {
1149 if ((cur->cursor_ypos == 0) &&
1150 (cur->start_line == 0))
1151 {
1152 cur->start_char++;
1153 return E_REQUEST_DENIED;
1154 }
1155
1156 if (cur->cursor_ypos == 0)
1157 cur->start_line--;
1158 else
1159 cur->cursor_ypos--;
1160
1161 end = _formi_find_eol(cur->buffers[0].string,
1162 cur->start_char);
1163 start = _formi_find_bol(cur->buffers[0].string,
1164 cur->start_char);
1165 if (end - start >= cur->cols) {
1166 cur->cursor_xpos = cur->cols - 1;
1167 cur->start_char = end - cur->cols;
1168 } else {
1169 cur->cursor_xpos = end - start;
1170 cur->start_char = start;
1171 }
1172 }
1173 } else
1174 cur->cursor_xpos--;
1175 break;
1176
1177 case REQ_RIGHT_CHAR:
1178 pos = cur->start_char + cur->cursor_xpos;
1179 if (cur->buffers[0].string[pos] == '\0')
1180 return E_REQUEST_DENIED;
1181
1182 #ifdef DEBUG
1183 fprintf(dbg, "req_right_char enter: start=%d, xpos=%d, c=%c\n",
1184 cur->start_char, cur->cursor_xpos,
1185 cur->buffers[0].string[pos]);
1186 #endif
1187
1188 if (cur->buffers[0].string[pos] == '\n') {
1189 start = pos + 1;
1190 if (cur->buffers[0].string[start] == 0)
1191 return E_REQUEST_DENIED;
1192 end = _formi_find_eol(cur->buffers[0].string, start);
1193 if (end - start > cur->cols) {
1194 cur->cursor_xpos = cur->cols - 1;
1195 cur->start_char = end - cur->cols - 1;
1196 } else {
1197 cur->cursor_xpos = end - start;
1198 cur->start_char = start;
1199 }
1200 } else {
1201 if (cur->cursor_xpos == cur->cols - 1)
1202 cur->start_char++;
1203 else
1204 cur->cursor_xpos++;
1205 }
1206 #ifdef DEBUG
1207 fprintf(dbg, "req_right_char exit: start=%d, xpos=%d, c=%c\n",
1208 cur->start_char, cur->cursor_xpos,
1209 cur->buffers[0].string[cur->start_char +
1210 cur->cursor_xpos]);
1211 #endif
1212 break;
1213
1214 case REQ_UP_CHAR:
1215 if (cur->cursor_ypos == 0) {
1216 if (cur->start_line == 0)
1217 return E_REQUEST_DENIED;
1218
1219 cur->start_line--;
1220 } else
1221 cur->cursor_ypos--;
1222
1223 start = find_cur_line(cur);
1224 end = _formi_find_eol(cur->buffers[0].string, start);
1225 cur->start_char = start;
1226 if (cur->cursor_xpos > end - start)
1227 cur->cursor_xpos = end - start;
1228 break;
1229
1230 case REQ_DOWN_CHAR:
1231 if (cur->cursor_ypos == cur->rows - 1) {
1232 if (cur->start_line + cur->rows == cur->row_count)
1233 return E_REQUEST_DENIED;
1234 cur->start_line++;
1235 } else
1236 cur->cursor_ypos++;
1237
1238 start = find_cur_line(cur);
1239 end = _formi_find_eol(cur->buffers[0].string, start);
1240 cur->start_char = start;
1241 if (cur->cursor_xpos > end - start)
1242 cur->cursor_xpos = end - start;
1243 break;
1244
1245 case REQ_NEW_LINE:
1246 _formi_add_char(cur, cur->start_char + cur->cursor_xpos, '\n');
1247 cur->row_count++;
1248 break;
1249
1250 case REQ_INS_CHAR:
1251 _formi_add_char(cur, cur->start_char + cur->cursor_xpos,
1252 cur->pad);
1253 break;
1254
1255 case REQ_INS_LINE:
1256 start = _formi_find_bol(cur->buffers[0].string, cur->start_char);
1257 _formi_add_char(cur, start, '\n');
1258 cur->row_count++;
1259 break;
1260
1261 case REQ_DEL_CHAR:
1262 if (cur->buffers[0].string[cur->start_char + cur->cursor_xpos]
1263 == '\0')
1264 return E_REQUEST_DENIED;
1265
1266 start = cur->start_char + cur->cursor_xpos;
1267 end = cur->buffers[0].length;
1268 if (cur->buffers[0].string[start] == '\n') {
1269 if (cur->row_count > 0) {
1270 cur->row_count--;
1271 _formi_join_line(cur, cur->buffers[0].string,
1272 start, JOIN_NEXT);
1273 } else
1274 cur->buffers[0].string[start] = '\0';
1275 } else {
1276 bcopy(&cur->buffers[0].string[start + 1],
1277 &cur->buffers[0].string[start],
1278 (unsigned) end - start + 1);
1279 }
1280
1281 cur->buffers[0].length--;
1282 break;
1283
1284 case REQ_DEL_PREV:
1285 if ((cur->cursor_xpos == 0) && (cur->start_char == 0))
1286 return E_REQUEST_DENIED;
1287
1288 start = cur->cursor_xpos + cur->start_char;
1289 end = cur->buffers[0].length;
1290
1291 if (cur->buffers[0].string[cur->start_char + cur->cursor_xpos] == '\n') {
1292 _formi_join_line(cur, cur->buffers[0].string,
1293 cur->start_char + cur->cursor_xpos,
1294 JOIN_PREV);
1295 cur->row_count--;
1296 } else {
1297 bcopy(&cur->buffers[0].string[start],
1298 &cur->buffers[0].string[start - 1],
1299 (unsigned) end - start + 1);
1300 }
1301
1302 cur->buffers[0].length--;
1303 if ((cur->cursor_xpos == 0) && (cur->start_char > 0))
1304 cur->start_char--;
1305 else if ((cur->cursor_xpos == cur->cols - 1)
1306 && (cur->start_char > 0))
1307 cur->start_char--;
1308 else if (cur->cursor_xpos > 0)
1309 cur->cursor_xpos--;
1310
1311 break;
1312
1313 case REQ_DEL_LINE:
1314 start = cur->start_char + cur->cursor_xpos;
1315 end = _formi_find_eol(cur->buffers[0].string, start);
1316 start = _formi_find_bol(cur->buffers[0].string, start);
1317 bcopy(&cur->buffers[0].string[end + 1],
1318 &cur->buffers[0].string[start],
1319 (unsigned) cur->buffers[0].length - end + 1);
1320 if (cur->row_count > 0)
1321 cur->row_count--;
1322 break;
1323
1324 case REQ_DEL_WORD:
1325 start = cur->start_char + cur->cursor_xpos;
1326 end = find_eow(cur->buffers[0].string, start);
1327 start = find_sow(cur->buffers[0].string, start);
1328 bcopy(&cur->buffers[0].string[end + 1],
1329 &cur->buffers[0].string[start],
1330 (unsigned) cur->buffers[0].length - end + 1);
1331 cur->buffers[0].length -= end - start;
1332 break;
1333
1334 case REQ_CLR_EOL:
1335 /*XXXX this right or should we just toast the chars? */
1336 start = cur->start_char + cur->cursor_xpos;
1337 end = _formi_find_eol(cur->buffers[0].string, start);
1338 for (i = start; i < end; i++)
1339 cur->buffers[0].string[i] = cur->pad;
1340 break;
1341
1342 case REQ_CLR_EOF:
1343 for (i = cur->start_char + cur->cursor_xpos;
1344 i < cur->buffers[0].length; i++)
1345 cur->buffers[0].string[i] = cur->pad;
1346 break;
1347
1348 case REQ_CLR_FIELD:
1349 for (i = 0; i < cur->buffers[0].length; i++)
1350 cur->buffers[0].string[i] = cur->pad;
1351 break;
1352
1353 case REQ_OVL_MODE:
1354 cur->overlay = 1;
1355 break;
1356
1357 case REQ_INS_MODE:
1358 cur->overlay = 0;
1359 break;
1360
1361 case REQ_SCR_FLINE:
1362 _formi_scroll_fwd(cur, 1);
1363 break;
1364
1365 case REQ_SCR_BLINE:
1366 _formi_scroll_back(cur, 1);
1367 break;
1368
1369 case REQ_SCR_FPAGE:
1370 _formi_scroll_fwd(cur, cur->rows);
1371 break;
1372
1373 case REQ_SCR_BPAGE:
1374 _formi_scroll_back(cur, cur->rows);
1375 break;
1376
1377 case REQ_SCR_FHPAGE:
1378 _formi_scroll_fwd(cur, cur->rows / 2);
1379 break;
1380
1381 case REQ_SCR_BHPAGE:
1382 _formi_scroll_back(cur, cur->rows / 2);
1383 break;
1384
1385 case REQ_SCR_FCHAR:
1386 _formi_hscroll_fwd(cur, 1);
1387 break;
1388
1389 case REQ_SCR_BCHAR:
1390 _formi_hscroll_back(cur, 1);
1391 break;
1392
1393 case REQ_SCR_HFLINE:
1394 _formi_hscroll_fwd(cur, cur->cols);
1395 break;
1396
1397 case REQ_SCR_HBLINE:
1398 _formi_hscroll_back(cur, cur->cols);
1399 break;
1400
1401 case REQ_SCR_HFHALF:
1402 _formi_hscroll_fwd(cur, cur->cols / 2);
1403 break;
1404
1405 case REQ_SCR_HBHALF:
1406 _formi_hscroll_back(cur, cur->cols / 2);
1407 break;
1408
1409 default:
1410 return 0;
1411 }
1412
1413 #ifdef DEBUG
1414 fprintf(dbg, "exit: xpos=%d, start_char=%d, length=%d, allocated=%d\n",
1415 cur->cursor_xpos, cur->start_char, cur->buffers[0].length,
1416 cur->buffers[0].allocated);
1417 fprintf(dbg, "exit: string=\"%s\"\n", cur->buffers[0].string);
1418 #endif
1419 return 1;
1420 }
1421
1422 /*
1423 * Validate the give character by passing it to any type character
1424 * checking routines, if they exist.
1425 */
1426 int
1427 _formi_validate_char(FIELD *field, char c)
1428 {
1429 int ret_val;
1430
1431 if (field->type == NULL)
1432 return E_OK;
1433
1434 ret_val = E_INVALID_FIELD;
1435 _formi_do_char_validation(field, field->type, c, &ret_val);
1436
1437 return ret_val;
1438 }
1439
1440
1441 /*
1442 * Perform the validation of the character, invoke all field_type validation
1443 * routines. If the field is ok then update ret_val to E_OK otherwise
1444 * ret_val is not changed.
1445 */
1446 static void
1447 _formi_do_char_validation(FIELD *field, FIELDTYPE *type, char c, int *ret_val)
1448 {
1449 if ((type->flags & _TYPE_IS_LINKED) == _TYPE_IS_LINKED) {
1450 _formi_do_char_validation(field, type->link->next, c, ret_val);
1451 _formi_do_char_validation(field, type->link->prev, c, ret_val);
1452 } else {
1453 if (type->char_check == NULL)
1454 *ret_val = E_OK;
1455 else {
1456 if (type->char_check((int)(unsigned char) c,
1457 field->args) == TRUE)
1458 *ret_val = E_OK;
1459 }
1460 }
1461 }
1462
1463 /*
1464 * Validate the current field. If the field validation returns success then
1465 * return E_OK otherwise return E_INVALID_FIELD.
1466 *
1467 */
1468 int
1469 _formi_validate_field(FORM *form)
1470 {
1471 FIELD *cur;
1472 char *bp;
1473 int ret_val, count;
1474
1475
1476 if ((form == NULL) || (form->fields == NULL) ||
1477 (form->fields[0] == NULL))
1478 return E_INVALID_FIELD;
1479
1480 cur = form->fields[form->cur_field];
1481
1482 if (((cur->opts & O_PASSOK) == O_PASSOK) && (cur->buf0_status = 0))
1483 return E_OK;
1484
1485 bp = cur->buffers[0].string;
1486 count = _formi_skip_blanks(bp, 0);
1487
1488 if (((cur->opts & O_NULLOK) != O_NULLOK) &&
1489 (cur->buffers[0].string[count] == '\0'))
1490 return E_INVALID_FIELD;
1491
1492 /* if there is no type then just accept the field */
1493 if (cur->type == NULL)
1494 return E_OK;
1495
1496 ret_val = E_INVALID_FIELD;
1497 _formi_do_validation(cur, cur->type, &ret_val);
1498
1499 return ret_val;
1500 }
1501
1502 /*
1503 * Perform the validation of the field, invoke all field_type validation
1504 * routines. If the field is ok then update ret_val to E_OK otherwise
1505 * ret_val is not changed.
1506 */
1507 static void
1508 _formi_do_validation(FIELD *field, FIELDTYPE *type, int *ret_val)
1509 {
1510 if ((type->flags & _TYPE_IS_LINKED) == _TYPE_IS_LINKED) {
1511 _formi_do_validation(field, type->link->next, ret_val);
1512 _formi_do_validation(field, type->link->prev, ret_val);
1513 } else {
1514 if (type->field_check == NULL)
1515 *ret_val = E_OK;
1516 else {
1517 if (type->field_check(field, field_buffer(field, 0))
1518 == TRUE)
1519 *ret_val = E_OK;
1520 }
1521 }
1522 }
1523
1524 /*
1525 * Select the next/previous choice for the field, the driver command
1526 * selecting the direction will be passed in c. Return 1 if a choice
1527 * selection succeeded, 0 otherwise.
1528 */
1529 int
1530 _formi_field_choice(FORM *form, int c)
1531 {
1532 FIELDTYPE *type;
1533 FIELD *field;
1534
1535 if ((form == NULL) || (form->fields == NULL) ||
1536 (form->fields[0] == NULL) ||
1537 (form->fields[form->cur_field]->type == NULL))
1538 return 0;
1539
1540 field = form->fields[form->cur_field];
1541 type = field->type;
1542
1543 switch (c) {
1544 case REQ_NEXT_CHOICE:
1545 if (type->next_choice == NULL)
1546 return 0;
1547 else
1548 return type->next_choice(field,
1549 field_buffer(field, 0));
1550
1551 case REQ_PREV_CHOICE:
1552 if (type->prev_choice == NULL)
1553 return 0;
1554 else
1555 return type->prev_choice(field,
1556 field_buffer(field, 0));
1557
1558 default: /* should never happen! */
1559 return 0;
1560 }
1561 }
1562
1563 /*
1564 * Update the fields if they have changed. The parameter old has the
1565 * previous current field as the current field may have been updated by
1566 * the driver. Return 1 if the form page needs updating.
1567 *
1568 */
1569 int
1570 _formi_update_field(FORM *form, int old_field)
1571 {
1572 int cur, i;
1573
1574 cur = form->cur_field;
1575
1576 if (old_field != cur) {
1577 if (!((cur >= form->page_starts[form->page].first) &&
1578 (cur <= form->page_starts[form->page].last))) {
1579 /* not on same page any more */
1580 for (i = 0; i < form->max_page; i++) {
1581 if ((form->page_starts[i].in_use == 1) &&
1582 (form->page_starts[i].first <= cur) &&
1583 (form->page_starts[i].last >= cur)) {
1584 form->page = i;
1585 return 1;
1586 }
1587 }
1588 }
1589 }
1590
1591 _formi_redraw_field(form, old_field);
1592 _formi_redraw_field(form, form->cur_field);
1593 return 0;
1594 }
1595
1596 /*
1597 * Compare function for the field sorting
1598 *
1599 */
1600 static int
1601 field_sort_compare(const void *one, const void *two)
1602 {
1603 const FIELD *a, *b;
1604 int tl;
1605
1606 /* LINTED const castaway; we don't modify these! */
1607 a = (const FIELD *) *((const FIELD **) one);
1608 b = (const FIELD *) *((const FIELD **) two);
1609
1610 if (a == NULL)
1611 return 1;
1612
1613 if (b == NULL)
1614 return -1;
1615
1616 /*
1617 * First check the page, we want the fields sorted by page.
1618 *
1619 */
1620 if (a->page != b->page)
1621 return ((a->page > b->page)? 1 : -1);
1622
1623 tl = _formi_top_left(a->parent, a->index, b->index);
1624
1625 /*
1626 * sort fields left to right, top to bottom so the top left is
1627 * the less than value....
1628 */
1629 return ((tl == a->index)? -1 : 1);
1630 }
1631
1632 /*
1633 * Sort the fields in a form ready for driver traversal.
1634 */
1635 void
1636 _formi_sort_fields(FORM *form)
1637 {
1638 FIELD **sort_area;
1639 int i;
1640
1641 CIRCLEQ_INIT(&form->sorted_fields);
1642
1643 if ((sort_area = (FIELD **) malloc(sizeof(FIELD *) * form->field_count))
1644 == NULL)
1645 return;
1646
1647 bcopy(form->fields, sort_area, sizeof(FIELD *) * form->field_count);
1648 qsort(sort_area, (unsigned) form->field_count, sizeof(FIELD *),
1649 field_sort_compare);
1650
1651 for (i = 0; i < form->field_count; i++)
1652 CIRCLEQ_INSERT_TAIL(&form->sorted_fields, sort_area[i], glue);
1653
1654 free(sort_area);
1655 }
1656
1657 /*
1658 * Set the neighbours for all the fields in the given form.
1659 */
1660 void
1661 _formi_stitch_fields(FORM *form)
1662 {
1663 int above_row, below_row, end_above, end_below, cur_row, real_end;
1664 FIELD *cur, *above, *below;
1665
1666 /*
1667 * check if the sorted fields circle queue is empty, just
1668 * return if it is.
1669 */
1670 if (CIRCLEQ_EMPTY(&form->sorted_fields))
1671 return;
1672
1673 /* initially nothing is above..... */
1674 above_row = -1;
1675 end_above = TRUE;
1676 above = NULL;
1677
1678 /* set up the first field as the current... */
1679 cur = CIRCLEQ_FIRST(&form->sorted_fields);
1680 cur_row = cur->form_row;
1681
1682 /* find the first field on the next row if any */
1683 below = CIRCLEQ_NEXT(cur, glue);
1684 below_row = -1;
1685 end_below = TRUE;
1686 real_end = TRUE;
1687 while (below != (void *)&form->sorted_fields) {
1688 if (below->form_row != cur_row) {
1689 below_row = below->form_row;
1690 end_below = FALSE;
1691 real_end = FALSE;
1692 break;
1693 }
1694 below = CIRCLEQ_NEXT(below, glue);
1695 }
1696
1697 /* walk the sorted fields, setting the neighbour pointers */
1698 while (cur != (void *) &form->sorted_fields) {
1699 if (cur == CIRCLEQ_FIRST(&form->sorted_fields))
1700 cur->left = NULL;
1701 else
1702 cur->left = CIRCLEQ_PREV(cur, glue);
1703
1704 if (cur == CIRCLEQ_LAST(&form->sorted_fields))
1705 cur->right = NULL;
1706 else
1707 cur->right = CIRCLEQ_NEXT(cur, glue);
1708
1709 if (end_above == TRUE)
1710 cur->up = NULL;
1711 else {
1712 cur->up = above;
1713 above = CIRCLEQ_NEXT(above, glue);
1714 if (above_row != above->form_row) {
1715 end_above = TRUE;
1716 above_row = above->form_row;
1717 }
1718 }
1719
1720 if (end_below == TRUE)
1721 cur->down = NULL;
1722 else {
1723 cur->down = below;
1724 below = CIRCLEQ_NEXT(below, glue);
1725 if (below == (void *) &form->sorted_fields) {
1726 end_below = TRUE;
1727 real_end = TRUE;
1728 } else if (below_row != below->form_row) {
1729 end_below = TRUE;
1730 below_row = below->form_row;
1731 }
1732 }
1733
1734 cur = CIRCLEQ_NEXT(cur, glue);
1735 if ((cur != (void *) &form->sorted_fields)
1736 && (cur_row != cur->form_row)) {
1737 cur_row = cur->form_row;
1738 if (end_above == FALSE) {
1739 for (; above != CIRCLEQ_FIRST(&form->sorted_fields);
1740 above = CIRCLEQ_NEXT(above, glue)) {
1741 if (above->form_row != above_row) {
1742 above_row = above->form_row;
1743 break;
1744 }
1745 }
1746 } else if (above == NULL) {
1747 above = CIRCLEQ_FIRST(&form->sorted_fields);
1748 end_above = FALSE;
1749 above_row = above->form_row;
1750 } else
1751 end_above = FALSE;
1752
1753 if (end_below == FALSE) {
1754 while (below_row == below->form_row) {
1755 below = CIRCLEQ_NEXT(below,
1756 glue);
1757 if (below ==
1758 (void *)&form->sorted_fields) {
1759 real_end = TRUE;
1760 end_below = TRUE;
1761 break;
1762 }
1763 }
1764
1765 if (below != (void *)&form->sorted_fields)
1766 below_row = below->form_row;
1767 } else if (real_end == FALSE)
1768 end_below = FALSE;
1769
1770 }
1771 }
1772 }
1773