1/* $NetBSD: form.c,v 1.17 2021/04/13 13:13:04 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.17 2021/04/13 13:13:04 christos Exp $"); 34 35#include <stdlib.h> 36#include <strings.h> 37#include <form.h> 38#include "internals.h" 39 40extern FIELD _formi_default_field; 41 42FORM _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 */ 72int 73set_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 */ 93WINDOW * 94form_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 */ 105int 106set_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 */ 126WINDOW * 127form_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 */ 138int 139scale_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 */ 165int 166set_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 */ 179void * 180form_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 */ 192int 193set_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 */ 206int 207form_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 */ 220int 221form_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 */ 235Form_Options 236form_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 */ 247int 248set_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 */ 261Form_Hook 262form_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 */ 273int 274set_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 */ 287Form_Hook 288form_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 */ 301int 302set_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 */ 362FIELD ** 363form_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 */ 374int 375field_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 */ 386int 387move_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 */ 403int 404set_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 */ 422int 423form_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 */ 434int 435form_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 */ 446int 447set_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 */ 470FIELD * 471current_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 */ 485FORM * 486new_form(FIELD **fields) 487{ 488 FORM *new; 489 490 if ((new = malloc(sizeof(*new))) == NULL) 491 return NULL; 492 493 494 /* copy in the defaults... */ 495 memcpy(new, &_formi_default_form, sizeof(*new)); 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 */ 513int 514free_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 */ 538int 539data_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 */ 559int 560data_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 */ 579int 580pos_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