1 /* $NetBSD: form.c,v 1.12 2002/05/20 15:00:11 blymn Exp $ */ 2 3 /*- 4 * Copyright (c) 1998-1999 Brett Lymn 5 * (blymn@baea.com.au, brett_lymn@yahoo.com.au) 6 * All rights reserved. 7 * 8 * This code has been donated to The NetBSD Foundation by the Author. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * 30 */ 31 32 #include <stdlib.h> 33 #include <strings.h> 34 #include <form.h> 35 #include "internals.h" 36 37 extern FIELD _formi_default_field; 38 39 FORM _formi_default_form = { 40 FALSE, /* true if performing a init or term function */ 41 FALSE, /* the form is posted */ 42 FALSE, /* make field list circular if true */ 43 NULL, /* window for the form */ 44 NULL, /* subwindow for the form */ 45 NULL, /* use this window for output */ 46 NULL, /* user defined pointer */ 47 0, /* options for the form */ 48 NULL, /* function called when form posted and 49 after page change */ 50 NULL, /* function called when form is unposted and 51 before page change */ 52 NULL, /* function called when form posted and after 53 current field changes */ 54 NULL, /* function called when form unposted and 55 before current field changes */ 56 0, /* number of fields attached */ 57 0, /* current field */ 58 0, /* current page of form */ 59 0, /* number of pages in the form */ 60 NULL, /* dynamic array of fields that start 61 the pages */ 62 {NULL, NULL}, /* sorted field list */ 63 NULL /* array of fields attached to this form. */ 64 }; 65 66 /* 67 * Set the window associated with the form 68 */ 69 int 70 set_form_win(FORM *form, WINDOW *win) 71 { 72 if (form == NULL) { 73 _formi_default_form.win = win; 74 _formi_default_form.scrwin = win; 75 } else { 76 if (form->posted == TRUE) 77 return E_POSTED; 78 else { 79 form->win = win; 80 form->scrwin = win; 81 } 82 } 83 84 return E_OK; 85 } 86 87 /* 88 * Return the window used by the given form 89 */ 90 WINDOW * 91 form_win(FORM *form) 92 { 93 if (form == NULL) 94 return _formi_default_form.win; 95 else 96 return form->win; 97 } 98 99 /* 100 * Set the subwindow for the form. 101 */ 102 int 103 set_form_sub(FORM *form, WINDOW *window) 104 { 105 if (form == NULL) { 106 _formi_default_form.subwin = window; 107 _formi_default_form.scrwin = window; 108 } else { 109 if (form->posted == TRUE) 110 return E_POSTED; 111 else { 112 form->subwin = window; 113 form->scrwin = window; 114 } 115 } 116 117 return E_OK; 118 } 119 120 /* 121 * Return the subwindow for the given form. 122 */ 123 WINDOW * 124 form_sub(FORM *form) 125 { 126 if (form == NULL) 127 return _formi_default_form.subwin; 128 else 129 return form->subwin; 130 } 131 132 /* 133 * Return the minimum size required to contain the form. 134 */ 135 int 136 scale_form(FORM *form, int *rows, int *cols) 137 { 138 int i, max_row, max_col, temp; 139 140 if ((form->fields == NULL) || (form->fields[0] == NULL)) 141 return E_NOT_CONNECTED; 142 143 max_row = 0; 144 max_col = 0; 145 146 for (i = 0; i < form->field_count; i++) { 147 temp = form->fields[i]->form_row + form->fields[i]->rows; 148 max_row = (temp > max_row)? temp : max_row; 149 temp = form->fields[i]->form_col + form->fields[i]->cols; 150 max_col = (temp > max_col)? temp : max_col; 151 } 152 153 (*rows) = max_row; 154 (*cols) = max_col; 155 156 return E_OK; 157 } 158 159 /* 160 * Set the user defined pointer for the form given. 161 */ 162 int 163 set_form_userptr(FORM *form, void *ptr) 164 { 165 if (form == NULL) 166 _formi_default_form.userptr = ptr; 167 else 168 form->userptr = ptr; 169 170 return E_OK; 171 } 172 173 /* 174 * Return the user defined pointer associated with the given form. 175 */ 176 void * 177 form_userptr(FORM *form) 178 { 179 180 if (form == NULL) 181 return _formi_default_form.userptr; 182 else 183 return form->userptr; 184 } 185 186 /* 187 * Set the form options to the given ones. 188 */ 189 int 190 set_form_opts(FORM *form, Form_Options options) 191 { 192 if (form == NULL) 193 _formi_default_form.opts = options; 194 else 195 form->opts = options; 196 197 return E_OK; 198 } 199 200 /* 201 * Turn the given options on for the form. 202 */ 203 int 204 form_opts_on(FORM *form, Form_Options options) 205 { 206 if (form == NULL) 207 _formi_default_form.opts |= options; 208 else 209 form->opts |= options; 210 211 return E_OK; 212 } 213 214 /* 215 * Turn the given options off for the form. 216 */ 217 int 218 form_opts_off(FORM *form, Form_Options options) 219 { 220 if (form == NULL) 221 _formi_default_form.opts &= ~options; 222 else 223 form->opts &= ~options; 224 225 226 return E_OK; 227 } 228 229 /* 230 * Return the options set for the given form. 231 */ 232 Form_Options 233 form_opts(FORM *form) 234 { 235 if (form == NULL) 236 return _formi_default_form.opts; 237 else 238 return form->opts; 239 } 240 241 /* 242 * Set the form init function for the given form 243 */ 244 int 245 set_form_init(FORM *form, Form_Hook func) 246 { 247 if (form == NULL) 248 _formi_default_form.form_init = func; 249 else 250 form->form_init = func; 251 252 return E_OK; 253 } 254 255 /* 256 * Return the init function associated with the given form. 257 */ 258 Form_Hook 259 form_init(FORM *form) 260 { 261 if (form == NULL) 262 return _formi_default_form.form_init; 263 else 264 return form->form_init; 265 } 266 267 /* 268 * Set the function to be called on form termination. 269 */ 270 int 271 set_form_term(FORM *form, Form_Hook function) 272 { 273 if (form == NULL) 274 _formi_default_form.form_term = function; 275 else 276 form->form_term = function; 277 278 return E_OK; 279 } 280 281 /* 282 * Return the function defined for the termination function. 283 */ 284 Form_Hook 285 form_term(FORM *form) 286 { 287 288 if (form == NULL) 289 return _formi_default_form.form_term; 290 else 291 return form->form_term; 292 } 293 294 295 /* 296 * Attach the given fields to the form. 297 */ 298 int 299 set_form_fields(FORM *form, FIELD **fields) 300 { 301 int num_fields = 0, i, maxpg = 1, status; 302 303 if (form == NULL) 304 return E_BAD_ARGUMENT; 305 306 if (form->posted == TRUE) 307 return E_POSTED; 308 309 if (fields == NULL) 310 return E_BAD_ARGUMENT; 311 312 while (fields[num_fields] != NULL) { 313 if ((fields[num_fields]->parent != NULL) && 314 (fields[num_fields]->parent != form)) 315 return E_CONNECTED; 316 num_fields++; 317 } 318 319 /* disconnect old fields, if any */ 320 if (form->fields != NULL) { 321 for (i = 0; i < form->field_count; i++) { 322 form->fields[i]->parent = NULL; 323 form->fields[i]->index = -1; 324 } 325 } 326 327 /* kill old page pointers if any */ 328 if (form->page_starts != NULL) 329 free(form->page_starts); 330 331 form->field_count = num_fields; 332 333 /* now connect the new fields to the form */ 334 for (i = 0; i < num_fields; i++) { 335 fields[i]->parent = form; 336 fields[i]->index = i; 337 /* set the page number of the field */ 338 if (fields[i]->page_break == 1) 339 maxpg++; 340 fields[i]->page = maxpg; 341 } 342 343 form->fields = fields; 344 form->cur_field = 0; 345 form->max_page = maxpg; 346 if ((status = _formi_find_pages(form)) != E_OK) 347 return status; 348 349 /* sort the fields and set the navigation pointers */ 350 _formi_sort_fields(form); 351 _formi_stitch_fields(form); 352 353 return E_OK; 354 } 355 356 /* 357 * Return the fields attached to the form given. 358 */ 359 FIELD ** 360 form_fields(FORM *form) 361 { 362 if (form == NULL) 363 return NULL; 364 365 return form->fields; 366 } 367 368 /* 369 * Return the number of fields attached to the given form. 370 */ 371 int 372 field_count(FORM *form) 373 { 374 if (form == NULL) 375 return -1; 376 377 return form->field_count; 378 } 379 380 /* 381 * Move the given field to the row and column given. 382 */ 383 int 384 move_field(FIELD *fptr, int frow, int fcol) 385 { 386 FIELD *field = (fptr == NULL) ? &_formi_default_field : fptr; 387 388 if (field->parent != NULL) 389 return E_CONNECTED; 390 391 field->form_row = frow; 392 field->form_col = fcol; 393 394 return E_OK; 395 } 396 397 /* 398 * Set the page of the form to the given page. 399 */ 400 int 401 set_form_page(FORM *form, int page) 402 { 403 if (form == NULL) 404 return E_BAD_ARGUMENT; 405 406 if (form->in_init == TRUE) 407 return E_BAD_STATE; 408 409 if (page > form->max_page) 410 return E_BAD_ARGUMENT; 411 412 form->page = page; 413 return E_OK; 414 } 415 416 /* 417 * Return the maximum page of the form. 418 */ 419 int 420 form_max_page(FORM *form) 421 { 422 if (form == NULL) 423 return _formi_default_form.max_page; 424 else 425 return form->max_page; 426 } 427 428 /* 429 * Return the current page of the form. 430 */ 431 int 432 form_page(FORM *form) 433 { 434 if (form == NULL) 435 return E_BAD_ARGUMENT; 436 437 return form->page; 438 } 439 440 /* 441 * Set the current field to the field given. 442 */ 443 int 444 set_current_field(FORM *form, FIELD *field) 445 { 446 if (form == NULL) 447 return E_BAD_ARGUMENT; 448 449 if (form->in_init == TRUE) 450 return E_BAD_STATE; 451 452 if (field == NULL) 453 return E_INVALID_FIELD; 454 455 if ((field->parent == NULL) || (field->parent != form)) 456 return E_INVALID_FIELD; /* field is not of this form */ 457 458 form->cur_field = field->index; 459 460 /* XXX update page if posted??? */ 461 return E_OK; 462 } 463 464 /* 465 * Return the current field of the given form. 466 */ 467 FIELD * 468 current_field(FORM *form) 469 { 470 if (form == NULL) 471 return NULL; 472 473 if (form->fields == NULL) 474 return NULL; 475 476 return form->fields[form->cur_field]; 477 } 478 479 /* 480 * Allocate a new form with the given fields. 481 */ 482 FORM * 483 new_form(FIELD **fields) 484 { 485 FORM *new; 486 487 if ((new = (FORM *) malloc(sizeof(FORM))) == NULL) 488 return NULL; 489 490 491 /* copy in the defaults... */ 492 bcopy(&_formi_default_form, new, sizeof(FORM)); 493 494 if (new->win == NULL) 495 new->scrwin = stdscr; /* something for curses to write to */ 496 497 if (fields != NULL) { /* attach the fields, if any */ 498 if (set_form_fields(new, fields) < 0) { 499 free(new); /* field attach failed, back out */ 500 return NULL; 501 } 502 } 503 504 return new; 505 } 506 507 /* 508 * Free the given form. 509 */ 510 int 511 free_form(FORM *form) 512 { 513 int i; 514 515 if (form == NULL) 516 return E_BAD_ARGUMENT; 517 518 if (form->posted == TRUE) 519 return E_POSTED; 520 521 for (i = 0; i < form->field_count; i++) { 522 /* detach all the fields from the form */ 523 form->fields[i]->parent = NULL; 524 form->fields[i]->index = -1; 525 } 526 527 free(*form->fields); 528 free(form); 529 530 return E_OK; 531 } 532 533 /* 534 * Tell if the current field of the form has offscreen data ahead 535 */ 536 int 537 data_ahead(FORM *form) 538 { 539 FIELD *cur; 540 541 if ((form == NULL) || (form->fields == NULL) 542 || (form->fields[0] == NULL)) 543 return FALSE; 544 545 cur = form->fields[form->cur_field]; 546 547 /*XXXX wrong */ 548 if (cur->lines[cur->start_line + cur->cursor_ypos].length > cur->cols) 549 return TRUE; 550 551 return FALSE; 552 } 553 554 /* 555 * Tell if current field of the form has offscreen data behind 556 */ 557 int 558 data_behind(FORM *form) 559 { 560 FIELD *cur; 561 562 if ((form == NULL) || (form->fields == NULL) 563 || (form->fields[0] == NULL)) 564 return FALSE; 565 566 cur = form->fields[form->cur_field]; 567 568 if (cur->start_char > 0) 569 return TRUE; 570 571 return FALSE; 572 } 573 574 /* 575 * Position the form cursor. 576 */ 577 int 578 pos_form_cursor(FORM *form) 579 { 580 FIELD *cur; 581 int row, col; 582 583 if ((form == NULL) || (form->fields == NULL) || 584 (form->fields[0] == NULL)) 585 return E_BAD_ARGUMENT; 586 587 if (form->posted != 1) 588 return E_NOT_POSTED; 589 590 cur = form->fields[form->cur_field]; 591 row = cur->form_row; 592 col = cur->form_col; 593 594 /* if the field is public then show the cursor pos */ 595 if ((cur->opts & O_PUBLIC) == O_PUBLIC) { 596 row += cur->cursor_ypos; 597 col += cur->cursor_xpos; 598 if (cur->cursor_xpos >= cur->cols) { 599 col = cur->form_col; 600 row++; 601 } 602 } 603 604 #ifdef DEBUG 605 fprintf(dbg, "pos_cursor: row=%d, col=%d\n", row, col); 606 #endif 607 608 wmove(form->scrwin, row, col); 609 610 return E_OK; 611 } 612