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