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