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