]> git.ipfire.org Git - thirdparty/newt.git/blob - form.c
c844199d20ef94b0c30bde137778837f9139d729
[thirdparty/newt.git] / form.c
1 #include <slang/slang.h>
2 #include <stdarg.h>
3 #include <stdlib.h>
4
5 #include "newt.h"
6 #include "newt_pr.h"
7
8 /****************************************************************************
9 These forms handle vertical scrolling of components with a height of 1
10
11 Horizontal scrolling won't work, and scrolling large widgets will fail
12 miserably. It shouldn't be too hard to fix either of those if anyone
13 cares to. I only use scrolling for listboxes and text boxes though so
14 I didn't bother.
15 *****************************************************************************/
16
17 struct element {
18 int top, left; /* actual, not virtual */
19 newtComponent co;
20 };
21
22 struct form {
23 int numCompsAlloced;
24 struct element * elements;
25 int numComps;
26 int currComp;
27 int fixedHeight;
28 int flags;
29 int vertOffset;
30 newtComponent vertBar, exitComp;
31 char * help;
32 int numRows;
33 int * hotKeys;
34 int numHotKeys;
35 int background;
36 };
37
38 static void gotoComponent(struct form * form, int newComp);
39 static struct eventResult formEvent(newtComponent co, struct event ev);
40 static struct eventResult sendEvent(newtComponent comp, struct event ev);
41
42 static struct componentOps formOps = {
43 newtDrawForm,
44 formEvent,
45 newtFormDestroy,
46 } ;
47
48 static inline int componentFits(newtComponent co, int compNum) {
49 struct form * form = co->data;
50 struct element * el = form->elements + compNum;
51
52 if ((co->top + form->vertOffset) > el->top) return 0;
53 if ((co->top + form->vertOffset + co->height) <
54 (el->top + el->co->height)) return 0;
55
56 return 1;
57 }
58
59 newtComponent newtForm(newtComponent vertBar, char * help, int flags) {
60 newtComponent co;
61 struct form * form;
62
63 co = malloc(sizeof(*co));
64 form = malloc(sizeof(*form));
65 co->data = form;
66 co->width = 0;
67 co->height = 0;
68 co->top = -1;
69 co->left = -1;
70
71 co->takesFocus = 1;
72 co->ops = &formOps;
73
74 form->help = help;
75 form->flags = flags;
76 form->numCompsAlloced = 5;
77 form->numComps = 0;
78 form->currComp = -1;
79 form->vertOffset = 0;
80 form->fixedHeight = 0;
81 form->numRows = 0;
82 form->elements = malloc(sizeof(*(form->elements)) * form->numCompsAlloced);
83
84 form->background = COLORSET_WINDOW;
85 form->hotKeys = malloc(sizeof(int));
86 form->numHotKeys = 0;
87 if (!(form->flags & NEWT_FORM_NOF12)) {
88 newtFormAddHotKey(co, NEWT_KEY_F12);
89 }
90
91 if (vertBar)
92 form->vertBar = vertBar;
93 else
94 form->vertBar = NULL;
95
96 return co;
97 }
98
99 newtComponent newtFormGetCurrent(newtComponent co) {
100 struct form * form = co->data;
101
102 return form->elements[form->currComp].co;
103 }
104
105 void newtFormSetCurrent(newtComponent co, newtComponent subco) {
106 struct form * form = co->data;
107 int i, new;
108
109 for (i = 0; i < form->numComps; i++) {
110 if (form->elements[i].co == subco) break;
111 }
112
113 if (form->elements[i].co != subco) return;
114 new = i;
115
116 if (!componentFits(co, new)) {
117 gotoComponent(form, -1);
118 form->vertOffset = form->elements[new].top - co->top - 1;
119 if (form->vertOffset > (form->numRows - co->height))
120 form->vertOffset = form->numRows - co->height;
121 }
122
123 gotoComponent(form, new);
124 }
125
126 void newtFormSetHeight(newtComponent co, int height) {
127 struct form * form = co->data;
128
129 form->fixedHeight = 1;
130 co->height = height;
131 }
132
133 void newtFormAddComponent(newtComponent co, newtComponent newco) {
134 struct form * form = co->data;
135 int delta;
136
137 if (form->numCompsAlloced == form->numComps) {
138 form->numCompsAlloced += 5;
139 form->elements = realloc(form->elements,
140 sizeof(*(form->elements)) * form->numCompsAlloced);
141 }
142
143 form->elements[form->numComps].left = newco->left;
144 form->elements[form->numComps].top = newco->top;
145 form->elements[form->numComps].co = newco;
146
147 if (newco->takesFocus && form->currComp == -1)
148 form->currComp = form->numComps;
149
150 form->numComps++;
151
152 if (co->left == -1) {
153 co->left = newco->left;
154 co->top = newco->top;
155 co->width = newco->width;
156 if (!form->fixedHeight) {
157 co->height = newco->height;
158 }
159 } else {
160 if (co->left > newco->left) {
161 delta = co->left - newco->left;
162 co->left -= delta;
163 co->width += delta;
164 }
165
166 if (co->top > newco->top) {
167 delta = co->top - newco->top;
168 co->top -= delta;
169 if (!form->fixedHeight)
170 co->height += delta;
171 }
172
173 if ((co->left + co->width) < (newco->left + newco->width))
174 co->width = (newco->left + newco->width) - co->left;
175
176 if (!form->fixedHeight) {
177 if ((co->top + co->height) < (newco->top + newco->height))
178 co->height = (newco->top + newco->height) - co->top;
179 }
180 }
181
182 if ((newco->top + newco->height - co->top) > form->numRows) {
183 form->numRows = newco->top + newco->height - co->top;
184 }
185
186 }
187
188 void newtFormAddComponents(newtComponent co, ...) {
189 va_list ap;
190 newtComponent subco;
191
192 va_start(ap, co);
193
194 while ((subco = va_arg(ap, newtComponent)))
195 newtFormAddComponent(co, subco);
196
197 va_end(ap);
198 }
199
200 void newtDrawForm(newtComponent co) {
201 struct form * form = co->data;
202 struct element * el;
203 int i;
204
205 SLsmg_set_color(form->background);
206 newtClearBox(co->left, co->top, co->width, co->height);
207 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
208 /* the scrollbar *always* fits */
209 if (el->co == form->vertBar)
210 el->co->ops->draw(el->co);
211 else {
212 /* only draw it if it'll fit on the screen vertically */
213 if (componentFits(co, i)) {
214 el->co->top = el->top - form->vertOffset;
215 el->co->ops->draw(el->co);
216 } else {
217 el->co->top = -1; /* tell it not to draw itself */
218 }
219 }
220 }
221
222 if (form->vertBar)
223 newtScrollbarSet(form->vertBar, form->vertOffset,
224 form->numRows - co->height);
225 }
226
227 static struct eventResult formEvent(newtComponent co, struct event ev) {
228 struct form * form = co->data;
229 newtComponent subco = form->elements[form->currComp].co;
230 int new, wrap = 0;
231 struct eventResult er;
232 int dir = 0, page = 0;
233 int i, num;
234
235 er.result = ER_IGNORED;
236
237 switch (ev.when) {
238 case EV_EARLY:
239 if (ev.event == EV_KEYPRESS) {
240 if (ev.u.key == NEWT_KEY_TAB) {
241 er.result = ER_SWALLOWED;
242 dir = 1;
243 wrap = 1;
244 } else if (ev.u.key == NEWT_KEY_UNTAB) {
245 er.result = ER_SWALLOWED;
246 dir = -1;
247 wrap = 1;
248 }
249 }
250
251 i = form->currComp;
252 num = 0;
253 while (er.result == ER_IGNORED && num != form->numComps ) {
254 er = form->elements[i].co->ops->event(form->elements[i].co, ev);
255
256 num++;
257 i++;
258 if (i == form->numComps) i = 0;
259 }
260
261 break;
262
263 case EV_NORMAL:
264 er = subco->ops->event(subco, ev);
265 switch (er.result) {
266 case ER_NEXTCOMP:
267 er.result = ER_SWALLOWED;
268 dir = 1;
269 break;
270
271 case ER_EXITFORM:
272 form->exitComp = subco;
273 break;
274
275 default:
276 break;
277 }
278 break;
279
280 case EV_LATE:
281 er = subco->ops->event(subco, ev);
282
283 if (er.result == ER_IGNORED) {
284 switch (ev.u.key) {
285 case NEWT_KEY_UP:
286 case NEWT_KEY_LEFT:
287 case NEWT_KEY_BKSPC:
288 er.result = ER_SWALLOWED;
289 dir = -1;
290 break;
291
292 case NEWT_KEY_DOWN:
293 case NEWT_KEY_RIGHT:
294 er.result = ER_SWALLOWED;
295 dir = 1;
296 break;
297
298 case NEWT_KEY_PGUP:
299 er.result = ER_SWALLOWED;
300 dir = -1;
301 page = 1;
302 break;
303
304 case NEWT_KEY_PGDN:
305 er.result = ER_SWALLOWED;
306 dir = 1;
307 page = 1;
308 break;
309 }
310 }
311 }
312
313 if (dir) {
314 new = form->currComp;
315
316 if (page) {
317 new += dir * co->height;
318 if (new < 0)
319 new = 0;
320 else if (new >= form->numComps)
321 new = (form->numComps - 1);
322
323 while (!form->elements[new].co->takesFocus)
324 new = new - dir;
325 } else {
326 do {
327 new += dir;
328
329 if (wrap) {
330 if (new < 0)
331 new = form->numComps - 1;
332 else if (new >= form->numComps)
333 new = 0;
334 } else if (new < 0 || new >= form->numComps)
335 return er;
336 } while (!form->elements[new].co->takesFocus);
337 }
338
339 /* make sure this component is visible */
340 if (!componentFits(co, new)) {
341 gotoComponent(form, -1);
342
343 if (dir < 0) {
344 /* make the new component the first one */
345 form->vertOffset = form->elements[new].top - co->top;
346 } else {
347 /* make the new component the last one */
348 form->vertOffset = (form->elements[new].top +
349 form->elements[new].co->height) -
350 (co->top + co->height);
351 }
352
353 if (form->vertOffset < 0) form->vertOffset = 0;
354 if (form->vertOffset > (form->numRows - co->height))
355 form->vertOffset = form->numRows - co->height;
356
357 newtDrawForm(co);
358 }
359
360 gotoComponent(form, new);
361 er.result = ER_SWALLOWED;
362 }
363
364 return er;
365 }
366
367 /* this also destroys all of the components on the form */
368 void newtFormDestroy(newtComponent co) {
369 newtComponent subco;
370 struct form * form = co->data;
371 int i;
372
373 /* first, destroy all of the components */
374 for (i = 0; i < form->numComps; i++) {
375 subco = form->elements[i].co;
376 if (subco->ops->destroy) {
377 subco->ops->destroy(subco);
378 } else {
379 if (subco->data) free(subco->data);
380 free(subco);
381 }
382 }
383
384 free(form->elements);
385 free(form);
386 free(co);
387 }
388
389 newtComponent newtRunForm(newtComponent co) {
390 struct newtExitStruct es;
391
392 newtFormRun(co, &es);
393 if (es.reason == NEWT_EXIT_HOTKEY) {
394 if (es.u.key == NEWT_KEY_F12) {
395 es.reason = NEWT_EXIT_COMPONENT;
396 es.u.co = co;
397 } else {
398 return NULL;
399 }
400 }
401
402 return es.u.co;
403 }
404
405 void newtFormAddHotKey(newtComponent co, int key) {
406 struct form * form = co->data;
407
408 form->numHotKeys++;
409 form->hotKeys = realloc(form->hotKeys, sizeof(int) * form->numHotKeys);
410 form->hotKeys[form->numHotKeys - 1] = key;
411 }
412
413 void newtFormRun(newtComponent co, struct newtExitStruct * es) {
414 struct form * form = co->data;
415 struct event ev;
416 struct eventResult er;
417 int key, i;
418 int done = 0;
419
420 /* first, draw all of the components */
421 newtDrawForm(co);
422
423 if (form->currComp == -1) {
424 gotoComponent(form, 0);
425 } else
426 gotoComponent(form, form->currComp);
427
428 while (!done) {
429 newtRefresh();
430 key = newtGetKey();
431
432 for (i = 0; i < form->numHotKeys; i++) {
433 if (form->hotKeys[i] == key) {
434 es->reason = NEWT_EXIT_HOTKEY;
435 es->u.key = key;
436 done = 1;
437 break;
438 }
439 }
440
441 if (!done) {
442 ev.event = EV_KEYPRESS;
443 ev.u.key = key;
444
445 er = sendEvent(co, ev);
446
447 if (er.result == ER_EXITFORM) {
448 done = 1;
449 es->reason = NEWT_EXIT_COMPONENT;
450 es->u.co = form->exitComp;
451 }
452 }
453 }
454
455 newtRefresh();
456 }
457
458 static struct eventResult sendEvent(newtComponent co, struct event ev) {
459 struct eventResult er;
460
461 ev.when = EV_EARLY;
462 er = co->ops->event(co, ev);
463
464 if (er.result == ER_IGNORED) {
465 ev.when = EV_NORMAL;
466 er = co->ops->event(co, ev);
467 }
468
469 if (er.result == ER_IGNORED) {
470 ev.when = EV_LATE;
471 er = co->ops->event(co, ev);
472 }
473
474 return er;
475 }
476
477 static void gotoComponent(struct form * form, int newComp) {
478 struct event ev;
479
480 if (form->currComp != -1) {
481 ev.event = EV_UNFOCUS;
482 sendEvent(form->elements[form->currComp].co, ev);
483 }
484
485 form->currComp = newComp;
486
487 if (form->currComp != -1) {
488 ev.event = EV_FOCUS;
489 ev.when = EV_NORMAL;
490 sendEvent(form->elements[form->currComp].co, ev);
491 }
492 }
493
494 void newtComponentAddCallback(newtComponent co, newtCallback f, void * data) {
495 co->callback = f;
496 co->callbackData = data;
497 }
498
499 void newtFormSetBackground(newtComponent co, int color) {
500 struct form * form = co->data;
501
502 form->background = color;
503 }