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