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