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