1 /**************************************************************************** 2 * Copyright (c) 1998-2006,2007 Free Software Foundation, Inc. * 3 * * 4 * Permission is hereby granted, free of charge, to any person obtaining a * 5 * copy of this software and associated documentation files (the * 6 * "Software"), to deal in the Software without restriction, including * 7 * without limitation the rights to use, copy, modify, merge, publish, * 8 * distribute, distribute with modifications, sublicense, and/or sell * 9 * copies of the Software, and to permit persons to whom the Software is * 10 * furnished to do so, subject to the following conditions: * 11 * * 12 * The above copyright notice and this permission notice shall be included * 13 * in all copies or substantial portions of the Software. * 14 * * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 22 * * 23 * Except as contained in this notice, the name(s) of the above copyright * 24 * holders shall not be used in advertising or otherwise to promote the * 25 * sale, use or other dealings in this Software without prior written * 26 * authorization. * 27 ****************************************************************************/ 28 29 /**************************************************************************** 30 * Author: Juergen Pfeifer, 1995,1997 * 31 ****************************************************************************/ 32 33 #include "form.priv.h" 34 35 MODULE_ID("$Id: frm_driver.c,v 1.85 2007/11/24 21:32:53 tom Exp $") 36 37 /*---------------------------------------------------------------------------- 38 This is the core module of the form library. It contains the majority 39 of the driver routines as well as the form_driver function. 40 41 Essentially this module is nearly the whole library. This is because 42 all the functions in this module depends on some others in the module, 43 so it makes no sense to split them into separate files because they 44 will always be linked together. The only acceptable concern is turnaround 45 time for this module, but now we have all Pentiums or RISCs, so what! 46 47 The driver routines are grouped into nine generic categories: 48 49 a) Page Navigation ( all functions prefixed by PN_ ) 50 The current page of the form is left and some new page is 51 entered. 52 b) Inter-Field Navigation ( all functions prefixed by FN_ ) 53 The current field of the form is left and some new field is 54 entered. 55 c) Intra-Field Navigation ( all functions prefixed by IFN_ ) 56 The current position in the current field is changed. 57 d) Vertical Scrolling ( all functions prefixed by VSC_ ) 58 Essentially this is a specialization of Intra-Field navigation. 59 It has to check for a multi-line field. 60 e) Horizontal Scrolling ( all functions prefixed by HSC_ ) 61 Essentially this is a specialization of Intra-Field navigation. 62 It has to check for a single-line field. 63 f) Field Editing ( all functions prefixed by FE_ ) 64 The content of the current field is changed 65 g) Edit Mode requests ( all functions prefixed by EM_ ) 66 Switching between insert and overlay mode 67 h) Field-Validation requests ( all functions prefixed by FV_ ) 68 Perform verifications of the field. 69 i) Choice requests ( all functions prefixed by CR_ ) 70 Requests to enumerate possible field values 71 --------------------------------------------------------------------------*/ 72 73 /*---------------------------------------------------------------------------- 74 Some remarks on the placements of assert() macros : 75 I use them only on "strategic" places, i.e. top level entries where 76 I want to make sure that things are set correctly. Throughout subordinate 77 routines I omit them mostly. 78 --------------------------------------------------------------------------*/ 79 80 /* 81 Some options that may effect compatibility in behavior to SVr4 forms, 82 but they are here to allow a more intuitive and user friendly behavior of 83 our form implementation. This doesn't affect the API, so we feel it is 84 uncritical. 85 86 The initial implementation tries to stay very close with the behavior 87 of the original SVr4 implementation, although in some areas it is quite 88 clear that this isn't the most appropriate way. As far as possible this 89 sources will allow you to build a forms lib that behaves quite similar 90 to SVr4, but now and in the future we will give you better options. 91 Perhaps at some time we will make this configurable at runtime. 92 */ 93 94 /* Implement a more user-friendly previous/next word behavior */ 95 #define FRIENDLY_PREV_NEXT_WORD (1) 96 /* Fix the wrong behavior for forms with all fields inactive */ 97 #define FIX_FORM_INACTIVE_BUG (1) 98 /* Allow dynamic field growth also when navigating past the end */ 99 #define GROW_IF_NAVIGATE (1) 100 101 #if USE_WIDEC_SUPPORT 102 #define myADDNSTR(w, s, n) wadd_wchnstr(w, s, n) 103 #define myINSNSTR(w, s, n) wins_wchnstr(w, s, n) 104 #define myINNSTR(w, s, n) fix_wchnstr(w, s, n) 105 #define myWCWIDTH(w, y, x) cell_width(w, y, x) 106 #else 107 #define myADDNSTR(w, s, n) waddnstr(w, s, n) 108 #define myINSNSTR(w, s, n) winsnstr(w, s, n) 109 #define myINNSTR(w, s, n) winnstr(w, s, n) 110 #define myWCWIDTH(w, y, x) 1 111 #endif 112 113 /*---------------------------------------------------------------------------- 114 Forward references to some internally used static functions 115 --------------------------------------------------------------------------*/ 116 static int Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form); 117 static int FN_Next_Field(FORM *form); 118 static int FN_Previous_Field(FORM *form); 119 static int FE_New_Line(FORM *); 120 static int FE_Delete_Previous(FORM *); 121 122 /*---------------------------------------------------------------------------- 123 Macro Definitions. 124 125 Some Remarks on that: I use the convention to use UPPERCASE for constants 126 defined by Macros. If I provide a macro as a kind of inline routine to 127 provide some logic, I use my Upper_Lower case style. 128 --------------------------------------------------------------------------*/ 129 130 /* Calculate the position of a single row in a field buffer */ 131 #define Position_Of_Row_In_Buffer(field,row) ((row)*(field)->dcols) 132 133 /* Calculate start address for the fields buffer# N */ 134 #define Address_Of_Nth_Buffer(field,N) \ 135 ((field)->buf + (N)*(1+Buffer_Length(field))) 136 137 /* Calculate the start address of the row in the fields specified buffer# N */ 138 #define Address_Of_Row_In_Nth_Buffer(field,N,row) \ 139 (Address_Of_Nth_Buffer(field,N) + Position_Of_Row_In_Buffer(field,row)) 140 141 /* Calculate the start address of the row in the fields primary buffer */ 142 #define Address_Of_Row_In_Buffer(field,row) \ 143 Address_Of_Row_In_Nth_Buffer(field,0,row) 144 145 /* Calculate the start address of the row in the forms current field 146 buffer# N */ 147 #define Address_Of_Current_Row_In_Nth_Buffer(form,N) \ 148 Address_Of_Row_In_Nth_Buffer((form)->current,N,(form)->currow) 149 150 /* Calculate the start address of the row in the forms current field 151 primary buffer */ 152 #define Address_Of_Current_Row_In_Buffer(form) \ 153 Address_Of_Current_Row_In_Nth_Buffer(form,0) 154 155 /* Calculate the address of the cursor in the forms current field 156 primary buffer */ 157 #define Address_Of_Current_Position_In_Nth_Buffer(form,N) \ 158 (Address_Of_Current_Row_In_Nth_Buffer(form,N) + (form)->curcol) 159 160 /* Calculate the address of the cursor in the forms current field 161 buffer# N */ 162 #define Address_Of_Current_Position_In_Buffer(form) \ 163 Address_Of_Current_Position_In_Nth_Buffer(form,0) 164 165 /* Logic to decide whether or not a field is actually a field with 166 vertical or horizontal scrolling */ 167 #define Is_Scroll_Field(field) \ 168 (((field)->drows > (field)->rows) || \ 169 ((field)->dcols > (field)->cols)) 170 171 /* Logic to decide whether or not a field needs to have an individual window 172 instead of a derived window because it contains invisible parts. 173 This is true for non-public fields and for scrollable fields. */ 174 #define Has_Invisible_Parts(field) \ 175 (!((field)->opts & O_PUBLIC) || \ 176 Is_Scroll_Field(field)) 177 178 /* Logic to decide whether or not a field needs justification */ 179 #define Justification_Allowed(field) \ 180 (((field)->just != NO_JUSTIFICATION) && \ 181 (Single_Line_Field(field)) && \ 182 (((field)->dcols == (field)->cols) && \ 183 ((field)->opts & O_STATIC)) ) 184 185 /* Logic to determine whether or not a dynamic field may still grow */ 186 #define Growable(field) ((field)->status & _MAY_GROW) 187 188 /* Macro to set the attributes for a fields window */ 189 #define Set_Field_Window_Attributes(field,win) \ 190 ( wbkgdset((win),(chtype)((field)->pad | (field)->back)), \ 191 wattrset((win),(field)->fore) ) 192 193 /* Logic to decide whether or not a field really appears on the form */ 194 #define Field_Really_Appears(field) \ 195 ((field->form) &&\ 196 (field->form->status & _POSTED) &&\ 197 (field->opts & O_VISIBLE) &&\ 198 (field->page == field->form->curpage)) 199 200 /* Logic to determine whether or not we are on the first position in the 201 current field */ 202 #define First_Position_In_Current_Field(form) \ 203 (((form)->currow==0) && ((form)->curcol==0)) 204 205 #define Minimum(a,b) (((a)<=(b)) ? (a) : (b)) 206 #define Maximum(a,b) (((a)>=(b)) ? (a) : (b)) 207 208 /*---------------------------------------------------------------------------- 209 Useful constants 210 --------------------------------------------------------------------------*/ 211 static FIELD_CELL myBLANK = BLANK; 212 static FIELD_CELL myZEROS; 213 214 #ifdef TRACE 215 static void 216 check_pos(FORM *form, int lineno) 217 { 218 int y, x; 219 220 if (form && form->w) 221 { 222 getyx(form->w, y, x); 223 if (y != form->currow || x != form->curcol) 224 { 225 T(("CHECKPOS %s@%d have position %d,%d vs want %d,%d", 226 __FILE__, lineno, 227 y, x, 228 form->currow, form->curcol)); 229 } 230 } 231 } 232 #define CHECKPOS(form) check_pos(form, __LINE__) 233 #else 234 #define CHECKPOS(form) /* nothing */ 235 #endif 236 237 /*---------------------------------------------------------------------------- 238 Wide-character special functions 239 --------------------------------------------------------------------------*/ 240 #if USE_WIDEC_SUPPORT 241 /* like winsnstr */ 242 static int 243 wins_wchnstr(WINDOW *w, cchar_t *s, int n) 244 { 245 int code = ERR; 246 int y, x; 247 248 while (n-- > 0) 249 { 250 getyx(w, y, x); 251 if ((code = wins_wch(w, s++)) != OK) 252 break; 253 if ((code = wmove(w, y, x + 1)) != OK) 254 break; 255 } 256 return code; 257 } 258 259 /* win_wchnstr is inconsistent with winnstr, since it returns OK rather than 260 * the number of items transferred. 261 */ 262 static int 263 fix_wchnstr(WINDOW *w, cchar_t *s, int n) 264 { 265 int x; 266 267 win_wchnstr(w, s, n); 268 /* 269 * This function is used to extract the text only from the window. 270 * Strip attributes and color from the string so they will not be added 271 * back when copying the string to the window. 272 */ 273 for (x = 0; x < n; ++x) 274 { 275 RemAttr(s[x], A_ATTRIBUTES); 276 SetPair(s[x], 0); 277 } 278 return n; 279 } 280 281 /* 282 * Returns the column of the base of the given cell. 283 */ 284 static int 285 cell_base(WINDOW *win, int y, int x) 286 { 287 int result = x; 288 289 while (LEGALYX(win, y, x)) 290 { 291 cchar_t *data = &(win->_line[y].text[x]); 292 293 if (isWidecBase(CHDEREF(data)) || !isWidecExt(CHDEREF(data))) 294 { 295 result = x; 296 break; 297 } 298 --x; 299 } 300 return result; 301 } 302 303 /* 304 * Returns the number of columns needed for the given cell in a window. 305 */ 306 static int 307 cell_width(WINDOW *win, int y, int x) 308 { 309 int result = 1; 310 311 if (LEGALYX(win, y, x)) 312 { 313 cchar_t *data = &(win->_line[y].text[x]); 314 315 if (isWidecExt(CHDEREF(data))) 316 { 317 /* recur, providing the number of columns to the next character */ 318 result = cell_width(win, y, x - 1); 319 } 320 else 321 { 322 result = wcwidth(CharOf(CHDEREF(data))); 323 } 324 } 325 return result; 326 } 327 328 /* 329 * There is no wide-character function such as wdel_wch(), so we must find 330 * all of the cells that comprise a multi-column character and delete them 331 * one-by-one. 332 */ 333 static void 334 delete_char(FORM *form) 335 { 336 int cells = cell_width(form->w, form->currow, form->curcol); 337 338 form->curcol = cell_base(form->w, form->currow, form->curcol); 339 wmove(form->w, form->currow, form->curcol); 340 while (cells-- > 0) 341 { 342 wdelch(form->w); 343 } 344 } 345 #define DeleteChar(form) delete_char(form) 346 #else 347 #define DeleteChar(form) \ 348 wmove((form)->w, (form)->currow, (form)->curcol), \ 349 wdelch((form)->w) 350 #endif 351 352 /*--------------------------------------------------------------------------- 353 | Facility : libnform 354 | Function : static char *Get_Start_Of_Data(char * buf, int blen) 355 | 356 | Description : Return pointer to first non-blank position in buffer. 357 | If buffer is empty return pointer to buffer itself. 358 | 359 | Return Values : Pointer to first non-blank position in buffer 360 +--------------------------------------------------------------------------*/ 361 NCURSES_INLINE static FIELD_CELL * 362 Get_Start_Of_Data(FIELD_CELL *buf, int blen) 363 { 364 FIELD_CELL *p = buf; 365 FIELD_CELL *end = &buf[blen]; 366 367 assert(buf && blen >= 0); 368 while ((p < end) && ISBLANK(*p)) 369 p++; 370 return ((p == end) ? buf : p); 371 } 372 373 /*--------------------------------------------------------------------------- 374 | Facility : libnform 375 | Function : static char *After_End_Of_Data(char * buf, int blen) 376 | 377 | Description : Return pointer after last non-blank position in buffer. 378 | If buffer is empty, return pointer to buffer itself. 379 | 380 | Return Values : Pointer to position after last non-blank position in 381 | buffer. 382 +--------------------------------------------------------------------------*/ 383 NCURSES_INLINE static FIELD_CELL * 384 After_End_Of_Data(FIELD_CELL *buf, int blen) 385 { 386 FIELD_CELL *p = &buf[blen]; 387 388 assert(buf && blen >= 0); 389 while ((p > buf) && ISBLANK(p[-1])) 390 p--; 391 return (p); 392 } 393 394 /*--------------------------------------------------------------------------- 395 | Facility : libnform 396 | Function : static char *Get_First_Whitespace_Character( 397 | char * buf, int blen) 398 | 399 | Description : Position to the first whitespace character. 400 | 401 | Return Values : Pointer to first whitespace character in buffer. 402 +--------------------------------------------------------------------------*/ 403 NCURSES_INLINE static FIELD_CELL * 404 Get_First_Whitespace_Character(FIELD_CELL *buf, int blen) 405 { 406 FIELD_CELL *p = buf; 407 FIELD_CELL *end = &p[blen]; 408 409 assert(buf && blen >= 0); 410 while ((p < end) && !ISBLANK(*p)) 411 p++; 412 return ((p == end) ? buf : p); 413 } 414 415 /*--------------------------------------------------------------------------- 416 | Facility : libnform 417 | Function : static char *After_Last_Whitespace_Character( 418 | char * buf, int blen) 419 | 420 | Description : Get the position after the last whitespace character. 421 | 422 | Return Values : Pointer to position after last whitespace character in 423 | buffer. 424 +--------------------------------------------------------------------------*/ 425 NCURSES_INLINE static FIELD_CELL * 426 After_Last_Whitespace_Character(FIELD_CELL *buf, int blen) 427 { 428 FIELD_CELL *p = &buf[blen]; 429 430 assert(buf && blen >= 0); 431 while ((p > buf) && !ISBLANK(p[-1])) 432 p--; 433 return (p); 434 } 435 436 /* Set this to 1 to use the div_t version. This is a good idea if your 437 compiler has an intrinsic div() support. Unfortunately GNU-C has it 438 not yet. 439 N.B.: This only works if form->curcol follows immediately form->currow 440 and both are of type int. 441 */ 442 #define USE_DIV_T (0) 443 444 /*--------------------------------------------------------------------------- 445 | Facility : libnform 446 | Function : static void Adjust_Cursor_Position( 447 | FORM * form, const char * pos) 448 | 449 | Description : Set current row and column of the form to values 450 | corresponding to the buffer position. 451 | 452 | Return Values : - 453 +--------------------------------------------------------------------------*/ 454 NCURSES_INLINE static void 455 Adjust_Cursor_Position(FORM *form, const FIELD_CELL *pos) 456 { 457 FIELD *field; 458 int idx; 459 460 field = form->current; 461 assert(pos >= field->buf && field->dcols > 0); 462 idx = (int)(pos - field->buf); 463 #if USE_DIV_T 464 *((div_t *) & (form->currow)) = div(idx, field->dcols); 465 #else 466 form->currow = idx / field->dcols; 467 form->curcol = idx - field->cols * form->currow; 468 #endif 469 if (field->drows < form->currow) 470 form->currow = 0; 471 } 472 473 /*--------------------------------------------------------------------------- 474 | Facility : libnform 475 | Function : static void Buffer_To_Window( 476 | const FIELD * field, 477 | WINDOW * win) 478 | 479 | Description : Copy the buffer to the window. If it is a multi-line 480 | field, the buffer is split to the lines of the 481 | window without any editing. 482 | 483 | Return Values : - 484 +--------------------------------------------------------------------------*/ 485 static void 486 Buffer_To_Window(const FIELD *field, WINDOW *win) 487 { 488 int width, height; 489 int y, x; 490 int len; 491 int row; 492 FIELD_CELL *pBuffer; 493 494 assert(win && field); 495 496 getyx(win, y, x); 497 width = getmaxx(win); 498 height = getmaxy(win); 499 500 for (row = 0, pBuffer = field->buf; 501 row < height; 502 row++, pBuffer += width) 503 { 504 if ((len = (int)(After_End_Of_Data(pBuffer, width) - pBuffer)) > 0) 505 { 506 wmove(win, row, 0); 507 myADDNSTR(win, pBuffer, len); 508 } 509 } 510 wmove(win, y, x); 511 } 512 513 /*--------------------------------------------------------------------------- 514 | Facility : libnform 515 | Function : static void Window_To_Buffer( 516 | WINDOW * win, 517 | FIELD * field) 518 | 519 | Description : Copy the content of the window into the buffer. 520 | The multiple lines of a window are simply 521 | concatenated into the buffer. Pad characters in 522 | the window will be replaced by blanks in the buffer. 523 | 524 | Return Values : - 525 +--------------------------------------------------------------------------*/ 526 static void 527 Window_To_Buffer(WINDOW *win, FIELD *field) 528 { 529 int pad; 530 int len = 0; 531 FIELD_CELL *p; 532 int row, height; 533 534 assert(win && field && field->buf); 535 536 pad = field->pad; 537 p = field->buf; 538 height = getmaxy(win); 539 540 for (row = 0; (row < height) && (row < field->drows); row++) 541 { 542 wmove(win, row, 0); 543 len += myINNSTR(win, p + len, field->dcols); 544 } 545 p[len] = myZEROS; 546 547 /* replace visual padding character by blanks in buffer */ 548 if (pad != C_BLANK) 549 { 550 int i; 551 552 for (i = 0; i < len; i++, p++) 553 { 554 if ((unsigned long)CharOf(*p) == ChCharOf(pad) 555 #if USE_WIDEC_SUPPORT 556 && p->chars[1] == 0 557 #endif 558 ) 559 *p = myBLANK; 560 } 561 } 562 } 563 564 /*--------------------------------------------------------------------------- 565 | Facility : libnform 566 | Function : static void Synchronize_Buffer(FORM * form) 567 | 568 | Description : If there was a change, copy the content of the 569 | window into the buffer, so the buffer is synchronized 570 | with the windows content. We have to indicate that the 571 | buffer needs validation due to the change. 572 | 573 | Return Values : - 574 +--------------------------------------------------------------------------*/ 575 NCURSES_INLINE static void 576 Synchronize_Buffer(FORM *form) 577 { 578 if (form->status & _WINDOW_MODIFIED) 579 { 580 form->status &= ~_WINDOW_MODIFIED; 581 form->status |= _FCHECK_REQUIRED; 582 Window_To_Buffer(form->w, form->current); 583 wmove(form->w, form->currow, form->curcol); 584 } 585 } 586 587 /*--------------------------------------------------------------------------- 588 | Facility : libnform 589 | Function : static bool Field_Grown( FIELD *field, int amount) 590 | 591 | Description : This function is called for growable dynamic fields 592 | only. It has to increase the buffers and to allocate 593 | a new window for this field. 594 | This function has the side effect to set a new 595 | field-buffer pointer, the dcols and drows values 596 | as well as a new current Window for the field. 597 | 598 | Return Values : TRUE - field successfully increased 599 | FALSE - there was some error 600 +--------------------------------------------------------------------------*/ 601 static bool 602 Field_Grown(FIELD *field, int amount) 603 { 604 bool result = FALSE; 605 606 if (field && Growable(field)) 607 { 608 bool single_line_field = Single_Line_Field(field); 609 int old_buflen = Buffer_Length(field); 610 int new_buflen; 611 int old_dcols = field->dcols; 612 int old_drows = field->drows; 613 FIELD_CELL *oldbuf = field->buf; 614 FIELD_CELL *newbuf; 615 616 int growth; 617 FORM *form = field->form; 618 bool need_visual_update = ((form != (FORM *)0) && 619 (form->status & _POSTED) && 620 (form->current == field)); 621 622 if (need_visual_update) 623 Synchronize_Buffer(form); 624 625 if (single_line_field) 626 { 627 growth = field->cols * amount; 628 if (field->maxgrow) 629 growth = Minimum(field->maxgrow - field->dcols, growth); 630 field->dcols += growth; 631 if (field->dcols == field->maxgrow) 632 field->status &= ~_MAY_GROW; 633 } 634 else 635 { 636 growth = (field->rows + field->nrow) * amount; 637 if (field->maxgrow) 638 growth = Minimum(field->maxgrow - field->drows, growth); 639 field->drows += growth; 640 if (field->drows == field->maxgrow) 641 field->status &= ~_MAY_GROW; 642 } 643 /* drows, dcols changed, so we get really the new buffer length */ 644 new_buflen = Buffer_Length(field); 645 newbuf = (FIELD_CELL *)malloc(Total_Buffer_Size(field)); 646 if (!newbuf) 647 { 648 /* restore to previous state */ 649 field->dcols = old_dcols; 650 field->drows = old_drows; 651 if ((single_line_field && (field->dcols != field->maxgrow)) || 652 (!single_line_field && (field->drows != field->maxgrow))) 653 field->status |= _MAY_GROW; 654 } 655 else 656 { 657 /* Copy all the buffers. This is the reason why we can't just use 658 * realloc(). 659 */ 660 int i, j; 661 FIELD_CELL *old_bp; 662 FIELD_CELL *new_bp; 663 664 result = TRUE; /* allow sharing of recovery on failure */ 665 666 T((T_CREATE("fieldcell %p"), newbuf)); 667 field->buf = newbuf; 668 for (i = 0; i <= field->nbuf; i++) 669 { 670 new_bp = Address_Of_Nth_Buffer(field, i); 671 old_bp = oldbuf + i * (1 + old_buflen); 672 for (j = 0; j < old_buflen; ++j) 673 new_bp[j] = old_bp[j]; 674 while (j < new_buflen) 675 new_bp[j++] = myBLANK; 676 new_bp[new_buflen] = myZEROS; 677 } 678 679 #if USE_WIDEC_SUPPORT 680 if (wresize(field->working, 1, Buffer_Length(field) + 1) == ERR) 681 result = FALSE; 682 #endif 683 684 if (need_visual_update && result) 685 { 686 WINDOW *new_window = newpad(field->drows, field->dcols); 687 688 if (new_window != 0) 689 { 690 assert(form != (FORM *)0); 691 if (form->w) 692 delwin(form->w); 693 form->w = new_window; 694 Set_Field_Window_Attributes(field, form->w); 695 werase(form->w); 696 Buffer_To_Window(field, form->w); 697 untouchwin(form->w); 698 wmove(form->w, form->currow, form->curcol); 699 } 700 else 701 result = FALSE; 702 } 703 704 if (result) 705 { 706 free(oldbuf); 707 /* reflect changes in linked fields */ 708 if (field != field->link) 709 { 710 FIELD *linked_field; 711 712 for (linked_field = field->link; 713 linked_field != field; 714 linked_field = linked_field->link) 715 { 716 linked_field->buf = field->buf; 717 linked_field->drows = field->drows; 718 linked_field->dcols = field->dcols; 719 } 720 } 721 } 722 else 723 { 724 /* restore old state */ 725 field->dcols = old_dcols; 726 field->drows = old_drows; 727 field->buf = oldbuf; 728 if ((single_line_field && 729 (field->dcols != field->maxgrow)) || 730 (!single_line_field && 731 (field->drows != field->maxgrow))) 732 field->status |= _MAY_GROW; 733 free(newbuf); 734 } 735 } 736 } 737 return (result); 738 } 739 740 #ifdef NCURSES_MOUSE_VERSION 741 /*--------------------------------------------------------------------------- 742 | Facility : libnform 743 | Function : int Field_encloses(FIELD *field, int ry, int rx) 744 | 745 | Description : Check if the given coordinates lie within the given field. 746 | 747 | Return Values : E_OK - success 748 | E_BAD_ARGUMENT - invalid form pointer 749 | E_SYSTEM_ERROR - form has no current field or 750 | field-window 751 +--------------------------------------------------------------------------*/ 752 static int 753 Field_encloses(FIELD *field, int ry, int rx) 754 { 755 T((T_CALLED("Field_encloses(%p)"), field)); 756 if (field != 0 757 && field->frow <= ry 758 && (field->frow + field->rows) > ry 759 && field->fcol <= rx 760 && (field->fcol + field->cols) > rx) 761 { 762 RETURN(E_OK); 763 } 764 RETURN(E_INVALID_FIELD); 765 } 766 #endif 767 768 /*--------------------------------------------------------------------------- 769 | Facility : libnform 770 | Function : int _nc_Position_Form_Cursor(FORM * form) 771 | 772 | Description : Position the cursor in the window for the current 773 | field to be in sync. with the currow and curcol 774 | values. 775 | 776 | Return Values : E_OK - success 777 | E_BAD_ARGUMENT - invalid form pointer 778 | E_SYSTEM_ERROR - form has no current field or 779 | field-window 780 +--------------------------------------------------------------------------*/ 781 NCURSES_EXPORT(int) 782 _nc_Position_Form_Cursor(FORM *form) 783 { 784 FIELD *field; 785 WINDOW *formwin; 786 787 if (!form) 788 return (E_BAD_ARGUMENT); 789 790 if (!form->w || !form->current) 791 return (E_SYSTEM_ERROR); 792 793 field = form->current; 794 formwin = Get_Form_Window(form); 795 796 wmove(form->w, form->currow, form->curcol); 797 if (Has_Invisible_Parts(field)) 798 { 799 /* in this case fieldwin isn't derived from formwin, so we have 800 to move the cursor in formwin by hand... */ 801 wmove(formwin, 802 field->frow + form->currow - form->toprow, 803 field->fcol + form->curcol - form->begincol); 804 wcursyncup(formwin); 805 } 806 else 807 wcursyncup(form->w); 808 return (E_OK); 809 } 810 811 /*--------------------------------------------------------------------------- 812 | Facility : libnform 813 | Function : int _nc_Refresh_Current_Field(FORM * form) 814 | 815 | Description : Propagate the changes in the fields window to the 816 | window of the form. 817 | 818 | Return Values : E_OK - on success 819 | E_BAD_ARGUMENT - invalid form pointer 820 | E_SYSTEM_ERROR - general error 821 +--------------------------------------------------------------------------*/ 822 NCURSES_EXPORT(int) 823 _nc_Refresh_Current_Field(FORM *form) 824 { 825 WINDOW *formwin; 826 FIELD *field; 827 828 T((T_CALLED("_nc_Refresh_Current_Field(%p)"), form)); 829 830 if (!form) 831 RETURN(E_BAD_ARGUMENT); 832 833 if (!form->w || !form->current) 834 RETURN(E_SYSTEM_ERROR); 835 836 field = form->current; 837 formwin = Get_Form_Window(form); 838 839 if (field->opts & O_PUBLIC) 840 { 841 if (Is_Scroll_Field(field)) 842 { 843 /* Again, in this case the fieldwin isn't derived from formwin, 844 so we have to perform a copy operation. */ 845 if (Single_Line_Field(field)) 846 { 847 /* horizontal scrolling */ 848 if (form->curcol < form->begincol) 849 form->begincol = form->curcol; 850 else 851 { 852 if (form->curcol >= (form->begincol + field->cols)) 853 form->begincol = form->curcol - field->cols + 1; 854 } 855 copywin(form->w, 856 formwin, 857 0, 858 form->begincol, 859 field->frow, 860 field->fcol, 861 field->frow, 862 field->cols + field->fcol - 1, 863 0); 864 } 865 else 866 { 867 /* A multi-line, i.e. vertical scrolling field */ 868 int row_after_bottom, first_modified_row, first_unmodified_row; 869 870 if (field->drows > field->rows) 871 { 872 row_after_bottom = form->toprow + field->rows; 873 if (form->currow < form->toprow) 874 { 875 form->toprow = form->currow; 876 field->status |= _NEWTOP; 877 } 878 if (form->currow >= row_after_bottom) 879 { 880 form->toprow = form->currow - field->rows + 1; 881 field->status |= _NEWTOP; 882 } 883 if (field->status & _NEWTOP) 884 { 885 /* means we have to copy whole range */ 886 first_modified_row = form->toprow; 887 first_unmodified_row = first_modified_row + field->rows; 888 field->status &= ~_NEWTOP; 889 } 890 else 891 { 892 /* we try to optimize : finding the range of touched 893 lines */ 894 first_modified_row = form->toprow; 895 while (first_modified_row < row_after_bottom) 896 { 897 if (is_linetouched(form->w, first_modified_row)) 898 break; 899 first_modified_row++; 900 } 901 first_unmodified_row = first_modified_row; 902 while (first_unmodified_row < row_after_bottom) 903 { 904 if (!is_linetouched(form->w, first_unmodified_row)) 905 break; 906 first_unmodified_row++; 907 } 908 } 909 } 910 else 911 { 912 first_modified_row = form->toprow; 913 first_unmodified_row = first_modified_row + field->rows; 914 } 915 if (first_unmodified_row != first_modified_row) 916 copywin(form->w, 917 formwin, 918 first_modified_row, 919 0, 920 field->frow + first_modified_row - form->toprow, 921 field->fcol, 922 field->frow + first_unmodified_row - form->toprow - 1, 923 field->cols + field->fcol - 1, 924 0); 925 } 926 wsyncup(formwin); 927 } 928 else 929 { 930 /* if the field-window is simply a derived window, i.e. contains no 931 * invisible parts, the whole thing is trivial 932 */ 933 wsyncup(form->w); 934 } 935 } 936 untouchwin(form->w); 937 returnCode(_nc_Position_Form_Cursor(form)); 938 } 939 940 /*--------------------------------------------------------------------------- 941 | Facility : libnform 942 | Function : static void Perform_Justification( 943 | FIELD * field, 944 | WINDOW * win) 945 | 946 | Description : Output field with requested justification 947 | 948 | Return Values : - 949 +--------------------------------------------------------------------------*/ 950 static void 951 Perform_Justification(FIELD *field, WINDOW *win) 952 { 953 FIELD_CELL *bp; 954 int len; 955 int col = 0; 956 957 bp = Get_Start_Of_Data(field->buf, Buffer_Length(field)); 958 len = (int)(After_End_Of_Data(field->buf, Buffer_Length(field)) - bp); 959 960 if (len > 0) 961 { 962 assert(win && (field->drows == 1) && (field->dcols == field->cols)); 963 964 switch (field->just) 965 { 966 case JUSTIFY_LEFT: 967 break; 968 case JUSTIFY_CENTER: 969 col = (field->cols - len) / 2; 970 break; 971 case JUSTIFY_RIGHT: 972 col = field->cols - len; 973 break; 974 default: 975 break; 976 } 977 978 wmove(win, 0, col); 979 myADDNSTR(win, bp, len); 980 } 981 } 982 983 /*--------------------------------------------------------------------------- 984 | Facility : libnform 985 | Function : static void Undo_Justification( 986 | FIELD * field, 987 | WINDOW * win) 988 | 989 | Description : Display field without any justification, i.e. 990 | left justified 991 | 992 | Return Values : - 993 +--------------------------------------------------------------------------*/ 994 static void 995 Undo_Justification(FIELD *field, WINDOW *win) 996 { 997 FIELD_CELL *bp; 998 int len; 999 1000 bp = Get_Start_Of_Data(field->buf, Buffer_Length(field)); 1001 len = (int)(After_End_Of_Data(field->buf, Buffer_Length(field)) - bp); 1002 1003 if (len > 0) 1004 { 1005 assert(win); 1006 wmove(win, 0, 0); 1007 myADDNSTR(win, bp, len); 1008 } 1009 } 1010 1011 /*--------------------------------------------------------------------------- 1012 | Facility : libnform 1013 | Function : static bool Check_Char( 1014 | FIELDTYPE * typ, 1015 | int ch, 1016 | TypeArgument *argp) 1017 | 1018 | Description : Perform a single character check for character ch 1019 | according to the fieldtype instance. 1020 | 1021 | Return Values : TRUE - Character is valid 1022 | FALSE - Character is invalid 1023 +--------------------------------------------------------------------------*/ 1024 static bool 1025 Check_Char(FIELDTYPE *typ, int ch, TypeArgument *argp) 1026 { 1027 if (typ) 1028 { 1029 if (typ->status & _LINKED_TYPE) 1030 { 1031 assert(argp); 1032 return ( 1033 Check_Char(typ->left, ch, argp->left) || 1034 Check_Char(typ->right, ch, argp->right)); 1035 } 1036 else 1037 { 1038 if (typ->ccheck) 1039 return typ->ccheck(ch, (void *)argp); 1040 } 1041 } 1042 return (!iscntrl(UChar(ch)) ? TRUE : FALSE); 1043 } 1044 1045 /*--------------------------------------------------------------------------- 1046 | Facility : libnform 1047 | Function : static int Display_Or_Erase_Field( 1048 | FIELD * field, 1049 | bool bEraseFlag) 1050 | 1051 | Description : Create a subwindow for the field and display the 1052 | buffer contents (apply justification if required) 1053 | or simply erase the field. 1054 | 1055 | Return Values : E_OK - on success 1056 | E_SYSTEM_ERROR - some error (typical no memory) 1057 +--------------------------------------------------------------------------*/ 1058 static int 1059 Display_Or_Erase_Field(FIELD *field, bool bEraseFlag) 1060 { 1061 WINDOW *win; 1062 WINDOW *fwin; 1063 1064 if (!field) 1065 return E_SYSTEM_ERROR; 1066 1067 fwin = Get_Form_Window(field->form); 1068 win = derwin(fwin, 1069 field->rows, field->cols, field->frow, field->fcol); 1070 1071 if (!win) 1072 return E_SYSTEM_ERROR; 1073 else 1074 { 1075 if (field->opts & O_VISIBLE) 1076 Set_Field_Window_Attributes(field, win); 1077 else 1078 wattrset(win, WINDOW_ATTRS(fwin)); 1079 werase(win); 1080 } 1081 1082 if (!bEraseFlag) 1083 { 1084 if (field->opts & O_PUBLIC) 1085 { 1086 if (Justification_Allowed(field)) 1087 Perform_Justification(field, win); 1088 else 1089 Buffer_To_Window(field, win); 1090 } 1091 field->status &= ~_NEWTOP; 1092 } 1093 wsyncup(win); 1094 delwin(win); 1095 return E_OK; 1096 } 1097 1098 /* Macros to preset the bEraseFlag */ 1099 #define Display_Field(field) Display_Or_Erase_Field(field,FALSE) 1100 #define Erase_Field(field) Display_Or_Erase_Field(field,TRUE) 1101 1102 /*--------------------------------------------------------------------------- 1103 | Facility : libnform 1104 | Function : static int Synchronize_Field(FIELD * field) 1105 | 1106 | Description : Synchronize the windows content with the value in 1107 | the buffer. 1108 | 1109 | Return Values : E_OK - success 1110 | E_BAD_ARGUMENT - invalid field pointer 1111 | E_SYSTEM_ERROR - some severe basic error 1112 +--------------------------------------------------------------------------*/ 1113 static int 1114 Synchronize_Field(FIELD *field) 1115 { 1116 FORM *form; 1117 int res = E_OK; 1118 1119 if (!field) 1120 return (E_BAD_ARGUMENT); 1121 1122 if (((form = field->form) != (FORM *)0) 1123 && Field_Really_Appears(field)) 1124 { 1125 if (field == form->current) 1126 { 1127 form->currow = form->curcol = form->toprow = form->begincol = 0; 1128 werase(form->w); 1129 1130 if ((field->opts & O_PUBLIC) && Justification_Allowed(field)) 1131 Undo_Justification(field, form->w); 1132 else 1133 Buffer_To_Window(field, form->w); 1134 1135 field->status |= _NEWTOP; 1136 res = _nc_Refresh_Current_Field(form); 1137 } 1138 else 1139 res = Display_Field(field); 1140 } 1141 field->status |= _CHANGED; 1142 return (res); 1143 } 1144 1145 /*--------------------------------------------------------------------------- 1146 | Facility : libnform 1147 | Function : static int Synchronize_Linked_Fields(FIELD * field) 1148 | 1149 | Description : Propagate the Synchronize_Field function to all linked 1150 | fields. The first error that occurs in the sequence 1151 | of updates is the return value. 1152 | 1153 | Return Values : E_OK - success 1154 | E_BAD_ARGUMENT - invalid field pointer 1155 | E_SYSTEM_ERROR - some severe basic error 1156 +--------------------------------------------------------------------------*/ 1157 static int 1158 Synchronize_Linked_Fields(FIELD *field) 1159 { 1160 FIELD *linked_field; 1161 int res = E_OK; 1162 int syncres; 1163 1164 if (!field) 1165 return (E_BAD_ARGUMENT); 1166 1167 if (!field->link) 1168 return (E_SYSTEM_ERROR); 1169 1170 for (linked_field = field->link; 1171 linked_field != field; 1172 linked_field = linked_field->link) 1173 { 1174 if (((syncres = Synchronize_Field(linked_field)) != E_OK) && 1175 (res == E_OK)) 1176 res = syncres; 1177 } 1178 return (res); 1179 } 1180 1181 /*--------------------------------------------------------------------------- 1182 | Facility : libnform 1183 | Function : int _nc_Synchronize_Attributes(FIELD * field) 1184 | 1185 | Description : If a fields visual attributes have changed, this 1186 | routine is called to propagate those changes to the 1187 | screen. 1188 | 1189 | Return Values : E_OK - success 1190 | E_BAD_ARGUMENT - invalid field pointer 1191 | E_SYSTEM_ERROR - some severe basic error 1192 +--------------------------------------------------------------------------*/ 1193 NCURSES_EXPORT(int) 1194 _nc_Synchronize_Attributes(FIELD *field) 1195 { 1196 FORM *form; 1197 int res = E_OK; 1198 WINDOW *formwin; 1199 1200 T((T_CALLED("_nc_Synchronize_Attributes(%p)"), field)); 1201 1202 if (!field) 1203 returnCode(E_BAD_ARGUMENT); 1204 1205 CHECKPOS(field->form); 1206 if (((form = field->form) != (FORM *)0) 1207 && Field_Really_Appears(field)) 1208 { 1209 if (form->current == field) 1210 { 1211 Synchronize_Buffer(form); 1212 Set_Field_Window_Attributes(field, form->w); 1213 werase(form->w); 1214 wmove(form->w, form->currow, form->curcol); 1215 1216 if (field->opts & O_PUBLIC) 1217 { 1218 if (Justification_Allowed(field)) 1219 Undo_Justification(field, form->w); 1220 else 1221 Buffer_To_Window(field, form->w); 1222 } 1223 else 1224 { 1225 formwin = Get_Form_Window(form); 1226 copywin(form->w, formwin, 1227 0, 0, 1228 field->frow, field->fcol, 1229 field->rows - 1, field->cols - 1, 0); 1230 wsyncup(formwin); 1231 Buffer_To_Window(field, form->w); 1232 field->status |= _NEWTOP; /* fake refresh to paint all */ 1233 _nc_Refresh_Current_Field(form); 1234 } 1235 } 1236 else 1237 { 1238 res = Display_Field(field); 1239 } 1240 } 1241 CHECKPOS(form); 1242 returnCode(res); 1243 } 1244 1245 /*--------------------------------------------------------------------------- 1246 | Facility : libnform 1247 | Function : int _nc_Synchronize_Options(FIELD * field, 1248 | Field_Options newopts) 1249 | 1250 | Description : If a fields options have changed, this routine is 1251 | called to propagate these changes to the screen and 1252 | to really change the behavior of the field. 1253 | 1254 | Return Values : E_OK - success 1255 | E_BAD_ARGUMENT - invalid field pointer 1256 | E_CURRENT - field is the current one 1257 | E_SYSTEM_ERROR - some severe basic error 1258 +--------------------------------------------------------------------------*/ 1259 NCURSES_EXPORT(int) 1260 _nc_Synchronize_Options(FIELD *field, Field_Options newopts) 1261 { 1262 Field_Options oldopts; 1263 Field_Options changed_opts; 1264 FORM *form; 1265 int res = E_OK; 1266 1267 T((T_CALLED("_nc_Synchronize_Options(%p,%#x)"), field, newopts)); 1268 1269 if (!field) 1270 returnCode(E_BAD_ARGUMENT); 1271 1272 oldopts = field->opts; 1273 changed_opts = oldopts ^ newopts; 1274 field->opts = newopts; 1275 form = field->form; 1276 1277 if (form) 1278 { 1279 if (form->current == field) 1280 { 1281 field->opts = oldopts; 1282 returnCode(E_CURRENT); 1283 } 1284 1285 if (form->status & _POSTED) 1286 { 1287 if ((form->curpage == field->page)) 1288 { 1289 if (changed_opts & O_VISIBLE) 1290 { 1291 if (newopts & O_VISIBLE) 1292 res = Display_Field(field); 1293 else 1294 res = Erase_Field(field); 1295 } 1296 else 1297 { 1298 if ((changed_opts & O_PUBLIC) && 1299 (newopts & O_VISIBLE)) 1300 res = Display_Field(field); 1301 } 1302 } 1303 } 1304 } 1305 1306 if (changed_opts & O_STATIC) 1307 { 1308 bool single_line_field = Single_Line_Field(field); 1309 int res2 = E_OK; 1310 1311 if (newopts & O_STATIC) 1312 { 1313 /* the field becomes now static */ 1314 field->status &= ~_MAY_GROW; 1315 /* if actually we have no hidden columns, justification may 1316 occur again */ 1317 if (single_line_field && 1318 (field->cols == field->dcols) && 1319 (field->just != NO_JUSTIFICATION) && 1320 Field_Really_Appears(field)) 1321 { 1322 res2 = Display_Field(field); 1323 } 1324 } 1325 else 1326 { 1327 /* field is no longer static */ 1328 if ((field->maxgrow == 0) || 1329 (single_line_field && (field->dcols < field->maxgrow)) || 1330 (!single_line_field && (field->drows < field->maxgrow))) 1331 { 1332 field->status |= _MAY_GROW; 1333 /* a field with justification now changes its behavior, 1334 so we must redisplay it */ 1335 if (single_line_field && 1336 (field->just != NO_JUSTIFICATION) && 1337 Field_Really_Appears(field)) 1338 { 1339 res2 = Display_Field(field); 1340 } 1341 } 1342 } 1343 if (res2 != E_OK) 1344 res = res2; 1345 } 1346 1347 returnCode(res); 1348 } 1349 1350 /*--------------------------------------------------------------------------- 1351 | Facility : libnform 1352 | Function : int _nc_Set_Current_Field(FORM * form, 1353 | FIELD * newfield) 1354 | 1355 | Description : Make the newfield the new current field. 1356 | 1357 | Return Values : E_OK - success 1358 | E_BAD_ARGUMENT - invalid form or field pointer 1359 | E_SYSTEM_ERROR - some severe basic error 1360 | E_NOT_CONNECTED - no fields are connected to the form 1361 +--------------------------------------------------------------------------*/ 1362 NCURSES_EXPORT(int) 1363 _nc_Set_Current_Field(FORM *form, FIELD *newfield) 1364 { 1365 FIELD *field; 1366 WINDOW *new_window; 1367 1368 T((T_CALLED("_nc_Set_Current_Field(%p,%p)"), form, newfield)); 1369 1370 if (!form || !newfield || !form->current || (newfield->form != form)) 1371 returnCode(E_BAD_ARGUMENT); 1372 1373 if ((form->status & _IN_DRIVER)) 1374 returnCode(E_BAD_STATE); 1375 1376 if (!(form->field)) 1377 returnCode(E_NOT_CONNECTED); 1378 1379 field = form->current; 1380 1381 if ((field != newfield) || 1382 !(form->status & _POSTED)) 1383 { 1384 if ((form->w) && 1385 (field->opts & O_VISIBLE) && 1386 (field->form->curpage == field->page)) 1387 { 1388 _nc_Refresh_Current_Field(form); 1389 if (field->opts & O_PUBLIC) 1390 { 1391 if (field->drows > field->rows) 1392 { 1393 if (form->toprow == 0) 1394 field->status &= ~_NEWTOP; 1395 else 1396 field->status |= _NEWTOP; 1397 } 1398 else 1399 { 1400 if (Justification_Allowed(field)) 1401 { 1402 Window_To_Buffer(form->w, field); 1403 werase(form->w); 1404 Perform_Justification(field, form->w); 1405 wsyncup(form->w); 1406 } 1407 } 1408 } 1409 delwin(form->w); 1410 form->w = (WINDOW *)0; 1411 } 1412 1413 field = newfield; 1414 1415 if (Has_Invisible_Parts(field)) 1416 new_window = newpad(field->drows, field->dcols); 1417 else 1418 new_window = derwin(Get_Form_Window(form), 1419 field->rows, field->cols, field->frow, field->fcol); 1420 1421 if (!new_window) 1422 returnCode(E_SYSTEM_ERROR); 1423 1424 form->current = field; 1425 1426 if (form->w) 1427 delwin(form->w); 1428 form->w = new_window; 1429 1430 form->status &= ~_WINDOW_MODIFIED; 1431 Set_Field_Window_Attributes(field, form->w); 1432 1433 if (Has_Invisible_Parts(field)) 1434 { 1435 werase(form->w); 1436 Buffer_To_Window(field, form->w); 1437 } 1438 else 1439 { 1440 if (Justification_Allowed(field)) 1441 { 1442 werase(form->w); 1443 Undo_Justification(field, form->w); 1444 wsyncup(form->w); 1445 } 1446 } 1447 1448 untouchwin(form->w); 1449 } 1450 1451 form->currow = form->curcol = form->toprow = form->begincol = 0; 1452 returnCode(E_OK); 1453 } 1454 1455 /*---------------------------------------------------------------------------- 1456 Intra-Field Navigation routines 1457 --------------------------------------------------------------------------*/ 1458 1459 /*--------------------------------------------------------------------------- 1460 | Facility : libnform 1461 | Function : static int IFN_Next_Character(FORM * form) 1462 | 1463 | Description : Move to the next character in the field. In a multi-line 1464 | field this wraps at the end of the line. 1465 | 1466 | Return Values : E_OK - success 1467 | E_REQUEST_DENIED - at the rightmost position 1468 +--------------------------------------------------------------------------*/ 1469 static int 1470 IFN_Next_Character(FORM *form) 1471 { 1472 FIELD *field = form->current; 1473 int step = myWCWIDTH(form->w, form->currow, form->curcol); 1474 1475 T((T_CALLED("IFN_Next_Character(%p)"), form)); 1476 if ((form->curcol += step) == field->dcols) 1477 { 1478 if ((++(form->currow)) == field->drows) 1479 { 1480 #if GROW_IF_NAVIGATE 1481 if (!Single_Line_Field(field) && Field_Grown(field, 1)) 1482 { 1483 form->curcol = 0; 1484 returnCode(E_OK); 1485 } 1486 #endif 1487 form->currow--; 1488 #if GROW_IF_NAVIGATE 1489 if (Single_Line_Field(field) && Field_Grown(field, 1)) 1490 returnCode(E_OK); 1491 #endif 1492 form->curcol -= step; 1493 returnCode(E_REQUEST_DENIED); 1494 } 1495 form->curcol = 0; 1496 } 1497 returnCode(E_OK); 1498 } 1499 1500 /*--------------------------------------------------------------------------- 1501 | Facility : libnform 1502 | Function : static int IFN_Previous_Character(FORM * form) 1503 | 1504 | Description : Move to the previous character in the field. In a 1505 | multi-line field this wraps and the beginning of the 1506 | line. 1507 | 1508 | Return Values : E_OK - success 1509 | E_REQUEST_DENIED - at the leftmost position 1510 +--------------------------------------------------------------------------*/ 1511 static int 1512 IFN_Previous_Character(FORM *form) 1513 { 1514 int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1); 1515 int oldcol = form->curcol; 1516 1517 T((T_CALLED("IFN_Previous_Character(%p)"), form)); 1518 if ((form->curcol -= amount) < 0) 1519 { 1520 if ((--(form->currow)) < 0) 1521 { 1522 form->currow++; 1523 form->curcol = oldcol; 1524 returnCode(E_REQUEST_DENIED); 1525 } 1526 form->curcol = form->current->dcols - 1; 1527 } 1528 returnCode(E_OK); 1529 } 1530 1531 /*--------------------------------------------------------------------------- 1532 | Facility : libnform 1533 | Function : static int IFN_Next_Line(FORM * form) 1534 | 1535 | Description : Move to the beginning of the next line in the field 1536 | 1537 | Return Values : E_OK - success 1538 | E_REQUEST_DENIED - at the last line 1539 +--------------------------------------------------------------------------*/ 1540 static int 1541 IFN_Next_Line(FORM *form) 1542 { 1543 FIELD *field = form->current; 1544 1545 T((T_CALLED("IFN_Next_Line(%p)"), form)); 1546 if ((++(form->currow)) == field->drows) 1547 { 1548 #if GROW_IF_NAVIGATE 1549 if (!Single_Line_Field(field) && Field_Grown(field, 1)) 1550 returnCode(E_OK); 1551 #endif 1552 form->currow--; 1553 returnCode(E_REQUEST_DENIED); 1554 } 1555 form->curcol = 0; 1556 returnCode(E_OK); 1557 } 1558 1559 /*--------------------------------------------------------------------------- 1560 | Facility : libnform 1561 | Function : static int IFN_Previous_Line(FORM * form) 1562 | 1563 | Description : Move to the beginning of the previous line in the field 1564 | 1565 | Return Values : E_OK - success 1566 | E_REQUEST_DENIED - at the first line 1567 +--------------------------------------------------------------------------*/ 1568 static int 1569 IFN_Previous_Line(FORM *form) 1570 { 1571 T((T_CALLED("IFN_Previous_Line(%p)"), form)); 1572 if ((--(form->currow)) < 0) 1573 { 1574 form->currow++; 1575 returnCode(E_REQUEST_DENIED); 1576 } 1577 form->curcol = 0; 1578 returnCode(E_OK); 1579 } 1580 1581 /*--------------------------------------------------------------------------- 1582 | Facility : libnform 1583 | Function : static int IFN_Next_Word(FORM * form) 1584 | 1585 | Description : Move to the beginning of the next word in the field. 1586 | 1587 | Return Values : E_OK - success 1588 | E_REQUEST_DENIED - there is no next word 1589 +--------------------------------------------------------------------------*/ 1590 static int 1591 IFN_Next_Word(FORM *form) 1592 { 1593 FIELD *field = form->current; 1594 FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form); 1595 FIELD_CELL *s; 1596 FIELD_CELL *t; 1597 1598 T((T_CALLED("IFN_Next_Word(%p)"), form)); 1599 1600 /* We really need access to the data, so we have to synchronize */ 1601 Synchronize_Buffer(form); 1602 1603 /* Go to the first whitespace after the current position (including 1604 current position). This is then the starting point to look for the 1605 next non-blank data */ 1606 s = Get_First_Whitespace_Character(bp, Buffer_Length(field) - 1607 (int)(bp - field->buf)); 1608 1609 /* Find the start of the next word */ 1610 t = Get_Start_Of_Data(s, Buffer_Length(field) - 1611 (int)(s - field->buf)); 1612 #if !FRIENDLY_PREV_NEXT_WORD 1613 if (s == t) 1614 returnCode(E_REQUEST_DENIED); 1615 else 1616 #endif 1617 { 1618 Adjust_Cursor_Position(form, t); 1619 returnCode(E_OK); 1620 } 1621 } 1622 1623 /*--------------------------------------------------------------------------- 1624 | Facility : libnform 1625 | Function : static int IFN_Previous_Word(FORM * form) 1626 | 1627 | Description : Move to the beginning of the previous word in the field. 1628 | 1629 | Return Values : E_OK - success 1630 | E_REQUEST_DENIED - there is no previous word 1631 +--------------------------------------------------------------------------*/ 1632 static int 1633 IFN_Previous_Word(FORM *form) 1634 { 1635 FIELD *field = form->current; 1636 FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form); 1637 FIELD_CELL *s; 1638 FIELD_CELL *t; 1639 bool again = FALSE; 1640 1641 T((T_CALLED("IFN_Previous_Word(%p)"), form)); 1642 1643 /* We really need access to the data, so we have to synchronize */ 1644 Synchronize_Buffer(form); 1645 1646 s = After_End_Of_Data(field->buf, (int)(bp - field->buf)); 1647 /* s points now right after the last non-blank in the buffer before bp. 1648 If bp was in a word, s equals bp. In this case we must find the last 1649 whitespace in the buffer before bp and repeat the game to really find 1650 the previous word! */ 1651 if (s == bp) 1652 again = TRUE; 1653 1654 /* And next call now goes backward to look for the last whitespace 1655 before that, pointing right after this, so it points to the begin 1656 of the previous word. 1657 */ 1658 t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf)); 1659 #if !FRIENDLY_PREV_NEXT_WORD 1660 if (s == t) 1661 returnCode(E_REQUEST_DENIED); 1662 #endif 1663 if (again) 1664 { 1665 /* and do it again, replacing bp by t */ 1666 s = After_End_Of_Data(field->buf, (int)(t - field->buf)); 1667 t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf)); 1668 #if !FRIENDLY_PREV_NEXT_WORD 1669 if (s == t) 1670 returnCode(E_REQUEST_DENIED); 1671 #endif 1672 } 1673 Adjust_Cursor_Position(form, t); 1674 returnCode(E_OK); 1675 } 1676 1677 /*--------------------------------------------------------------------------- 1678 | Facility : libnform 1679 | Function : static int IFN_Beginning_Of_Field(FORM * form) 1680 | 1681 | Description : Place the cursor at the first non-pad character in 1682 | the field. 1683 | 1684 | Return Values : E_OK - success 1685 +--------------------------------------------------------------------------*/ 1686 static int 1687 IFN_Beginning_Of_Field(FORM *form) 1688 { 1689 FIELD *field = form->current; 1690 1691 T((T_CALLED("IFN_Beginning_Of_Field(%p)"), form)); 1692 Synchronize_Buffer(form); 1693 Adjust_Cursor_Position(form, 1694 Get_Start_Of_Data(field->buf, Buffer_Length(field))); 1695 returnCode(E_OK); 1696 } 1697 1698 /*--------------------------------------------------------------------------- 1699 | Facility : libnform 1700 | Function : static int IFN_End_Of_Field(FORM * form) 1701 | 1702 | Description : Place the cursor after the last non-pad character in 1703 | the field. If the field occupies the last position in 1704 | the buffer, the cursor is positioned on the last 1705 | character. 1706 | 1707 | Return Values : E_OK - success 1708 +--------------------------------------------------------------------------*/ 1709 static int 1710 IFN_End_Of_Field(FORM *form) 1711 { 1712 FIELD *field = form->current; 1713 FIELD_CELL *pos; 1714 1715 T((T_CALLED("IFN_End_Of_Field(%p)"), form)); 1716 Synchronize_Buffer(form); 1717 pos = After_End_Of_Data(field->buf, Buffer_Length(field)); 1718 if (pos == (field->buf + Buffer_Length(field))) 1719 pos--; 1720 Adjust_Cursor_Position(form, pos); 1721 returnCode(E_OK); 1722 } 1723 1724 /*--------------------------------------------------------------------------- 1725 | Facility : libnform 1726 | Function : static int IFN_Beginning_Of_Line(FORM * form) 1727 | 1728 | Description : Place the cursor on the first non-pad character in 1729 | the current line of the field. 1730 | 1731 | Return Values : E_OK - success 1732 +--------------------------------------------------------------------------*/ 1733 static int 1734 IFN_Beginning_Of_Line(FORM *form) 1735 { 1736 FIELD *field = form->current; 1737 1738 T((T_CALLED("IFN_Beginning_Of_Line(%p)"), form)); 1739 Synchronize_Buffer(form); 1740 Adjust_Cursor_Position(form, 1741 Get_Start_Of_Data(Address_Of_Current_Row_In_Buffer(form), 1742 field->dcols)); 1743 returnCode(E_OK); 1744 } 1745 1746 /*--------------------------------------------------------------------------- 1747 | Facility : libnform 1748 | Function : static int IFN_End_Of_Line(FORM * form) 1749 | 1750 | Description : Place the cursor after the last non-pad character in the 1751 | current line of the field. If the field occupies the 1752 | last column in the line, the cursor is positioned on the 1753 | last character of the line. 1754 | 1755 | Return Values : E_OK - success 1756 +--------------------------------------------------------------------------*/ 1757 static int 1758 IFN_End_Of_Line(FORM *form) 1759 { 1760 FIELD *field = form->current; 1761 FIELD_CELL *pos; 1762 FIELD_CELL *bp; 1763 1764 T((T_CALLED("IFN_End_Of_Line(%p)"), form)); 1765 Synchronize_Buffer(form); 1766 bp = Address_Of_Current_Row_In_Buffer(form); 1767 pos = After_End_Of_Data(bp, field->dcols); 1768 if (pos == (bp + field->dcols)) 1769 pos--; 1770 Adjust_Cursor_Position(form, pos); 1771 returnCode(E_OK); 1772 } 1773 1774 /*--------------------------------------------------------------------------- 1775 | Facility : libnform 1776 | Function : static int IFN_Left_Character(FORM * form) 1777 | 1778 | Description : Move one character to the left in the current line. 1779 | This doesn't cycle. 1780 | 1781 | Return Values : E_OK - success 1782 | E_REQUEST_DENIED - already in first column 1783 +--------------------------------------------------------------------------*/ 1784 static int 1785 IFN_Left_Character(FORM *form) 1786 { 1787 int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1); 1788 int oldcol = form->curcol; 1789 1790 T((T_CALLED("IFN_Left_Character(%p)"), form)); 1791 if ((form->curcol -= amount) < 0) 1792 { 1793 form->curcol = oldcol; 1794 returnCode(E_REQUEST_DENIED); 1795 } 1796 returnCode(E_OK); 1797 } 1798 1799 /*--------------------------------------------------------------------------- 1800 | Facility : libnform 1801 | Function : static int IFN_Right_Character(FORM * form) 1802 | 1803 | Description : Move one character to the right in the current line. 1804 | This doesn't cycle. 1805 | 1806 | Return Values : E_OK - success 1807 | E_REQUEST_DENIED - already in last column 1808 +--------------------------------------------------------------------------*/ 1809 static int 1810 IFN_Right_Character(FORM *form) 1811 { 1812 int amount = myWCWIDTH(form->w, form->currow, form->curcol); 1813 int oldcol = form->curcol; 1814 1815 T((T_CALLED("IFN_Right_Character(%p)"), form)); 1816 if ((form->curcol += amount) >= form->current->dcols) 1817 { 1818 #if GROW_IF_NAVIGATE 1819 FIELD *field = form->current; 1820 1821 if (Single_Line_Field(field) && Field_Grown(field, 1)) 1822 returnCode(E_OK); 1823 #endif 1824 form->curcol = oldcol; 1825 returnCode(E_REQUEST_DENIED); 1826 } 1827 returnCode(E_OK); 1828 } 1829 1830 /*--------------------------------------------------------------------------- 1831 | Facility : libnform 1832 | Function : static int IFN_Up_Character(FORM * form) 1833 | 1834 | Description : Move one line up. This doesn't cycle through the lines 1835 | of the field. 1836 | 1837 | Return Values : E_OK - success 1838 | E_REQUEST_DENIED - already in last column 1839 +--------------------------------------------------------------------------*/ 1840 static int 1841 IFN_Up_Character(FORM *form) 1842 { 1843 T((T_CALLED("IFN_Up_Character(%p)"), form)); 1844 if ((--(form->currow)) < 0) 1845 { 1846 form->currow++; 1847 returnCode(E_REQUEST_DENIED); 1848 } 1849 returnCode(E_OK); 1850 } 1851 1852 /*--------------------------------------------------------------------------- 1853 | Facility : libnform 1854 | Function : static int IFN_Down_Character(FORM * form) 1855 | 1856 | Description : Move one line down. This doesn't cycle through the 1857 | lines of the field. 1858 | 1859 | Return Values : E_OK - success 1860 | E_REQUEST_DENIED - already in last column 1861 +--------------------------------------------------------------------------*/ 1862 static int 1863 IFN_Down_Character(FORM *form) 1864 { 1865 FIELD *field = form->current; 1866 1867 T((T_CALLED("IFN_Down_Character(%p)"), form)); 1868 if ((++(form->currow)) == field->drows) 1869 { 1870 #if GROW_IF_NAVIGATE 1871 if (!Single_Line_Field(field) && Field_Grown(field, 1)) 1872 returnCode(E_OK); 1873 #endif 1874 --(form->currow); 1875 returnCode(E_REQUEST_DENIED); 1876 } 1877 returnCode(E_OK); 1878 } 1879 /*---------------------------------------------------------------------------- 1880 END of Intra-Field Navigation routines 1881 --------------------------------------------------------------------------*/ 1882 1883 /*---------------------------------------------------------------------------- 1884 Vertical scrolling helper routines 1885 --------------------------------------------------------------------------*/ 1886 1887 /*--------------------------------------------------------------------------- 1888 | Facility : libnform 1889 | Function : static int VSC_Generic(FORM *form, int nlines) 1890 | 1891 | Description : Scroll multi-line field forward (nlines>0) or 1892 | backward (nlines<0) this many lines. 1893 | 1894 | Return Values : E_OK - success 1895 | E_REQUEST_DENIED - can't scroll 1896 +--------------------------------------------------------------------------*/ 1897 static int 1898 VSC_Generic(FORM *form, int nlines) 1899 { 1900 FIELD *field = form->current; 1901 int res = E_REQUEST_DENIED; 1902 int rows_to_go = (nlines > 0 ? nlines : -nlines); 1903 1904 if (nlines > 0) 1905 { 1906 if ((rows_to_go + form->toprow) > (field->drows - field->rows)) 1907 rows_to_go = (field->drows - field->rows - form->toprow); 1908 1909 if (rows_to_go > 0) 1910 { 1911 form->currow += rows_to_go; 1912 form->toprow += rows_to_go; 1913 res = E_OK; 1914 } 1915 } 1916 else 1917 { 1918 if (rows_to_go > form->toprow) 1919 rows_to_go = form->toprow; 1920 1921 if (rows_to_go > 0) 1922 { 1923 form->currow -= rows_to_go; 1924 form->toprow -= rows_to_go; 1925 res = E_OK; 1926 } 1927 } 1928 return (res); 1929 } 1930 /*---------------------------------------------------------------------------- 1931 End of Vertical scrolling helper routines 1932 --------------------------------------------------------------------------*/ 1933 1934 /*---------------------------------------------------------------------------- 1935 Vertical scrolling routines 1936 --------------------------------------------------------------------------*/ 1937 1938 /*--------------------------------------------------------------------------- 1939 | Facility : libnform 1940 | Function : static int Vertical_Scrolling( 1941 | int (* const fct) (FORM *), 1942 | FORM * form) 1943 | 1944 | Description : Performs the generic vertical scrolling routines. 1945 | This has to check for a multi-line field and to set 1946 | the _NEWTOP flag if scrolling really occurred. 1947 | 1948 | Return Values : Propagated error code from low-level driver calls 1949 +--------------------------------------------------------------------------*/ 1950 static int 1951 Vertical_Scrolling(int (*const fct) (FORM *), FORM *form) 1952 { 1953 int res = E_REQUEST_DENIED; 1954 1955 if (!Single_Line_Field(form->current)) 1956 { 1957 res = fct(form); 1958 if (res == E_OK) 1959 form->current->status |= _NEWTOP; 1960 } 1961 return (res); 1962 } 1963 1964 /*--------------------------------------------------------------------------- 1965 | Facility : libnform 1966 | Function : static int VSC_Scroll_Line_Forward(FORM * form) 1967 | 1968 | Description : Scroll multi-line field forward a line 1969 | 1970 | Return Values : E_OK - success 1971 | E_REQUEST_DENIED - no data ahead 1972 +--------------------------------------------------------------------------*/ 1973 static int 1974 VSC_Scroll_Line_Forward(FORM *form) 1975 { 1976 T((T_CALLED("VSC_Scroll_Line_Forward(%p)"), form)); 1977 returnCode(VSC_Generic(form, 1)); 1978 } 1979 1980 /*--------------------------------------------------------------------------- 1981 | Facility : libnform 1982 | Function : static int VSC_Scroll_Line_Backward(FORM * form) 1983 | 1984 | Description : Scroll multi-line field backward a line 1985 | 1986 | Return Values : E_OK - success 1987 | E_REQUEST_DENIED - no data behind 1988 +--------------------------------------------------------------------------*/ 1989 static int 1990 VSC_Scroll_Line_Backward(FORM *form) 1991 { 1992 T((T_CALLED("VSC_Scroll_Line_Backward(%p)"), form)); 1993 returnCode(VSC_Generic(form, -1)); 1994 } 1995 1996 /*--------------------------------------------------------------------------- 1997 | Facility : libnform 1998 | Function : static int VSC_Scroll_Page_Forward(FORM * form) 1999 | 2000 | Description : Scroll a multi-line field forward a page 2001 | 2002 | Return Values : E_OK - success 2003 | E_REQUEST_DENIED - no data ahead 2004 +--------------------------------------------------------------------------*/ 2005 static int 2006 VSC_Scroll_Page_Forward(FORM *form) 2007 { 2008 T((T_CALLED("VSC_Scroll_Page_Forward(%p)"), form)); 2009 returnCode(VSC_Generic(form, form->current->rows)); 2010 } 2011 2012 /*--------------------------------------------------------------------------- 2013 | Facility : libnform 2014 | Function : static int VSC_Scroll_Half_Page_Forward(FORM * form) 2015 | 2016 | Description : Scroll a multi-line field forward half a page 2017 | 2018 | Return Values : E_OK - success 2019 | E_REQUEST_DENIED - no data ahead 2020 +--------------------------------------------------------------------------*/ 2021 static int 2022 VSC_Scroll_Half_Page_Forward(FORM *form) 2023 { 2024 T((T_CALLED("VSC_Scroll_Half_Page_Forward(%p)"), form)); 2025 returnCode(VSC_Generic(form, (form->current->rows + 1) / 2)); 2026 } 2027 2028 /*--------------------------------------------------------------------------- 2029 | Facility : libnform 2030 | Function : static int VSC_Scroll_Page_Backward(FORM * form) 2031 | 2032 | Description : Scroll a multi-line field backward a page 2033 | 2034 | Return Values : E_OK - success 2035 | E_REQUEST_DENIED - no data behind 2036 +--------------------------------------------------------------------------*/ 2037 static int 2038 VSC_Scroll_Page_Backward(FORM *form) 2039 { 2040 T((T_CALLED("VSC_Scroll_Page_Backward(%p)"), form)); 2041 returnCode(VSC_Generic(form, -(form->current->rows))); 2042 } 2043 2044 /*--------------------------------------------------------------------------- 2045 | Facility : libnform 2046 | Function : static int VSC_Scroll_Half_Page_Backward(FORM * form) 2047 | 2048 | Description : Scroll a multi-line field backward half a page 2049 | 2050 | Return Values : E_OK - success 2051 | E_REQUEST_DENIED - no data behind 2052 +--------------------------------------------------------------------------*/ 2053 static int 2054 VSC_Scroll_Half_Page_Backward(FORM *form) 2055 { 2056 T((T_CALLED("VSC_Scroll_Half_Page_Backward(%p)"), form)); 2057 returnCode(VSC_Generic(form, -((form->current->rows + 1) / 2))); 2058 } 2059 /*---------------------------------------------------------------------------- 2060 End of Vertical scrolling routines 2061 --------------------------------------------------------------------------*/ 2062 2063 /*---------------------------------------------------------------------------- 2064 Horizontal scrolling helper routines 2065 --------------------------------------------------------------------------*/ 2066 2067 /*--------------------------------------------------------------------------- 2068 | Facility : libnform 2069 | Function : static int HSC_Generic(FORM *form, int ncolumns) 2070 | 2071 | Description : Scroll single-line field forward (ncolumns>0) or 2072 | backward (ncolumns<0) this many columns. 2073 | 2074 | Return Values : E_OK - success 2075 | E_REQUEST_DENIED - can't scroll 2076 +--------------------------------------------------------------------------*/ 2077 static int 2078 HSC_Generic(FORM *form, int ncolumns) 2079 { 2080 FIELD *field = form->current; 2081 int res = E_REQUEST_DENIED; 2082 int cols_to_go = (ncolumns > 0 ? ncolumns : -ncolumns); 2083 2084 if (ncolumns > 0) 2085 { 2086 if ((cols_to_go + form->begincol) > (field->dcols - field->cols)) 2087 cols_to_go = field->dcols - field->cols - form->begincol; 2088 2089 if (cols_to_go > 0) 2090 { 2091 form->curcol += cols_to_go; 2092 form->begincol += cols_to_go; 2093 res = E_OK; 2094 } 2095 } 2096 else 2097 { 2098 if (cols_to_go > form->begincol) 2099 cols_to_go = form->begincol; 2100 2101 if (cols_to_go > 0) 2102 { 2103 form->curcol -= cols_to_go; 2104 form->begincol -= cols_to_go; 2105 res = E_OK; 2106 } 2107 } 2108 return (res); 2109 } 2110 /*---------------------------------------------------------------------------- 2111 End of Horizontal scrolling helper routines 2112 --------------------------------------------------------------------------*/ 2113 2114 /*---------------------------------------------------------------------------- 2115 Horizontal scrolling routines 2116 --------------------------------------------------------------------------*/ 2117 2118 /*--------------------------------------------------------------------------- 2119 | Facility : libnform 2120 | Function : static int Horizontal_Scrolling( 2121 | int (* const fct) (FORM *), 2122 | FORM * form) 2123 | 2124 | Description : Performs the generic horizontal scrolling routines. 2125 | This has to check for a single-line field. 2126 | 2127 | Return Values : Propagated error code from low-level driver calls 2128 +--------------------------------------------------------------------------*/ 2129 static int 2130 Horizontal_Scrolling(int (*const fct) (FORM *), FORM *form) 2131 { 2132 if (Single_Line_Field(form->current)) 2133 return fct(form); 2134 else 2135 return (E_REQUEST_DENIED); 2136 } 2137 2138 /*--------------------------------------------------------------------------- 2139 | Facility : libnform 2140 | Function : static int HSC_Scroll_Char_Forward(FORM * form) 2141 | 2142 | Description : Scroll single-line field forward a character 2143 | 2144 | Return Values : E_OK - success 2145 | E_REQUEST_DENIED - no data ahead 2146 +--------------------------------------------------------------------------*/ 2147 static int 2148 HSC_Scroll_Char_Forward(FORM *form) 2149 { 2150 T((T_CALLED("HSC_Scroll_Char_Forward(%p)"), form)); 2151 returnCode(HSC_Generic(form, 1)); 2152 } 2153 2154 /*--------------------------------------------------------------------------- 2155 | Facility : libnform 2156 | Function : static int HSC_Scroll_Char_Backward(FORM * form) 2157 | 2158 | Description : Scroll single-line field backward a character 2159 | 2160 | Return Values : E_OK - success 2161 | E_REQUEST_DENIED - no data behind 2162 +--------------------------------------------------------------------------*/ 2163 static int 2164 HSC_Scroll_Char_Backward(FORM *form) 2165 { 2166 T((T_CALLED("HSC_Scroll_Char_Backward(%p)"), form)); 2167 returnCode(HSC_Generic(form, -1)); 2168 } 2169 2170 /*--------------------------------------------------------------------------- 2171 | Facility : libnform 2172 | Function : static int HSC_Horizontal_Line_Forward(FORM* form) 2173 | 2174 | Description : Scroll single-line field forward a line 2175 | 2176 | Return Values : E_OK - success 2177 | E_REQUEST_DENIED - no data ahead 2178 +--------------------------------------------------------------------------*/ 2179 static int 2180 HSC_Horizontal_Line_Forward(FORM *form) 2181 { 2182 T((T_CALLED("HSC_Horizontal_Line_Forward(%p)"), form)); 2183 returnCode(HSC_Generic(form, form->current->cols)); 2184 } 2185 2186 /*--------------------------------------------------------------------------- 2187 | Facility : libnform 2188 | Function : static int HSC_Horizontal_Half_Line_Forward(FORM* form) 2189 | 2190 | Description : Scroll single-line field forward half a line 2191 | 2192 | Return Values : E_OK - success 2193 | E_REQUEST_DENIED - no data ahead 2194 +--------------------------------------------------------------------------*/ 2195 static int 2196 HSC_Horizontal_Half_Line_Forward(FORM *form) 2197 { 2198 T((T_CALLED("HSC_Horizontal_Half_Line_Forward(%p)"), form)); 2199 returnCode(HSC_Generic(form, (form->current->cols + 1) / 2)); 2200 } 2201 2202 /*--------------------------------------------------------------------------- 2203 | Facility : libnform 2204 | Function : static int HSC_Horizontal_Line_Backward(FORM* form) 2205 | 2206 | Description : Scroll single-line field backward a line 2207 | 2208 | Return Values : E_OK - success 2209 | E_REQUEST_DENIED - no data behind 2210 +--------------------------------------------------------------------------*/ 2211 static int 2212 HSC_Horizontal_Line_Backward(FORM *form) 2213 { 2214 T((T_CALLED("HSC_Horizontal_Line_Backward(%p)"), form)); 2215 returnCode(HSC_Generic(form, -(form->current->cols))); 2216 } 2217 2218 /*--------------------------------------------------------------------------- 2219 | Facility : libnform 2220 | Function : static int HSC_Horizontal_Half_Line_Backward(FORM* form) 2221 | 2222 | Description : Scroll single-line field backward half a line 2223 | 2224 | Return Values : E_OK - success 2225 | E_REQUEST_DENIED - no data behind 2226 +--------------------------------------------------------------------------*/ 2227 static int 2228 HSC_Horizontal_Half_Line_Backward(FORM *form) 2229 { 2230 T((T_CALLED("HSC_Horizontal_Half_Line_Backward(%p)"), form)); 2231 returnCode(HSC_Generic(form, -((form->current->cols + 1) / 2))); 2232 } 2233 2234 /*---------------------------------------------------------------------------- 2235 End of Horizontal scrolling routines 2236 --------------------------------------------------------------------------*/ 2237 2238 /*---------------------------------------------------------------------------- 2239 Helper routines for Field Editing 2240 --------------------------------------------------------------------------*/ 2241 2242 /*--------------------------------------------------------------------------- 2243 | Facility : libnform 2244 | Function : static bool Is_There_Room_For_A_Line(FORM * form) 2245 | 2246 | Description : Check whether or not there is enough room in the 2247 | buffer to enter a whole line. 2248 | 2249 | Return Values : TRUE - there is enough space 2250 | FALSE - there is not enough space 2251 +--------------------------------------------------------------------------*/ 2252 NCURSES_INLINE static bool 2253 Is_There_Room_For_A_Line(FORM *form) 2254 { 2255 FIELD *field = form->current; 2256 FIELD_CELL *begin_of_last_line, *s; 2257 2258 Synchronize_Buffer(form); 2259 begin_of_last_line = Address_Of_Row_In_Buffer(field, (field->drows - 1)); 2260 s = After_End_Of_Data(begin_of_last_line, field->dcols); 2261 return ((s == begin_of_last_line) ? TRUE : FALSE); 2262 } 2263 2264 /*--------------------------------------------------------------------------- 2265 | Facility : libnform 2266 | Function : static bool Is_There_Room_For_A_Char_In_Line(FORM * form) 2267 | 2268 | Description : Checks whether or not there is room for a new character 2269 | in the current line. 2270 | 2271 | Return Values : TRUE - there is room 2272 | FALSE - there is not enough room (line full) 2273 +--------------------------------------------------------------------------*/ 2274 NCURSES_INLINE static bool 2275 Is_There_Room_For_A_Char_In_Line(FORM *form) 2276 { 2277 int last_char_in_line; 2278 2279 wmove(form->w, form->currow, form->current->dcols - 1); 2280 last_char_in_line = (int)(winch(form->w) & A_CHARTEXT); 2281 wmove(form->w, form->currow, form->curcol); 2282 return (((last_char_in_line == form->current->pad) || 2283 is_blank(last_char_in_line)) ? TRUE : FALSE); 2284 } 2285 2286 #define There_Is_No_Room_For_A_Char_In_Line(f) \ 2287 !Is_There_Room_For_A_Char_In_Line(f) 2288 2289 /*--------------------------------------------------------------------------- 2290 | Facility : libnform 2291 | Function : static int Insert_String( 2292 | FORM * form, 2293 | int row, 2294 | char *txt, 2295 | int len ) 2296 | 2297 | Description : Insert the 'len' characters beginning at pointer 'txt' 2298 | into the 'row' of the 'form'. The insertion occurs 2299 | on the beginning of the row, all other characters are 2300 | moved to the right. After the text a pad character will 2301 | be inserted to separate the text from the rest. If 2302 | necessary the insertion moves characters on the next 2303 | line to make place for the requested insertion string. 2304 | 2305 | Return Values : E_OK - success 2306 | E_REQUEST_DENIED - 2307 | E_SYSTEM_ERROR - system error 2308 +--------------------------------------------------------------------------*/ 2309 static int 2310 Insert_String(FORM *form, int row, FIELD_CELL *txt, int len) 2311 { 2312 FIELD *field = form->current; 2313 FIELD_CELL *bp = Address_Of_Row_In_Buffer(field, row); 2314 int datalen = (int)(After_End_Of_Data(bp, field->dcols) - bp); 2315 int freelen = field->dcols - datalen; 2316 int requiredlen = len + 1; 2317 FIELD_CELL *split; 2318 int result = E_REQUEST_DENIED; 2319 2320 if (freelen >= requiredlen) 2321 { 2322 wmove(form->w, row, 0); 2323 myINSNSTR(form->w, txt, len); 2324 wmove(form->w, row, len); 2325 myINSNSTR(form->w, &myBLANK, 1); 2326 return E_OK; 2327 } 2328 else 2329 { 2330 /* we have to move characters on the next line. If we are on the 2331 last line this may work, if the field is growable */ 2332 if ((row == (field->drows - 1)) && Growable(field)) 2333 { 2334 if (!Field_Grown(field, 1)) 2335 return (E_SYSTEM_ERROR); 2336 /* !!!Side-Effect : might be changed due to growth!!! */ 2337 bp = Address_Of_Row_In_Buffer(field, row); 2338 } 2339 2340 if (row < (field->drows - 1)) 2341 { 2342 split = 2343 After_Last_Whitespace_Character(bp, 2344 (int)(Get_Start_Of_Data(bp 2345 + field->dcols 2346 - requiredlen, 2347 requiredlen) 2348 - bp)); 2349 /* split points now to the first character of the portion of the 2350 line that must be moved to the next line */ 2351 datalen = (int)(split - bp); /* + freelen has to stay on this line */ 2352 freelen = field->dcols - (datalen + freelen); /* for the next line */ 2353 2354 if ((result = Insert_String(form, row + 1, split, freelen)) == E_OK) 2355 { 2356 wmove(form->w, row, datalen); 2357 wclrtoeol(form->w); 2358 wmove(form->w, row, 0); 2359 myINSNSTR(form->w, txt, len); 2360 wmove(form->w, row, len); 2361 myINSNSTR(form->w, &myBLANK, 1); 2362 return E_OK; 2363 } 2364 } 2365 return (result); 2366 } 2367 } 2368 2369 /*--------------------------------------------------------------------------- 2370 | Facility : libnform 2371 | Function : static int Wrapping_Not_Necessary_Or_Wrapping_Ok( 2372 | FORM * form) 2373 | 2374 | Description : If a character has been entered into a field, it may 2375 | be that wrapping has to occur. This routine checks 2376 | whether or not wrapping is required and if so, performs 2377 | the wrapping. 2378 | 2379 | Return Values : E_OK - no wrapping required or wrapping 2380 | was successful 2381 | E_REQUEST_DENIED - 2382 | E_SYSTEM_ERROR - some system error 2383 +--------------------------------------------------------------------------*/ 2384 static int 2385 Wrapping_Not_Necessary_Or_Wrapping_Ok(FORM *form) 2386 { 2387 FIELD *field = form->current; 2388 int result = E_REQUEST_DENIED; 2389 bool Last_Row = ((field->drows - 1) == form->currow); 2390 2391 if ((field->opts & O_WRAP) && /* wrapping wanted */ 2392 (!Single_Line_Field(field)) && /* must be multi-line */ 2393 (There_Is_No_Room_For_A_Char_In_Line(form)) && /* line is full */ 2394 (!Last_Row || Growable(field))) /* there are more lines */ 2395 { 2396 FIELD_CELL *bp; 2397 FIELD_CELL *split; 2398 int chars_to_be_wrapped; 2399 int chars_to_remain_on_line; 2400 2401 if (Last_Row) 2402 { 2403 /* the above logic already ensures, that in this case the field 2404 is growable */ 2405 if (!Field_Grown(field, 1)) 2406 return E_SYSTEM_ERROR; 2407 } 2408 bp = Address_Of_Current_Row_In_Buffer(form); 2409 Window_To_Buffer(form->w, field); 2410 split = After_Last_Whitespace_Character(bp, field->dcols); 2411 /* split points to the first character of the sequence to be brought 2412 on the next line */ 2413 chars_to_remain_on_line = (int)(split - bp); 2414 chars_to_be_wrapped = field->dcols - chars_to_remain_on_line; 2415 if (chars_to_remain_on_line > 0) 2416 { 2417 if ((result = Insert_String(form, form->currow + 1, split, 2418 chars_to_be_wrapped)) == E_OK) 2419 { 2420 wmove(form->w, form->currow, chars_to_remain_on_line); 2421 wclrtoeol(form->w); 2422 if (form->curcol >= chars_to_remain_on_line) 2423 { 2424 form->currow++; 2425 form->curcol -= chars_to_remain_on_line; 2426 } 2427 return E_OK; 2428 } 2429 } 2430 else 2431 return E_OK; 2432 if (result != E_OK) 2433 { 2434 DeleteChar(form); 2435 Window_To_Buffer(form->w, field); 2436 result = E_REQUEST_DENIED; 2437 } 2438 } 2439 else 2440 result = E_OK; /* wrapping was not necessary */ 2441 return (result); 2442 } 2443 2444 /*---------------------------------------------------------------------------- 2445 Field Editing routines 2446 --------------------------------------------------------------------------*/ 2447 2448 /*--------------------------------------------------------------------------- 2449 | Facility : libnform 2450 | Function : static int Field_Editing( 2451 | int (* const fct) (FORM *), 2452 | FORM * form) 2453 | 2454 | Description : Generic routine for field editing requests. The driver 2455 | routines are only called for editable fields, the 2456 | _WINDOW_MODIFIED flag is set if editing occurred. 2457 | This is somewhat special due to the overload semantics 2458 | of the NEW_LINE and DEL_PREV requests. 2459 | 2460 | Return Values : Error code from low level drivers. 2461 +--------------------------------------------------------------------------*/ 2462 static int 2463 Field_Editing(int (*const fct) (FORM *), FORM *form) 2464 { 2465 int res = E_REQUEST_DENIED; 2466 2467 /* We have to deal here with the specific case of the overloaded 2468 behavior of New_Line and Delete_Previous requests. 2469 They may end up in navigational requests if we are on the first 2470 character in a field. But navigation is also allowed on non- 2471 editable fields. 2472 */ 2473 if ((fct == FE_Delete_Previous) && 2474 (form->opts & O_BS_OVERLOAD) && 2475 First_Position_In_Current_Field(form)) 2476 { 2477 res = Inter_Field_Navigation(FN_Previous_Field, form); 2478 } 2479 else 2480 { 2481 if (fct == FE_New_Line) 2482 { 2483 if ((form->opts & O_NL_OVERLOAD) && 2484 First_Position_In_Current_Field(form)) 2485 { 2486 res = Inter_Field_Navigation(FN_Next_Field, form); 2487 } 2488 else 2489 /* FE_New_Line deals itself with the _WINDOW_MODIFIED flag */ 2490 res = fct(form); 2491 } 2492 else 2493 { 2494 /* From now on, everything must be editable */ 2495 if (form->current->opts & O_EDIT) 2496 { 2497 res = fct(form); 2498 if (res == E_OK) 2499 form->status |= _WINDOW_MODIFIED; 2500 } 2501 } 2502 } 2503 return res; 2504 } 2505 2506 /*--------------------------------------------------------------------------- 2507 | Facility : libnform 2508 | Function : static int FE_New_Line(FORM * form) 2509 | 2510 | Description : Perform a new line request. This is rather complex 2511 | compared to other routines in this code due to the 2512 | rather difficult to understand description in the 2513 | manuals. 2514 | 2515 | Return Values : E_OK - success 2516 | E_REQUEST_DENIED - new line not allowed 2517 | E_SYSTEM_ERROR - system error 2518 +--------------------------------------------------------------------------*/ 2519 static int 2520 FE_New_Line(FORM *form) 2521 { 2522 FIELD *field = form->current; 2523 FIELD_CELL *bp, *t; 2524 bool Last_Row = ((field->drows - 1) == form->currow); 2525 2526 T((T_CALLED("FE_New_Line(%p)"), form)); 2527 if (form->status & _OVLMODE) 2528 { 2529 if (Last_Row && 2530 (!(Growable(field) && !Single_Line_Field(field)))) 2531 { 2532 if (!(form->opts & O_NL_OVERLOAD)) 2533 returnCode(E_REQUEST_DENIED); 2534 wmove(form->w, form->currow, form->curcol); 2535 wclrtoeol(form->w); 2536 /* we have to set this here, although it is also 2537 handled in the generic routine. The reason is, 2538 that FN_Next_Field may fail, but the form is 2539 definitively changed */ 2540 form->status |= _WINDOW_MODIFIED; 2541 returnCode(Inter_Field_Navigation(FN_Next_Field, form)); 2542 } 2543 else 2544 { 2545 if (Last_Row && !Field_Grown(field, 1)) 2546 { 2547 /* N.B.: due to the logic in the 'if', LastRow==TRUE 2548 means here that the field is growable and not 2549 a single-line field */ 2550 returnCode(E_SYSTEM_ERROR); 2551 } 2552 wmove(form->w, form->currow, form->curcol); 2553 wclrtoeol(form->w); 2554 form->currow++; 2555 form->curcol = 0; 2556 form->status |= _WINDOW_MODIFIED; 2557 returnCode(E_OK); 2558 } 2559 } 2560 else 2561 { 2562 /* Insert Mode */ 2563 if (Last_Row && 2564 !(Growable(field) && !Single_Line_Field(field))) 2565 { 2566 if (!(form->opts & O_NL_OVERLOAD)) 2567 returnCode(E_REQUEST_DENIED); 2568 returnCode(Inter_Field_Navigation(FN_Next_Field, form)); 2569 } 2570 else 2571 { 2572 bool May_Do_It = !Last_Row && Is_There_Room_For_A_Line(form); 2573 2574 if (!(May_Do_It || Growable(field))) 2575 returnCode(E_REQUEST_DENIED); 2576 if (!May_Do_It && !Field_Grown(field, 1)) 2577 returnCode(E_SYSTEM_ERROR); 2578 2579 bp = Address_Of_Current_Position_In_Buffer(form); 2580 t = After_End_Of_Data(bp, field->dcols - form->curcol); 2581 wmove(form->w, form->currow, form->curcol); 2582 wclrtoeol(form->w); 2583 form->currow++; 2584 form->curcol = 0; 2585 wmove(form->w, form->currow, form->curcol); 2586 winsertln(form->w); 2587 myADDNSTR(form->w, bp, (int)(t - bp)); 2588 form->status |= _WINDOW_MODIFIED; 2589 returnCode(E_OK); 2590 } 2591 } 2592 } 2593 2594 /*--------------------------------------------------------------------------- 2595 | Facility : libnform 2596 | Function : static int FE_Insert_Character(FORM * form) 2597 | 2598 | Description : Insert blank character at the cursor position 2599 | 2600 | Return Values : E_OK 2601 | E_REQUEST_DENIED 2602 +--------------------------------------------------------------------------*/ 2603 static int 2604 FE_Insert_Character(FORM *form) 2605 { 2606 FIELD *field = form->current; 2607 int result = E_REQUEST_DENIED; 2608 2609 T((T_CALLED("FE_Insert_Character(%p)"), form)); 2610 if (Check_Char(field->type, (int)C_BLANK, (TypeArgument *)(field->arg))) 2611 { 2612 bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form); 2613 2614 if (There_Is_Room || 2615 ((Single_Line_Field(field) && Growable(field)))) 2616 { 2617 if (!There_Is_Room && !Field_Grown(field, 1)) 2618 result = E_SYSTEM_ERROR; 2619 else 2620 { 2621 winsch(form->w, (chtype)C_BLANK); 2622 result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form); 2623 } 2624 } 2625 } 2626 returnCode(result); 2627 } 2628 2629 /*--------------------------------------------------------------------------- 2630 | Facility : libnform 2631 | Function : static int FE_Insert_Line(FORM * form) 2632 | 2633 | Description : Insert a blank line at the cursor position 2634 | 2635 | Return Values : E_OK - success 2636 | E_REQUEST_DENIED - line can not be inserted 2637 +--------------------------------------------------------------------------*/ 2638 static int 2639 FE_Insert_Line(FORM *form) 2640 { 2641 FIELD *field = form->current; 2642 int result = E_REQUEST_DENIED; 2643 2644 T((T_CALLED("FE_Insert_Line(%p)"), form)); 2645 if (Check_Char(field->type, (int)C_BLANK, (TypeArgument *)(field->arg))) 2646 { 2647 bool Maybe_Done = (form->currow != (field->drows - 1)) && 2648 Is_There_Room_For_A_Line(form); 2649 2650 if (!Single_Line_Field(field) && 2651 (Maybe_Done || Growable(field))) 2652 { 2653 if (!Maybe_Done && !Field_Grown(field, 1)) 2654 result = E_SYSTEM_ERROR; 2655 else 2656 { 2657 form->curcol = 0; 2658 winsertln(form->w); 2659 result = E_OK; 2660 } 2661 } 2662 } 2663 returnCode(result); 2664 } 2665 2666 /*--------------------------------------------------------------------------- 2667 | Facility : libnform 2668 | Function : static int FE_Delete_Character(FORM * form) 2669 | 2670 | Description : Delete character at the cursor position 2671 | 2672 | Return Values : E_OK - success 2673 +--------------------------------------------------------------------------*/ 2674 static int 2675 FE_Delete_Character(FORM *form) 2676 { 2677 T((T_CALLED("FE_Delete_Character(%p)"), form)); 2678 DeleteChar(form); 2679 returnCode(E_OK); 2680 } 2681 2682 /*--------------------------------------------------------------------------- 2683 | Facility : libnform 2684 | Function : static int FE_Delete_Previous(FORM * form) 2685 | 2686 | Description : Delete character before cursor. Again this is a rather 2687 | difficult piece compared to others due to the overloading 2688 | semantics of backspace. 2689 | N.B.: The case of overloaded BS on first field position 2690 | is already handled in the generic routine. 2691 | 2692 | Return Values : E_OK - success 2693 | E_REQUEST_DENIED - Character can't be deleted 2694 +--------------------------------------------------------------------------*/ 2695 static int 2696 FE_Delete_Previous(FORM *form) 2697 { 2698 FIELD *field = form->current; 2699 2700 T((T_CALLED("FE_Delete_Previous(%p)"), form)); 2701 if (First_Position_In_Current_Field(form)) 2702 returnCode(E_REQUEST_DENIED); 2703 2704 if ((--(form->curcol)) < 0) 2705 { 2706 FIELD_CELL *this_line, *prev_line, *prev_end, *this_end; 2707 int this_row = form->currow; 2708 2709 form->curcol++; 2710 if (form->status & _OVLMODE) 2711 returnCode(E_REQUEST_DENIED); 2712 2713 prev_line = Address_Of_Row_In_Buffer(field, (form->currow - 1)); 2714 this_line = Address_Of_Row_In_Buffer(field, (form->currow)); 2715 Synchronize_Buffer(form); 2716 prev_end = After_End_Of_Data(prev_line, field->dcols); 2717 this_end = After_End_Of_Data(this_line, field->dcols); 2718 if ((int)(this_end - this_line) > 2719 (field->cols - (int)(prev_end - prev_line))) 2720 returnCode(E_REQUEST_DENIED); 2721 wmove(form->w, form->currow, form->curcol); 2722 wdeleteln(form->w); 2723 Adjust_Cursor_Position(form, prev_end); 2724 /* 2725 * If we did not really move to the previous line, help the user a 2726 * little. It is however a little inconsistent. Normally, when 2727 * backspacing around the point where text wraps to a new line in a 2728 * multi-line form, we absorb one keystroke for the wrapping point. That 2729 * is consistent with SVr4 forms. However, SVr4 does not allow typing 2730 * into the last column of the field, and requires the user to enter a 2731 * newline to move to the next line. Therefore it can consistently eat 2732 * that keystroke. Since ncurses allows the last column, it wraps 2733 * automatically (given the proper options). But we cannot eat the 2734 * keystroke to back over the wrapping point, since that would put the 2735 * cursor past the end of the form field. In this case, just delete the 2736 * character at the end of the field. 2737 */ 2738 if (form->currow == this_row && this_row > 0) 2739 { 2740 form->currow -= 1; 2741 form->curcol = field->dcols - 1; 2742 DeleteChar(form); 2743 } 2744 else 2745 { 2746 wmove(form->w, form->currow, form->curcol); 2747 myADDNSTR(form->w, this_line, (int)(this_end - this_line)); 2748 } 2749 } 2750 else 2751 { 2752 DeleteChar(form); 2753 } 2754 returnCode(E_OK); 2755 } 2756 2757 /*--------------------------------------------------------------------------- 2758 | Facility : libnform 2759 | Function : static int FE_Delete_Line(FORM * form) 2760 | 2761 | Description : Delete line at cursor position. 2762 | 2763 | Return Values : E_OK - success 2764 +--------------------------------------------------------------------------*/ 2765 static int 2766 FE_Delete_Line(FORM *form) 2767 { 2768 T((T_CALLED("FE_Delete_Line(%p)"), form)); 2769 form->curcol = 0; 2770 wdeleteln(form->w); 2771 returnCode(E_OK); 2772 } 2773 2774 /*--------------------------------------------------------------------------- 2775 | Facility : libnform 2776 | Function : static int FE_Delete_Word(FORM * form) 2777 | 2778 | Description : Delete word at cursor position 2779 | 2780 | Return Values : E_OK - success 2781 | E_REQUEST_DENIED - failure 2782 +--------------------------------------------------------------------------*/ 2783 static int 2784 FE_Delete_Word(FORM *form) 2785 { 2786 FIELD *field = form->current; 2787 FIELD_CELL *bp = Address_Of_Current_Row_In_Buffer(form); 2788 FIELD_CELL *ep = bp + field->dcols; 2789 FIELD_CELL *cp = bp + form->curcol; 2790 FIELD_CELL *s; 2791 2792 T((T_CALLED("FE_Delete_Word(%p)"), form)); 2793 Synchronize_Buffer(form); 2794 if (ISBLANK(*cp)) 2795 returnCode(E_REQUEST_DENIED); /* not in word */ 2796 2797 /* move cursor to begin of word and erase to end of screen-line */ 2798 Adjust_Cursor_Position(form, 2799 After_Last_Whitespace_Character(bp, form->curcol)); 2800 wmove(form->w, form->currow, form->curcol); 2801 wclrtoeol(form->w); 2802 2803 /* skip over word in buffer */ 2804 s = Get_First_Whitespace_Character(cp, (int)(ep - cp)); 2805 /* to begin of next word */ 2806 s = Get_Start_Of_Data(s, (int)(ep - s)); 2807 if ((s != cp) && !ISBLANK(*s)) 2808 { 2809 /* copy remaining line to window */ 2810 myADDNSTR(form->w, s, (int)(s - After_End_Of_Data(s, (int)(ep - s)))); 2811 } 2812 returnCode(E_OK); 2813 } 2814 2815 /*--------------------------------------------------------------------------- 2816 | Facility : libnform 2817 | Function : static int FE_Clear_To_End_Of_Line(FORM * form) 2818 | 2819 | Description : Clear to end of current line. 2820 | 2821 | Return Values : E_OK - success 2822 +--------------------------------------------------------------------------*/ 2823 static int 2824 FE_Clear_To_End_Of_Line(FORM *form) 2825 { 2826 T((T_CALLED("FE_Clear_To_End_Of_Line(%p)"), form)); 2827 wmove(form->w, form->currow, form->curcol); 2828 wclrtoeol(form->w); 2829 returnCode(E_OK); 2830 } 2831 2832 /*--------------------------------------------------------------------------- 2833 | Facility : libnform 2834 | Function : static int FE_Clear_To_End_Of_Field(FORM * form) 2835 | 2836 | Description : Clear to end of field. 2837 | 2838 | Return Values : E_OK - success 2839 +--------------------------------------------------------------------------*/ 2840 static int 2841 FE_Clear_To_End_Of_Field(FORM *form) 2842 { 2843 T((T_CALLED("FE_Clear_To_End_Of_Field(%p)"), form)); 2844 wmove(form->w, form->currow, form->curcol); 2845 wclrtobot(form->w); 2846 returnCode(E_OK); 2847 } 2848 2849 /*--------------------------------------------------------------------------- 2850 | Facility : libnform 2851 | Function : static int FE_Clear_Field(FORM * form) 2852 | 2853 | Description : Clear entire field. 2854 | 2855 | Return Values : E_OK - success 2856 +--------------------------------------------------------------------------*/ 2857 static int 2858 FE_Clear_Field(FORM *form) 2859 { 2860 T((T_CALLED("FE_Clear_Field(%p)"), form)); 2861 form->currow = form->curcol = 0; 2862 werase(form->w); 2863 returnCode(E_OK); 2864 } 2865 /*---------------------------------------------------------------------------- 2866 END of Field Editing routines 2867 --------------------------------------------------------------------------*/ 2868 2869 /*---------------------------------------------------------------------------- 2870 Edit Mode routines 2871 --------------------------------------------------------------------------*/ 2872 2873 /*--------------------------------------------------------------------------- 2874 | Facility : libnform 2875 | Function : static int EM_Overlay_Mode(FORM * form) 2876 | 2877 | Description : Switch to overlay mode. 2878 | 2879 | Return Values : E_OK - success 2880 +--------------------------------------------------------------------------*/ 2881 static int 2882 EM_Overlay_Mode(FORM *form) 2883 { 2884 T((T_CALLED("EM_Overlay_Mode(%p)"), form)); 2885 form->status |= _OVLMODE; 2886 returnCode(E_OK); 2887 } 2888 2889 /*--------------------------------------------------------------------------- 2890 | Facility : libnform 2891 | Function : static int EM_Insert_Mode(FORM * form) 2892 | 2893 | Description : Switch to insert mode 2894 | 2895 | Return Values : E_OK - success 2896 +--------------------------------------------------------------------------*/ 2897 static int 2898 EM_Insert_Mode(FORM *form) 2899 { 2900 T((T_CALLED("EM_Insert_Mode(%p)"), form)); 2901 form->status &= ~_OVLMODE; 2902 returnCode(E_OK); 2903 } 2904 2905 /*---------------------------------------------------------------------------- 2906 END of Edit Mode routines 2907 --------------------------------------------------------------------------*/ 2908 2909 /*---------------------------------------------------------------------------- 2910 Helper routines for Choice Requests 2911 --------------------------------------------------------------------------*/ 2912 2913 /*--------------------------------------------------------------------------- 2914 | Facility : libnform 2915 | Function : static bool Next_Choice( 2916 | FIELDTYPE * typ, 2917 | FIELD * field, 2918 | TypeArgument *argp) 2919 | 2920 | Description : Get the next field choice. For linked types this is 2921 | done recursively. 2922 | 2923 | Return Values : TRUE - next choice successfully retrieved 2924 | FALSE - couldn't retrieve next choice 2925 +--------------------------------------------------------------------------*/ 2926 static bool 2927 Next_Choice(FIELDTYPE *typ, FIELD *field, TypeArgument *argp) 2928 { 2929 if (!typ || !(typ->status & _HAS_CHOICE)) 2930 return FALSE; 2931 2932 if (typ->status & _LINKED_TYPE) 2933 { 2934 assert(argp); 2935 return ( 2936 Next_Choice(typ->left, field, argp->left) || 2937 Next_Choice(typ->right, field, argp->right)); 2938 } 2939 else 2940 { 2941 assert(typ->next); 2942 return typ->next(field, (void *)argp); 2943 } 2944 } 2945 2946 /*--------------------------------------------------------------------------- 2947 | Facility : libnform 2948 | Function : static bool Previous_Choice( 2949 | FIELDTYPE * typ, 2950 | FIELD * field, 2951 | TypeArgument *argp) 2952 | 2953 | Description : Get the previous field choice. For linked types this 2954 | is done recursively. 2955 | 2956 | Return Values : TRUE - previous choice successfully retrieved 2957 | FALSE - couldn't retrieve previous choice 2958 +--------------------------------------------------------------------------*/ 2959 static bool 2960 Previous_Choice(FIELDTYPE *typ, FIELD *field, TypeArgument *argp) 2961 { 2962 if (!typ || !(typ->status & _HAS_CHOICE)) 2963 return FALSE; 2964 2965 if (typ->status & _LINKED_TYPE) 2966 { 2967 assert(argp); 2968 return ( 2969 Previous_Choice(typ->left, field, argp->left) || 2970 Previous_Choice(typ->right, field, argp->right)); 2971 } 2972 else 2973 { 2974 assert(typ->prev); 2975 return typ->prev(field, (void *)argp); 2976 } 2977 } 2978 /*---------------------------------------------------------------------------- 2979 End of Helper routines for Choice Requests 2980 --------------------------------------------------------------------------*/ 2981 2982 /*---------------------------------------------------------------------------- 2983 Routines for Choice Requests 2984 --------------------------------------------------------------------------*/ 2985 2986 /*--------------------------------------------------------------------------- 2987 | Facility : libnform 2988 | Function : static int CR_Next_Choice(FORM * form) 2989 | 2990 | Description : Get the next field choice. 2991 | 2992 | Return Values : E_OK - success 2993 | E_REQUEST_DENIED - next choice couldn't be retrieved 2994 +--------------------------------------------------------------------------*/ 2995 static int 2996 CR_Next_Choice(FORM *form) 2997 { 2998 FIELD *field = form->current; 2999 3000 T((T_CALLED("CR_Next_Choice(%p)"), form)); 3001 Synchronize_Buffer(form); 3002 returnCode((Next_Choice(field->type, field, (TypeArgument *)(field->arg))) 3003 ? E_OK 3004 : E_REQUEST_DENIED); 3005 } 3006 3007 /*--------------------------------------------------------------------------- 3008 | Facility : libnform 3009 | Function : static int CR_Previous_Choice(FORM * form) 3010 | 3011 | Description : Get the previous field choice. 3012 | 3013 | Return Values : E_OK - success 3014 | E_REQUEST_DENIED - prev. choice couldn't be retrieved 3015 +--------------------------------------------------------------------------*/ 3016 static int 3017 CR_Previous_Choice(FORM *form) 3018 { 3019 FIELD *field = form->current; 3020 3021 T((T_CALLED("CR_Previous_Choice(%p)"), form)); 3022 Synchronize_Buffer(form); 3023 returnCode((Previous_Choice(field->type, field, (TypeArgument *)(field->arg))) 3024 ? E_OK 3025 : E_REQUEST_DENIED); 3026 } 3027 /*---------------------------------------------------------------------------- 3028 End of Routines for Choice Requests 3029 --------------------------------------------------------------------------*/ 3030 3031 /*---------------------------------------------------------------------------- 3032 Helper routines for Field Validations. 3033 --------------------------------------------------------------------------*/ 3034 3035 /*--------------------------------------------------------------------------- 3036 | Facility : libnform 3037 | Function : static bool Check_Field( 3038 | FIELDTYPE * typ, 3039 | FIELD * field, 3040 | TypeArgument * argp) 3041 | 3042 | Description : Check the field according to its fieldtype and its 3043 | actual arguments. For linked fieldtypes this is done 3044 | recursively. 3045 | 3046 | Return Values : TRUE - field is valid 3047 | FALSE - field is invalid. 3048 +--------------------------------------------------------------------------*/ 3049 static bool 3050 Check_Field(FIELDTYPE *typ, FIELD *field, TypeArgument *argp) 3051 { 3052 if (typ) 3053 { 3054 if (field->opts & O_NULLOK) 3055 { 3056 FIELD_CELL *bp = field->buf; 3057 3058 assert(bp); 3059 while (ISBLANK(*bp)) 3060 { 3061 bp++; 3062 } 3063 if (CharOf(*bp) == 0) 3064 return TRUE; 3065 } 3066 3067 if (typ->status & _LINKED_TYPE) 3068 { 3069 assert(argp); 3070 return ( 3071 Check_Field(typ->left, field, argp->left) || 3072 Check_Field(typ->right, field, argp->right)); 3073 } 3074 else 3075 { 3076 if (typ->fcheck) 3077 return typ->fcheck(field, (void *)argp); 3078 } 3079 } 3080 return TRUE; 3081 } 3082 3083 /*--------------------------------------------------------------------------- 3084 | Facility : libnform 3085 | Function : bool _nc_Internal_Validation(FORM * form ) 3086 | 3087 | Description : Validate the current field of the form. 3088 | 3089 | Return Values : TRUE - field is valid 3090 | FALSE - field is invalid 3091 +--------------------------------------------------------------------------*/ 3092 NCURSES_EXPORT(bool) 3093 _nc_Internal_Validation(FORM *form) 3094 { 3095 FIELD *field; 3096 3097 field = form->current; 3098 3099 Synchronize_Buffer(form); 3100 if ((form->status & _FCHECK_REQUIRED) || 3101 (!(field->opts & O_PASSOK))) 3102 { 3103 if (!Check_Field(field->type, field, (TypeArgument *)(field->arg))) 3104 return FALSE; 3105 form->status &= ~_FCHECK_REQUIRED; 3106 field->status |= _CHANGED; 3107 Synchronize_Linked_Fields(field); 3108 } 3109 return TRUE; 3110 } 3111 /*---------------------------------------------------------------------------- 3112 End of Helper routines for Field Validations. 3113 --------------------------------------------------------------------------*/ 3114 3115 /*---------------------------------------------------------------------------- 3116 Routines for Field Validation. 3117 --------------------------------------------------------------------------*/ 3118 3119 /*--------------------------------------------------------------------------- 3120 | Facility : libnform 3121 | Function : static int FV_Validation(FORM * form) 3122 | 3123 | Description : Validate the current field of the form. 3124 | 3125 | Return Values : E_OK - field valid 3126 | E_INVALID_FIELD - field not valid 3127 +--------------------------------------------------------------------------*/ 3128 static int 3129 FV_Validation(FORM *form) 3130 { 3131 T((T_CALLED("FV_Validation(%p)"), form)); 3132 if (_nc_Internal_Validation(form)) 3133 returnCode(E_OK); 3134 else 3135 returnCode(E_INVALID_FIELD); 3136 } 3137 /*---------------------------------------------------------------------------- 3138 End of routines for Field Validation. 3139 --------------------------------------------------------------------------*/ 3140 3141 /*---------------------------------------------------------------------------- 3142 Helper routines for Inter-Field Navigation 3143 --------------------------------------------------------------------------*/ 3144 3145 /*--------------------------------------------------------------------------- 3146 | Facility : libnform 3147 | Function : static FIELD *Next_Field_On_Page(FIELD * field) 3148 | 3149 | Description : Get the next field after the given field on the current 3150 | page. The order of fields is the one defined by the 3151 | fields array. Only visible and active fields are 3152 | counted. 3153 | 3154 | Return Values : Pointer to the next field. 3155 +--------------------------------------------------------------------------*/ 3156 NCURSES_INLINE static FIELD * 3157 Next_Field_On_Page(FIELD *field) 3158 { 3159 FORM *form = field->form; 3160 FIELD **field_on_page = &form->field[field->index]; 3161 FIELD **first_on_page = &form->field[form->page[form->curpage].pmin]; 3162 FIELD **last_on_page = &form->field[form->page[form->curpage].pmax]; 3163 3164 do 3165 { 3166 field_on_page = 3167 (field_on_page == last_on_page) ? first_on_page : field_on_page + 1; 3168 if (Field_Is_Selectable(*field_on_page)) 3169 break; 3170 } 3171 while (field != (*field_on_page)); 3172 return (*field_on_page); 3173 } 3174 3175 /*--------------------------------------------------------------------------- 3176 | Facility : libnform 3177 | Function : FIELD* _nc_First_Active_Field(FORM * form) 3178 | 3179 | Description : Get the first active field on the current page, 3180 | if there are such. If there are none, get the first 3181 | visible field on the page. If there are also none, 3182 | we return the first field on page and hope the best. 3183 | 3184 | Return Values : Pointer to calculated field. 3185 +--------------------------------------------------------------------------*/ 3186 NCURSES_EXPORT(FIELD *) 3187 _nc_First_Active_Field(FORM *form) 3188 { 3189 FIELD **last_on_page = &form->field[form->page[form->curpage].pmax]; 3190 FIELD *proposed = Next_Field_On_Page(*last_on_page); 3191 3192 if (proposed == *last_on_page) 3193 { 3194 /* there might be the special situation, where there is no 3195 active and visible field on the current page. We then select 3196 the first visible field on this readonly page 3197 */ 3198 if (Field_Is_Not_Selectable(proposed)) 3199 { 3200 FIELD **field = &form->field[proposed->index]; 3201 FIELD **first = &form->field[form->page[form->curpage].pmin]; 3202 3203 do 3204 { 3205 field = (field == last_on_page) ? first : field + 1; 3206 if (((*field)->opts & O_VISIBLE)) 3207 break; 3208 } 3209 while (proposed != (*field)); 3210 3211 proposed = *field; 3212 3213 if ((proposed == *last_on_page) && !(proposed->opts & O_VISIBLE)) 3214 { 3215 /* This means, there is also no visible field on the page. 3216 So we propose the first one and hope the very best... 3217 Some very clever user has designed a readonly and invisible 3218 page on this form. 3219 */ 3220 proposed = *first; 3221 } 3222 } 3223 } 3224 return (proposed); 3225 } 3226 3227 /*--------------------------------------------------------------------------- 3228 | Facility : libnform 3229 | Function : static FIELD *Previous_Field_On_Page(FIELD * field) 3230 | 3231 | Description : Get the previous field before the given field on the 3232 | current page. The order of fields is the one defined by 3233 | the fields array. Only visible and active fields are 3234 | counted. 3235 | 3236 | Return Values : Pointer to the previous field. 3237 +--------------------------------------------------------------------------*/ 3238 NCURSES_INLINE static FIELD * 3239 Previous_Field_On_Page(FIELD *field) 3240 { 3241 FORM *form = field->form; 3242 FIELD **field_on_page = &form->field[field->index]; 3243 FIELD **first_on_page = &form->field[form->page[form->curpage].pmin]; 3244 FIELD **last_on_page = &form->field[form->page[form->curpage].pmax]; 3245 3246 do 3247 { 3248 field_on_page = 3249 (field_on_page == first_on_page) ? last_on_page : field_on_page - 1; 3250 if (Field_Is_Selectable(*field_on_page)) 3251 break; 3252 } 3253 while (field != (*field_on_page)); 3254 3255 return (*field_on_page); 3256 } 3257 3258 /*--------------------------------------------------------------------------- 3259 | Facility : libnform 3260 | Function : static FIELD *Sorted_Next_Field(FIELD * field) 3261 | 3262 | Description : Get the next field after the given field on the current 3263 | page. The order of fields is the one defined by the 3264 | (row,column) geometry, rows are major. 3265 | 3266 | Return Values : Pointer to the next field. 3267 +--------------------------------------------------------------------------*/ 3268 NCURSES_INLINE static FIELD * 3269 Sorted_Next_Field(FIELD *field) 3270 { 3271 FIELD *field_on_page = field; 3272 3273 do 3274 { 3275 field_on_page = field_on_page->snext; 3276 if (Field_Is_Selectable(field_on_page)) 3277 break; 3278 } 3279 while (field_on_page != field); 3280 3281 return (field_on_page); 3282 } 3283 3284 /*--------------------------------------------------------------------------- 3285 | Facility : libnform 3286 | Function : static FIELD *Sorted_Previous_Field(FIELD * field) 3287 | 3288 | Description : Get the previous field before the given field on the 3289 | current page. The order of fields is the one defined 3290 | by the (row,column) geometry, rows are major. 3291 | 3292 | Return Values : Pointer to the previous field. 3293 +--------------------------------------------------------------------------*/ 3294 NCURSES_INLINE static FIELD * 3295 Sorted_Previous_Field(FIELD *field) 3296 { 3297 FIELD *field_on_page = field; 3298 3299 do 3300 { 3301 field_on_page = field_on_page->sprev; 3302 if (Field_Is_Selectable(field_on_page)) 3303 break; 3304 } 3305 while (field_on_page != field); 3306 3307 return (field_on_page); 3308 } 3309 3310 /*--------------------------------------------------------------------------- 3311 | Facility : libnform 3312 | Function : static FIELD *Left_Neighbor_Field(FIELD * field) 3313 | 3314 | Description : Get the left neighbor of the field on the same line 3315 | and the same page. Cycles through the line. 3316 | 3317 | Return Values : Pointer to left neighbor field. 3318 +--------------------------------------------------------------------------*/ 3319 NCURSES_INLINE static FIELD * 3320 Left_Neighbor_Field(FIELD *field) 3321 { 3322 FIELD *field_on_page = field; 3323 3324 /* For a field that has really a left neighbor, the while clause 3325 immediately fails and the loop is left, positioned at the right 3326 neighbor. Otherwise we cycle backwards through the sorted field list 3327 until we enter the same line (from the right end). 3328 */ 3329 do 3330 { 3331 field_on_page = Sorted_Previous_Field(field_on_page); 3332 } 3333 while (field_on_page->frow != field->frow); 3334 3335 return (field_on_page); 3336 } 3337 3338 /*--------------------------------------------------------------------------- 3339 | Facility : libnform 3340 | Function : static FIELD *Right_Neighbor_Field(FIELD * field) 3341 | 3342 | Description : Get the right neighbor of the field on the same line 3343 | and the same page. 3344 | 3345 | Return Values : Pointer to right neighbor field. 3346 +--------------------------------------------------------------------------*/ 3347 NCURSES_INLINE static FIELD * 3348 Right_Neighbor_Field(FIELD *field) 3349 { 3350 FIELD *field_on_page = field; 3351 3352 /* See the comments on Left_Neighbor_Field to understand how it works */ 3353 do 3354 { 3355 field_on_page = Sorted_Next_Field(field_on_page); 3356 } 3357 while (field_on_page->frow != field->frow); 3358 3359 return (field_on_page); 3360 } 3361 3362 /*--------------------------------------------------------------------------- 3363 | Facility : libnform 3364 | Function : static FIELD *Upper_Neighbor_Field(FIELD * field) 3365 | 3366 | Description : Because of the row-major nature of sorting the fields, 3367 | it is more difficult to define whats the upper neighbor 3368 | field really means. We define that it must be on a 3369 | 'previous' line (cyclic order!) and is the rightmost 3370 | field laying on the left side of the given field. If 3371 | this set is empty, we take the first field on the line. 3372 | 3373 | Return Values : Pointer to the upper neighbor field. 3374 +--------------------------------------------------------------------------*/ 3375 static FIELD * 3376 Upper_Neighbor_Field(FIELD *field) 3377 { 3378 FIELD *field_on_page = field; 3379 int frow = field->frow; 3380 int fcol = field->fcol; 3381 3382 /* Walk back to the 'previous' line. The second term in the while clause 3383 just guarantees that we stop if we cycled through the line because 3384 there might be no 'previous' line if the page has just one line. 3385 */ 3386 do 3387 { 3388 field_on_page = Sorted_Previous_Field(field_on_page); 3389 } 3390 while (field_on_page->frow == frow && field_on_page->fcol != fcol); 3391 3392 if (field_on_page->frow != frow) 3393 { 3394 /* We really found a 'previous' line. We are positioned at the 3395 rightmost field on this line */ 3396 frow = field_on_page->frow; 3397 3398 /* We walk to the left as long as we are really right of the 3399 field. */ 3400 while (field_on_page->frow == frow && field_on_page->fcol > fcol) 3401 field_on_page = Sorted_Previous_Field(field_on_page); 3402 3403 /* If we wrapped, just go to the right which is the first field on 3404 the row */ 3405 if (field_on_page->frow != frow) 3406 field_on_page = Sorted_Next_Field(field_on_page); 3407 } 3408 3409 return (field_on_page); 3410 } 3411 3412 /*--------------------------------------------------------------------------- 3413 | Facility : libnform 3414 | Function : static FIELD *Down_Neighbor_Field(FIELD * field) 3415 | 3416 | Description : Because of the row-major nature of sorting the fields, 3417 | its more difficult to define whats the down neighbor 3418 | field really means. We define that it must be on a 3419 | 'next' line (cyclic order!) and is the leftmost 3420 | field laying on the right side of the given field. If 3421 | this set is empty, we take the last field on the line. 3422 | 3423 | Return Values : Pointer to the upper neighbor field. 3424 +--------------------------------------------------------------------------*/ 3425 static FIELD * 3426 Down_Neighbor_Field(FIELD *field) 3427 { 3428 FIELD *field_on_page = field; 3429 int frow = field->frow; 3430 int fcol = field->fcol; 3431 3432 /* Walk forward to the 'next' line. The second term in the while clause 3433 just guarantees that we stop if we cycled through the line because 3434 there might be no 'next' line if the page has just one line. 3435 */ 3436 do 3437 { 3438 field_on_page = Sorted_Next_Field(field_on_page); 3439 } 3440 while (field_on_page->frow == frow && field_on_page->fcol != fcol); 3441 3442 if (field_on_page->frow != frow) 3443 { 3444 /* We really found a 'next' line. We are positioned at the rightmost 3445 field on this line */ 3446 frow = field_on_page->frow; 3447 3448 /* We walk to the right as long as we are really left of the 3449 field. */ 3450 while (field_on_page->frow == frow && field_on_page->fcol < fcol) 3451 field_on_page = Sorted_Next_Field(field_on_page); 3452 3453 /* If we wrapped, just go to the left which is the last field on 3454 the row */ 3455 if (field_on_page->frow != frow) 3456 field_on_page = Sorted_Previous_Field(field_on_page); 3457 } 3458 3459 return (field_on_page); 3460 } 3461 3462 /*---------------------------------------------------------------------------- 3463 Inter-Field Navigation routines 3464 --------------------------------------------------------------------------*/ 3465 3466 /*--------------------------------------------------------------------------- 3467 | Facility : libnform 3468 | Function : static int Inter_Field_Navigation( 3469 | int (* const fct) (FORM *), 3470 | FORM * form) 3471 | 3472 | Description : Generic behavior for changing the current field, the 3473 | field is left and a new field is entered. So the field 3474 | must be validated and the field init/term hooks must 3475 | be called. 3476 | 3477 | Return Values : E_OK - success 3478 | E_INVALID_FIELD - field is invalid 3479 | some other - error from subordinate call 3480 +--------------------------------------------------------------------------*/ 3481 static int 3482 Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form) 3483 { 3484 int res; 3485 3486 if (!_nc_Internal_Validation(form)) 3487 res = E_INVALID_FIELD; 3488 else 3489 { 3490 Call_Hook(form, fieldterm); 3491 res = fct(form); 3492 Call_Hook(form, fieldinit); 3493 } 3494 return res; 3495 } 3496 3497 /*--------------------------------------------------------------------------- 3498 | Facility : libnform 3499 | Function : static int FN_Next_Field(FORM * form) 3500 | 3501 | Description : Move to the next field on the current page of the form 3502 | 3503 | Return Values : E_OK - success 3504 | != E_OK - error from subordinate call 3505 +--------------------------------------------------------------------------*/ 3506 static int 3507 FN_Next_Field(FORM *form) 3508 { 3509 T((T_CALLED("FN_Next_Field(%p)"), form)); 3510 returnCode(_nc_Set_Current_Field(form, 3511 Next_Field_On_Page(form->current))); 3512 } 3513 3514 /*--------------------------------------------------------------------------- 3515 | Facility : libnform 3516 | Function : static int FN_Previous_Field(FORM * form) 3517 | 3518 | Description : Move to the previous field on the current page of the 3519 | form 3520 | 3521 | Return Values : E_OK - success 3522 | != E_OK - error from subordinate call 3523 +--------------------------------------------------------------------------*/ 3524 static int 3525 FN_Previous_Field(FORM *form) 3526 { 3527 T((T_CALLED("FN_Previous_Field(%p)"), form)); 3528 returnCode(_nc_Set_Current_Field(form, 3529 Previous_Field_On_Page(form->current))); 3530 } 3531 3532 /*--------------------------------------------------------------------------- 3533 | Facility : libnform 3534 | Function : static int FN_First_Field(FORM * form) 3535 | 3536 | Description : Move to the first field on the current page of the form 3537 | 3538 | Return Values : E_OK - success 3539 | != E_OK - error from subordinate call 3540 +--------------------------------------------------------------------------*/ 3541 static int 3542 FN_First_Field(FORM *form) 3543 { 3544 T((T_CALLED("FN_First_Field(%p)"), form)); 3545 returnCode(_nc_Set_Current_Field(form, 3546 Next_Field_On_Page(form->field[form->page[form->curpage].pmax]))); 3547 } 3548 3549 /*--------------------------------------------------------------------------- 3550 | Facility : libnform 3551 | Function : static int FN_Last_Field(FORM * form) 3552 | 3553 | Description : Move to the last field on the current page of the form 3554 | 3555 | Return Values : E_OK - success 3556 | != E_OK - error from subordinate call 3557 +--------------------------------------------------------------------------*/ 3558 static int 3559 FN_Last_Field(FORM *form) 3560 { 3561 T((T_CALLED("FN_Last_Field(%p)"), form)); 3562 returnCode( 3563 _nc_Set_Current_Field(form, 3564 Previous_Field_On_Page(form->field[form->page[form->curpage].pmin]))); 3565 } 3566 3567 /*--------------------------------------------------------------------------- 3568 | Facility : libnform 3569 | Function : static int FN_Sorted_Next_Field(FORM * form) 3570 | 3571 | Description : Move to the sorted next field on the current page 3572 | of the form. 3573 | 3574 | Return Values : E_OK - success 3575 | != E_OK - error from subordinate call 3576 +--------------------------------------------------------------------------*/ 3577 static int 3578 FN_Sorted_Next_Field(FORM *form) 3579 { 3580 T((T_CALLED("FN_Sorted_Next_Field(%p)"), form)); 3581 returnCode(_nc_Set_Current_Field(form, 3582 Sorted_Next_Field(form->current))); 3583 } 3584 3585 /*--------------------------------------------------------------------------- 3586 | Facility : libnform 3587 | Function : static int FN_Sorted_Previous_Field(FORM * form) 3588 | 3589 | Description : Move to the sorted previous field on the current page 3590 | of the form. 3591 | 3592 | Return Values : E_OK - success 3593 | != E_OK - error from subordinate call 3594 +--------------------------------------------------------------------------*/ 3595 static int 3596 FN_Sorted_Previous_Field(FORM *form) 3597 { 3598 T((T_CALLED("FN_Sorted_Previous_Field(%p)"), form)); 3599 returnCode(_nc_Set_Current_Field(form, 3600 Sorted_Previous_Field(form->current))); 3601 } 3602 3603 /*--------------------------------------------------------------------------- 3604 | Facility : libnform 3605 | Function : static int FN_Sorted_First_Field(FORM * form) 3606 | 3607 | Description : Move to the sorted first field on the current page 3608 | of the form. 3609 | 3610 | Return Values : E_OK - success 3611 | != E_OK - error from subordinate call 3612 +--------------------------------------------------------------------------*/ 3613 static int 3614 FN_Sorted_First_Field(FORM *form) 3615 { 3616 T((T_CALLED("FN_Sorted_First_Field(%p)"), form)); 3617 returnCode(_nc_Set_Current_Field(form, 3618 Sorted_Next_Field(form->field[form->page[form->curpage].smax]))); 3619 } 3620 3621 /*--------------------------------------------------------------------------- 3622 | Facility : libnform 3623 | Function : static int FN_Sorted_Last_Field(FORM * form) 3624 | 3625 | Description : Move to the sorted last field on the current page 3626 | of the form. 3627 | 3628 | Return Values : E_OK - success 3629 | != E_OK - error from subordinate call 3630 +--------------------------------------------------------------------------*/ 3631 static int 3632 FN_Sorted_Last_Field(FORM *form) 3633 { 3634 T((T_CALLED("FN_Sorted_Last_Field(%p)"), form)); 3635 returnCode(_nc_Set_Current_Field(form, 3636 Sorted_Previous_Field(form->field[form->page[form->curpage].smin]))); 3637 } 3638 3639 /*--------------------------------------------------------------------------- 3640 | Facility : libnform 3641 | Function : static int FN_Left_Field(FORM * form) 3642 | 3643 | Description : Get the field on the left of the current field on the 3644 | same line and the same page. Cycles through the line. 3645 | 3646 | Return Values : E_OK - success 3647 | != E_OK - error from subordinate call 3648 +--------------------------------------------------------------------------*/ 3649 static int 3650 FN_Left_Field(FORM *form) 3651 { 3652 T((T_CALLED("FN_Left_Field(%p)"), form)); 3653 returnCode(_nc_Set_Current_Field(form, 3654 Left_Neighbor_Field(form->current))); 3655 } 3656 3657 /*--------------------------------------------------------------------------- 3658 | Facility : libnform 3659 | Function : static int FN_Right_Field(FORM * form) 3660 | 3661 | Description : Get the field on the right of the current field on the 3662 | same line and the same page. Cycles through the line. 3663 | 3664 | Return Values : E_OK - success 3665 | != E_OK - error from subordinate call 3666 +--------------------------------------------------------------------------*/ 3667 static int 3668 FN_Right_Field(FORM *form) 3669 { 3670 T((T_CALLED("FN_Right_Field(%p)"), form)); 3671 returnCode(_nc_Set_Current_Field(form, 3672 Right_Neighbor_Field(form->current))); 3673 } 3674 3675 /*--------------------------------------------------------------------------- 3676 | Facility : libnform 3677 | Function : static int FN_Up_Field(FORM * form) 3678 | 3679 | Description : Get the upper neighbor of the current field. This 3680 | cycles through the page. See the comments of the 3681 | Upper_Neighbor_Field function to understand how 3682 | 'upper' is defined. 3683 | 3684 | Return Values : E_OK - success 3685 | != E_OK - error from subordinate call 3686 +--------------------------------------------------------------------------*/ 3687 static int 3688 FN_Up_Field(FORM *form) 3689 { 3690 T((T_CALLED("FN_Up_Field(%p)"), form)); 3691 returnCode(_nc_Set_Current_Field(form, 3692 Upper_Neighbor_Field(form->current))); 3693 } 3694 3695 /*--------------------------------------------------------------------------- 3696 | Facility : libnform 3697 | Function : static int FN_Down_Field(FORM * form) 3698 | 3699 | Description : Get the down neighbor of the current field. This 3700 | cycles through the page. See the comments of the 3701 | Down_Neighbor_Field function to understand how 3702 | 'down' is defined. 3703 | 3704 | Return Values : E_OK - success 3705 | != E_OK - error from subordinate call 3706 +--------------------------------------------------------------------------*/ 3707 static int 3708 FN_Down_Field(FORM *form) 3709 { 3710 T((T_CALLED("FN_Down_Field(%p)"), form)); 3711 returnCode(_nc_Set_Current_Field(form, 3712 Down_Neighbor_Field(form->current))); 3713 } 3714 /*---------------------------------------------------------------------------- 3715 END of Field Navigation routines 3716 --------------------------------------------------------------------------*/ 3717 3718 /*---------------------------------------------------------------------------- 3719 Helper routines for Page Navigation 3720 --------------------------------------------------------------------------*/ 3721 3722 /*--------------------------------------------------------------------------- 3723 | Facility : libnform 3724 | Function : int _nc_Set_Form_Page(FORM * form, 3725 | int page, 3726 | FIELD * field) 3727 | 3728 | Description : Make the given page number the current page and make 3729 | the given field the current field on the page. If 3730 | for the field NULL is given, make the first field on 3731 | the page the current field. The routine acts only 3732 | if the requested page is not the current page. 3733 | 3734 | Return Values : E_OK - success 3735 | != E_OK - error from subordinate call 3736 | E_BAD_ARGUMENT - invalid field pointer 3737 | E_SYSTEM_ERROR - some severe basic error 3738 +--------------------------------------------------------------------------*/ 3739 NCURSES_EXPORT(int) 3740 _nc_Set_Form_Page(FORM *form, int page, FIELD *field) 3741 { 3742 int res = E_OK; 3743 3744 if ((form->curpage != page)) 3745 { 3746 FIELD *last_field, *field_on_page; 3747 3748 werase(Get_Form_Window(form)); 3749 form->curpage = page; 3750 last_field = field_on_page = form->field[form->page[page].smin]; 3751 do 3752 { 3753 if (field_on_page->opts & O_VISIBLE) 3754 if ((res = Display_Field(field_on_page)) != E_OK) 3755 return (res); 3756 field_on_page = field_on_page->snext; 3757 } 3758 while (field_on_page != last_field); 3759 3760 if (field) 3761 res = _nc_Set_Current_Field(form, field); 3762 else 3763 /* N.B.: we don't encapsulate this by Inter_Field_Navigation(), 3764 because this is already executed in a page navigation 3765 context that contains field navigation 3766 */ 3767 res = FN_First_Field(form); 3768 } 3769 return (res); 3770 } 3771 3772 /*--------------------------------------------------------------------------- 3773 | Facility : libnform 3774 | Function : static int Next_Page_Number(const FORM * form) 3775 | 3776 | Description : Calculate the page number following the current page 3777 | number. This cycles if the highest page number is 3778 | reached. 3779 | 3780 | Return Values : The next page number 3781 +--------------------------------------------------------------------------*/ 3782 NCURSES_INLINE static int 3783 Next_Page_Number(const FORM *form) 3784 { 3785 return (form->curpage + 1) % form->maxpage; 3786 } 3787 3788 /*--------------------------------------------------------------------------- 3789 | Facility : libnform 3790 | Function : static int Previous_Page_Number(const FORM * form) 3791 | 3792 | Description : Calculate the page number before the current page 3793 | number. This cycles if the first page number is 3794 | reached. 3795 | 3796 | Return Values : The previous page number 3797 +--------------------------------------------------------------------------*/ 3798 NCURSES_INLINE static int 3799 Previous_Page_Number(const FORM *form) 3800 { 3801 return (form->curpage != 0 ? form->curpage - 1 : form->maxpage - 1); 3802 } 3803 3804 /*---------------------------------------------------------------------------- 3805 Page Navigation routines 3806 --------------------------------------------------------------------------*/ 3807 3808 /*--------------------------------------------------------------------------- 3809 | Facility : libnform 3810 | Function : static int Page_Navigation( 3811 | int (* const fct) (FORM *), 3812 | FORM * form) 3813 | 3814 | Description : Generic behavior for changing a page. This means 3815 | that the field is left and a new field is entered. 3816 | So the field must be validated and the field init/term 3817 | hooks must be called. Because also the page is changed, 3818 | the forms init/term hooks must be called also. 3819 | 3820 | Return Values : E_OK - success 3821 | E_INVALID_FIELD - field is invalid 3822 | some other - error from subordinate call 3823 +--------------------------------------------------------------------------*/ 3824 static int 3825 Page_Navigation(int (*const fct) (FORM *), FORM *form) 3826 { 3827 int res; 3828 3829 if (!_nc_Internal_Validation(form)) 3830 res = E_INVALID_FIELD; 3831 else 3832 { 3833 Call_Hook(form, fieldterm); 3834 Call_Hook(form, formterm); 3835 res = fct(form); 3836 Call_Hook(form, forminit); 3837 Call_Hook(form, fieldinit); 3838 } 3839 return res; 3840 } 3841 3842 /*--------------------------------------------------------------------------- 3843 | Facility : libnform 3844 | Function : static int PN_Next_Page(FORM * form) 3845 | 3846 | Description : Move to the next page of the form 3847 | 3848 | Return Values : E_OK - success 3849 | != E_OK - error from subordinate call 3850 +--------------------------------------------------------------------------*/ 3851 static int 3852 PN_Next_Page(FORM *form) 3853 { 3854 T((T_CALLED("PN_Next_Page(%p)"), form)); 3855 returnCode(_nc_Set_Form_Page(form, Next_Page_Number(form), (FIELD *)0)); 3856 } 3857 3858 /*--------------------------------------------------------------------------- 3859 | Facility : libnform 3860 | Function : static int PN_Previous_Page(FORM * form) 3861 | 3862 | Description : Move to the previous page of the form 3863 | 3864 | Return Values : E_OK - success 3865 | != E_OK - error from subordinate call 3866 +--------------------------------------------------------------------------*/ 3867 static int 3868 PN_Previous_Page(FORM *form) 3869 { 3870 T((T_CALLED("PN_Previous_Page(%p)"), form)); 3871 returnCode(_nc_Set_Form_Page(form, Previous_Page_Number(form), (FIELD *)0)); 3872 } 3873 3874 /*--------------------------------------------------------------------------- 3875 | Facility : libnform 3876 | Function : static int PN_First_Page(FORM * form) 3877 | 3878 | Description : Move to the first page of the form 3879 | 3880 | Return Values : E_OK - success 3881 | != E_OK - error from subordinate call 3882 +--------------------------------------------------------------------------*/ 3883 static int 3884 PN_First_Page(FORM *form) 3885 { 3886 T((T_CALLED("PN_First_Page(%p)"), form)); 3887 returnCode(_nc_Set_Form_Page(form, 0, (FIELD *)0)); 3888 } 3889 3890 /*--------------------------------------------------------------------------- 3891 | Facility : libnform 3892 | Function : static int PN_Last_Page(FORM * form) 3893 | 3894 | Description : Move to the last page of the form 3895 | 3896 | Return Values : E_OK - success 3897 | != E_OK - error from subordinate call 3898 +--------------------------------------------------------------------------*/ 3899 static int 3900 PN_Last_Page(FORM *form) 3901 { 3902 T((T_CALLED("PN_Last_Page(%p)"), form)); 3903 returnCode(_nc_Set_Form_Page(form, form->maxpage - 1, (FIELD *)0)); 3904 } 3905 3906 /*---------------------------------------------------------------------------- 3907 END of Field Navigation routines 3908 --------------------------------------------------------------------------*/ 3909 3910 /*---------------------------------------------------------------------------- 3911 Helper routines for the core form driver. 3912 --------------------------------------------------------------------------*/ 3913 3914 /*--------------------------------------------------------------------------- 3915 | Facility : libnform 3916 | Function : static int Data_Entry(FORM * form,int c) 3917 | 3918 | Description : Enter character c into at the current position of the 3919 | current field of the form. 3920 | 3921 | Return Values : E_OK - success 3922 | E_REQUEST_DENIED - driver could not process the request 3923 | E_SYSTEM_ERROR - 3924 +--------------------------------------------------------------------------*/ 3925 static int 3926 Data_Entry(FORM *form, int c) 3927 { 3928 FIELD *field = form->current; 3929 int result = E_REQUEST_DENIED; 3930 3931 T((T_CALLED("Data_Entry(%p,%s)"), form, _tracechtype((chtype)c))); 3932 if ((field->opts & O_EDIT) 3933 #if FIX_FORM_INACTIVE_BUG 3934 && (field->opts & O_ACTIVE) 3935 #endif 3936 ) 3937 { 3938 if ((field->opts & O_BLANK) && 3939 First_Position_In_Current_Field(form) && 3940 !(form->status & _FCHECK_REQUIRED) && 3941 !(form->status & _WINDOW_MODIFIED)) 3942 werase(form->w); 3943 3944 if (form->status & _OVLMODE) 3945 { 3946 waddch(form->w, (chtype)c); 3947 } 3948 else 3949 /* no _OVLMODE */ 3950 { 3951 bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form); 3952 3953 if (!(There_Is_Room || 3954 ((Single_Line_Field(field) && Growable(field))))) 3955 RETURN(E_REQUEST_DENIED); 3956 3957 if (!There_Is_Room && !Field_Grown(field, 1)) 3958 RETURN(E_SYSTEM_ERROR); 3959 3960 winsch(form->w, (chtype)c); 3961 } 3962 3963 if ((result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form)) == E_OK) 3964 { 3965 bool End_Of_Field = (((field->drows - 1) == form->currow) && 3966 ((field->dcols - 1) == form->curcol)); 3967 3968 form->status |= _WINDOW_MODIFIED; 3969 if (End_Of_Field && !Growable(field) && (field->opts & O_AUTOSKIP)) 3970 result = Inter_Field_Navigation(FN_Next_Field, form); 3971 else 3972 { 3973 if (End_Of_Field && Growable(field) && !Field_Grown(field, 1)) 3974 result = E_SYSTEM_ERROR; 3975 else 3976 { 3977 #if USE_WIDEC_SUPPORT 3978 /* 3979 * We have just added a byte to the form field. It may have 3980 * been part of a multibyte character. If it was, the 3981 * addch_used field is nonzero and we should not try to move 3982 * to a new column. 3983 */ 3984 if (WINDOW_EXT(form->w, addch_used) == 0) 3985 IFN_Next_Character(form); 3986 #else 3987 IFN_Next_Character(form); 3988 #endif 3989 result = E_OK; 3990 } 3991 } 3992 } 3993 } 3994 RETURN(result); 3995 } 3996 3997 /* Structure to describe the binding of a request code to a function. 3998 The member keycode codes the request value as well as the generic 3999 routine to use for the request. The code for the generic routine 4000 is coded in the upper 16 Bits while the request code is coded in 4001 the lower 16 bits. 4002 4003 In terms of C++ you might think of a request as a class with a 4004 virtual method "perform". The different types of request are 4005 derived from this base class and overload (or not) the base class 4006 implementation of perform. 4007 */ 4008 typedef struct 4009 { 4010 int keycode; /* must be at least 32 bit: hi:mode, lo: key */ 4011 int (*cmd) (FORM *); /* low level driver routine for this key */ 4012 } 4013 Binding_Info; 4014 4015 /* You may see this is the class-id of the request type class */ 4016 #define ID_PN (0x00000000) /* Page navigation */ 4017 #define ID_FN (0x00010000) /* Inter-Field navigation */ 4018 #define ID_IFN (0x00020000) /* Intra-Field navigation */ 4019 #define ID_VSC (0x00030000) /* Vertical Scrolling */ 4020 #define ID_HSC (0x00040000) /* Horizontal Scrolling */ 4021 #define ID_FE (0x00050000) /* Field Editing */ 4022 #define ID_EM (0x00060000) /* Edit Mode */ 4023 #define ID_FV (0x00070000) /* Field Validation */ 4024 #define ID_CH (0x00080000) /* Choice */ 4025 #define ID_Mask (0xffff0000) 4026 #define Key_Mask (0x0000ffff) 4027 #define ID_Shft (16) 4028 4029 /* This array holds all the Binding Infos */ 4030 /* *INDENT-OFF* */ 4031 static const Binding_Info bindings[MAX_FORM_COMMAND - MIN_FORM_COMMAND + 1] = 4032 { 4033 { REQ_NEXT_PAGE |ID_PN ,PN_Next_Page}, 4034 { REQ_PREV_PAGE |ID_PN ,PN_Previous_Page}, 4035 { REQ_FIRST_PAGE |ID_PN ,PN_First_Page}, 4036 { REQ_LAST_PAGE |ID_PN ,PN_Last_Page}, 4037 4038 { REQ_NEXT_FIELD |ID_FN ,FN_Next_Field}, 4039 { REQ_PREV_FIELD |ID_FN ,FN_Previous_Field}, 4040 { REQ_FIRST_FIELD |ID_FN ,FN_First_Field}, 4041 { REQ_LAST_FIELD |ID_FN ,FN_Last_Field}, 4042 { REQ_SNEXT_FIELD |ID_FN ,FN_Sorted_Next_Field}, 4043 { REQ_SPREV_FIELD |ID_FN ,FN_Sorted_Previous_Field}, 4044 { REQ_SFIRST_FIELD |ID_FN ,FN_Sorted_First_Field}, 4045 { REQ_SLAST_FIELD |ID_FN ,FN_Sorted_Last_Field}, 4046 { REQ_LEFT_FIELD |ID_FN ,FN_Left_Field}, 4047 { REQ_RIGHT_FIELD |ID_FN ,FN_Right_Field}, 4048 { REQ_UP_FIELD |ID_FN ,FN_Up_Field}, 4049 { REQ_DOWN_FIELD |ID_FN ,FN_Down_Field}, 4050 4051 { REQ_NEXT_CHAR |ID_IFN ,IFN_Next_Character}, 4052 { REQ_PREV_CHAR |ID_IFN ,IFN_Previous_Character}, 4053 { REQ_NEXT_LINE |ID_IFN ,IFN_Next_Line}, 4054 { REQ_PREV_LINE |ID_IFN ,IFN_Previous_Line}, 4055 { REQ_NEXT_WORD |ID_IFN ,IFN_Next_Word}, 4056 { REQ_PREV_WORD |ID_IFN ,IFN_Previous_Word}, 4057 { REQ_BEG_FIELD |ID_IFN ,IFN_Beginning_Of_Field}, 4058 { REQ_END_FIELD |ID_IFN ,IFN_End_Of_Field}, 4059 { REQ_BEG_LINE |ID_IFN ,IFN_Beginning_Of_Line}, 4060 { REQ_END_LINE |ID_IFN ,IFN_End_Of_Line}, 4061 { REQ_LEFT_CHAR |ID_IFN ,IFN_Left_Character}, 4062 { REQ_RIGHT_CHAR |ID_IFN ,IFN_Right_Character}, 4063 { REQ_UP_CHAR |ID_IFN ,IFN_Up_Character}, 4064 { REQ_DOWN_CHAR |ID_IFN ,IFN_Down_Character}, 4065 4066 { REQ_NEW_LINE |ID_FE ,FE_New_Line}, 4067 { REQ_INS_CHAR |ID_FE ,FE_Insert_Character}, 4068 { REQ_INS_LINE |ID_FE ,FE_Insert_Line}, 4069 { REQ_DEL_CHAR |ID_FE ,FE_Delete_Character}, 4070 { REQ_DEL_PREV |ID_FE ,FE_Delete_Previous}, 4071 { REQ_DEL_LINE |ID_FE ,FE_Delete_Line}, 4072 { REQ_DEL_WORD |ID_FE ,FE_Delete_Word}, 4073 { REQ_CLR_EOL |ID_FE ,FE_Clear_To_End_Of_Line}, 4074 { REQ_CLR_EOF |ID_FE ,FE_Clear_To_End_Of_Field}, 4075 { REQ_CLR_FIELD |ID_FE ,FE_Clear_Field}, 4076 4077 { REQ_OVL_MODE |ID_EM ,EM_Overlay_Mode}, 4078 { REQ_INS_MODE |ID_EM ,EM_Insert_Mode}, 4079 4080 { REQ_SCR_FLINE |ID_VSC ,VSC_Scroll_Line_Forward}, 4081 { REQ_SCR_BLINE |ID_VSC ,VSC_Scroll_Line_Backward}, 4082 { REQ_SCR_FPAGE |ID_VSC ,VSC_Scroll_Page_Forward}, 4083 { REQ_SCR_BPAGE |ID_VSC ,VSC_Scroll_Page_Backward}, 4084 { REQ_SCR_FHPAGE |ID_VSC ,VSC_Scroll_Half_Page_Forward}, 4085 { REQ_SCR_BHPAGE |ID_VSC ,VSC_Scroll_Half_Page_Backward}, 4086 4087 { REQ_SCR_FCHAR |ID_HSC ,HSC_Scroll_Char_Forward}, 4088 { REQ_SCR_BCHAR |ID_HSC ,HSC_Scroll_Char_Backward}, 4089 { REQ_SCR_HFLINE |ID_HSC ,HSC_Horizontal_Line_Forward}, 4090 { REQ_SCR_HBLINE |ID_HSC ,HSC_Horizontal_Line_Backward}, 4091 { REQ_SCR_HFHALF |ID_HSC ,HSC_Horizontal_Half_Line_Forward}, 4092 { REQ_SCR_HBHALF |ID_HSC ,HSC_Horizontal_Half_Line_Backward}, 4093 4094 { REQ_VALIDATION |ID_FV ,FV_Validation}, 4095 4096 { REQ_NEXT_CHOICE |ID_CH ,CR_Next_Choice}, 4097 { REQ_PREV_CHOICE |ID_CH ,CR_Previous_Choice} 4098 }; 4099 /* *INDENT-ON* */ 4100 4101 /*--------------------------------------------------------------------------- 4102 | Facility : libnform 4103 | Function : int form_driver(FORM * form,int c) 4104 | 4105 | Description : This is the workhorse of the forms system. It checks 4106 | to determine whether the character c is a request or 4107 | data. If it is a request, the form driver executes 4108 | the request and returns the result. If it is data 4109 | (printable character), it enters the data into the 4110 | current position in the current field. If it is not 4111 | recognized, the form driver assumes it is an application 4112 | defined command and returns E_UNKNOWN_COMMAND. 4113 | Application defined command should be defined relative 4114 | to MAX_FORM_COMMAND, the maximum value of a request. 4115 | 4116 | Return Values : E_OK - success 4117 | E_SYSTEM_ERROR - system error 4118 | E_BAD_ARGUMENT - an argument is incorrect 4119 | E_NOT_POSTED - form is not posted 4120 | E_INVALID_FIELD - field contents are invalid 4121 | E_BAD_STATE - called from inside a hook routine 4122 | E_REQUEST_DENIED - request failed 4123 | E_NOT_CONNECTED - no fields are connected to the form 4124 | E_UNKNOWN_COMMAND - command not known 4125 +--------------------------------------------------------------------------*/ 4126 NCURSES_EXPORT(int) 4127 form_driver(FORM *form, int c) 4128 { 4129 const Binding_Info *BI = (Binding_Info *) 0; 4130 int res = E_UNKNOWN_COMMAND; 4131 4132 T((T_CALLED("form_driver(%p,%d)"), form, c)); 4133 4134 if (!form) 4135 RETURN(E_BAD_ARGUMENT); 4136 4137 if (!(form->field)) 4138 RETURN(E_NOT_CONNECTED); 4139 4140 assert(form->page); 4141 4142 if (c == FIRST_ACTIVE_MAGIC) 4143 { 4144 form->current = _nc_First_Active_Field(form); 4145 RETURN(E_OK); 4146 } 4147 4148 assert(form->current && 4149 form->current->buf && 4150 (form->current->form == form) 4151 ); 4152 4153 if (form->status & _IN_DRIVER) 4154 RETURN(E_BAD_STATE); 4155 4156 if (!(form->status & _POSTED)) 4157 RETURN(E_NOT_POSTED); 4158 4159 if ((c >= MIN_FORM_COMMAND && c <= MAX_FORM_COMMAND) && 4160 ((bindings[c - MIN_FORM_COMMAND].keycode & Key_Mask) == c)) 4161 BI = &(bindings[c - MIN_FORM_COMMAND]); 4162 4163 if (BI) 4164 { 4165 typedef int (*Generic_Method) (int (*const) (FORM *), FORM *); 4166 static const Generic_Method Generic_Methods[] = 4167 { 4168 Page_Navigation, /* overloaded to call field&form hooks */ 4169 Inter_Field_Navigation, /* overloaded to call field hooks */ 4170 NULL, /* Intra-Field is generic */ 4171 Vertical_Scrolling, /* Overloaded to check multi-line */ 4172 Horizontal_Scrolling, /* Overloaded to check single-line */ 4173 Field_Editing, /* Overloaded to mark modification */ 4174 NULL, /* Edit Mode is generic */ 4175 NULL, /* Field Validation is generic */ 4176 NULL /* Choice Request is generic */ 4177 }; 4178 size_t nMethods = (sizeof(Generic_Methods) / sizeof(Generic_Methods[0])); 4179 size_t method = ((BI->keycode & ID_Mask) >> ID_Shft) & 0xffff; 4180 4181 if ((method >= nMethods) || !(BI->cmd)) 4182 res = E_SYSTEM_ERROR; 4183 else 4184 { 4185 Generic_Method fct = Generic_Methods[method]; 4186 4187 if (fct) 4188 res = fct(BI->cmd, form); 4189 else 4190 res = (BI->cmd) (form); 4191 } 4192 } 4193 #ifdef NCURSES_MOUSE_VERSION 4194 else if (KEY_MOUSE == c) 4195 { 4196 MEVENT event; 4197 WINDOW *win = form->win ? form->win : stdscr; 4198 WINDOW *sub = form->sub ? form->sub : win; 4199 4200 getmouse(&event); 4201 if ((event.bstate & (BUTTON1_CLICKED | 4202 BUTTON1_DOUBLE_CLICKED | 4203 BUTTON1_TRIPLE_CLICKED)) 4204 && wenclose(win, event.y, event.x)) 4205 { /* we react only if the click was in the userwin, that means 4206 * inside the form display area or at the decoration window. 4207 */ 4208 int ry = event.y, rx = event.x; /* screen coordinates */ 4209 4210 res = E_REQUEST_DENIED; 4211 if (mouse_trafo(&ry, &rx, FALSE)) 4212 { /* rx, ry are now "curses" coordinates */ 4213 if (ry < sub->_begy) 4214 { /* we clicked above the display region; this is 4215 * interpreted as "scroll up" request 4216 */ 4217 if (event.bstate & BUTTON1_CLICKED) 4218 res = form_driver(form, REQ_PREV_FIELD); 4219 else if (event.bstate & BUTTON1_DOUBLE_CLICKED) 4220 res = form_driver(form, REQ_PREV_PAGE); 4221 else if (event.bstate & BUTTON1_TRIPLE_CLICKED) 4222 res = form_driver(form, REQ_FIRST_FIELD); 4223 } 4224 else if (ry > sub->_begy + sub->_maxy) 4225 { /* we clicked below the display region; this is 4226 * interpreted as "scroll down" request 4227 */ 4228 if (event.bstate & BUTTON1_CLICKED) 4229 res = form_driver(form, REQ_NEXT_FIELD); 4230 else if (event.bstate & BUTTON1_DOUBLE_CLICKED) 4231 res = form_driver(form, REQ_NEXT_PAGE); 4232 else if (event.bstate & BUTTON1_TRIPLE_CLICKED) 4233 res = form_driver(form, REQ_LAST_FIELD); 4234 } 4235 else if (wenclose(sub, event.y, event.x)) 4236 { /* Inside the area we try to find the hit item */ 4237 int i; 4238 4239 ry = event.y; 4240 rx = event.x; 4241 if (wmouse_trafo(sub, &ry, &rx, FALSE)) 4242 { 4243 int min_field = form->page[form->curpage].pmin; 4244 int max_field = form->page[form->curpage].pmax; 4245 4246 for (i = min_field; i <= max_field; ++i) 4247 { 4248 FIELD *field = form->field[i]; 4249 4250 if (Field_Is_Selectable(field) 4251 && Field_encloses(field, ry, rx) == E_OK) 4252 { 4253 res = _nc_Set_Current_Field(form, field); 4254 if (res == E_OK) 4255 res = _nc_Position_Form_Cursor(form); 4256 if (res == E_OK 4257 && (event.bstate & BUTTON1_DOUBLE_CLICKED)) 4258 res = E_UNKNOWN_COMMAND; 4259 break; 4260 } 4261 } 4262 } 4263 } 4264 } 4265 } 4266 else 4267 res = E_REQUEST_DENIED; 4268 } 4269 #endif /* NCURSES_MOUSE_VERSION */ 4270 else if (!(c & (~(int)MAX_REGULAR_CHARACTER))) 4271 { 4272 /* 4273 * If we're using 8-bit characters, iscntrl+isprint cover the whole set. 4274 * But with multibyte characters, there is a third possibility, i.e., 4275 * parts of characters that build up into printable characters which are 4276 * not considered printable. 4277 * 4278 * FIXME: the wide-character branch should also use Check_Char(). 4279 */ 4280 #if USE_WIDEC_SUPPORT 4281 if (!iscntrl(UChar(c))) 4282 #else 4283 if (isprint(UChar(c)) && 4284 Check_Char(form->current->type, c, 4285 (TypeArgument *)(form->current->arg))) 4286 #endif 4287 res = Data_Entry(form, c); 4288 } 4289 _nc_Refresh_Current_Field(form); 4290 RETURN(res); 4291 } 4292 4293 /*---------------------------------------------------------------------------- 4294 Field-Buffer manipulation routines. 4295 The effects of setting a buffer are tightly coupled to the core of the form 4296 driver logic. This is especially true in the case of growable fields. 4297 So I don't separate this into a separate module. 4298 --------------------------------------------------------------------------*/ 4299 4300 /*--------------------------------------------------------------------------- 4301 | Facility : libnform 4302 | Function : int set_field_buffer(FIELD *field, 4303 | int buffer, char *value) 4304 | 4305 | Description : Set the given buffer of the field to the given value. 4306 | Buffer 0 stores the displayed content of the field. 4307 | For dynamic fields this may grow the fieldbuffers if 4308 | the length of the value exceeds the current buffer 4309 | length. For buffer 0 only printable values are allowed. 4310 | For static fields, the value needs not to be zero ter- 4311 | minated. It is copied up to the length of the buffer. 4312 | 4313 | Return Values : E_OK - success 4314 | E_BAD_ARGUMENT - invalid argument 4315 | E_SYSTEM_ERROR - system error 4316 +--------------------------------------------------------------------------*/ 4317 NCURSES_EXPORT(int) 4318 set_field_buffer(FIELD *field, int buffer, const char *value) 4319 { 4320 FIELD_CELL *p; 4321 int res = E_OK; 4322 unsigned int i; 4323 unsigned int len; 4324 4325 #if USE_WIDEC_SUPPORT 4326 FIELD_CELL *widevalue = 0; 4327 #endif 4328 4329 T((T_CALLED("set_field_buffer(%p,%d,%s)"), field, buffer, _nc_visbuf(value))); 4330 4331 if (!field || !value || ((buffer < 0) || (buffer > field->nbuf))) 4332 RETURN(E_BAD_ARGUMENT); 4333 4334 len = Buffer_Length(field); 4335 4336 if (buffer == 0) 4337 { 4338 for (i = 0; (value[i] != '\0') && (i < len); ++i) 4339 { 4340 if (iscntrl(UChar(value[i]))) 4341 RETURN(E_BAD_ARGUMENT); 4342 } 4343 } 4344 4345 if (Growable(field)) 4346 { 4347 /* for a growable field we must assume zero terminated strings, because 4348 somehow we have to detect the length of what should be copied. 4349 */ 4350 unsigned int vlen = strlen(value); 4351 4352 if (vlen > len) 4353 { 4354 if (!Field_Grown(field, 4355 (int)(1 + (vlen - len) / ((field->rows + field->nrow) 4356 * field->cols)))) 4357 RETURN(E_SYSTEM_ERROR); 4358 4359 /* in this case we also have to check, whether or not the remaining 4360 characters in value are also printable for buffer 0. */ 4361 if (buffer == 0) 4362 { 4363 for (i = len; i < vlen; i++) 4364 if (iscntrl(UChar(value[i]))) 4365 RETURN(E_BAD_ARGUMENT); 4366 } 4367 len = vlen; 4368 } 4369 } 4370 4371 p = Address_Of_Nth_Buffer(field, buffer); 4372 4373 #if USE_WIDEC_SUPPORT 4374 /* 4375 * Use addstr's logic for converting a string to an array of cchar_t's. 4376 * There should be a better way, but this handles nonspacing characters 4377 * and other special cases that we really do not want to handle here. 4378 */ 4379 wclear(field->working); 4380 mvwaddstr(field->working, 0, 0, value); 4381 4382 if ((widevalue = typeCalloc(FIELD_CELL, len + 1)) == 0) 4383 { 4384 RETURN(E_SYSTEM_ERROR); 4385 } 4386 else 4387 { 4388 mvwin_wchnstr(field->working, 0, 0, widevalue, (int)len); 4389 for (i = 0; i < len; ++i) 4390 { 4391 if (CharEq(myZEROS, widevalue[i])) 4392 { 4393 while (i < len) 4394 p[i++] = myBLANK; 4395 break; 4396 } 4397 p[i] = widevalue[i]; 4398 } 4399 free(widevalue); 4400 } 4401 #else 4402 for (i = 0; i < len; ++i) 4403 { 4404 if (value[i] == '\0') 4405 { 4406 while (i < len) 4407 p[i++] = myBLANK; 4408 break; 4409 } 4410 p[i] = value[i]; 4411 } 4412 #endif 4413 4414 if (buffer == 0) 4415 { 4416 int syncres; 4417 4418 if (((syncres = Synchronize_Field(field)) != E_OK) && 4419 (res == E_OK)) 4420 res = syncres; 4421 if (((syncres = Synchronize_Linked_Fields(field)) != E_OK) && 4422 (res == E_OK)) 4423 res = syncres; 4424 } 4425 RETURN(res); 4426 } 4427 4428 /*--------------------------------------------------------------------------- 4429 | Facility : libnform 4430 | Function : char *field_buffer(const FIELD *field,int buffer) 4431 | 4432 | Description : Return the address of the buffer for the field. 4433 | 4434 | Return Values : Pointer to buffer or NULL if arguments were invalid. 4435 +--------------------------------------------------------------------------*/ 4436 NCURSES_EXPORT(char *) 4437 field_buffer(const FIELD *field, int buffer) 4438 { 4439 char *result = 0; 4440 4441 T((T_CALLED("field_buffer(%p,%d)"), field, buffer)); 4442 4443 if (field && (buffer >= 0) && (buffer <= field->nbuf)) 4444 { 4445 #if USE_WIDEC_SUPPORT 4446 FIELD_CELL *data = Address_Of_Nth_Buffer(field, buffer); 4447 unsigned need = 0; 4448 int size = Buffer_Length(field); 4449 int n; 4450 4451 /* determine the number of bytes needed to store the expanded string */ 4452 for (n = 0; n < size; ++n) 4453 { 4454 if (!isWidecExt(data[n])) 4455 { 4456 mbstate_t state; 4457 size_t next; 4458 4459 init_mb(state); 4460 next = _nc_wcrtomb(0, data[n].chars[0], &state); 4461 if (!isEILSEQ(next)) 4462 need += next; 4463 } 4464 } 4465 4466 /* allocate a place to store the expanded string */ 4467 if (field->expanded[buffer] != 0) 4468 free(field->expanded[buffer]); 4469 field->expanded[buffer] = typeMalloc(char, need + 1); 4470 4471 /* expand the multibyte data */ 4472 if ((result = field->expanded[buffer]) != 0) 4473 { 4474 wclear(field->working); 4475 mvwadd_wchnstr(field->working, 0, 0, data, size); 4476 mvwinnstr(field->working, 0, 0, result, (int)need); 4477 } 4478 #else 4479 result = Address_Of_Nth_Buffer(field, buffer); 4480 #endif 4481 } 4482 returnPtr(result); 4483 } 4484 4485 #if USE_WIDEC_SUPPORT 4486 4487 /* FIXME: see lib_get_wch.c */ 4488 #if HAVE_MBTOWC && HAVE_MBLEN 4489 #define reset_mbytes(state) mblen(NULL, 0), mbtowc(NULL, NULL, 0) 4490 #define count_mbytes(buffer,length,state) mblen(buffer,length) 4491 #define trans_mbytes(wch,buffer,length,state) \ 4492 (int) mbtowc(&wch, buffer, length) 4493 #elif HAVE_MBRTOWC && HAVE_MBRLEN 4494 #define NEED_STATE 4495 #define reset_mbytes(state) init_mb(state) 4496 #define count_mbytes(buffer,length,state) mbrlen(buffer,length,&state) 4497 #define trans_mbytes(wch,buffer,length,state) \ 4498 (int) mbrtowc(&wch, buffer, length, &state) 4499 #else 4500 make an error 4501 #endif 4502 4503 /*--------------------------------------------------------------------------- 4504 | Convert a multibyte string to a wide-character string. The result must be 4505 | freed by the caller. 4506 +--------------------------------------------------------------------------*/ 4507 NCURSES_EXPORT(wchar_t *) 4508 _nc_Widen_String(char *source, int *lengthp) 4509 { 4510 wchar_t *result = 0; 4511 wchar_t wch; 4512 size_t given = strlen(source); 4513 size_t tries; 4514 int pass; 4515 int status; 4516 4517 #ifdef NEED_STATE 4518 mbstate_t state; 4519 #endif 4520 4521 for (pass = 0; pass < 2; ++pass) 4522 { 4523 unsigned need = 0; 4524 size_t passed = 0; 4525 4526 while (passed < given) 4527 { 4528 bool found = FALSE; 4529 4530 for (tries = 1, status = 0; tries <= (given - passed); ++tries) 4531 { 4532 int save = source[passed + tries]; 4533 4534 source[passed + tries] = 0; 4535 reset_mbytes(state); 4536 status = trans_mbytes(wch, source + passed, tries, state); 4537 source[passed + tries] = save; 4538 4539 if (status > 0) 4540 { 4541 found = TRUE; 4542 break; 4543 } 4544 } 4545 if (found) 4546 { 4547 if (pass) 4548 { 4549 result[need] = wch; 4550 } 4551 passed += status; 4552 ++need; 4553 } 4554 else 4555 { 4556 if (pass) 4557 { 4558 result[need] = source[passed]; 4559 } 4560 ++need; 4561 ++passed; 4562 } 4563 } 4564 4565 if (!pass) 4566 { 4567 if (!need) 4568 break; 4569 result = typeCalloc(wchar_t, need); 4570 4571 *lengthp = need; 4572 if (result == 0) 4573 break; 4574 } 4575 } 4576 4577 return result; 4578 } 4579 #endif 4580 4581 /* frm_driver.c ends here */ 4582