xref: /netbsd/lib/libform/form.c (revision 13876d4e)
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 
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 = 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  */
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 	_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