driver.c revision 1.6 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 (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 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