]>
Commit | Line | Data |
---|---|---|
139f06bc | 1 | #include <slang.h> |
6fb96a3f | 2 | #include <stdarg.h> |
3 | #include <stdlib.h> | |
4 | ||
5 | #include "newt.h" | |
6 | #include "newt_pr.h" | |
7 | ||
f5daa14e | 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 { | |
3da9c9a2 | 18 | int top, left; /* Actual, not virtual. These are translated */ |
19 | newtComponent co; /* into actual through vertOffset */ | |
f5daa14e | 20 | }; |
21 | ||
c4827b34 | 22 | struct form { |
6fb96a3f | 23 | int numCompsAlloced; |
f5daa14e | 24 | struct element * elements; |
6fb96a3f | 25 | int numComps; |
26 | int currComp; | |
8c4e1047 | 27 | int fixedHeight; |
02b180e8 | 28 | int flags; |
f5daa14e | 29 | int vertOffset; |
02b180e8 | 30 | newtComponent vertBar, exitComp; |
d4109c37 | 31 | const char * help; |
f1f3611f | 32 | int numRows; |
da151ca8 | 33 | int * hotKeys; |
34 | int numHotKeys; | |
8f4e87fc | 35 | int background; |
3da9c9a2 | 36 | int beenSet; |
6fb96a3f | 37 | }; |
38 | ||
c4827b34 | 39 | static void gotoComponent(struct form * form, int newComp); |
40 | static struct eventResult formEvent(newtComponent co, struct event ev); | |
434c766b | 41 | static struct eventResult sendEvent(newtComponent comp, struct event ev); |
8f52cd47 | 42 | static void formPlace(newtComponent co, int left, int top); |
c4827b34 | 43 | |
8f52cd47 | 44 | /* this isn't static as grid.c tests against it to find forms */ |
45 | struct componentOps formOps = { | |
8b68158d | 46 | newtDrawForm, |
c4827b34 | 47 | formEvent, |
48 | newtFormDestroy, | |
8f52cd47 | 49 | formPlace, |
50 | newtDefaultMappedHandler, | |
c4827b34 | 51 | } ; |
52 | ||
f5daa14e | 53 | static inline int componentFits(newtComponent co, int compNum) { |
54 | struct form * form = co->data; | |
55 | struct element * el = form->elements + compNum; | |
56 | ||
57 | if ((co->top + form->vertOffset) > el->top) return 0; | |
58 | if ((co->top + form->vertOffset + co->height) < | |
59 | (el->top + el->co->height)) return 0; | |
60 | ||
61 | return 1; | |
62 | } | |
63 | ||
d4109c37 | 64 | newtComponent newtForm(newtComponent vertBar, const char * help, int flags) { |
c4827b34 | 65 | newtComponent co; |
66 | struct form * form; | |
67 | ||
68 | co = malloc(sizeof(*co)); | |
69 | form = malloc(sizeof(*form)); | |
70 | co->data = form; | |
71 | co->width = 0; | |
72 | co->height = 0; | |
73 | co->top = -1; | |
74 | co->left = -1; | |
3da9c9a2 | 75 | co->isMapped = 0; |
c4827b34 | 76 | |
8f52cd47 | 77 | co->takesFocus = 0; /* we may have 0 components */ |
c4827b34 | 78 | co->ops = &formOps; |
6fb96a3f | 79 | |
02b180e8 | 80 | form->help = help; |
81 | form->flags = flags; | |
6fb96a3f | 82 | form->numCompsAlloced = 5; |
83 | form->numComps = 0; | |
84 | form->currComp = -1; | |
f5daa14e | 85 | form->vertOffset = 0; |
8c4e1047 | 86 | form->fixedHeight = 0; |
f1f3611f | 87 | form->numRows = 0; |
3da9c9a2 | 88 | form->beenSet = 0; |
f5daa14e | 89 | form->elements = malloc(sizeof(*(form->elements)) * form->numCompsAlloced); |
6fb96a3f | 90 | |
8f4e87fc | 91 | form->background = COLORSET_WINDOW; |
da151ca8 | 92 | form->hotKeys = malloc(sizeof(int)); |
93 | form->numHotKeys = 0; | |
4a93351d | 94 | if (!(form->flags & NEWT_FLAG_NOF12)) { |
da151ca8 | 95 | newtFormAddHotKey(co, NEWT_KEY_F12); |
96 | } | |
97 | ||
02b180e8 | 98 | if (vertBar) |
99 | form->vertBar = vertBar; | |
f1f3611f | 100 | else |
101 | form->vertBar = NULL; | |
102 | ||
c4827b34 | 103 | return co; |
6fb96a3f | 104 | } |
105 | ||
2a2998a0 | 106 | newtComponent newtFormGetCurrent(newtComponent co) { |
107 | struct form * form = co->data; | |
108 | ||
109 | return form->elements[form->currComp].co; | |
110 | } | |
111 | ||
7ea3dccd | 112 | void newtFormSetCurrent(newtComponent co, newtComponent subco) { |
113 | struct form * form = co->data; | |
114 | int i, new; | |
115 | ||
116 | for (i = 0; i < form->numComps; i++) { | |
117 | if (form->elements[i].co == subco) break; | |
118 | } | |
119 | ||
120 | if (form->elements[i].co != subco) return; | |
121 | new = i; | |
122 | ||
3da9c9a2 | 123 | if (co->isMapped && !componentFits(co, new)) { |
7ea3dccd | 124 | gotoComponent(form, -1); |
125 | form->vertOffset = form->elements[new].top - co->top - 1; | |
d1a682d1 | 126 | if (form->vertOffset > (form->numRows - co->height)) |
127 | form->vertOffset = form->numRows - co->height; | |
7ea3dccd | 128 | } |
129 | ||
130 | gotoComponent(form, new); | |
131 | } | |
132 | ||
8c4e1047 | 133 | void newtFormSetHeight(newtComponent co, int height) { |
f5daa14e | 134 | struct form * form = co->data; |
135 | ||
8c4e1047 | 136 | form->fixedHeight = 1; |
f5daa14e | 137 | co->height = height; |
138 | } | |
139 | ||
ea947804 | 140 | void newtFormSetWidth(newtComponent co, int width) { |
141 | co->width = width; | |
142 | } | |
143 | ||
c4827b34 | 144 | void newtFormAddComponent(newtComponent co, newtComponent newco) { |
145 | struct form * form = co->data; | |
6fb96a3f | 146 | |
8f52cd47 | 147 | co->takesFocus = 1; |
148 | ||
6fb96a3f | 149 | if (form->numCompsAlloced == form->numComps) { |
150 | form->numCompsAlloced += 5; | |
f5daa14e | 151 | form->elements = realloc(form->elements, |
152 | sizeof(*(form->elements)) * form->numCompsAlloced); | |
6fb96a3f | 153 | } |
154 | ||
f7540d34 | 155 | /* we grab real values for these a bit later */ |
156 | form->elements[form->numComps].left = -2; | |
157 | form->elements[form->numComps].top = -2; | |
f5daa14e | 158 | form->elements[form->numComps].co = newco; |
f1f3611f | 159 | |
160 | if (newco->takesFocus && form->currComp == -1) | |
161 | form->currComp = form->numComps; | |
162 | ||
f5daa14e | 163 | form->numComps++; |
6fb96a3f | 164 | } |
165 | ||
c4827b34 | 166 | void newtFormAddComponents(newtComponent co, ...) { |
6fb96a3f | 167 | va_list ap; |
c4827b34 | 168 | newtComponent subco; |
6fb96a3f | 169 | |
c4827b34 | 170 | va_start(ap, co); |
6fb96a3f | 171 | |
c4827b34 | 172 | while ((subco = va_arg(ap, newtComponent))) |
173 | newtFormAddComponent(co, subco); | |
6fb96a3f | 174 | |
175 | va_end(ap); | |
176 | } | |
177 | ||
8f52cd47 | 178 | static void formPlace(newtComponent co, int left, int top) { |
179 | struct form * form = co->data; | |
180 | int vertDelta, horizDelta; | |
181 | struct element * el; | |
182 | int i; | |
183 | ||
184 | newtFormSetSize(co); | |
185 | ||
186 | vertDelta = top - co->top; | |
187 | horizDelta = left - co->left; | |
188 | co->top = top; | |
189 | co->left = left; | |
190 | ||
191 | for (i = 0, el = form->elements; i < form->numComps; i++, el++) { | |
192 | el->co->top += vertDelta; | |
193 | el->top += vertDelta; | |
194 | el->co->left += horizDelta; | |
195 | el->left += horizDelta; | |
196 | } | |
197 | } | |
198 | ||
8b68158d | 199 | void newtDrawForm(newtComponent co) { |
c4827b34 | 200 | struct form * form = co->data; |
f5daa14e | 201 | struct element * el; |
6fb96a3f | 202 | int i; |
6fb96a3f | 203 | |
3da9c9a2 | 204 | newtFormSetSize(co); |
205 | ||
8f4e87fc | 206 | SLsmg_set_color(form->background); |
a1331450 | 207 | newtClearBox(co->left, co->top, co->width, co->height); |
f5daa14e | 208 | for (i = 0, el = form->elements; i < form->numComps; i++, el++) { |
8f52cd47 | 209 | /* the scrollbar *always* fits somewhere */ |
210 | if (el->co == form->vertBar) { | |
211 | el->co->ops->mapped(el->co, 1); | |
f5daa14e | 212 | el->co->ops->draw(el->co); |
8f52cd47 | 213 | } else { |
f1f3611f | 214 | /* only draw it if it'll fit on the screen vertically */ |
215 | if (componentFits(co, i)) { | |
216 | el->co->top = el->top - form->vertOffset; | |
8f52cd47 | 217 | el->co->ops->mapped(el->co, 1); |
f1f3611f | 218 | el->co->ops->draw(el->co); |
219 | } else { | |
8f52cd47 | 220 | el->co->ops->mapped(el->co, 0); |
f1f3611f | 221 | } |
f5daa14e | 222 | } |
6fb96a3f | 223 | } |
f1f3611f | 224 | |
225 | if (form->vertBar) | |
226 | newtScrollbarSet(form->vertBar, form->vertOffset, | |
227 | form->numRows - co->height); | |
c4827b34 | 228 | } |
6fb96a3f | 229 | |
c4827b34 | 230 | static struct eventResult formEvent(newtComponent co, struct event ev) { |
231 | struct form * form = co->data; | |
f5daa14e | 232 | newtComponent subco = form->elements[form->currComp].co; |
d1a682d1 | 233 | int new, wrap = 0; |
c4827b34 | 234 | struct eventResult er; |
d1a682d1 | 235 | int dir = 0, page = 0; |
434c766b | 236 | int i, num; |
6fb96a3f | 237 | |
c4827b34 | 238 | er.result = ER_IGNORED; |
8f52cd47 | 239 | if (!form->numComps) return er; |
240 | ||
241 | subco = form->elements[form->currComp].co; | |
6fb96a3f | 242 | |
434c766b | 243 | switch (ev.when) { |
244 | case EV_EARLY: | |
245 | if (ev.event == EV_KEYPRESS) { | |
246 | if (ev.u.key == NEWT_KEY_TAB) { | |
247 | er.result = ER_SWALLOWED; | |
248 | dir = 1; | |
249 | wrap = 1; | |
250 | } else if (ev.u.key == NEWT_KEY_UNTAB) { | |
251 | er.result = ER_SWALLOWED; | |
252 | dir = -1; | |
253 | wrap = 1; | |
254 | } | |
8c4e1047 | 255 | } |
6fb96a3f | 256 | |
8f52cd47 | 257 | if (form->numComps) { |
258 | i = form->currComp; | |
259 | num = 0; | |
260 | while (er.result == ER_IGNORED && num != form->numComps ) { | |
261 | er = form->elements[i].co->ops->event(form->elements[i].co, ev); | |
434c766b | 262 | |
8f52cd47 | 263 | num++; |
264 | i++; | |
265 | if (i == form->numComps) i = 0; | |
266 | } | |
6fb96a3f | 267 | } |
268 | ||
434c766b | 269 | break; |
270 | ||
271 | case EV_NORMAL: | |
272 | er = subco->ops->event(subco, ev); | |
e90f9163 | 273 | switch (er.result) { |
274 | case ER_NEXTCOMP: | |
275 | er.result = ER_SWALLOWED; | |
276 | dir = 1; | |
e90f9163 | 277 | break; |
278 | ||
02b180e8 | 279 | case ER_EXITFORM: |
280 | form->exitComp = subco; | |
281 | break; | |
282 | ||
e90f9163 | 283 | default: |
284 | break; | |
285 | } | |
434c766b | 286 | break; |
287 | ||
288 | case EV_LATE: | |
289 | er = subco->ops->event(subco, ev); | |
290 | ||
6fb96a3f | 291 | if (er.result == ER_IGNORED) { |
434c766b | 292 | switch (ev.u.key) { |
293 | case NEWT_KEY_UP: | |
294 | case NEWT_KEY_LEFT: | |
295 | case NEWT_KEY_BKSPC: | |
296 | er.result = ER_SWALLOWED; | |
297 | dir = -1; | |
434c766b | 298 | break; |
299 | ||
300 | case NEWT_KEY_DOWN: | |
301 | case NEWT_KEY_RIGHT: | |
434c766b | 302 | er.result = ER_SWALLOWED; |
303 | dir = 1; | |
d1a682d1 | 304 | break; |
305 | ||
306 | case NEWT_KEY_PGUP: | |
307 | er.result = ER_SWALLOWED; | |
308 | dir = -1; | |
309 | page = 1; | |
310 | break; | |
311 | ||
312 | case NEWT_KEY_PGDN: | |
313 | er.result = ER_SWALLOWED; | |
314 | dir = 1; | |
315 | page = 1; | |
434c766b | 316 | break; |
6fb96a3f | 317 | } |
318 | } | |
c4827b34 | 319 | } |
6fb96a3f | 320 | |
f5daa14e | 321 | if (dir) { |
322 | new = form->currComp; | |
d1a682d1 | 323 | |
324 | if (page) { | |
325 | new += dir * co->height; | |
326 | if (new < 0) | |
327 | new = 0; | |
328 | else if (new >= form->numComps) | |
329 | new = (form->numComps - 1); | |
330 | ||
331 | while (!form->elements[new].co->takesFocus) | |
332 | new = new - dir; | |
333 | } else { | |
334 | do { | |
335 | new += dir; | |
336 | ||
337 | if (wrap) { | |
338 | if (new < 0) | |
339 | new = form->numComps - 1; | |
340 | else if (new >= form->numComps) | |
341 | new = 0; | |
342 | } else if (new < 0 || new >= form->numComps) | |
343 | return er; | |
344 | } while (!form->elements[new].co->takesFocus); | |
345 | } | |
f5daa14e | 346 | |
347 | /* make sure this component is visible */ | |
348 | if (!componentFits(co, new)) { | |
349 | gotoComponent(form, -1); | |
350 | ||
351 | if (dir < 0) { | |
352 | /* make the new component the first one */ | |
353 | form->vertOffset = form->elements[new].top - co->top; | |
354 | } else { | |
355 | /* make the new component the last one */ | |
356 | form->vertOffset = (form->elements[new].top + | |
357 | form->elements[new].co->height) - | |
358 | (co->top + co->height); | |
359 | } | |
360 | ||
361 | if (form->vertOffset < 0) form->vertOffset = 0; | |
d1a682d1 | 362 | if (form->vertOffset > (form->numRows - co->height)) |
363 | form->vertOffset = form->numRows - co->height; | |
f5daa14e | 364 | |
8b68158d | 365 | newtDrawForm(co); |
c4827b34 | 366 | } |
f5daa14e | 367 | |
368 | gotoComponent(form, new); | |
369 | er.result = ER_SWALLOWED; | |
c4827b34 | 370 | } |
6fb96a3f | 371 | |
c4827b34 | 372 | return er; |
6fb96a3f | 373 | } |
374 | ||
375 | /* this also destroys all of the components on the form */ | |
c4827b34 | 376 | void newtFormDestroy(newtComponent co) { |
c4827b34 | 377 | newtComponent subco; |
378 | struct form * form = co->data; | |
f1f3611f | 379 | int i; |
6fb96a3f | 380 | |
381 | /* first, destroy all of the components */ | |
382 | for (i = 0; i < form->numComps; i++) { | |
f5daa14e | 383 | subco = form->elements[i].co; |
c4827b34 | 384 | if (subco->ops->destroy) { |
385 | subco->ops->destroy(subco); | |
6fb96a3f | 386 | } else { |
c4827b34 | 387 | if (subco->data) free(subco->data); |
388 | free(subco); | |
6fb96a3f | 389 | } |
390 | } | |
391 | ||
f5daa14e | 392 | free(form->elements); |
6fb96a3f | 393 | free(form); |
c4827b34 | 394 | free(co); |
395 | } | |
396 | ||
397 | newtComponent newtRunForm(newtComponent co) { | |
da151ca8 | 398 | struct newtExitStruct es; |
399 | ||
400 | newtFormRun(co, &es); | |
401 | if (es.reason == NEWT_EXIT_HOTKEY) { | |
402 | if (es.u.key == NEWT_KEY_F12) { | |
403 | es.reason = NEWT_EXIT_COMPONENT; | |
404 | es.u.co = co; | |
405 | } else { | |
406 | return NULL; | |
407 | } | |
408 | } | |
409 | ||
410 | return es.u.co; | |
411 | } | |
412 | ||
413 | void newtFormAddHotKey(newtComponent co, int key) { | |
414 | struct form * form = co->data; | |
415 | ||
416 | form->numHotKeys++; | |
417 | form->hotKeys = realloc(form->hotKeys, sizeof(int) * form->numHotKeys); | |
418 | form->hotKeys[form->numHotKeys - 1] = key; | |
419 | } | |
420 | ||
3da9c9a2 | 421 | void newtFormSetSize(newtComponent co) { |
f7540d34 | 422 | struct form * form = co->data; |
423 | int delta, i; | |
424 | struct element * el; | |
425 | ||
3da9c9a2 | 426 | if (form->beenSet) return; |
427 | ||
428 | form->beenSet = 1; | |
429 | ||
8f52cd47 | 430 | if (!form->numComps) return; |
431 | ||
f7540d34 | 432 | co->width = 0; |
3da9c9a2 | 433 | if (!form->fixedHeight) co->height = 0; |
434 | ||
f7540d34 | 435 | co->top = form->elements[0].co->top; |
436 | co->left = form->elements[0].co->left; | |
437 | for (i = 0, el = form->elements; i < form->numComps; i++, el++) { | |
438 | if (el->co->ops == &formOps) | |
439 | newtFormSetSize(el->co); | |
440 | ||
441 | el->left = el->co->left; | |
442 | el->top = el->co->top; | |
443 | ||
444 | if (co->left > el->co->left) { | |
445 | delta = co->left - el->co->left; | |
446 | co->left -= delta; | |
447 | co->width += delta; | |
448 | } | |
449 | ||
450 | if (co->top > el->co->top) { | |
451 | delta = co->top - el->co->top; | |
452 | co->top -= delta; | |
453 | if (!form->fixedHeight) | |
454 | co->height += delta; | |
455 | } | |
456 | ||
457 | if ((co->left + co->width) < (el->co->left + el->co->width)) | |
458 | co->width = (el->co->left + el->co->width) - co->left; | |
459 | ||
460 | if (!form->fixedHeight) { | |
461 | if ((co->top + co->height) < (el->co->top + el->co->height)) | |
462 | co->height = (el->co->top + el->co->height) - co->top; | |
463 | } | |
464 | ||
465 | if ((el->co->top + el->co->height - co->top) > form->numRows) { | |
466 | form->numRows = el->co->top + el->co->height - co->top; | |
467 | } | |
468 | } | |
469 | } | |
470 | ||
da151ca8 | 471 | void newtFormRun(newtComponent co, struct newtExitStruct * es) { |
c4827b34 | 472 | struct form * form = co->data; |
473 | struct event ev; | |
474 | struct eventResult er; | |
da151ca8 | 475 | int key, i; |
476 | int done = 0; | |
c4827b34 | 477 | |
f7540d34 | 478 | newtFormSetSize(co); |
479 | /* draw all of the components */ | |
8b68158d | 480 | newtDrawForm(co); |
c4827b34 | 481 | |
f1f3611f | 482 | if (form->currComp == -1) { |
c4827b34 | 483 | gotoComponent(form, 0); |
f1f3611f | 484 | } else |
c4827b34 | 485 | gotoComponent(form, form->currComp); |
486 | ||
da151ca8 | 487 | while (!done) { |
c4827b34 | 488 | newtRefresh(); |
489 | key = newtGetKey(); | |
490 | ||
ae1235d0 | 491 | if (key == NEWT_KEY_RESIZE) { |
492 | /* newtResizeScreen(1); */ | |
493 | continue; | |
494 | } | |
495 | ||
da151ca8 | 496 | for (i = 0; i < form->numHotKeys; i++) { |
497 | if (form->hotKeys[i] == key) { | |
498 | es->reason = NEWT_EXIT_HOTKEY; | |
499 | es->u.key = key; | |
500 | done = 1; | |
501 | break; | |
502 | } | |
503 | } | |
c4827b34 | 504 | |
da151ca8 | 505 | if (!done) { |
506 | ev.event = EV_KEYPRESS; | |
507 | ev.u.key = key; | |
508 | ||
509 | er = sendEvent(co, ev); | |
510 | ||
511 | if (er.result == ER_EXITFORM) { | |
512 | done = 1; | |
513 | es->reason = NEWT_EXIT_COMPONENT; | |
514 | es->u.co = form->exitComp; | |
515 | } | |
516 | } | |
517 | } | |
c4827b34 | 518 | |
519 | newtRefresh(); | |
6fb96a3f | 520 | } |
521 | ||
434c766b | 522 | static struct eventResult sendEvent(newtComponent co, struct event ev) { |
523 | struct eventResult er; | |
524 | ||
525 | ev.when = EV_EARLY; | |
526 | er = co->ops->event(co, ev); | |
527 | ||
528 | if (er.result == ER_IGNORED) { | |
529 | ev.when = EV_NORMAL; | |
530 | er = co->ops->event(co, ev); | |
531 | } | |
532 | ||
533 | if (er.result == ER_IGNORED) { | |
534 | ev.when = EV_LATE; | |
535 | er = co->ops->event(co, ev); | |
536 | } | |
537 | ||
538 | return er; | |
539 | } | |
540 | ||
c4827b34 | 541 | static void gotoComponent(struct form * form, int newComp) { |
6fb96a3f | 542 | struct event ev; |
543 | ||
544 | if (form->currComp != -1) { | |
545 | ev.event = EV_UNFOCUS; | |
434c766b | 546 | sendEvent(form->elements[form->currComp].co, ev); |
6fb96a3f | 547 | } |
548 | ||
549 | form->currComp = newComp; | |
f5daa14e | 550 | |
551 | if (form->currComp != -1) { | |
552 | ev.event = EV_FOCUS; | |
434c766b | 553 | ev.when = EV_NORMAL; |
554 | sendEvent(form->elements[form->currComp].co, ev); | |
f5daa14e | 555 | } |
6fb96a3f | 556 | } |
da151ca8 | 557 | |
558 | void newtComponentAddCallback(newtComponent co, newtCallback f, void * data) { | |
559 | co->callback = f; | |
560 | co->callbackData = data; | |
561 | } | |
562 | ||
41d86a3c | 563 | void newtComponentTakesFocus(newtComponent co, int val) { |
564 | co->takesFocus = val; | |
565 | } | |
566 | ||
8f4e87fc | 567 | void newtFormSetBackground(newtComponent co, int color) { |
568 | struct form * form = co->data; | |
569 | ||
570 | form->background = color; | |
571 | } |