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