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