Home | History | Annotate | Line # | Download | only in libform
driver.c revision 1.15
      1 /*	$NetBSD: driver.c,v 1.15 2003/03/09 00:57:17 lukem Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1998-1999 Brett Lymn
      5  *                         (blymn (at) baea.com.au, brett_lymn (at) 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: driver.c,v 1.15 2003/03/09 00:57:17 lukem Exp $");
     34 
     35 #include <ctype.h>
     36 #include "form.h"
     37 #include "internals.h"
     38 
     39 static int
     40 traverse_form_links(FORM *form, int direction);
     41 
     42 /*
     43  * Traverse the links of the current field in the given direction until
     44  * either a active & visible field is found or we return to the current
     45  * field.  Direction is the REQ_{LEFT,RIGHT,UP,DOWN}_FIELD driver commands.
     46  * The function returns E_OK if a valid field is found, E_REQUEST_DENIED
     47  * otherwise.
     48  */
     49 static int
     50 traverse_form_links(FORM *form, int direction)
     51 {
     52 	unsigned idx;
     53 
     54 	idx = form->cur_field;
     55 
     56 	do {
     57 		switch (direction) {
     58 		case REQ_LEFT_FIELD:
     59 			if (form->fields[idx]->left == NULL)
     60 				return E_REQUEST_DENIED;
     61 			idx = form->fields[idx]->left->index;
     62 			break;
     63 
     64 		case REQ_RIGHT_FIELD:
     65 			if (form->fields[idx]->right == NULL)
     66 				return E_REQUEST_DENIED;
     67 			idx = form->fields[idx]->right->index;
     68 			break;
     69 
     70 		case REQ_UP_FIELD:
     71 			if (form->fields[idx]->up == NULL)
     72 				return E_REQUEST_DENIED;
     73 			idx = form->fields[idx]->up->index;
     74 			break;
     75 
     76 		case REQ_DOWN_FIELD:
     77 			if (form->fields[idx]->down == NULL)
     78 				return E_REQUEST_DENIED;
     79 			idx = form->fields[idx]->down->index;
     80 			break;
     81 
     82 		default:
     83 			return E_REQUEST_DENIED;
     84 		}
     85 
     86 		if ((form->fields[idx]->opts & (O_ACTIVE | O_VISIBLE))
     87 		    == (O_ACTIVE | O_VISIBLE)) {
     88 			form->cur_field = idx;
     89 			return E_OK;
     90 		}
     91 	} while (idx != form->cur_field);
     92 
     93 	return E_REQUEST_DENIED;
     94 }
     95 
     96 int
     97 form_driver(FORM *form, int c)
     98 {
     99 	FIELD *fieldp;
    100 	int update_page, update_field, old_field, old_page, status;
    101 	int start_field;
    102 	unsigned int pos;
    103 
    104 	if (form == NULL)
    105 		return E_BAD_ARGUMENT;
    106 
    107 	if ((form->fields == NULL) || (*(form->fields) == NULL))
    108 		return E_INVALID_FIELD;
    109 
    110 	if (form->posted != 1)
    111 		return E_NOT_POSTED;
    112 
    113 	if (form->in_init == 1)
    114 		return E_BAD_STATE;
    115 
    116 
    117 	old_field = start_field = form->cur_field;
    118 	fieldp = form->fields[form->cur_field];
    119 	update_page = update_field = 0;
    120 	status = E_OK;
    121 
    122 	if (c < REQ_MIN_REQUEST) {
    123 		if (isprint(c) || isblank(c)) {
    124 			do {
    125 				pos = fieldp->start_char + fieldp->row_xpos
    126 	       + fieldp->lines[fieldp->start_line + fieldp->cursor_ypos].start;
    127 
    128 			      /* check if we are allowed to edit this field */
    129 				if ((fieldp->opts & O_EDIT) != O_EDIT)
    130 					return E_REQUEST_DENIED;
    131 
    132 				if ((status =
    133 				     (_formi_add_char(fieldp, pos, c)))
    134 				    == E_REQUEST_DENIED) {
    135 
    136 					  /*
    137 					   * Need to check here if we
    138 					   * want to autoskip.  we
    139 					   * call the form driver
    140 					   * recursively to pos us on
    141 					   * the next field and then
    142 					   * we loop back to ensure
    143 					   * the next field selected
    144 					   * can have data added to it
    145 					   */
    146 					if ((fieldp->opts & O_AUTOSKIP)
    147 					    != O_AUTOSKIP)
    148 						return E_REQUEST_DENIED;
    149 					status = form_driver(form,
    150 							     REQ_NEXT_FIELD);
    151 					if (status != E_OK)
    152 						return status;
    153 
    154 					  /*
    155 					   * check if we have looped
    156                                            * around all the fields.
    157                                            * This can easily happen if
    158                                            * all the fields are full.
    159 					   */
    160 					if (start_field == form->cur_field)
    161 						return E_REQUEST_DENIED;
    162 
    163 					old_field = form->cur_field;
    164 					fieldp = form->fields[form->cur_field];
    165 					status = _formi_add_char(fieldp,
    166 							fieldp->start_char
    167 							+ fieldp->cursor_xpos,
    168 							c);
    169 				} else if (status == E_INVALID_FIELD)
    170 					  /* char failed validation, just
    171 					   * return the status.
    172 					   */
    173 					return status;
    174 				else if (status == E_NO_ROOM)
    175 					  /* we will get this if the line
    176 					   * wrapping fails.  Deny the
    177 					   * request.
    178 					   */
    179 					return E_REQUEST_DENIED;
    180 			}
    181 			while (status != E_OK);
    182 			update_field = (status == E_OK);
    183 		} else
    184 			return E_REQUEST_DENIED;
    185 	} else {
    186 		if (c > REQ_MAX_COMMAND)
    187 			return E_UNKNOWN_COMMAND;
    188 
    189 		if ((c >= REQ_NEXT_PAGE) && (c <= REQ_DOWN_FIELD)) {
    190 			  /* first check the field we are in is ok */
    191 			if (_formi_validate_field(form) != E_OK)
    192 				return E_INVALID_FIELD;
    193 
    194 			if (form->field_term != NULL)
    195 				form->field_term(form);
    196 
    197 			  /*
    198 			   * if we have a page movement then the form term
    199 			   * needs to be called too
    200 			   */
    201 			if ((c <= REQ_LAST_PAGE) && (form->form_term != NULL))
    202 				form->form_term(form);
    203 		}
    204 
    205 
    206 		switch (c) {
    207 		case REQ_NEXT_PAGE:
    208 			if (form->page < form->max_page) {
    209 				old_page = form->page;
    210 				form->page++;
    211 				update_page = 1;
    212 				if (_formi_pos_first_field(form) != E_OK) {
    213 					form->page = old_page;
    214 					status = E_REQUEST_DENIED;
    215 				}
    216 			} else
    217 				status = E_REQUEST_DENIED;
    218 			break;
    219 
    220 		case REQ_PREV_PAGE:
    221 			if (form->page > 0) {
    222 				old_page = form->page;
    223 				form->page--;
    224 				update_page = 1;
    225 				if (_formi_pos_first_field(form) != E_OK) {
    226 					form->page = old_page;
    227 					status = E_REQUEST_DENIED;
    228 				}
    229 			} else
    230 				status = E_REQUEST_DENIED;
    231 			break;
    232 
    233 		case REQ_FIRST_PAGE:
    234 			old_page = form->page;
    235 			form->page = 0;
    236 			update_page = 1;
    237 			if (_formi_pos_first_field(form) != E_OK) {
    238 				form->page = old_page;
    239 				status = E_REQUEST_DENIED;
    240 			}
    241 			break;
    242 
    243 		case REQ_LAST_PAGE:
    244 			old_page = form->page;
    245 			form->page = form->max_page - 1;
    246 			update_page = 1;
    247 			if (_formi_pos_first_field(form) != E_OK) {
    248 				form->page = old_page;
    249 				status = E_REQUEST_DENIED;
    250 			}
    251 			break;
    252 
    253 		case REQ_NEXT_FIELD:
    254 			status = _formi_pos_new_field(form, _FORMI_FORWARD,
    255 						      FALSE);
    256 			update_field = 1;
    257 			break;
    258 
    259 		case REQ_PREV_FIELD:
    260 			status = _formi_pos_new_field(form, _FORMI_BACKWARD,
    261 						      FALSE);
    262 			update_field = 1;
    263 			break;
    264 
    265 		case REQ_FIRST_FIELD:
    266 			form->cur_field = 0;
    267 			update_field = 1;
    268 			break;
    269 
    270 		case REQ_LAST_FIELD:
    271 			form->cur_field = form->field_count - 1;
    272 			update_field = 1;
    273 			break;
    274 
    275 		case REQ_SNEXT_FIELD:
    276 			status = _formi_pos_new_field(form, _FORMI_FORWARD,
    277 						      TRUE);
    278 			update_field = 1;
    279 			break;
    280 
    281 		case REQ_SPREV_FIELD:
    282 			status = _formi_pos_new_field(form, _FORMI_BACKWARD,
    283 						      TRUE);
    284 			update_field = 1;
    285 			break;
    286 
    287 		case REQ_SFIRST_FIELD:
    288 			fieldp = CIRCLEQ_FIRST(&form->sorted_fields);
    289 			form->cur_field = fieldp->index;
    290 			update_field = 1;
    291 			break;
    292 
    293 		case REQ_SLAST_FIELD:
    294 			fieldp = CIRCLEQ_LAST(&form->sorted_fields);
    295 			form->cur_field = fieldp->index;
    296 			update_field = 1;
    297 			break;
    298 
    299 			  /*
    300 			   * The up, down, left and right field traversals
    301 			   * are rolled up into a single function, allow a
    302 			   * fall through to that function.
    303 			   */
    304 			  /* FALLTHROUGH */
    305 		case REQ_LEFT_FIELD:
    306 		case REQ_RIGHT_FIELD:
    307 		case REQ_UP_FIELD:
    308 		case REQ_DOWN_FIELD:
    309 			status = traverse_form_links(form, c);
    310 			update_field = 1;
    311 			break;
    312 
    313 			  /* the following commands modify the buffer, check if
    314 			     this is allowed first before falling through. */
    315 			  /* FALLTHROUGH */
    316 		case REQ_DEL_PREV:
    317 			  /*
    318 			   * need to check for the overloading of this
    319 			   * request.  If overload flag set and we are
    320 			   * at the start of field this request turns
    321 			   * into a previous field request. Otherwise
    322 			   * fallthrough to the field handler.
    323 			   */
    324 			if ((form->opts & O_BS_OVERLOAD) == O_BS_OVERLOAD) {
    325 				if ((fieldp->start_char == 0) &&
    326 				    (fieldp->start_line == 0) &&
    327 				    (fieldp->row_xpos == 0)) {
    328 					update_field =
    329 						_formi_manipulate_field(form,
    330 							REQ_PREV_FIELD);
    331 					break;
    332 				}
    333 			}
    334 
    335 			  /* FALLTHROUGH */
    336 		case REQ_NEW_LINE:
    337 			  /*
    338 			   * need to check for the overloading of this
    339 			   * request.  If overload flag set and we are
    340 			   * at the start of field this request turns
    341 			   * into a next field request. Otherwise
    342 			   * fallthrough to the field handler.
    343 			   */
    344 			if ((form->opts & O_NL_OVERLOAD) == O_NL_OVERLOAD) {
    345 				if ((fieldp->start_char == 0) &&
    346 				    (fieldp->start_line == 0) &&
    347 				    (fieldp->row_xpos == 0)) {
    348 					update_field =
    349 						_formi_manipulate_field(form,
    350 							REQ_NEXT_FIELD);
    351 					break;
    352 				}
    353 			}
    354 
    355 			  /* FALLTHROUGH */
    356 		case REQ_INS_CHAR:
    357 		case REQ_INS_LINE:
    358 		case REQ_DEL_CHAR:
    359 		case REQ_DEL_LINE:
    360 		case REQ_DEL_WORD:
    361 		case REQ_CLR_EOL:
    362 		case REQ_CLR_EOF:
    363 		case REQ_CLR_FIELD:
    364 		case REQ_OVL_MODE:
    365 		case REQ_INS_MODE:
    366 			  /* check if we are allowed to edit the field and fall
    367 			   * through if we are.
    368 			   */
    369 			if ((form->fields[form->cur_field]->opts & O_EDIT) != O_EDIT)
    370 				return E_REQUEST_DENIED;
    371 
    372 			  /* the following manipulate the field contents, bundle
    373 			     them into one function.... */
    374 			  /* FALLTHROUGH */
    375 		case REQ_NEXT_CHAR:
    376 		case REQ_PREV_CHAR:
    377 		case REQ_NEXT_LINE:
    378 		case REQ_PREV_LINE:
    379 		case REQ_NEXT_WORD:
    380 		case REQ_PREV_WORD:
    381 		case REQ_BEG_FIELD:
    382 		case REQ_END_FIELD:
    383 		case REQ_BEG_LINE:
    384 		case REQ_END_LINE:
    385 		case REQ_LEFT_CHAR:
    386 		case REQ_RIGHT_CHAR:
    387 		case REQ_UP_CHAR:
    388 		case REQ_DOWN_CHAR:
    389 		case REQ_SCR_FLINE:
    390 		case REQ_SCR_BLINE:
    391 		case REQ_SCR_FPAGE:
    392 		case REQ_SCR_BPAGE:
    393 		case REQ_SCR_FHPAGE:
    394 		case REQ_SCR_BHPAGE:
    395 		case REQ_SCR_FCHAR:
    396 		case REQ_SCR_BCHAR:
    397 		case REQ_SCR_HFLINE:
    398 		case REQ_SCR_HBLINE:
    399 		case REQ_SCR_HFHALF:
    400 		case REQ_SCR_HBHALF:
    401 			update_field = _formi_manipulate_field(form, c);
    402 			break;
    403 
    404 		case REQ_VALIDATION:
    405 			return _formi_validate_field(form);
    406 			  /* NOTREACHED */
    407 			break;
    408 
    409 		case REQ_PREV_CHOICE:
    410 		case REQ_NEXT_CHOICE:
    411 			update_field = _formi_field_choice(form, c);
    412 			  /* reinit the cursor pos just in case */
    413 			if (update_field == 1) {
    414 				_formi_init_field_xpos(fieldp);
    415 				fieldp->row_xpos = 0;
    416 			}
    417 			break;
    418 
    419 		default: /* should not need to do this, but.... */
    420 			return E_UNKNOWN_COMMAND;
    421 			  /* NOTREACHED */
    422 			break;
    423 		}
    424 	}
    425 
    426 	  /* call the field and form init functions if required. */
    427 	if ((c >= REQ_NEXT_PAGE) && (c <= REQ_DOWN_FIELD)) {
    428 		if (form->field_init != NULL)
    429 			form->field_init(form);
    430 
    431 		  /*
    432 		   * if we have a page movement then the form init
    433 		   * needs to be called too
    434 		   */
    435 		if ((c <= REQ_LAST_PAGE) && (form->form_init != NULL))
    436 			form->form_init(form);
    437 
    438 		  /*
    439 		   * if there was an error just return now...
    440 		   */
    441 		if (status != E_OK)
    442 			return status;
    443 
    444 		  /* if we have no error, reset the various offsets */
    445 		fieldp = form->fields[form->cur_field];
    446 		fieldp->start_char = 0;
    447 		fieldp->start_line = 0;
    448 		fieldp->row_xpos = 0;
    449 		fieldp->cursor_ypos = 0;
    450 		_formi_init_field_xpos(fieldp);
    451 	}
    452 
    453 	if (update_field < 0)
    454 		return update_field;
    455 
    456 	if (update_field == 1)
    457 		update_page |= _formi_update_field(form, old_field);
    458 
    459 	if (update_page == 1)
    460 		_formi_draw_page(form);
    461 
    462 	pos_form_cursor(form);
    463 
    464 	if ((update_page == 1) || (update_field == 1))
    465 		wrefresh(form->scrwin);
    466 
    467 	return E_OK;
    468 }
    469