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