1 /* ______ ___ ___
2 * /\ _ \ /\_ \ /\_ \
3 * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
4 * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
5 * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
6 * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7 * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8 * /\____/
9 * \_/__/
10 *
11 * The standard GUI dialog object procedures.
12 *
13 * By Shawn Hargreaves.
14 *
15 * Radio button, icon, and slider objects by Chris La Mantia.
16 *
17 * Scrolling d_edit_proc by VolkerOth.
18 *
19 * Text box object by Doug Eleveld.
20 *
21 * d_text_list_proc by Andy Goth.
22 *
23 * See readme.txt for copyright information.
24 */
25
26
27 #include "allegro.h"
28 #include "allegro/internal/aintern.h"
29
30
31 #ifdef ALLEGRO_WINDOWS
32 /* exported address of d_clear_proc */
33 int (*_d_clear_proc)(int, DIALOG *, int) = NULL;
34 #endif
35
36 /* typedef for the listbox callback functions */
37 typedef char *(*getfuncptr)(int, int *);
38
39
40
41 /* gui_textout_ex:
42 * Wrapper function for drawing text to the screen, which interprets the
43 * & character as an underbar for displaying keyboard shortcuts. Returns
44 * the width of the output string in pixels.
45 */
gui_textout_ex(BITMAP * bmp,AL_CONST char * s,int x,int y,int color,int bg,int centre)46 int gui_textout_ex(BITMAP *bmp, AL_CONST char *s, int x, int y, int color, int bg, int centre)
47 {
48 char tmp[1024];
49 int hline_pos = -1;
50 int len = 0;
51 int in_pos = 0;
52 int out_pos = 0;
53 int pix_len, c;
54 ASSERT(s);
55
56 while (((c = ugetc(s+in_pos)) != 0) && (out_pos<(int)(sizeof(tmp)-ucwidth(0)))) {
57 if (c == '&') {
58 in_pos += uwidth(s+in_pos);
59 c = ugetc(s+in_pos);
60 if (c == '&') {
61 out_pos += usetc(tmp+out_pos, '&');
62 in_pos += uwidth(s+in_pos);
63 len++;
64 }
65 else
66 hline_pos = len;
67 }
68 else {
69 out_pos += usetc(tmp+out_pos, c);
70 in_pos += uwidth(s+in_pos);
71 len++;
72 }
73 }
74
75 usetc(tmp+out_pos, 0);
76 pix_len = text_length(font, tmp);
77
78 if (centre)
79 x -= pix_len / 2;
80
81 if (bmp) {
82 textout_ex(bmp, font, tmp, x, y, color, bg);
83
84 if (hline_pos >= 0) {
85 c = ugetat(tmp, hline_pos);
86 usetat(tmp, hline_pos, 0);
87 hline_pos = text_length(font, tmp);
88 c = usetc(tmp, c);
89 usetc(tmp+c, 0);
90 c = text_length(font, tmp);
91 hline(bmp, x+hline_pos, y+text_height(font)-gui_font_baseline, x+hline_pos+c-1, color);
92 }
93 }
94
95 return pix_len;
96 }
97
98
99
100 /* gui_strlen:
101 * Returns the length of a string in pixels, ignoring '&' characters.
102 */
gui_strlen(AL_CONST char * s)103 int gui_strlen(AL_CONST char *s)
104 {
105 ASSERT(s);
106 return gui_textout_ex(NULL, s, 0, 0, 0, 0, 0);
107 }
108
109
110
111 /* dotted_rect:
112 * Draws a dotted rectangle, for showing an object has the input focus.
113 */
dotted_rect(int x1,int y1,int x2,int y2,int fg,int bg)114 static void dotted_rect(int x1, int y1, int x2, int y2, int fg, int bg)
115 {
116 BITMAP *gui_bmp = gui_get_screen();
117 int x = ((x1+y1) & 1) ? 1 : 0;
118 int c;
119
120 /* two loops to avoid bank switches */
121 for (c=x1; c<=x2; c++)
122 putpixel(gui_bmp, c, y1, (((c+y1) & 1) == x) ? fg : bg);
123 for (c=x1; c<=x2; c++)
124 putpixel(gui_bmp, c, y2, (((c+y2) & 1) == x) ? fg : bg);
125
126 for (c=y1+1; c<y2; c++) {
127 putpixel(gui_bmp, x1, c, (((c+x1) & 1) == x) ? fg : bg);
128 putpixel(gui_bmp, x2, c, (((c+x2) & 1) == x) ? fg : bg);
129 }
130 }
131
132
133
134 /* d_yield_proc:
135 * Simple dialog procedure which just yields the timeslice when the dialog
136 * is idle.
137 */
d_yield_proc(int msg,DIALOG * d,int c)138 int d_yield_proc(int msg, DIALOG *d, int c)
139 {
140 if (msg == MSG_IDLE)
141 rest(1);
142
143 return D_O_K;
144 }
145
146
147
148 /* d_clear_proc:
149 * Simple dialog procedure which just clears the screen. Useful as the
150 * first object in a dialog.
151 */
d_clear_proc(int msg,DIALOG * d,int c)152 int d_clear_proc(int msg, DIALOG *d, int c)
153 {
154 ASSERT(d);
155
156 #ifdef ALLEGRO_WINDOWS
157 /* kludge to get the exported address of d_clear_proc */
158 if (!_d_clear_proc)
159 _d_clear_proc = d->proc;
160 #endif
161
162 if (msg == MSG_DRAW) {
163 BITMAP *gui_bmp = gui_get_screen();
164 int w, h;
165
166 /* Get width and height of target bitmap. We can't use SCREEN_W and
167 * SCREEN_H because the target might not be the screen, but we cannot use
168 * bmp->w and bmp->h either because if it is the screen these are actually
169 * wrong. Ugh!
170 */
171 w = (gui_bmp == screen) ? SCREEN_W: gui_bmp->w;
172 h = (gui_bmp == screen) ? SCREEN_H: gui_bmp->h;
173
174 set_clip_rect(gui_bmp, 0, 0, w-1, h-1);
175 set_clip_state(gui_bmp, TRUE);
176 clear_to_color(gui_bmp, d->bg);
177 }
178
179 return D_O_K;
180 }
181
182
183
184 /* d_box_proc:
185 * Simple dialog procedure: just draws a box.
186 */
d_box_proc(int msg,DIALOG * d,int c)187 int d_box_proc(int msg, DIALOG *d, int c)
188 {
189 ASSERT(d);
190 if (msg==MSG_DRAW) {
191 int fg = (d->flags & D_DISABLED) ? gui_mg_color : d->fg;
192 BITMAP *gui_bmp = gui_get_screen();
193
194 rectfill(gui_bmp, d->x+1, d->y+1, d->x+d->w-2, d->y+d->h-2, d->bg);
195 rect(gui_bmp, d->x, d->y, d->x+d->w-1, d->y+d->h-1, fg);
196 }
197
198 return D_O_K;
199 }
200
201
202
203 /* d_shadow_box_proc:
204 * Simple dialog procedure: draws a box with a shadow.
205 */
d_shadow_box_proc(int msg,DIALOG * d,int c)206 int d_shadow_box_proc(int msg, DIALOG *d, int c)
207 {
208 ASSERT(d);
209 if (msg==MSG_DRAW) {
210 int fg = (d->flags & D_DISABLED) ? gui_mg_color : d->fg;
211 int black = makecol(0,0,0);
212 BITMAP *gui_bmp = gui_get_screen();
213
214 rectfill(gui_bmp, d->x+1, d->y+1, d->x+d->w-3, d->y+d->h-3, d->bg);
215 rect(gui_bmp, d->x, d->y, d->x+d->w-2, d->y+d->h-2, fg);
216 vline(gui_bmp, d->x+d->w-1, d->y+1, d->y+d->h-1, black);
217 hline(gui_bmp, d->x+1, d->y+d->h-1, d->x+d->w-1, black);
218 }
219
220 return D_O_K;
221 }
222
223
224
225 /* d_bitmap_proc:
226 * Simple dialog procedure: draws the bitmap which is pointed to by dp.
227 */
d_bitmap_proc(int msg,DIALOG * d,int c)228 int d_bitmap_proc(int msg, DIALOG *d, int c)
229 {
230 BITMAP *b;
231 ASSERT(d);
232
233 b = (BITMAP *)d->dp;
234 if (msg==MSG_DRAW)
235 blit(b, gui_get_screen(), 0, 0, d->x, d->y, d->w, d->h);
236
237 return D_O_K;
238 }
239
240
241
242 /* d_text_proc:
243 * Simple dialog procedure: draws the text string which is pointed to by dp.
244 */
d_text_proc(int msg,DIALOG * d,int c)245 int d_text_proc(int msg, DIALOG *d, int c)
246 {
247 ASSERT(d);
248 if (msg==MSG_DRAW) {
249 int fg = (d->flags & D_DISABLED) ? gui_mg_color : d->fg;
250 FONT *oldfont = font;
251
252 if (d->dp2)
253 font = d->dp2;
254
255 gui_textout_ex(gui_get_screen(), d->dp, d->x, d->y, fg, d->bg, FALSE);
256
257 font = oldfont;
258 }
259
260 return D_O_K;
261 }
262
263
264
265 /* d_ctext_proc:
266 * Simple dialog procedure: draws the text string which is pointed to by dp,
267 * centering it around the object's x coordinate.
268 */
d_ctext_proc(int msg,DIALOG * d,int c)269 int d_ctext_proc(int msg, DIALOG *d, int c)
270 {
271 ASSERT(d);
272 if (msg==MSG_DRAW) {
273 int fg = (d->flags & D_DISABLED) ? gui_mg_color : d->fg;
274 FONT *oldfont = font;
275
276 if (d->dp2)
277 font = d->dp2;
278
279 gui_textout_ex(gui_get_screen(), d->dp, d->x + d->w/2, d->y, fg, d->bg, TRUE);
280
281 font = oldfont;
282 }
283
284 return D_O_K;
285 }
286
287
288
289 /* d_rtext_proc:
290 * Simple dialog procedure: draws the text string which is pointed to by dp,
291 * right aligning it.
292 */
d_rtext_proc(int msg,DIALOG * d,int c)293 int d_rtext_proc(int msg, DIALOG *d, int c)
294 {
295 ASSERT(d);
296 if (msg==MSG_DRAW) {
297 int fg = (d->flags & D_DISABLED) ? gui_mg_color : d->fg;
298 FONT *oldfont = font;
299
300 if (d->dp2)
301 font = d->dp2;
302
303 gui_textout_ex(gui_get_screen(), d->dp, d->x + d->w - gui_strlen(d->dp), d->y, fg, d->bg, FALSE);
304
305 font = oldfont;
306 }
307
308 return D_O_K;
309 }
310
311
312
313 /* d_button_proc:
314 * A button object (the dp field points to the text string). This object
315 * can be selected by clicking on it with the mouse or by pressing its
316 * keyboard shortcut. If the D_EXIT flag is set, selecting it will close
317 * the dialog, otherwise it will toggle on and off.
318 */
d_button_proc(int msg,DIALOG * d,int c)319 int d_button_proc(int msg, DIALOG *d, int c)
320 {
321 BITMAP *gui_bmp;
322 int state1, state2;
323 int black;
324 int swap;
325 int g;
326 ASSERT(d);
327
328 gui_bmp = gui_get_screen();
329
330 switch (msg) {
331
332 case MSG_DRAW:
333 if (d->flags & D_SELECTED) {
334 g = 1;
335 state1 = d->bg;
336 state2 = (d->flags & D_DISABLED) ? gui_mg_color : d->fg;
337 }
338 else {
339 g = 0;
340 state1 = (d->flags & D_DISABLED) ? gui_mg_color : d->fg;
341 state2 = d->bg;
342 }
343
344 rectfill(gui_bmp, d->x+1+g, d->y+1+g, d->x+d->w-3+g, d->y+d->h-3+g, state2);
345 rect(gui_bmp, d->x+g, d->y+g, d->x+d->w-2+g, d->y+d->h-2+g, state1);
346 gui_textout_ex(gui_bmp, d->dp, d->x+d->w/2+g, d->y+d->h/2-text_height(font)/2+g, state1, -1, TRUE);
347
348 if (d->flags & D_SELECTED) {
349 vline(gui_bmp, d->x, d->y, d->y+d->h-2, d->bg);
350 hline(gui_bmp, d->x, d->y, d->x+d->w-2, d->bg);
351 }
352 else {
353 black = makecol(0,0,0);
354 vline(gui_bmp, d->x+d->w-1, d->y+1, d->y+d->h-2, black);
355 hline(gui_bmp, d->x+1, d->y+d->h-1, d->x+d->w-1, black);
356 }
357 if ((d->flags & D_GOTFOCUS) &&
358 (!(d->flags & D_SELECTED) || !(d->flags & D_EXIT)))
359 dotted_rect(d->x+1+g, d->y+1+g, d->x+d->w-3+g, d->y+d->h-3+g, state1, state2);
360 break;
361
362 case MSG_WANTFOCUS:
363 return D_WANTFOCUS;
364
365 case MSG_KEY:
366 /* close dialog? */
367 if (d->flags & D_EXIT) {
368 return D_CLOSE;
369 }
370
371 /* or just toggle */
372 d->flags ^= D_SELECTED;
373 object_message(d, MSG_DRAW, 0);
374 break;
375
376 case MSG_CLICK:
377 /* what state was the button originally in? */
378 state1 = d->flags & D_SELECTED;
379 if (d->flags & D_EXIT)
380 swap = FALSE;
381 else
382 swap = state1;
383
384 /* track the mouse until it is released */
385 while (gui_mouse_b()) {
386 state2 = ((gui_mouse_x() >= d->x) && (gui_mouse_y() >= d->y) &&
387 (gui_mouse_x() < d->x + d->w) && (gui_mouse_y() < d->y + d->h));
388 if (swap)
389 state2 = !state2;
390
391 /* redraw? */
392 if (((state1) && (!state2)) || ((state2) && (!state1))) {
393 d->flags ^= D_SELECTED;
394 state1 = d->flags & D_SELECTED;
395 object_message(d, MSG_DRAW, 0);
396 }
397
398 /* let other objects continue to animate */
399 broadcast_dialog_message(MSG_IDLE, 0);
400 }
401
402 /* should we close the dialog? */
403 if ((d->flags & D_SELECTED) && (d->flags & D_EXIT)) {
404 d->flags ^= D_SELECTED;
405 return D_CLOSE;
406 }
407 break;
408 }
409
410 return D_O_K;
411 }
412
413
414
415 /* d_check_proc:
416 * Who needs C++ after all? This is derived from d_button_proc,
417 * but overrides the drawing routine to provide a check box.
418 */
d_check_proc(int msg,DIALOG * d,int c)419 int d_check_proc(int msg, DIALOG *d, int c)
420 {
421 BITMAP *gui_bmp = gui_get_screen();
422 int x, y, h;
423 int fg, bg;
424 ASSERT(d);
425
426 if (msg==MSG_DRAW) {
427 fg = (d->flags & D_DISABLED) ? gui_mg_color : d->fg;
428 bg = (d->bg < 0) ? gui_bg_color : d->bg;
429
430 h = text_height(font);
431
432 rectfill(gui_bmp, d->x, d->y, d->x+d->w-1, d->y+d->h-1, bg);
433 if (d->flags & D_GOTFOCUS)
434 dotted_rect(d->x, d->y, d->x+d->w-1, d->y+d->h-1, fg, bg);
435
436 y = d->y + ((d->h-(h-gui_font_baseline))/2);
437 x = d->x + ((d->d1) ? 0 : gui_textout_ex(gui_bmp, d->dp, d->x, y, fg, -1, FALSE) + h/2);
438
439 rect(gui_bmp, x, y, x+h-1, y+h-1, fg);
440 if (d->d1)
441 gui_textout_ex(gui_bmp, d->dp, x+h+h/2, y, fg, -1, FALSE);
442 if (d->flags & D_SELECTED) {
443 line(gui_bmp, x, y, x+h-1, y+h-1, fg);
444 line(gui_bmp, x, y+h-1, x+h-1, y, fg);
445 }
446
447 return D_O_K;
448 }
449
450 return d_button_proc(msg, d, 0);
451 }
452
453
454
455 /* d_radio_proc:
456 * GUI procedure for radio buttons.
457 * Parameters: d1-button group number; d2-button style (0=circle,1=square);
458 * dp-text to appear as label to the right of the button.
459 */
d_radio_proc(int msg,DIALOG * d,int c)460 int d_radio_proc(int msg, DIALOG *d, int c)
461 {
462 BITMAP *gui_bmp = gui_get_screen();
463 int x, y, h, r, ret, fg, bg;
464 int centerx, centery;
465 ASSERT(d);
466
467 switch(msg) {
468
469 case MSG_DRAW:
470 fg = (d->flags & D_DISABLED) ? gui_mg_color : d->fg;
471 bg = (d->bg < 0) ? gui_bg_color : d->bg;
472
473 rectfill(gui_bmp, d->x, d->y, d->x+d->w-1, d->y+d->h-1, bg);
474 if (d->flags & D_GOTFOCUS)
475 dotted_rect(d->x, d->y, d->x+d->w-1, d->y+d->h-1, fg, bg);
476
477 h = text_height(font);
478 y = d->y+(d->h-(h-gui_font_baseline))/2;
479
480 gui_textout_ex(gui_bmp, d->dp, d->x+h+h/2, y, fg, -1, FALSE);
481
482 x = d->x;
483 r = h/2;
484
485 centerx = d->x+r;
486 centery = d->y+d->h/2;
487
488 switch (d->d2) {
489
490 case 1:
491 rect(gui_bmp, d->x, y, x+h-1, y+h-1, fg);
492 if (d->flags & D_SELECTED)
493 rectfill(gui_bmp, centerx-r/2, centery-r/2, centerx+r/2-1, centery+r/2-1, fg);
494 break;
495
496 default:
497 circle(gui_bmp, centerx, centery, r, fg);
498 if (d->flags & D_SELECTED)
499 circlefill(gui_bmp, centerx, centery, r/2, fg);
500 break;
501 }
502
503 return D_O_K;
504
505 case MSG_KEY:
506 case MSG_CLICK:
507 if (d->flags & D_SELECTED) {
508 return D_O_K;
509 }
510 break;
511
512 case MSG_RADIO:
513 if ((c == d->d1) && (d->flags & D_SELECTED)) {
514 d->flags &= ~D_SELECTED;
515 object_message(d, MSG_DRAW, 0);
516 }
517 break;
518 }
519
520 ret = d_button_proc(msg, d, 0);
521
522 if (((msg==MSG_KEY) || (msg==MSG_CLICK)) &&
523 (d->flags & D_SELECTED) && (!(d->flags & D_EXIT))) {
524 d->flags &= ~D_SELECTED;
525 broadcast_dialog_message(MSG_RADIO, d->d1);
526 d->flags |= D_SELECTED;
527 }
528
529 return ret;
530 }
531
532
533
534 /* d_icon_proc:
535 * Allows graphic icons to be used as buttons.
536 *
537 * Parameters:
538 * fg = color dotted line showing focus will be drawn in
539 * bg = shadow color used to fill in top and left sides of
540 * button when "pressed"
541 * d1 = "push depth": number of pixels icon will be shifted
542 * to right and down when selected (default=2) if there is
543 * no "selected" image
544 * d2 = distance dotted line showing focus is indented (default=2)
545 * dp = pointer to a bitmap for the icon
546 * dp2 = pointer to a "selected" bitmap for the icon (OPTIONAL)
547 * dp3 = pointer to a "disabled" bitmap for the icon (OPTIONAL)
548 */
d_icon_proc(int msg,DIALOG * d,int c)549 int d_icon_proc(int msg, DIALOG *d, int c)
550 {
551 BITMAP *butimage;
552 BITMAP *gui_bmp;
553 int butx;
554 int buty;
555 int index;
556 int indent;
557 int depth;
558 ASSERT(d);
559
560 butimage = (BITMAP *)d->dp;
561 gui_bmp = gui_get_screen();
562 if ((msg == MSG_DRAW) && (!(d->flags & D_HIDDEN))) {
563 depth = 0;
564 if ((d->dp2 == NULL) && (d->flags & D_SELECTED)) {
565 depth = d->d1;
566 if (depth<1)
567 depth = 2;
568 }
569 if ((d->dp2 != NULL) && (d->flags & D_SELECTED)) {
570 butimage = (BITMAP *)d->dp2;
571 }
572 if ((d->dp3 != NULL) && (d->flags & D_DISABLED)) {
573 butimage = (BITMAP *)d->dp3;
574 }
575 indent = d->d2;
576 if (indent==0)
577 indent = 2;
578
579 /* put the graphic on screen, scaled as needed */
580 butx = butimage->w;
581 buty = butimage->h;
582 stretch_blit(butimage, gui_bmp, 0, 0, butx-depth, buty-depth,
583 d->x+depth, d->y+depth, d->w-depth, d->h-depth);
584
585 if ((d->flags & D_GOTFOCUS) &&
586 (!(d->flags & D_SELECTED) || !(d->flags & D_EXIT))) {
587 /* draw focus lines */
588 for (index=indent; index<d->w-(indent+1); index+=2) {
589 putpixel(gui_bmp, d->x+index+depth, d->y+indent+depth,d->fg);
590 putpixel(gui_bmp, d->x+index+depth, d->y+d->h-(indent+1)+depth, d->fg);
591 }
592 for (index=indent; index<d->h-(indent+1); index+=2) {
593 putpixel(gui_bmp, d->x+indent+depth, d->y+index+depth, d->fg);
594 putpixel(gui_bmp, d->x+d->w-(indent+1)+depth, d->y+index+depth, d->fg);
595 }
596 }
597
598 /* draw shadowing */
599 for (index=0; index<depth; index++) {
600 hline(gui_bmp, d->x, d->y+index, d->x+d->w-1, d->bg);
601 vline(gui_bmp, d->x+index, d->y, d->y+d->h-1, d->bg);
602 }
603
604 return D_O_K;
605 }
606
607 return d_button_proc(msg, d, c);
608 }
609
610
611
612 /* d_keyboard_proc:
613 * Invisible object for implementing keyboard shortcuts. When its key
614 * is pressed, it calls the function pointed to by dp. This should return
615 * an integer, which will be passed back to the dialog manager. The key
616 * can be specified by putting an ASCII code in the key field or by
617 * putting scancodes in d1 and d2.
618 */
d_keyboard_proc(int msg,DIALOG * d,int c)619 int d_keyboard_proc(int msg, DIALOG *d, int c)
620 {
621 int (*proc)(void);
622 int ret = D_O_K;
623 ASSERT(d);
624
625 switch (msg) {
626
627 case MSG_START:
628 d->w = d->h = 0;
629 break;
630
631 case MSG_XCHAR:
632 if (((c>>8) != d->d1) && ((c>>8) != d->d2))
633 break;
634
635 ret |= D_USED_CHAR;
636 /* fall through */
637
638 case MSG_KEY:
639 proc = d->dp;
640 ret |= (*proc)();
641 break;
642 }
643
644 return ret;
645 }
646
647
648
649 /* d_edit_proc:
650 * An editable text object (the dp field points to the string). When it
651 * has the input focus (obtained by clicking on it with the mouse), text
652 * can be typed into this object. The d1 field specifies the maximum
653 * number of characters that it will accept, and d2 is the text cursor
654 * position within the string.
655 */
d_edit_proc(int msg,DIALOG * d,int c)656 int d_edit_proc(int msg, DIALOG *d, int c)
657 {
658 static int ignore_next_uchar = FALSE;
659 BITMAP *gui_bmp;
660 int last_was_space, new_pos, i, k;
661 int f, l, p, w, x, fg, b, scroll;
662 char buf[16];
663 char *s, *t;
664 ASSERT(d);
665
666 gui_bmp = gui_get_screen();
667
668 s = d->dp;
669 l = ustrlen(s);
670 if (d->d2 > l)
671 d->d2 = l;
672
673 /* calculate maximal number of displayable characters */
674 if (d->d2 == l) {
675 usetc(buf+usetc(buf, ' '), 0);
676 x = text_length(font, buf);
677 }
678 else
679 x = 0;
680
681 b = 0;
682
683 for (p=d->d2; p>=0; p--) {
684 usetc(buf+usetc(buf, ugetat(s, p)), 0);
685 x += text_length(font, buf);
686 b++;
687 if (x > d->w)
688 break;
689 }
690
691 if (x <= d->w) {
692 b = l;
693 scroll = FALSE;
694 }
695 else {
696 b--;
697 scroll = TRUE;
698 }
699
700 switch (msg) {
701
702 case MSG_START:
703 d->d2 = l;
704 break;
705
706 case MSG_DRAW:
707 fg = (d->flags & D_DISABLED) ? gui_mg_color : d->fg;
708 x = 0;
709
710 if (scroll) {
711 p = d->d2-b+1;
712 b = d->d2;
713 }
714 else
715 p = 0;
716
717 for (; p<=b; p++) {
718 f = ugetat(s, p);
719 usetc(buf+usetc(buf, (f) ? f : ' '), 0);
720 w = text_length(font, buf);
721 if (x+w > d->w)
722 break;
723 f = ((p == d->d2) && (d->flags & D_GOTFOCUS));
724 textout_ex(gui_bmp, font, buf, d->x+x, d->y, (f) ? d->bg : fg, (f) ? fg : d->bg);
725 x += w;
726 }
727 if (x < d->w)
728 rectfill(gui_bmp, d->x+x, d->y, d->x+d->w-1, d->y+text_height(font)-1, d->bg);
729 break;
730
731 case MSG_CLICK:
732 x = d->x;
733
734 if (scroll) {
735 p = d->d2-b+1;
736 b = d->d2;
737 }
738 else
739 p = 0;
740
741 for (; p<b; p++) {
742 usetc(buf+usetc(buf, ugetat(s, p)), 0);
743 x += text_length(font, buf);
744 if (x > gui_mouse_x())
745 break;
746 }
747 d->d2 = CLAMP(0, p, l);
748 object_message(d, MSG_DRAW, 0);
749 break;
750
751 case MSG_WANTFOCUS:
752 case MSG_LOSTFOCUS:
753 case MSG_KEY:
754 return D_WANTFOCUS;
755
756 case MSG_CHAR:
757 ignore_next_uchar = FALSE;
758
759 if ((c >> 8) == KEY_LEFT) {
760 if (d->d2 > 0) {
761 if (key_shifts & KB_CTRL_FLAG) {
762 last_was_space = TRUE;
763 new_pos = 0;
764 t = s;
765 for (i = 0; i < d->d2; i++) {
766 k = ugetx(&t);
767 if (uisspace(k))
768 last_was_space = TRUE;
769 else if (last_was_space) {
770 last_was_space = FALSE;
771 new_pos = i;
772 }
773 }
774 d->d2 = new_pos;
775 }
776 else
777 d->d2--;
778 }
779 }
780 else if ((c >> 8) == KEY_RIGHT) {
781 if (d->d2 < l) {
782 if (key_shifts & KB_CTRL_FLAG) {
783 t = s + uoffset(s, d->d2);
784 for (k = ugetx(&t); uisspace(k); k = ugetx(&t))
785 d->d2++;
786 for (; k && !uisspace(k); k = ugetx(&t))
787 d->d2++;
788 }
789 else
790 d->d2++;
791 }
792 }
793 else if ((c >> 8) == KEY_HOME) {
794 d->d2 = 0;
795 }
796 else if ((c >> 8) == KEY_END) {
797 d->d2 = l;
798 }
799 else if ((c >> 8) == KEY_DEL) {
800 if (d->d2 < l)
801 uremove(s, d->d2);
802 }
803 else if ((c >> 8) == KEY_BACKSPACE) {
804 if (d->d2 > 0) {
805 d->d2--;
806 uremove(s, d->d2);
807 }
808 }
809 else if ((c >> 8) == KEY_ENTER) {
810 if (d->flags & D_EXIT) {
811 object_message(d, MSG_DRAW, 0);
812 return D_CLOSE;
813 }
814 else
815 return D_O_K;
816 }
817 else if ((c >> 8) == KEY_TAB) {
818 ignore_next_uchar = TRUE;
819 return D_O_K;
820 }
821 else {
822 /* don't process regular keys here: MSG_UCHAR will do that */
823 break;
824 }
825 object_message(d, MSG_DRAW, 0);
826 return D_USED_CHAR;
827
828 case MSG_UCHAR:
829 if ((c >= ' ') && (uisok(c)) && (!ignore_next_uchar)) {
830 if (l < d->d1) {
831 uinsert(s, d->d2, c);
832 d->d2++;
833
834 object_message(d, MSG_DRAW, 0);
835 }
836 return D_USED_CHAR;
837 }
838 break;
839 }
840
841 return D_O_K;
842 }
843
844
845
846 /* _handle_scrollable_click:
847 * Helper to process a click on a scrollable object.
848 */
_handle_scrollable_scroll_click(DIALOG * d,int listsize,int * offset,int height)849 void _handle_scrollable_scroll_click(DIALOG *d, int listsize, int *offset, int height)
850 {
851 int xx, yy;
852 int hh = d->h - 5;
853
854 while (gui_mouse_b()) {
855 int i = (hh * height + listsize/2) / listsize;
856 int len = (hh * (*offset) + listsize/2) / listsize + 2;
857
858 if ((gui_mouse_y() >= d->y+len) && (gui_mouse_y() <= d->y+len+i)) {
859 xx = gui_mouse_y() - len + 2;
860 while (gui_mouse_b()) {
861 yy = (listsize * (gui_mouse_y() - xx) + hh/2) / hh;
862 if (yy > listsize-height)
863 yy = listsize-height;
864
865 if (yy < 0)
866 yy = 0;
867
868 if (yy != *offset) {
869 *offset = yy;
870 object_message(d, MSG_DRAW, 0);
871 }
872
873 /* let other objects continue to animate */
874 broadcast_dialog_message(MSG_IDLE, 0);
875 }
876 }
877 else {
878 if (gui_mouse_y() <= d->y+len)
879 yy = *offset - height;
880 else
881 yy = *offset + height;
882
883 if (yy > listsize-height)
884 yy = listsize-height;
885
886 if (yy < 0)
887 yy = 0;
888
889 if (yy != *offset) {
890 *offset = yy;
891 object_message(d, MSG_DRAW, 0);
892 }
893 }
894
895 /* let other objects continue to animate */
896 broadcast_dialog_message(MSG_IDLE, 0);
897 }
898 }
899
900
901
902 /* _handle_scrollable_scroll:
903 * Helper function to scroll through a scrollable object.
904 */
_handle_scrollable_scroll(DIALOG * d,int listsize,int * index,int * offset)905 void _handle_scrollable_scroll(DIALOG *d, int listsize, int *index, int *offset)
906 {
907 int height = (d->h-4) / text_height(font);
908
909 if (listsize <= 0) {
910 *index = *offset = 0;
911 return;
912 }
913
914 /* check selected item */
915 if (*index < 0)
916 *index = 0;
917 else if (*index >= listsize)
918 *index = listsize - 1;
919
920 /* check scroll position */
921 while ((*offset > 0) && (*offset + height > listsize))
922 (*offset)--;
923
924 if (*offset >= *index) {
925 if (*index < 0)
926 *offset = 0;
927 else
928 *offset = *index;
929 }
930 else {
931 while ((*offset + height - 1) < *index)
932 (*offset)++;
933 }
934 }
935
936
937
938 /* idle_cb:
939 * rest_callback() routine to keep dialogs animating nice and smoothly.
940 */
idle_cb(void)941 static void idle_cb(void)
942 {
943 broadcast_dialog_message(MSG_IDLE, 0);
944 }
945
946
947
948 /* _handle_listbox_click:
949 * Helper to process a click on a listbox, doing hit-testing and moving
950 * the selection.
951 */
_handle_listbox_click(DIALOG * d)952 void _handle_listbox_click(DIALOG *d)
953 {
954 char *sel = d->dp2;
955 int listsize, height;
956 int i, j;
957
958 (*(getfuncptr)d->dp)(-1, &listsize);
959 if (!listsize)
960 return;
961
962 height = (d->h-4) / text_height(font);
963
964 i = CLAMP(0, ((gui_mouse_y() - d->y - 2) / text_height(font)),
965 ((d->h-4) / text_height(font) - 1));
966 i += d->d2;
967 if (i < d->d2)
968 i = d->d2;
969 else {
970 if (i > d->d2 + height-1)
971 i = d->d2 + height-1;
972 if (i >= listsize)
973 i = listsize-1;
974 }
975
976 if (gui_mouse_y() <= d->y)
977 i = MAX(i-1, 0);
978 else if (gui_mouse_y() >= d->y+d->h-1)
979 i = MIN(i+1, listsize-1);
980
981 if (i != d->d1) {
982 if (sel) {
983 if (key_shifts & (KB_SHIFT_FLAG | KB_CTRL_FLAG)) {
984 if ((key_shifts & KB_SHIFT_FLAG) || (d->flags & D_INTERNAL)) {
985 for (j=MIN(i, d->d1); j<=MAX(i, d->d1); j++)
986 sel[j] = TRUE;
987 }
988 else
989 sel[i] = !sel[i];
990 }
991 else
992 sel[i] = TRUE;
993 }
994
995 d->d1 = i;
996 i = d->d2;
997 _handle_scrollable_scroll(d, listsize, &d->d1, &d->d2);
998
999 d->flags |= D_DIRTY;
1000
1001 if (i != d->d2)
1002 rest_callback(CLAMP(10, text_height(font)*16-d->h-1, 100), idle_cb);
1003 }
1004 else {
1005 if (!(d->flags & D_INTERNAL)) {
1006 if (sel) {
1007 if((key_shifts & KB_CTRL_FLAG))
1008 sel[i] = !sel[i];
1009 else
1010 sel[i] = TRUE;
1011
1012 d->flags |= D_DIRTY;
1013 }
1014 }
1015 }
1016 }
1017
1018
1019
1020 /* _draw_scrollable_frame:
1021 * Helper function to draw a frame for all objects with vertical scrollbars.
1022 */
_draw_scrollable_frame(DIALOG * d,int listsize,int offset,int height,int fg_color,int bg)1023 void _draw_scrollable_frame(DIALOG *d, int listsize, int offset, int height, int fg_color, int bg)
1024 {
1025 BITMAP *gui_bmp = gui_get_screen();
1026 int i, len;
1027 BITMAP *pattern;
1028 int xx, yy;
1029
1030 /* draw frame */
1031 rect(gui_bmp, d->x, d->y, d->x+d->w-1, d->y+d->h-1, fg_color);
1032
1033 /* possibly draw scrollbar */
1034 if (listsize > height) {
1035 vline(gui_bmp, d->x+d->w-13, d->y+1, d->y+d->h-2, fg_color);
1036
1037 /* scrollbar with focus */
1038 if (d->flags & D_GOTFOCUS) {
1039 dotted_rect(d->x+1, d->y+1, d->x+d->w-14, d->y+d->h-2, fg_color, bg);
1040 dotted_rect(d->x+d->w-12, d->y+1, d->x+d->w-2, d->y+d->h-2, fg_color, bg);
1041 }
1042 else {
1043 rect(gui_bmp, d->x+1, d->y+1, d->x+d->w-14, d->y+d->h-2, bg);
1044 rect(gui_bmp, d->x+d->w-12, d->y+1, d->x+d->w-2, d->y+d->h-2, bg);
1045 }
1046
1047 /* create and draw the scrollbar */
1048 pattern = create_bitmap(2, 2);
1049 i = ((d->h-5) * height + listsize/2) / listsize;
1050 xx = d->x+d->w-11;
1051 yy = d->y+2;
1052
1053 putpixel(pattern, 0, 1, bg);
1054 putpixel(pattern, 1, 0, bg);
1055 putpixel(pattern, 0, 0, fg_color);
1056 putpixel(pattern, 1, 1, fg_color);
1057
1058 if (offset > 0) {
1059 len = (((d->h-5) * offset) + listsize/2) / listsize;
1060 rectfill(gui_bmp, xx, yy, xx+8, yy+len, bg);
1061 yy += len;
1062 }
1063 if (yy+i < d->y+d->h-3) {
1064 drawing_mode(DRAW_MODE_COPY_PATTERN, pattern, 0, 0);
1065 rectfill(gui_bmp, xx, yy, xx+8, yy+i, 0);
1066 solid_mode();
1067 yy += i+1;
1068 rectfill(gui_bmp, xx, yy, xx+8, d->y+d->h-3, bg);
1069 }
1070 else {
1071 drawing_mode(DRAW_MODE_COPY_PATTERN, pattern, 0, 0);
1072 rectfill(gui_bmp, xx, yy, xx+8, d->y+d->h-3, 0);
1073 solid_mode();
1074 }
1075 destroy_bitmap(pattern);
1076 }
1077 else {
1078 /* no scrollbar necessary */
1079 if (d->flags & D_GOTFOCUS)
1080 dotted_rect(d->x+1, d->y+1, d->x+d->w-2, d->y+d->h-2, fg_color, bg);
1081 else
1082 rect(gui_bmp, d->x+1, d->y+1, d->x+d->w-2, d->y+d->h-2, bg);
1083 }
1084 }
1085
1086
1087
1088 /* draw_listbox:
1089 * Helper function to draw a listbox object.
1090 */
_draw_listbox(DIALOG * d)1091 void _draw_listbox(DIALOG *d)
1092 {
1093 BITMAP *gui_bmp = gui_get_screen();
1094 int height, listsize, i, len, bar, x, y, w;
1095 int fg_color, fg, bg;
1096 char *sel = d->dp2;
1097 char s[1024];
1098
1099 (*(getfuncptr)d->dp)(-1, &listsize);
1100 height = (d->h-4) / text_height(font);
1101 bar = (listsize > height);
1102 w = (bar ? d->w-15 : d->w-3);
1103 fg_color = (d->flags & D_DISABLED) ? gui_mg_color : d->fg;
1104
1105 /* draw box contents */
1106 for (i=0; i<height; i++) {
1107 if (d->d2+i < listsize) {
1108 if (sel) {
1109 if ((sel[d->d2+i]) && (d->d2+i == d->d1)) {
1110 fg = d->bg;
1111 bg = fg_color;
1112 }
1113 else if (sel[d->d2+i]) {
1114 fg = d->bg;
1115 bg = gui_mg_color;
1116 }
1117 else {
1118 fg = fg_color;
1119 bg = d->bg;
1120 }
1121 }
1122 else if (d->d2+i == d->d1) {
1123 fg = d->bg;
1124 bg = fg_color;
1125 }
1126 else {
1127 fg = fg_color;
1128 bg = d->bg;
1129 }
1130 ustrzcpy(s, sizeof(s), (*(getfuncptr)d->dp)(i+d->d2, NULL));
1131 x = d->x + 2;
1132 y = d->y + 2 + i*text_height(font);
1133 rectfill(gui_bmp, x, y, x+7, y+text_height(font)-1, bg);
1134 x += 8;
1135 len = ustrlen(s);
1136 while (text_length(font, s) >= MAX(d->w - 1 - (bar ? 22 : 10), 1)) {
1137 len--;
1138 usetat(s, len, 0);
1139 }
1140 textout_ex(gui_bmp, font, s, x, y, fg, bg);
1141 x += text_length(font, s);
1142 if (x <= d->x+w)
1143 rectfill(gui_bmp, x, y, d->x+w, y+text_height(font)-1, bg);
1144 if (d->d2+i == d->d1)
1145 dotted_rect(d->x+2, y, d->x+d->w-(bar ? 15 : 3),
1146 y+text_height(font)-1,d->fg, d->bg);
1147 }
1148 else {
1149 rectfill(gui_bmp, d->x+2, d->y+2+i*text_height(font),
1150 d->x+w, d->y+1+(i+1)*text_height(font), d->bg);
1151 }
1152 }
1153
1154 if (d->y+2+i*text_height(font) <= d->y+d->h-3)
1155 rectfill(gui_bmp, d->x+2, d->y+2+i*text_height(font),
1156 d->x+w, d->y+d->h-3, d->bg);
1157
1158 /* draw frame, maybe with scrollbar */
1159 _draw_scrollable_frame(d, listsize, d->d2, height, fg_color, d->bg);
1160 }
1161
1162
1163
1164 /* d_list_proc:
1165 * A list box object. The dp field points to a function which it will call
1166 * to obtain information about the list. This should follow the form:
1167 * const char *<list_func_name> (int index, int *list_size);
1168 * If index is zero or positive, the function should return a pointer to
1169 * the string which is to be displayed at position index in the list. If
1170 * index is negative, it should return null and list_size should be set
1171 * to the number of items in the list. The list box object will allow the
1172 * user to scroll through the list and to select items list by clicking
1173 * on them, and if it has the input focus also by using the arrow keys. If
1174 * the D_EXIT flag is set, double clicking on a list item will cause it to
1175 * close the dialog. The index of the selected item is held in the d1
1176 * field, and d2 is used to store how far it has scrolled through the list.
1177 */
d_list_proc(int msg,DIALOG * d,int c)1178 int d_list_proc(int msg, DIALOG *d, int c)
1179 {
1180 int listsize, i, bottom, height, bar, orig;
1181 char *sel = d->dp2;
1182 int redraw = FALSE;
1183 ASSERT(d);
1184
1185 switch (msg) {
1186
1187 case MSG_START:
1188 (*(getfuncptr)d->dp)(-1, &listsize);
1189 _handle_scrollable_scroll(d, listsize, &d->d1, &d->d2);
1190 break;
1191
1192 case MSG_DRAW:
1193 _draw_listbox(d);
1194 break;
1195
1196 case MSG_CLICK:
1197 (*(getfuncptr)d->dp)(-1, &listsize);
1198 height = (d->h-4) / text_height(font);
1199 bar = (listsize > height);
1200 if ((!bar) || (gui_mouse_x() < d->x+d->w-13)) {
1201 if ((sel) && (!(key_shifts & KB_CTRL_FLAG))) {
1202 for (i=0; i<listsize; i++) {
1203 if (sel[i]) {
1204 redraw = TRUE;
1205 sel[i] = FALSE;
1206 }
1207 }
1208 if (redraw)
1209 object_message(d, MSG_DRAW, 0);
1210 }
1211 _handle_listbox_click(d);
1212 while (gui_mouse_b()) {
1213 broadcast_dialog_message(MSG_IDLE, 0);
1214 d->flags |= D_INTERNAL;
1215 _handle_listbox_click(d);
1216 d->flags &= ~D_INTERNAL;
1217 }
1218 }
1219 else {
1220 _handle_scrollable_scroll_click(d, listsize, &d->d2, height);
1221 }
1222 break;
1223
1224 case MSG_DCLICK:
1225 (*(getfuncptr)d->dp)(-1, &listsize);
1226 height = (d->h-4) / text_height(font);
1227 bar = (listsize > height);
1228 if ((!bar) || (gui_mouse_x() < d->x+d->w-13)) {
1229 if (d->flags & D_EXIT) {
1230 if (listsize) {
1231 i = d->d1;
1232 object_message(d, MSG_CLICK, 0);
1233 if (i == d->d1)
1234 return D_CLOSE;
1235 }
1236 }
1237 }
1238 break;
1239
1240 case MSG_WHEEL:
1241 (*(getfuncptr)d->dp)(-1, &listsize);
1242 height = (d->h-4) / text_height(font);
1243 if (height < listsize) {
1244 int delta = (height > 3) ? 3 : 1;
1245 if (c > 0)
1246 i = MAX(0, d->d2-delta);
1247 else
1248 i = MIN(listsize-height, d->d2+delta);
1249 if (i != d->d2) {
1250 d->d2 = i;
1251 object_message(d, MSG_DRAW, 0);
1252 }
1253 }
1254 break;
1255
1256 case MSG_KEY:
1257 (*(getfuncptr)d->dp)(-1, &listsize);
1258 if ((listsize) && (d->flags & D_EXIT))
1259 return D_CLOSE;
1260 break;
1261
1262 case MSG_WANTFOCUS:
1263 return D_WANTFOCUS;
1264
1265 case MSG_CHAR:
1266 (*(getfuncptr)d->dp)(-1, &listsize);
1267
1268 if (listsize) {
1269 c >>= 8;
1270
1271 bottom = d->d2 + (d->h-4)/text_height(font) - 1;
1272 if (bottom >= listsize-1)
1273 bottom = listsize-1;
1274
1275 orig = d->d1;
1276
1277 if (c == KEY_UP)
1278 d->d1--;
1279 else if (c == KEY_DOWN)
1280 d->d1++;
1281 else if (c == KEY_HOME)
1282 d->d1 = 0;
1283 else if (c == KEY_END)
1284 d->d1 = listsize-1;
1285 else if (c == KEY_PGUP) {
1286 if (d->d1 > d->d2)
1287 d->d1 = d->d2;
1288 else
1289 d->d1 -= (bottom - d->d2) ? bottom - d->d2 : 1;
1290 }
1291 else if (c == KEY_PGDN) {
1292 if (d->d1 < bottom)
1293 d->d1 = bottom;
1294 else
1295 d->d1 += (bottom - d->d2) ? bottom - d->d2 : 1;
1296 }
1297 else
1298 return D_O_K;
1299
1300 if (sel) {
1301 if (!(key_shifts & (KB_SHIFT_FLAG | KB_CTRL_FLAG))) {
1302 for (i=0; i<listsize; i++)
1303 sel[i] = FALSE;
1304 }
1305 else if (key_shifts & KB_SHIFT_FLAG) {
1306 for (i=MIN(orig, d->d1); i<=MAX(orig, d->d1); i++) {
1307 if (key_shifts & KB_CTRL_FLAG)
1308 sel[i] = (i != d->d1);
1309 else
1310 sel[i] = TRUE;
1311 }
1312 }
1313 }
1314
1315 /* if we changed something, better redraw... */
1316 _handle_scrollable_scroll(d, listsize, &d->d1, &d->d2);
1317 d->flags |= D_DIRTY;
1318 return D_USED_CHAR;
1319 }
1320 break;
1321 }
1322
1323 return D_O_K;
1324 }
1325
1326
1327
1328 /* d_text_list_proc:
1329 * Like d_list_proc, but allows the user to type in the first few characters
1330 * of a listbox entry in order to select it. Uses dp3 internally, so you
1331 * mustn't store anything important there yourself.
1332 */
d_text_list_proc(int msg,DIALOG * d,int c)1333 int d_text_list_proc(int msg, DIALOG *d, int c)
1334 {
1335 int listsize, i, a, failure;
1336 char *selected, *thisitem;
1337 char *sel = d->dp2;
1338 ASSERT(d);
1339
1340 switch (msg) {
1341
1342 case MSG_START:
1343 case MSG_CLICK:
1344 case MSG_DCLICK:
1345 case MSG_WANTFOCUS:
1346 case MSG_LOSTFOCUS:
1347 d->dp3 = 0;
1348 break;
1349
1350 case MSG_CHAR:
1351 if ((c & 0xFF) < ' ')
1352 d->dp3 = 0;
1353 break;
1354
1355 case MSG_UCHAR:
1356 (*(getfuncptr)d->dp)(-1, &listsize);
1357
1358 if (listsize) {
1359 if (c >= ' ') {
1360 selected = (*(getfuncptr)d->dp)(d->d1, NULL);
1361
1362 i = d->d1;
1363
1364 do {
1365 thisitem = (*(getfuncptr)d->dp)(i, NULL);
1366 failure = FALSE;
1367
1368 if ((int)((unsigned long)d->dp3) < ustrlen(thisitem)) {
1369 for (a=0; a < (int)((unsigned long)d->dp3); a++) {
1370 if (utolower(ugetat(thisitem, a)) != utolower(ugetat(selected, a))) {
1371 failure = TRUE;
1372 break;
1373 }
1374 }
1375
1376 if ((!failure) && (utolower(ugetat(thisitem, (int)(unsigned long)d->dp3)) == utolower(c))) {
1377 d->d1 = i;
1378 d->dp3 = (void *)((unsigned long)d->dp3 + 1);
1379
1380 if (sel) {
1381 for (i=0; i<listsize; i++)
1382 sel[i] = FALSE;
1383 }
1384
1385 _handle_scrollable_scroll(d, listsize, &d->d1, &d->d2);
1386 object_message(d, MSG_DRAW, 0);
1387 return D_USED_CHAR;
1388 }
1389 }
1390
1391 i++;
1392 if (i >= listsize)
1393 i = 0;
1394
1395 } while (i != d->d1);
1396
1397 if (d->dp3) {
1398 d->dp3 = 0;
1399 return d_text_list_proc(msg, d, c);
1400 }
1401 }
1402 }
1403 break;
1404 }
1405
1406 return d_list_proc(msg, d, c);
1407 }
1408
1409
1410
1411 /* _draw_textbox:
1412 * Helper function to draw a textbox object.
1413 */
_draw_textbox(char * thetext,int * listsize,int draw,int offset,int wword,int tabsize,int x,int y,int w,int h,int disabled,int fore,int deselect,int disable)1414 void _draw_textbox(char *thetext, int *listsize, int draw, int offset,
1415 int wword, int tabsize, int x, int y, int w, int h,
1416 int disabled, int fore, int deselect, int disable)
1417 {
1418 BITMAP *gui_bmp = gui_get_screen();
1419 int fg = fore;
1420 int y1 = y+4;
1421 int x1;
1422 int len;
1423 int ww = w-6;
1424 char s[16];
1425 char text[16];
1426 char space[16];
1427 char *printed = text;
1428 char *scanned = text;
1429 char *oldscan = text;
1430 char *ignore = NULL;
1431 char *tmp, *ptmp;
1432 int width;
1433 int line = 0;
1434 int i = 0;
1435 int noignore;
1436
1437 usetc(s+usetc(s, '.'), 0);
1438 usetc(text+usetc(text, ' '), 0);
1439 usetc(space+usetc(space, ' '), 0);
1440
1441 /* find the correct text */
1442 if (thetext != NULL) {
1443 printed = thetext;
1444 scanned = thetext;
1445 }
1446
1447 /* do some drawing setup */
1448 if (draw) {
1449 /* initial start blanking at the top */
1450 rectfill(gui_bmp, x+2, y+2, x+w-3, y1-1, deselect);
1451 }
1452
1453 /* choose the text color */
1454 if (disabled)
1455 fg = disable;
1456
1457 /* loop over the entire string */
1458 while (1) {
1459 width = 0;
1460
1461 /* find the next break */
1462 while (ugetc(scanned)) {
1463 /* check for a forced break */
1464 if (ugetc(scanned) == '\n') {
1465 scanned += uwidth(scanned);
1466
1467 /* we are done parsing the line end */
1468 break;
1469 }
1470
1471 /* the next character length */
1472 usetc(s+usetc(s, ugetc(scanned)), 0);
1473 len = text_length(font, s);
1474
1475 /* modify length if its a tab */
1476 if (ugetc(s) == '\t')
1477 len = tabsize * text_length(font, space);
1478
1479 /* check for the end of a line by excess width of next char */
1480 if (width+len >= ww) {
1481 /* we have reached end of line do we go back to find start */
1482 if (wword) {
1483 /* remember where we were */
1484 oldscan = scanned;
1485 noignore = FALSE;
1486
1487 /* go backwards looking for start of word */
1488 while (!uisspace(ugetc(scanned))) {
1489 /* don't wrap too far */
1490 if (scanned == printed) {
1491 /* the whole line is filled, so stop here */
1492 tmp = ptmp = scanned;
1493 while (ptmp != oldscan) {
1494 ptmp = tmp;
1495 tmp += uwidth(tmp);
1496 }
1497 scanned = ptmp;
1498 noignore = TRUE;
1499 break;
1500 }
1501 /* look further backwards to wrap */
1502 tmp = ptmp = printed;
1503 while (tmp < scanned) {
1504 ptmp = tmp;
1505 tmp += uwidth(tmp);
1506 }
1507 scanned = ptmp;
1508 }
1509 /* put the space at the end of the line */
1510 if (!noignore) {
1511 ignore = scanned;
1512 scanned += uwidth(scanned);
1513 }
1514 else
1515 ignore = NULL;
1516
1517 /* check for endline at the convenient place */
1518 if (ugetc(scanned) == '\n')
1519 scanned += uwidth(scanned);
1520 }
1521 /* we are done parsing the line end */
1522 break;
1523 }
1524
1525 /* the character can be added */
1526 scanned += uwidth(scanned);
1527 width += len;
1528 }
1529
1530 /* check if we are to print it */
1531 if ((draw) && (line >= offset) && (y1+text_height(font) < (y+h-3))) {
1532 x1 = x+4;
1533
1534 /* the initial blank bit */
1535 rectfill(gui_bmp, x+2, y1, x1-1, y1+text_height(font), deselect);
1536
1537 /* print up to the marked character */
1538 while (printed != scanned) {
1539 /* do special stuff for each charater */
1540 switch (ugetc(printed)) {
1541
1542 case '\r':
1543 case '\n':
1544 /* don't print endlines in the text */
1545 break;
1546
1547 /* possibly expand the tabs */
1548 case '\t':
1549 for (i=0; i<tabsize; i++) {
1550 usetc(s+usetc(s, ' '), 0);
1551 textout_ex(gui_bmp, font, s, x1, y1, fg, deselect);
1552 x1 += text_length(font, s);
1553 }
1554 break;
1555
1556 /* print a normal character */
1557 default:
1558 if (printed != ignore) {
1559 usetc(s+usetc(s, ugetc(printed)), 0);
1560 textout_ex(gui_bmp, font, s, x1, y1, fg, deselect);
1561 x1 += text_length(font, s);
1562 }
1563 }
1564
1565 /* goto the next character */
1566 printed += uwidth(printed);
1567 }
1568 /* the last blank bit */
1569 if (x1 <= x+w-3)
1570 rectfill(gui_bmp, x1, y1, x+w-3, y1+text_height(font)-1, deselect);
1571
1572 /* print the line end */
1573 y1 += text_height(font);
1574 }
1575 printed = scanned;
1576
1577 /* we have done a line */
1578 line++;
1579
1580 /* check if we are at the end of the string */
1581 if (!ugetc(printed)) {
1582 /* the under blank bit */
1583 if (draw)
1584 rectfill(gui_bmp, x+1, y1, x+w-3, y+h-1, deselect);
1585
1586 /* tell how many lines we found */
1587 *listsize = line;
1588 return;
1589 }
1590 }
1591
1592 }
1593
1594
1595
1596 /* d_textbox_proc:
1597 * A text box object. The dp field points to a char * which is the text
1598 * to be displayed in the text box. If the text is long, there will be
1599 * a vertical scrollbar on the right hand side of the object which can
1600 * be used to scroll through the text. The default is to print the text
1601 * with word wrapping, but if the D_SELECTED flag is set, the text will
1602 * be printed with character wrapping. The d1 field is used internally
1603 * to store the number of lines of text, and d2 is used to store how far
1604 * it has scrolled through the text.
1605 */
d_textbox_proc(int msg,DIALOG * d,int c)1606 int d_textbox_proc(int msg, DIALOG *d, int c)
1607 {
1608 int height, bar, ret = D_O_K;
1609 int start, top, bottom, l;
1610 int used, delta;
1611 int fg_color;
1612 ASSERT(d);
1613
1614 fg_color = (d->flags & D_DISABLED) ? gui_mg_color : d->fg;
1615 /* calculate the actual height */
1616 height = (d->h-8) / text_height(font);
1617
1618 switch (msg) {
1619
1620 case MSG_START:
1621 /* measure how many lines of text we contain */
1622 _draw_textbox(d->dp, &d->d1,
1623 0, /* DONT DRAW anything */
1624 d->d2, !(d->flags & D_SELECTED), 8,
1625 d->x, d->y, d->w, d->h,
1626 (d->flags & D_DISABLED),
1627 0, 0, 0);
1628 break;
1629
1630 case MSG_DRAW:
1631 /* tell the object to sort of draw, but only calculate the listsize */
1632 _draw_textbox(d->dp, &d->d1,
1633 0, /* DONT DRAW anything */
1634 d->d2, !(d->flags & D_SELECTED), 8,
1635 d->x, d->y, d->w, d->h,
1636 (d->flags & D_DISABLED),
1637 0, 0, 0);
1638
1639 if (d->d1 > height) {
1640 bar = 12;
1641 }
1642 else {
1643 bar = 0;
1644 d->d2 = 0;
1645 }
1646
1647 /* now do the actual drawing */
1648 _draw_textbox(d->dp, &d->d1, 1, d->d2,
1649 !(d->flags & D_SELECTED), 8,
1650 d->x, d->y, d->w-bar, d->h,
1651 (d->flags & D_DISABLED),
1652 fg_color, d->bg, gui_mg_color);
1653
1654 /* draw the frame around */
1655 _draw_scrollable_frame(d, d->d1, d->d2, height, fg_color, d->bg);
1656 break;
1657
1658 case MSG_CLICK:
1659 /* figure out if it's on the text or the scrollbar */
1660 bar = (d->d1 > height);
1661
1662 if ((!bar) || (gui_mouse_x() < d->x+d->w-13)) {
1663 /* clicked on the text area */
1664 ret = D_O_K;
1665 }
1666 else {
1667 /* clicked on the scroll area */
1668 _handle_scrollable_scroll_click(d, d->d1, &d->d2, height);
1669 }
1670 break;
1671
1672 case MSG_CHAR:
1673 start = d->d2;
1674 used = D_USED_CHAR;
1675
1676 if (d->d1 > 0) {
1677 if (d->d2 > 0)
1678 top = d->d2+1;
1679 else
1680 top = 0;
1681
1682 l = (d->h-8)/text_height(font);
1683
1684 bottom = d->d2 + l - 1;
1685 if (bottom >= d->d1-1)
1686 bottom = d->d1-1;
1687 else
1688 bottom--;
1689
1690 if ((c>>8) == KEY_UP)
1691 d->d2--;
1692 else if ((c>>8) == KEY_DOWN)
1693 d->d2++;
1694 else if ((c>>8) == KEY_HOME)
1695 d->d2 = 0;
1696 else if ((c>>8) == KEY_END)
1697 d->d2 = d->d1-l;
1698 else if ((c>>8) == KEY_PGUP)
1699 d->d2 -= (bottom-top) ? bottom-top : 1;
1700 else if ((c>>8) == KEY_PGDN)
1701 d->d2 += (bottom-top) ? bottom-top : 1;
1702 else
1703 used = D_O_K;
1704
1705 /* make sure that the list stays in bounds */
1706 if (d->d2 > d->d1-l)
1707 d->d2 = d->d1-l;
1708 if (d->d2 < 0)
1709 d->d2 = 0;
1710 }
1711 else
1712 used = D_O_K;
1713
1714 /* if we changed something, better redraw... */
1715 if (d->d2 != start)
1716 d->flags |= D_DIRTY;
1717
1718 ret = used;
1719 break;
1720
1721 case MSG_WHEEL:
1722 l = (d->h-8)/text_height(font);
1723 delta = (l > 3) ? 3 : 1;
1724
1725 /* scroll, making sure that the list stays in bounds */
1726 start = d->d2;
1727 d->d2 = (c > 0) ? MAX(0, d->d2-delta) : MIN(d->d1-l, d->d2+delta);
1728
1729 /* if we changed something, better redraw... */
1730 if (d->d2 != start)
1731 d->flags |= D_DIRTY;
1732
1733 ret = D_O_K;
1734 break;
1735
1736 case MSG_WANTFOCUS:
1737 /* if we don't have a scrollbar we can't do anything with the focus */
1738 if (d->d1 > height)
1739 ret = D_WANTFOCUS;
1740 break;
1741
1742 default:
1743 ret = D_O_K;
1744 }
1745
1746 return ret;
1747 }
1748
1749
1750
1751 /* d_slider_proc:
1752 * A slider control object. This object returns a value in d2, in the
1753 * range from 0 to d1. It will display as a vertical slider if h is
1754 * greater than or equal to w; otherwise, it will display as a horizontal
1755 * slider. dp can contain an optional bitmap to use for the slider handle;
1756 * dp2 can contain an optional callback function, which is called each
1757 * time d2 changes. The callback function should have the following
1758 * prototype:
1759 *
1760 * int function(void *dp3, int d2);
1761 *
1762 * The d_slider_proc object will return the value of the callback function.
1763 */
d_slider_proc(int msg,DIALOG * d,int c)1764 int d_slider_proc(int msg, DIALOG *d, int c)
1765 {
1766 BITMAP *gui_bmp = gui_get_screen();
1767 BITMAP *slhan = NULL;
1768 int oldpos, newpos;
1769 int sfg; /* slider foreground color */
1770 int vert = TRUE; /* flag: is slider vertical? */
1771 int hh = 7; /* handle height (width for horizontal sliders) */
1772 int hmar; /* handle margin */
1773 int slp; /* slider position */
1774 int mp; /* mouse position */
1775 int irange;
1776 int slx, sly, slh, slw;
1777 int msx, msy;
1778 int retval = D_O_K;
1779 int upkey, downkey;
1780 int pgupkey, pgdnkey;
1781 int homekey, endkey;
1782 int delta;
1783 fixed slratio, slmax, slpos;
1784 int (*proc)(void *cbpointer, int d2value);
1785 int oldval;
1786 ASSERT(d);
1787
1788 /* check for slider direction */
1789 if (d->h < d->w)
1790 vert = FALSE;
1791
1792 /* set up the metrics for the control */
1793 if (d->dp != NULL) {
1794 slhan = (BITMAP *)d->dp;
1795 if (vert)
1796 hh = slhan->h;
1797 else
1798 hh = slhan->w;
1799 }
1800
1801 hmar = hh/2;
1802 irange = (vert) ? d->h : d->w;
1803 slmax = itofix(irange-hh);
1804 slratio = slmax / (d->d1);
1805 slpos = slratio * d->d2;
1806 slp = fixtoi(slpos);
1807
1808 switch (msg) {
1809
1810 case MSG_DRAW:
1811 sfg = (d->flags & D_DISABLED) ? gui_mg_color : d->fg;
1812
1813 if (vert) {
1814 rectfill(gui_bmp, d->x, d->y, d->x+d->w/2-2, d->y+d->h-1, d->bg);
1815 rectfill(gui_bmp, d->x+d->w/2-1, d->y, d->x+d->w/2+1, d->y+d->h-1, sfg);
1816 rectfill(gui_bmp, d->x+d->w/2+2, d->y, d->x+d->w-1, d->y+d->h-1, d->bg);
1817 }
1818 else {
1819 rectfill(gui_bmp, d->x, d->y, d->x+d->w-1, d->y+d->h/2-2, d->bg);
1820 rectfill(gui_bmp, d->x, d->y+d->h/2-1, d->x+d->w-1, d->y+d->h/2+1, sfg);
1821 rectfill(gui_bmp, d->x, d->y+d->h/2+2, d->x+d->w-1, d->y+d->h-1, d->bg);
1822 }
1823
1824 /* okay, background and slot are drawn, now draw the handle */
1825 if (slhan) {
1826 if (vert) {
1827 slx = d->x+(d->w/2)-(slhan->w/2);
1828 sly = d->y+(d->h-1)-(hh+slp);
1829 }
1830 else {
1831 slx = d->x+slp;
1832 sly = d->y+(d->h/2)-(slhan->h/2);
1833 }
1834 draw_sprite(gui_bmp, slhan, slx, sly);
1835 }
1836 else {
1837 /* draw default handle */
1838 if (vert) {
1839 slx = d->x;
1840 sly = d->y+(d->h)-(hh+slp);
1841 slw = d->w-1;
1842 slh = hh-1;
1843 }
1844 else {
1845 slx = d->x+slp;
1846 sly = d->y;
1847 slw = hh-1;
1848 slh = d->h-1;
1849 }
1850
1851 /* draw body */
1852 rectfill(gui_bmp, slx+2, sly, slx+(slw-2), sly+slh, sfg);
1853 vline(gui_bmp, slx+1, sly+1, sly+slh-1, sfg);
1854 vline(gui_bmp, slx+slw-1, sly+1, sly+slh-1, sfg);
1855 vline(gui_bmp, slx, sly+2, sly+slh-2, sfg);
1856 vline(gui_bmp, slx+slw, sly+2, sly+slh-2, sfg);
1857 vline(gui_bmp, slx+1, sly+2, sly+slh-2, d->bg);
1858 hline(gui_bmp, slx+2, sly+1, slx+slw-2, d->bg);
1859 putpixel(gui_bmp, slx+2, sly+2, d->bg);
1860 }
1861
1862 if (d->flags & D_GOTFOCUS)
1863 dotted_rect(d->x, d->y, d->x+d->w-1, d->y+d->h-1, sfg, d->bg);
1864 break;
1865
1866 case MSG_WANTFOCUS:
1867 case MSG_LOSTFOCUS:
1868 return D_WANTFOCUS;
1869
1870 case MSG_KEY:
1871 if (!(d->flags & D_GOTFOCUS))
1872 return D_WANTFOCUS;
1873 else
1874 return D_O_K;
1875
1876 case MSG_CHAR:
1877 /* handle movement keys to move slider */
1878 c >>= 8;
1879
1880 if (vert) {
1881 upkey = KEY_UP;
1882 downkey = KEY_DOWN;
1883 pgupkey = KEY_PGUP;
1884 pgdnkey = KEY_PGDN;
1885 homekey = KEY_END;
1886 endkey = KEY_HOME;
1887 }
1888 else {
1889 upkey = KEY_RIGHT;
1890 downkey = KEY_LEFT;
1891 pgupkey = KEY_PGDN;
1892 pgdnkey = KEY_PGUP;
1893 homekey = KEY_HOME;
1894 endkey = KEY_END;
1895 }
1896
1897 if (c == upkey)
1898 delta = 1;
1899 else if (c == downkey)
1900 delta = -1;
1901 else if (c == pgdnkey)
1902 delta = -d->d1 / 16;
1903 else if (c == pgupkey)
1904 delta = d->d1 / 16;
1905 else if (c == homekey)
1906 delta = -d->d2;
1907 else if (c == endkey)
1908 delta = d->d1 - d->d2;
1909 else
1910 delta = 0;
1911
1912 if (delta) {
1913 oldpos = slp;
1914 oldval = d->d2;
1915
1916 while (1) {
1917 d->d2 = d->d2+delta;
1918 slpos = slratio*d->d2;
1919 slp = fixtoi(slpos);
1920 if ((slp != oldpos) || (d->d2 <= 0) || (d->d2 >= d->d1))
1921 break;
1922 }
1923
1924 if (d->d2 < 0)
1925 d->d2 = 0;
1926 if (d->d2 > d->d1)
1927 d->d2 = d->d1;
1928
1929 retval = D_USED_CHAR;
1930
1931 if (d->d2 != oldval) {
1932 /* call callback function here */
1933 if (d->dp2) {
1934 proc = d->dp2;
1935 retval |= (*proc)(d->dp3, d->d2);
1936 }
1937
1938 object_message(d, MSG_DRAW, 0);
1939 }
1940 }
1941 break;
1942
1943 case MSG_WHEEL:
1944 oldval = d->d2;
1945 d->d2 = CLAMP(0, d->d2+c, d->d1);
1946 if (d->d2 != oldval) {
1947 /* call callback function here */
1948 if (d->dp2) {
1949 proc = d->dp2;
1950 retval |= (*proc)(d->dp3, d->d2);
1951 }
1952
1953 object_message(d, MSG_DRAW, 0);
1954 }
1955 break;
1956
1957 case MSG_CLICK:
1958 /* track the mouse until it is released */
1959 mp = slp;
1960
1961 while (gui_mouse_b()) {
1962 msx = gui_mouse_x();
1963 msy = gui_mouse_y();
1964 oldval = d->d2;
1965 if (vert)
1966 mp = (d->y+d->h-hmar)-msy;
1967 else
1968 mp = msx-(d->x+hmar);
1969 if (mp < 0)
1970 mp = 0;
1971 if (mp > irange-hh)
1972 mp = irange-hh;
1973 slpos = itofix(mp);
1974 slmax = fixdiv(slpos, slratio);
1975 newpos = fixtoi(slmax);
1976 if (newpos != oldval) {
1977 d->d2 = newpos;
1978
1979 /* call callback function here */
1980 if (d->dp2 != NULL) {
1981 proc = d->dp2;
1982 retval |= (*proc)(d->dp3, d->d2);
1983 }
1984
1985 object_message(d, MSG_DRAW, 0);
1986 }
1987
1988 /* let other objects continue to animate */
1989 broadcast_dialog_message(MSG_IDLE, 0);
1990 }
1991 break;
1992 }
1993
1994 return retval;
1995 }
1996
1997
1998
1999 /* Overridable procedures used by standard GUI dialogs. */
2000
2001 #define MAKE_PROC(proc, default) \
2002 int (*proc)(int, DIALOG *, int); \
2003 int _##proc(int msg, DIALOG *d, int c) \
2004 { \
2005 return proc ? proc(msg, d, c) : default(msg, d, c); \
2006 }
2007
2008 MAKE_PROC(gui_shadow_box_proc, d_shadow_box_proc);
2009 MAKE_PROC(gui_ctext_proc, d_ctext_proc);
2010 MAKE_PROC(gui_button_proc, d_button_proc);
2011 MAKE_PROC(gui_edit_proc, d_edit_proc);
2012 MAKE_PROC(gui_list_proc, d_text_list_proc);
2013 MAKE_PROC(gui_text_list_proc, d_text_list_proc);
2014