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