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