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