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