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