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