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