1 /* $NetBSD: form.c,v 1.15 2004/11/24 11:57:09 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 <sys/cdefs.h>
33 __RCSID("$NetBSD: form.c,v 1.15 2004/11/24 11:57:09 blymn 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
set_form_win(FORM * form,WINDOW * win)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 *
form_win(FORM * form)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
set_form_sub(FORM * form,WINDOW * window)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 *
form_sub(FORM * form)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
scale_form(FORM * form,int * rows,int * cols)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
set_form_userptr(FORM * form,void * ptr)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 *
form_userptr(FORM * form)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
set_form_opts(FORM * form,Form_Options options)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
form_opts_on(FORM * form,Form_Options options)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
form_opts_off(FORM * form,Form_Options options)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
form_opts(FORM * form)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
set_form_init(FORM * form,Form_Hook func)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
form_init(FORM * form)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
set_form_term(FORM * form,Form_Hook function)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
form_term(FORM * form)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
set_form_fields(FORM * form,FIELD ** fields)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 **
form_fields(FORM * form)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
field_count(FORM * form)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
move_field(FIELD * fptr,int frow,int fcol)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
set_form_page(FORM * form,int page)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
form_max_page(FORM * form)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
form_page(FORM * form)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
set_current_field(FORM * form,FIELD * field)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 *
current_field(FORM * form)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 *
new_form(FIELD ** fields)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
free_form(FORM * form)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
data_ahead(FORM * form)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
data_behind(FORM * form)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
pos_form_cursor(FORM * form)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 #ifdef DEBUG
607 fprintf(dbg, "pos_cursor: row=%d, col=%d\n", row, col);
608 #endif
609
610 wmove(form->scrwin, row, col);
611
612 return E_OK;
613 }
614