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