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