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