1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
22 /* values for DrawGadget() */
23 #define DG_UNPRESSED 0
28 static struct GadgetInfo *gadget_list_first_entry = NULL;
29 static struct GadgetInfo *gadget_list_last_entry = NULL;
30 static int next_free_gadget_id = 1;
31 static boolean gadget_id_wrapped = FALSE;
33 static struct GadgetInfo *getGadgetInfoFromGadgetID(int id)
35 struct GadgetInfo *gi = gadget_list_first_entry;
37 while (gi && gi->id != id)
43 static int getNewGadgetID()
45 int id = next_free_gadget_id++;
47 if (next_free_gadget_id <= 0) /* counter overrun */
49 gadget_id_wrapped = TRUE; /* now we must check each ID */
50 next_free_gadget_id = 0;
53 if (gadget_id_wrapped)
55 next_free_gadget_id++;
56 while (getGadgetInfoFromGadgetID(next_free_gadget_id) != NULL)
57 next_free_gadget_id++;
60 if (next_free_gadget_id <= 0) /* cannot get new gadget id */
61 Error(ERR_EXIT, "too much gadgets -- this should not happen");
67 void DUMP_GADGET_MAP_STATE()
69 struct GadgetInfo *gi = gadget_list_first_entry;
73 printf("-XXX-1-> '%s': %s\n",
74 gi->info_text, (gi->mapped ? "mapped" : "not mapped"));
81 static struct GadgetInfo *getGadgetInfoFromMousePosition(int mx, int my)
83 struct GadgetInfo *gi = gadget_list_first_entry;
88 ((mx >= gi->x && mx < gi->x + gi->width &&
89 my >= gi->y && my < gi->y + gi->height) ||
90 (gi->type & GD_TYPE_SELECTBOX && gi->selectbox.open &&
91 mx >= gi->selectbox.x && mx < gi->selectbox.x+gi->selectbox.width &&
92 my >= gi->selectbox.y && my < gi->selectbox.y+gi->selectbox.height)))
101 static void default_callback_info(void *ptr)
106 static void default_callback_action(void *ptr)
111 static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
113 int state = (pressed ? 1 : 0);
114 struct GadgetDesign *gd = (gi->checked ?
115 &gi->alt_design[state] :
117 boolean redraw_selectbox = FALSE;
121 case GD_TYPE_NORMAL_BUTTON:
122 case GD_TYPE_CHECK_BUTTON:
123 case GD_TYPE_RADIO_BUTTON:
124 BlitBitmapOnBackground(gd->bitmap, drawto,
125 gd->x, gd->y, gi->width, gi->height,
127 if (gi->deco.design.bitmap)
128 BlitBitmap(gi->deco.design.bitmap, drawto,
129 gi->deco.design.x, gi->deco.design.y,
130 gi->deco.width, gi->deco.height,
131 gi->x + gi->deco.x + (pressed ? gi->deco.xshift : 0),
132 gi->y + gi->deco.y + (pressed ? gi->deco.yshift : 0));
135 case GD_TYPE_TEXTINPUT_ALPHANUMERIC:
136 case GD_TYPE_TEXTINPUT_NUMERIC:
140 char cursor_string[3];
141 char text[MAX_GADGET_TEXTSIZE + 1];
142 int font_type = gi->text.font_type;
143 int font_width = getFontWidth(font_type);
144 int border = gi->border.size;
146 /* left part of gadget */
147 BlitBitmapOnBackground(gd->bitmap, drawto,
148 gd->x, gd->y, border, gi->height, gi->x, gi->y);
150 /* middle part of gadget */
151 for (i=0; i < gi->text.size + 1; i++)
152 BlitBitmapOnBackground(gd->bitmap, drawto,
153 gd->x + border, gd->y, font_width, gi->height,
154 gi->x + border + i * font_width, gi->y);
156 /* right part of gadget */
157 BlitBitmapOnBackground(gd->bitmap, drawto,
158 gd->x + gi->border.width - border, gd->y,border,
159 gi->height, gi->x + gi->width - border, gi->y);
162 strcpy(text, gi->text.value);
165 /* gadget text value */
167 gi->x + border, gi->y + border, text,
168 font_type, BLIT_MASKED);
170 cursor_letter = gi->text.value[gi->text.cursor_position];
171 cursor_string[0] = '~';
172 cursor_string[1] = (cursor_letter != '\0' ? cursor_letter : ' ');
173 cursor_string[2] = '\0';
175 SetInverseTextColor(gi->text.inverse_color);
177 /* draw cursor, if active */
180 gi->x + border + gi->text.cursor_position * font_width,
181 gi->y + border, cursor_string,
182 font_type, BLIT_MASKED);
186 case GD_TYPE_SELECTBOX:
189 char text[MAX_GADGET_TEXTSIZE + 1];
190 int font_type = gi->selectbox.font_type;
191 int font_width = getFontWidth(font_type);
192 int font_height = getFontHeight(font_type);
193 int border = gi->border.size;
194 int button = gi->border.size_selectbutton;
195 int width_inner = gi->border.width - button - 2 * border;
196 int box_width = gi->selectbox.width;
197 int box_height = gi->selectbox.height;
199 /* left part of gadget */
200 BlitBitmapOnBackground(gd->bitmap, drawto,
201 gd->x, gd->y, border, gi->height, gi->x, gi->y);
203 /* middle part of gadget */
204 for (i=0; i < gi->selectbox.size; i++)
205 BlitBitmapOnBackground(gd->bitmap, drawto,
206 gd->x + border, gd->y, font_width, gi->height,
207 gi->x + border + i * font_width, gi->y);
209 /* button part of gadget */
210 BlitBitmapOnBackground(gd->bitmap, drawto,
211 gd->x + border + width_inner, gd->y,
213 gi->x + gi->width - border - button, gi->y);
215 /* right part of gadget */
216 BlitBitmapOnBackground(gd->bitmap, drawto,
217 gd->x + gi->border.width - border, gd->y,border,
218 gi->height, gi->x + gi->width - border, gi->y);
221 strncpy(text, gi->selectbox.values[gi->selectbox.index],
223 text[gi->selectbox.size] = '\0';
225 /* gadget text value */
226 DrawTextExt(drawto, gi->x + border, gi->y + border, text,
227 font_type, BLIT_MASKED);
231 if (!gi->selectbox.open)
233 gi->selectbox.open = TRUE;
234 gi->selectbox.stay_open = FALSE;
235 gi->selectbox.current_index = gi->selectbox.index;
237 /* save background under selectbox */
238 BlitBitmap(drawto, gfx.field_save_buffer,
239 gi->selectbox.x, gi->selectbox.y,
240 gi->selectbox.width, gi->selectbox.height,
241 gi->selectbox.x, gi->selectbox.y);
244 /* draw open selectbox */
246 /* top left part of gadget border */
247 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
249 gi->selectbox.x, gi->selectbox.y);
251 /* top middle part of gadget border */
252 for (i=0; i < gi->selectbox.size; i++)
253 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border, gd->y,
255 gi->selectbox.x + border + i * font_width,
258 /* top button part of gadget border */
259 BlitBitmapOnBackground(gd->bitmap, drawto,
260 gd->x + border + width_inner, gd->y,
262 gi->selectbox.x + box_width - border - button,
265 /* top right part of gadget border */
266 BlitBitmapOnBackground(gd->bitmap, drawto,
267 gd->x + gi->border.width - border, gd->y,
269 gi->selectbox.x + box_width - border,
272 /* left and right part of gadget border for each row */
273 for (i=0; i < gi->selectbox.num_values; i++)
275 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y + border,
278 gi->selectbox.y + border + i * font_height);
279 BlitBitmapOnBackground(gd->bitmap, drawto,
280 gd->x + gi->border.width - border,
283 gi->selectbox.x + box_width - border,
284 gi->selectbox.y + border + i * font_height);
287 /* bottom left part of gadget border */
288 BlitBitmapOnBackground(gd->bitmap, drawto,
289 gd->x, gd->y + gi->height - border,
292 gi->selectbox.y + box_height - border);
294 /* bottom middle part of gadget border */
295 for (i=0; i < gi->selectbox.size; i++)
296 BlitBitmapOnBackground(gd->bitmap, drawto,
297 gd->x + border, gd->y + gi->height - border,
299 gi->selectbox.x + border + i * font_width,
300 gi->selectbox.y + box_height - border);
302 /* bottom button part of gadget border */
303 BlitBitmapOnBackground(gd->bitmap, drawto,
304 gd->x + border + width_inner,
305 gd->y + gi->height - border,
307 gi->selectbox.x + box_width - border - button,
308 gi->selectbox.y + box_height - border);
310 /* bottom right part of gadget border */
311 BlitBitmapOnBackground(gd->bitmap, drawto,
312 gd->x + gi->border.width - border,
313 gd->y + gi->height - border,
315 gi->selectbox.x + box_width - border,
316 gi->selectbox.y + box_height - border);
318 ClearRectangleOnBackground(drawto,
319 gi->selectbox.x + border,
320 gi->selectbox.y + border,
321 gi->selectbox.width - 2 * border,
322 gi->selectbox.height - 2 * border);
324 /* selectbox text values */
325 for (i=0; i < gi->selectbox.num_values; i++)
327 if (i == gi->selectbox.current_index)
329 FillRectangle(drawto,
330 gi->selectbox.x + border,
331 gi->selectbox.y + border + i * font_height,
332 gi->selectbox.width - 2 * border, font_height,
333 gi->selectbox.inverse_color);
336 strncpy(&text[1], gi->selectbox.values[i], gi->selectbox.size);
337 text[1 + gi->selectbox.size] = '\0';
339 SetInverseTextColor(gi->selectbox.inverse_color);
343 strncpy(text, gi->selectbox.values[i], gi->selectbox.size);
344 text[gi->selectbox.size] = '\0';
348 gi->selectbox.x + border,
349 gi->selectbox.y + border + i * font_height, text,
350 font_type, BLIT_MASKED);
353 redraw_selectbox = TRUE;
355 else if (gi->selectbox.open)
357 gi->selectbox.open = FALSE;
359 /* redraw closed selectbox */
360 DrawGadget(gi, FALSE, FALSE);
362 /* restore background under selectbox */
363 BlitBitmap(gfx.field_save_buffer, drawto,
364 gi->selectbox.x, gi->selectbox.y,
365 gi->selectbox.width, gi->selectbox.height,
366 gi->selectbox.x, gi->selectbox.y);
368 redraw_selectbox = TRUE;
373 case GD_TYPE_SCROLLBAR_VERTICAL:
377 int ypos = gi->y + gi->scrollbar.position;
378 int design_full = gi->width;
379 int design_body = design_full - 2 * gi->border.size;
380 int size_full = gi->scrollbar.size;
381 int size_body = size_full - 2 * gi->border.size;
382 int num_steps = size_body / design_body;
383 int step_size_remain = size_body - num_steps * design_body;
385 /* clear scrollbar area */
386 ClearRectangleOnBackground(backbuffer, gi->x, gi->y,
387 gi->width, gi->height);
389 /* upper part of gadget */
390 BlitBitmapOnBackground(gd->bitmap, drawto,
392 gi->width, gi->border.size,
395 /* middle part of gadget */
396 for (i=0; i<num_steps; i++)
397 BlitBitmapOnBackground(gd->bitmap, drawto,
398 gd->x, gd->y + gi->border.size,
399 gi->width, design_body,
401 ypos + gi->border.size + i * design_body);
403 /* remaining middle part of gadget */
404 if (step_size_remain > 0)
405 BlitBitmapOnBackground(gd->bitmap, drawto,
406 gd->x, gd->y + gi->border.size,
407 gi->width, step_size_remain,
409 ypos + gi->border.size
410 + num_steps * design_body);
412 /* lower part of gadget */
413 BlitBitmapOnBackground(gd->bitmap, drawto,
414 gd->x, gd->y + design_full - gi->border.size,
415 gi->width, gi->border.size,
416 xpos, ypos + size_full - gi->border.size);
420 case GD_TYPE_SCROLLBAR_HORIZONTAL:
423 int xpos = gi->x + gi->scrollbar.position;
425 int design_full = gi->height;
426 int design_body = design_full - 2 * gi->border.size;
427 int size_full = gi->scrollbar.size;
428 int size_body = size_full - 2 * gi->border.size;
429 int num_steps = size_body / design_body;
430 int step_size_remain = size_body - num_steps * design_body;
432 /* clear scrollbar area */
433 ClearRectangleOnBackground(backbuffer, gi->x, gi->y,
434 gi->width, gi->height);
436 /* left part of gadget */
437 BlitBitmapOnBackground(gd->bitmap, drawto,
439 gi->border.size, gi->height,
442 /* middle part of gadget */
443 for (i=0; i<num_steps; i++)
444 BlitBitmapOnBackground(gd->bitmap, drawto,
445 gd->x + gi->border.size, gd->y,
446 design_body, gi->height,
447 xpos + gi->border.size + i * design_body,
450 /* remaining middle part of gadget */
451 if (step_size_remain > 0)
452 BlitBitmapOnBackground(gd->bitmap, drawto,
453 gd->x + gi->border.size, gd->y,
454 step_size_remain, gi->height,
455 xpos + gi->border.size
456 + num_steps * design_body,
459 /* right part of gadget */
460 BlitBitmapOnBackground(gd->bitmap, drawto,
461 gd->x + design_full - gi->border.size, gd->y,
462 gi->border.size, gi->height,
463 xpos + size_full - gi->border.size, ypos);
473 BlitBitmap(drawto, window,
474 gi->x, gi->y, gi->width, gi->height, gi->x, gi->y);
476 if (gi->type == GD_TYPE_SELECTBOX && redraw_selectbox)
477 BlitBitmap(drawto, window,
478 gi->selectbox.x, gi->selectbox.y,
479 gi->selectbox.width, gi->selectbox.height,
480 gi->selectbox.x, gi->selectbox.y);
483 redraw_mask |= (gi->x < gfx.sx + gfx.sxsize ? REDRAW_FIELD :
484 gi->y < gfx.dy + gfx.dysize ? REDRAW_DOOR_1 :
485 gi->y > gfx.vy ? REDRAW_DOOR_2 : REDRAW_DOOR_3);
488 static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
492 while (tag != GDI_END)
497 gi->custom_id = va_arg(ap, int);
500 case GDI_CUSTOM_TYPE_ID:
501 gi->custom_type_id = va_arg(ap, int);
506 int max_textsize = MAX_INFO_TEXTSIZE - 1;
507 char *text = va_arg(ap, char *);
510 strncpy(gi->info_text, text, max_textsize);
514 gi->info_text[max_textsize] = '\0';
519 gi->x = va_arg(ap, int);
523 gi->y = va_arg(ap, int);
527 gi->width = va_arg(ap, int);
531 gi->height = va_arg(ap, int);
535 gi->type = va_arg(ap, unsigned long);
539 gi->state = va_arg(ap, unsigned long);
543 /* take care here: "boolean" is typedef'ed as "unsigned char",
544 which gets promoted to "int" */
545 gi->checked = (boolean)va_arg(ap, int);
549 gi->radio_nr = va_arg(ap, unsigned long);
552 case GDI_NUMBER_VALUE:
553 gi->text.number_value = va_arg(ap, long);
554 sprintf(gi->text.value, "%d", gi->text.number_value);
555 gi->text.cursor_position = strlen(gi->text.value);
559 gi->text.number_min = va_arg(ap, long);
560 if (gi->text.number_value < gi->text.number_min)
562 gi->text.number_value = gi->text.number_min;
563 sprintf(gi->text.value, "%d", gi->text.number_value);
568 gi->text.number_max = va_arg(ap, long);
569 if (gi->text.number_value > gi->text.number_max)
571 gi->text.number_value = gi->text.number_max;
572 sprintf(gi->text.value, "%d", gi->text.number_value);
578 int max_textsize = MAX_GADGET_TEXTSIZE;
581 max_textsize = MIN(gi->text.size, MAX_GADGET_TEXTSIZE - 1);
583 strncpy(gi->text.value, va_arg(ap, char *), max_textsize);
584 gi->text.value[max_textsize] = '\0';
585 gi->text.cursor_position = strlen(gi->text.value);
591 int tag_value = va_arg(ap, int);
592 int max_textsize = MIN(tag_value, MAX_GADGET_TEXTSIZE - 1);
594 gi->text.size = max_textsize;
595 gi->text.value[max_textsize] = '\0';
597 /* same tag also used for selectbox definition */
598 gi->selectbox.size = gi->text.size;
603 gi->text.font_type = va_arg(ap, int);
605 /* same tag also used for selectbox definition */
606 gi->selectbox.font_type = gi->text.font_type;
609 case GDI_SELECTBOX_VALUES:
610 gi->selectbox.values = va_arg(ap, const char **);
613 case GDI_SELECTBOX_INDEX:
614 gi->selectbox.index = va_arg(ap, int);
617 case GDI_DESIGN_UNPRESSED:
618 gi->design[GD_BUTTON_UNPRESSED].bitmap = va_arg(ap, Bitmap *);
619 gi->design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
620 gi->design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
623 case GDI_DESIGN_PRESSED:
624 gi->design[GD_BUTTON_PRESSED].bitmap = va_arg(ap, Bitmap *);
625 gi->design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
626 gi->design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
629 case GDI_ALT_DESIGN_UNPRESSED:
630 gi->alt_design[GD_BUTTON_UNPRESSED].bitmap= va_arg(ap, Bitmap *);
631 gi->alt_design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
632 gi->alt_design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
635 case GDI_ALT_DESIGN_PRESSED:
636 gi->alt_design[GD_BUTTON_PRESSED].bitmap = va_arg(ap, Bitmap *);
637 gi->alt_design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
638 gi->alt_design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
641 case GDI_BORDER_SIZE:
642 gi->border.size = va_arg(ap, int);
645 case GDI_BORDER_SIZE_SELECTBUTTON:
646 gi->border.size_selectbutton = va_arg(ap, int);
649 case GDI_TEXTINPUT_DESIGN_WIDTH:
650 gi->border.width = va_arg(ap, int);
653 case GDI_DECORATION_DESIGN:
654 gi->deco.design.bitmap = va_arg(ap, Bitmap *);
655 gi->deco.design.x = va_arg(ap, int);
656 gi->deco.design.y = va_arg(ap, int);
659 case GDI_DECORATION_POSITION:
660 gi->deco.x = va_arg(ap, int);
661 gi->deco.y = va_arg(ap, int);
664 case GDI_DECORATION_SIZE:
665 gi->deco.width = va_arg(ap, int);
666 gi->deco.height = va_arg(ap, int);
669 case GDI_DECORATION_SHIFTING:
670 gi->deco.xshift = va_arg(ap, int);
671 gi->deco.yshift = va_arg(ap, int);
675 gi->event_mask = va_arg(ap, unsigned long);
679 gi->drawing.area_xsize = va_arg(ap, int);
680 gi->drawing.area_ysize = va_arg(ap, int);
682 /* determine dependent values for drawing area gadget, if needed */
683 if (gi->width == 0 && gi->height == 0 &&
684 gi->drawing.item_xsize !=0 && gi->drawing.item_ysize !=0)
686 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
687 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
689 else if (gi->drawing.item_xsize == 0 && gi->drawing.item_ysize == 0 &&
690 gi->width != 0 && gi->height != 0)
692 gi->drawing.item_xsize = gi->width / gi->drawing.area_xsize;
693 gi->drawing.item_ysize = gi->height / gi->drawing.area_ysize;
698 gi->drawing.item_xsize = va_arg(ap, int);
699 gi->drawing.item_ysize = va_arg(ap, int);
701 /* determine dependent values for drawing area gadget, if needed */
702 if (gi->width == 0 && gi->height == 0 &&
703 gi->drawing.area_xsize !=0 && gi->drawing.area_ysize !=0)
705 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
706 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
708 else if (gi->drawing.area_xsize == 0 && gi->drawing.area_ysize == 0 &&
709 gi->width != 0 && gi->height != 0)
711 gi->drawing.area_xsize = gi->width / gi->drawing.item_xsize;
712 gi->drawing.area_ysize = gi->height / gi->drawing.item_ysize;
716 case GDI_SCROLLBAR_ITEMS_MAX:
717 gi->scrollbar.items_max = va_arg(ap, int);
720 case GDI_SCROLLBAR_ITEMS_VISIBLE:
721 gi->scrollbar.items_visible = va_arg(ap, int);
724 case GDI_SCROLLBAR_ITEM_POSITION:
725 gi->scrollbar.item_position = va_arg(ap, int);
728 case GDI_CALLBACK_INFO:
729 gi->callback_info = va_arg(ap, gadget_function);
732 case GDI_CALLBACK_ACTION:
733 gi->callback_action = va_arg(ap, gadget_function);
737 Error(ERR_EXIT, "HandleGadgetTags(): unknown tag %d", tag);
740 tag = va_arg(ap, int); /* read next tag */
743 /* check if gadget is complete */
744 if (gi->type != GD_TYPE_DRAWING_AREA &&
745 (!gi->design[GD_BUTTON_UNPRESSED].bitmap ||
746 !gi->design[GD_BUTTON_PRESSED].bitmap))
747 Error(ERR_EXIT, "gadget incomplete (missing Bitmap)");
749 /* adjust gadget values in relation to other gadget values */
751 if (gi->type & GD_TYPE_TEXTINPUT)
753 int font_nr = gi->text.font_type;
754 int font_width = getFontWidth(font_nr);
755 int font_height = getFontHeight(font_nr);
756 int border_size = gi->border.size;
760 gi->width = 2 * border_size + (gi->text.size + 1) * font_width;
761 gi->height = 2 * border_size + font_height;
763 getFontCharSource(font_nr, FONT_ASCII_CURSOR, &src_bitmap, &src_x, &src_y);
764 src_x += font_width / 2;
765 src_y += font_height / 2;
766 gi->text.inverse_color = GetPixel(src_bitmap, src_x, src_y);
769 if (gi->type & GD_TYPE_SELECTBOX)
771 int font_nr = gi->selectbox.font_type;
772 int font_width = getFontWidth(font_nr);
773 int font_height = getFontHeight(font_nr);
774 int border_size = gi->border.size;
775 int button_size = gi->border.size_selectbutton;
779 gi->width = 2 * border_size + gi->text.size * font_width + button_size;
780 gi->height = 2 * border_size + font_height;
782 if (gi->selectbox.values == NULL)
783 Error(ERR_EXIT, "selectbox gadget incomplete (missing values array)");
785 gi->selectbox.num_values = 0;
786 while (gi->selectbox.values[gi->selectbox.num_values] != NULL)
787 gi->selectbox.num_values++;
789 /* calculate values for open selectbox */
790 gi->selectbox.width = gi->width;
791 gi->selectbox.height =
792 2 * border_size + gi->selectbox.num_values * font_height;
794 gi->selectbox.x = gi->x;
795 gi->selectbox.y = gi->y + gi->height;
796 if (gi->selectbox.y + gi->selectbox.height > gfx.real_sy + gfx.full_sysize)
797 gi->selectbox.y = gi->y - gi->selectbox.height;
798 if (gi->selectbox.y < 0)
799 gi->selectbox.y = gfx.real_sy + gfx.full_sysize - gi->selectbox.height;
801 getFontCharSource(font_nr, FONT_ASCII_CURSOR, &src_bitmap, &src_x, &src_y);
802 src_x += font_width / 2;
803 src_y += font_height / 2;
804 gi->selectbox.inverse_color = GetPixel(src_bitmap, src_x, src_y);
806 /* always start with closed selectbox */
807 gi->selectbox.open = FALSE;
810 if (gi->type & GD_TYPE_TEXTINPUT_NUMERIC)
812 struct GadgetTextInput *text = &gi->text;
813 int value = text->number_value;
815 text->number_value = (value < text->number_min ? text->number_min :
816 value > text->number_max ? text->number_max :
819 sprintf(text->value, "%d", text->number_value);
822 if (gi->type & GD_TYPE_SCROLLBAR)
824 struct GadgetScrollbar *gs = &gi->scrollbar;
826 if (gi->width == 0 || gi->height == 0 ||
827 gs->items_max == 0 || gs->items_visible == 0)
828 Error(ERR_EXIT, "scrollbar gadget incomplete (missing tags)");
830 /* calculate internal scrollbar values */
831 gs->size_max = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ?
832 gi->height : gi->width);
833 gs->size = gs->size_max * gs->items_visible / gs->items_max;
834 gs->position = gs->size_max * gs->item_position / gs->items_max;
835 gs->position_max = gs->size_max - gs->size;
836 gs->correction = gs->size_max / gs->items_max / 2;
838 /* finetuning for maximal right/bottom position */
839 if (gs->item_position == gs->items_max - gs->items_visible)
840 gs->position = gs->position_max;
844 void ModifyGadget(struct GadgetInfo *gi, int first_tag, ...)
848 va_start(ap, first_tag);
849 HandleGadgetTags(gi, first_tag, ap);
855 void RedrawGadget(struct GadgetInfo *gi)
858 DrawGadget(gi, gi->state, DG_DIRECT);
861 struct GadgetInfo *CreateGadget(int first_tag, ...)
863 struct GadgetInfo *new_gadget = checked_calloc(sizeof(struct GadgetInfo));
866 /* always start with reliable default values */
867 new_gadget->id = getNewGadgetID();
868 new_gadget->callback_info = default_callback_info;
869 new_gadget->callback_action = default_callback_action;
870 new_gadget->next = NULL;
872 va_start(ap, first_tag);
873 HandleGadgetTags(new_gadget, first_tag, ap);
876 /* insert new gadget into global gadget list */
877 if (gadget_list_last_entry)
879 gadget_list_last_entry->next = new_gadget;
880 gadget_list_last_entry = gadget_list_last_entry->next;
883 gadget_list_first_entry = gadget_list_last_entry = new_gadget;
888 void FreeGadget(struct GadgetInfo *gi)
890 struct GadgetInfo *gi_previous = gadget_list_first_entry;
892 while (gi_previous != NULL && gi_previous->next != gi)
893 gi_previous = gi_previous->next;
895 if (gi == gadget_list_first_entry)
896 gadget_list_first_entry = gi->next;
898 if (gi == gadget_list_last_entry)
899 gadget_list_last_entry = gi_previous;
901 if (gi_previous != NULL)
902 gi_previous->next = gi->next;
907 static void CheckRangeOfNumericInputGadget(struct GadgetInfo *gi)
909 if (gi->type != GD_TYPE_TEXTINPUT_NUMERIC)
912 gi->text.number_value = atoi(gi->text.value);
914 if (gi->text.number_value < gi->text.number_min)
915 gi->text.number_value = gi->text.number_min;
916 if (gi->text.number_value > gi->text.number_max)
917 gi->text.number_value = gi->text.number_max;
919 sprintf(gi->text.value, "%d", gi->text.number_value);
921 if (gi->text.cursor_position < 0)
922 gi->text.cursor_position = 0;
923 else if (gi->text.cursor_position > strlen(gi->text.value))
924 gi->text.cursor_position = strlen(gi->text.value);
927 /* global pointer to gadget actually in use (when mouse button pressed) */
928 static struct GadgetInfo *last_gi = NULL;
930 static void MapGadgetExt(struct GadgetInfo *gi, boolean redraw)
932 if (gi == NULL || gi->mapped)
938 DrawGadget(gi, DG_UNPRESSED, DG_BUFFERED);
941 void MapGadget(struct GadgetInfo *gi)
943 MapGadgetExt(gi, TRUE);
946 void UnmapGadget(struct GadgetInfo *gi)
948 if (gi == NULL || !gi->mapped)
957 #define MAX_NUM_GADGETS 1024
958 #define MULTIMAP_UNMAP (1 << 0)
959 #define MULTIMAP_REMAP (1 << 1)
960 #define MULTIMAP_REDRAW (1 << 2)
961 #define MULTIMAP_PLAYFIELD (1 << 3)
962 #define MULTIMAP_DOOR_1 (1 << 4)
963 #define MULTIMAP_DOOR_2 (1 << 5)
964 #define MULTIMAP_ALL (MULTIMAP_PLAYFIELD | \
968 static void MultiMapGadgets(int mode)
970 struct GadgetInfo *gi = gadget_list_first_entry;
971 static boolean map_state[MAX_NUM_GADGETS];
976 if ((mode & MULTIMAP_PLAYFIELD &&
977 gi->x < gfx.sx + gfx.sxsize) ||
978 (mode & MULTIMAP_DOOR_1 &&
979 gi->x >= gfx.dx && gi->y < gfx.dy + gfx.dysize) ||
980 (mode & MULTIMAP_DOOR_2 &&
981 gi->x >= gfx.dx && gi->y > gfx.dy + gfx.dysize) ||
982 (mode & MULTIMAP_ALL) == MULTIMAP_ALL)
984 if (mode & MULTIMAP_UNMAP)
986 map_state[map_count++ % MAX_NUM_GADGETS] = gi->mapped;
991 if (map_state[map_count++ % MAX_NUM_GADGETS])
992 MapGadgetExt(gi, (mode & MULTIMAP_REDRAW));
1000 void UnmapAllGadgets()
1002 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_UNMAP);
1005 void RemapAllGadgets()
1007 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_REMAP);
1010 boolean anyTextInputGadgetActive()
1012 return (last_gi && (last_gi->type & GD_TYPE_TEXTINPUT) && last_gi->mapped);
1015 boolean anySelectboxGadgetActive()
1017 return (last_gi && (last_gi->type & GD_TYPE_SELECTBOX) && last_gi->mapped);
1020 boolean anyTextGadgetActive()
1022 return (anyTextInputGadgetActive() || anySelectboxGadgetActive());
1025 void ClickOnGadget(struct GadgetInfo *gi, int button)
1027 /* simulate releasing mouse button over last gadget, if still pressed */
1029 HandleGadgets(-1, -1, 0);
1031 /* simulate pressing mouse button over specified gadget */
1032 HandleGadgets(gi->x, gi->y, button);
1034 /* simulate releasing mouse button over specified gadget */
1035 HandleGadgets(gi->x, gi->y, 0);
1038 void HandleGadgets(int mx, int my, int button)
1040 static struct GadgetInfo *last_info_gi = NULL;
1041 static unsigned long pressed_delay = 0;
1042 static int last_button = 0;
1043 static int last_mx = 0, last_my = 0;
1044 int scrollbar_mouse_pos = 0;
1045 struct GadgetInfo *new_gi, *gi;
1046 boolean press_event;
1047 boolean release_event;
1048 boolean mouse_moving;
1049 boolean gadget_pressed;
1050 boolean gadget_pressed_repeated;
1051 boolean gadget_moving;
1052 boolean gadget_moving_inside;
1053 boolean gadget_moving_off_borders;
1054 boolean gadget_released;
1055 boolean gadget_released_inside;
1056 boolean gadget_released_inside_select_line;
1057 boolean gadget_released_inside_select_area;
1058 boolean gadget_released_off_borders;
1059 boolean changed_position = FALSE;
1061 /* check if there are any gadgets defined */
1062 if (gadget_list_first_entry == NULL)
1065 /* check which gadget is under the mouse pointer */
1066 new_gi = getGadgetInfoFromMousePosition(mx, my);
1068 /* check if button state has changed since last invocation */
1069 press_event = (button != 0 && last_button == 0);
1070 release_event = (button == 0 && last_button != 0);
1071 last_button = button;
1073 /* check if mouse has been moved since last invocation */
1074 mouse_moving = ((mx != last_mx || my != last_my) && motion_status);
1078 /* special treatment for text and number input gadgets */
1079 if (anyTextInputGadgetActive() && button != 0 && !motion_status)
1081 struct GadgetInfo *gi = last_gi;
1083 if (new_gi == last_gi)
1085 int old_cursor_position = gi->text.cursor_position;
1087 /* if mouse button pressed inside activated text gadget, set cursor */
1088 gi->text.cursor_position =
1089 (mx - gi->x - gi->border.size) / getFontWidth(gi->text.font_type);
1091 if (gi->text.cursor_position < 0)
1092 gi->text.cursor_position = 0;
1093 else if (gi->text.cursor_position > strlen(gi->text.value))
1094 gi->text.cursor_position = strlen(gi->text.value);
1096 if (gi->text.cursor_position != old_cursor_position)
1097 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1101 /* if mouse button pressed outside text input gadget, deactivate it */
1102 CheckRangeOfNumericInputGadget(gi);
1103 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1105 gi->event.type = GD_EVENT_TEXT_LEAVING;
1107 if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
1108 gi->callback_action(gi);
1114 /* special treatment for selectbox gadgets */
1115 if (anySelectboxGadgetActive() && button != 0 && !motion_status)
1117 struct GadgetInfo *gi = last_gi;
1119 if (new_gi == last_gi)
1121 int old_index = gi->selectbox.current_index;
1123 /* if mouse button pressed inside activated selectbox, select value */
1124 if (my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
1125 gi->selectbox.current_index =
1126 (my - gi->selectbox.y - gi->border.size) /
1127 getFontWidth(gi->selectbox.font_type);
1129 if (gi->selectbox.current_index < 0)
1130 gi->selectbox.current_index = 0;
1131 else if (gi->selectbox.current_index > gi->selectbox.num_values - 1)
1132 gi->selectbox.current_index = gi->selectbox.num_values - 1;
1134 if (gi->selectbox.current_index != old_index)
1135 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1139 /* if mouse button pressed outside selectbox gadget, deactivate it */
1140 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1142 gi->event.type = GD_EVENT_TEXT_LEAVING;
1144 if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
1145 gi->callback_action(gi);
1152 (button != 0 && last_gi == NULL && new_gi != NULL && press_event);
1153 gadget_pressed_repeated =
1154 (button != 0 && last_gi != NULL && new_gi == last_gi);
1156 gadget_released = (release_event && last_gi != NULL);
1157 gadget_released_inside = (gadget_released && new_gi == last_gi);
1158 gadget_released_off_borders = (gadget_released && new_gi != last_gi);
1160 gadget_moving = (button != 0 && last_gi != NULL && mouse_moving);
1161 gadget_moving_inside = (gadget_moving && new_gi == last_gi);
1162 gadget_moving_off_borders = (gadget_moving && new_gi != last_gi);
1164 /* when handling selectbox, set additional state values */
1165 if (gadget_released_inside && (last_gi->type & GD_TYPE_SELECTBOX))
1167 struct GadgetInfo *gi = last_gi;
1169 gadget_released_inside_select_line =
1170 (mx >= gi->x && mx < gi->x + gi->width &&
1171 my >= gi->y && my < gi->y + gi->height);
1172 gadget_released_inside_select_area =
1173 (mx >= gi->selectbox.x && mx < gi->selectbox.x+gi->selectbox.width &&
1174 my >= gi->selectbox.y && my < gi->selectbox.y+gi->selectbox.height);
1178 gadget_released_inside_select_line = FALSE;
1179 gadget_released_inside_select_area = FALSE;
1182 /* if new gadget pressed, store this gadget */
1186 /* 'gi' is actually handled gadget */
1189 /* if gadget is scrollbar, choose mouse position value */
1190 if (gi && gi->type & GD_TYPE_SCROLLBAR)
1191 scrollbar_mouse_pos =
1192 (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? mx - gi->x : my - gi->y);
1194 /* if mouse button released, no gadget needs to be handled anymore */
1195 if (gadget_released)
1197 if ((last_gi->type & GD_TYPE_SELECTBOX) &&
1198 (gadget_released_inside_select_line ||
1199 gadget_released_off_borders)) /* selectbox stays open */
1200 gi->selectbox.stay_open = TRUE;
1201 else if (!(last_gi->type & GD_TYPE_TEXTINPUT)) /* text input stays open */
1205 /* modify event position values even if no gadget is pressed */
1206 if (button == 0 && !release_event)
1211 int last_x = gi->event.x;
1212 int last_y = gi->event.y;
1214 gi->event.x = mx - gi->x;
1215 gi->event.y = my - gi->y;
1217 if (gi->type == GD_TYPE_DRAWING_AREA)
1219 gi->event.x /= gi->drawing.item_xsize;
1220 gi->event.y /= gi->drawing.item_ysize;
1222 if (last_x != gi->event.x || last_y != gi->event.y)
1223 changed_position = TRUE;
1225 else if (gi->type & GD_TYPE_SELECTBOX)
1227 int old_index = gi->selectbox.current_index;
1229 /* if mouse moving inside activated selectbox, select value */
1230 if (my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
1231 gi->selectbox.current_index =
1232 (my - gi->selectbox.y - gi->border.size) /
1233 getFontWidth(gi->selectbox.font_type);
1235 if (gi->selectbox.current_index < 0)
1236 gi->selectbox.current_index = 0;
1237 else if (gi->selectbox.current_index > gi->selectbox.num_values - 1)
1238 gi->selectbox.current_index = gi->selectbox.num_values - 1;
1240 if (gi->selectbox.current_index != old_index)
1241 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1245 /* handle gadget popup info text */
1246 if (last_info_gi != new_gi ||
1247 (new_gi && new_gi->type == GD_TYPE_DRAWING_AREA && changed_position))
1249 if (new_gi != NULL && (button == 0 || new_gi == last_gi))
1251 new_gi->event.type = GD_EVENT_INFO_ENTERING;
1252 new_gi->callback_info(new_gi);
1254 else if (last_info_gi != NULL)
1256 last_info_gi->event.type = GD_EVENT_INFO_LEAVING;
1257 last_info_gi->callback_info(last_info_gi);
1260 default_callback_info(NULL);
1262 printf("It seems that we are leaving gadget [%s]!\n",
1263 (last_info_gi != NULL &&
1264 last_info_gi->info_text != NULL ?
1265 last_info_gi->info_text : ""));
1269 last_info_gi = new_gi;
1274 if (gi->type == GD_TYPE_CHECK_BUTTON)
1276 gi->checked = !gi->checked;
1278 else if (gi->type == GD_TYPE_RADIO_BUTTON)
1280 struct GadgetInfo *rgi = gadget_list_first_entry;
1285 rgi->type == GD_TYPE_RADIO_BUTTON &&
1286 rgi->radio_nr == gi->radio_nr &&
1289 rgi->checked = FALSE;
1290 DrawGadget(rgi, DG_UNPRESSED, DG_DIRECT);
1298 else if (gi->type & GD_TYPE_SCROLLBAR)
1302 if (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL)
1313 if (mpos >= gpos + gi->scrollbar.position &&
1314 mpos < gpos + gi->scrollbar.position + gi->scrollbar.size)
1316 /* drag scrollbar */
1317 gi->scrollbar.drag_position =
1318 scrollbar_mouse_pos - gi->scrollbar.position;
1322 /* click scrollbar one scrollbar length up/left or down/right */
1324 struct GadgetScrollbar *gs = &gi->scrollbar;
1325 int old_item_position = gs->item_position;
1327 changed_position = FALSE;
1329 gs->item_position +=
1330 gs->items_visible * (mpos < gpos + gi->scrollbar.position ? -1 : +1);
1332 if (gs->item_position < 0)
1333 gs->item_position = 0;
1334 if (gs->item_position > gs->items_max - gs->items_visible)
1335 gs->item_position = gs->items_max - gs->items_visible;
1337 if (old_item_position != gs->item_position)
1339 gi->event.item_position = gs->item_position;
1340 changed_position = TRUE;
1343 ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, gs->item_position,
1346 gi->state = GD_BUTTON_UNPRESSED;
1347 gi->event.type = GD_EVENT_MOVING;
1348 gi->event.off_borders = FALSE;
1350 if (gi->event_mask & GD_EVENT_MOVING && changed_position)
1351 gi->callback_action(gi);
1353 /* don't handle this scrollbar anymore while mouse button pressed */
1360 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1362 gi->state = GD_BUTTON_PRESSED;
1363 gi->event.type = GD_EVENT_PRESSED;
1364 gi->event.button = button;
1365 gi->event.off_borders = FALSE;
1367 /* initialize delay counter */
1368 DelayReached(&pressed_delay, 0);
1370 if (gi->event_mask & GD_EVENT_PRESSED)
1371 gi->callback_action(gi);
1374 if (gadget_pressed_repeated)
1376 gi->event.type = GD_EVENT_PRESSED;
1378 if (gi->event_mask & GD_EVENT_REPEATED &&
1379 DelayReached(&pressed_delay, GADGET_FRAME_DELAY))
1380 gi->callback_action(gi);
1385 if (gi->type & GD_TYPE_BUTTON)
1387 if (gadget_moving_inside && gi->state == GD_BUTTON_UNPRESSED)
1388 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1389 else if (gadget_moving_off_borders && gi->state == GD_BUTTON_PRESSED)
1390 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1392 else if (gi->type & GD_TYPE_SELECTBOX)
1394 int old_index = gi->selectbox.current_index;
1396 /* if mouse moving inside activated selectbox, select value */
1397 if (my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
1398 gi->selectbox.current_index =
1399 (my - gi->selectbox.y - gi->border.size) /
1400 getFontWidth(gi->selectbox.font_type);
1402 if (gi->selectbox.current_index < 0)
1403 gi->selectbox.current_index = 0;
1404 else if (gi->selectbox.current_index > gi->selectbox.num_values - 1)
1405 gi->selectbox.current_index = gi->selectbox.num_values - 1;
1407 if (gi->selectbox.current_index != old_index)
1408 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1410 else if (gi->type & GD_TYPE_SCROLLBAR)
1412 struct GadgetScrollbar *gs = &gi->scrollbar;
1413 int old_item_position = gs->item_position;
1415 gs->position = scrollbar_mouse_pos - gs->drag_position;
1417 if (gs->position < 0)
1419 if (gs->position > gs->position_max)
1420 gs->position = gs->position_max;
1423 gs->items_max * (gs->position + gs->correction) / gs->size_max;
1425 if (gs->item_position < 0)
1426 gs->item_position = 0;
1427 if (gs->item_position > gs->items_max - 1)
1428 gs->item_position = gs->items_max - 1;
1430 if (old_item_position != gs->item_position)
1432 gi->event.item_position = gs->item_position;
1433 changed_position = TRUE;
1436 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1439 gi->state = (gadget_moving_inside || gi->type & GD_TYPE_SCROLLBAR ?
1440 GD_BUTTON_PRESSED : GD_BUTTON_UNPRESSED);
1441 gi->event.type = GD_EVENT_MOVING;
1442 gi->event.off_borders = gadget_moving_off_borders;
1444 if (gi->event_mask & GD_EVENT_MOVING && changed_position &&
1445 (gadget_moving_inside || gi->event_mask & GD_EVENT_OFF_BORDERS))
1446 gi->callback_action(gi);
1449 if (gadget_released_inside)
1451 boolean deactivate_gadget = TRUE;
1453 if (gi->type & GD_TYPE_SELECTBOX)
1455 if (gadget_released_inside_select_line ||
1456 gadget_released_off_borders) /* selectbox stays open */
1457 deactivate_gadget = FALSE;
1459 gi->selectbox.index = gi->selectbox.current_index;
1462 if (deactivate_gadget &&
1463 !(gi->type & GD_TYPE_TEXTINPUT)) /* text input stays open */
1464 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1466 gi->state = GD_BUTTON_UNPRESSED;
1467 gi->event.type = GD_EVENT_RELEASED;
1469 if ((gi->event_mask & GD_EVENT_RELEASED) && deactivate_gadget)
1470 gi->callback_action(gi);
1473 if (gadget_released_off_borders)
1475 if (gi->type & GD_TYPE_SCROLLBAR)
1476 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1478 gi->event.type = GD_EVENT_RELEASED;
1480 if (gi->event_mask & GD_EVENT_RELEASED &&
1481 gi->event_mask & GD_EVENT_OFF_BORDERS)
1482 gi->callback_action(gi);
1486 void HandleGadgetsKeyInput(Key key)
1488 struct GadgetInfo *gi = last_gi;
1490 if (gi == NULL || !gi->mapped ||
1491 !((gi->type & GD_TYPE_TEXTINPUT) || (gi->type & GD_TYPE_SELECTBOX)))
1494 if (key == KSYM_Return) /* valid for both text input and selectbox */
1496 if (gi->type & GD_TYPE_TEXTINPUT)
1497 CheckRangeOfNumericInputGadget(gi);
1498 else if (gi->type & GD_TYPE_SELECTBOX)
1499 gi->selectbox.index = gi->selectbox.current_index;
1501 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1503 gi->event.type = GD_EVENT_TEXT_RETURN;
1505 if (gi->event_mask & GD_EVENT_TEXT_RETURN)
1506 gi->callback_action(gi);
1510 else if (gi->type & GD_TYPE_TEXTINPUT) /* only valid for text input */
1512 char text[MAX_GADGET_TEXTSIZE];
1513 int text_length = strlen(gi->text.value);
1514 int cursor_pos = gi->text.cursor_position;
1515 char letter = getCharFromKey(key);
1516 boolean legal_letter = (gi->type == GD_TYPE_TEXTINPUT_NUMERIC ?
1517 letter >= '0' && letter <= '9' :
1520 if (legal_letter && text_length < gi->text.size)
1522 strcpy(text, gi->text.value);
1523 strcpy(&gi->text.value[cursor_pos + 1], &text[cursor_pos]);
1524 gi->text.value[cursor_pos] = letter;
1525 gi->text.cursor_position++;
1527 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1529 else if (key == KSYM_Left && cursor_pos > 0)
1531 gi->text.cursor_position--;
1532 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1534 else if (key == KSYM_Right && cursor_pos < text_length)
1536 gi->text.cursor_position++;
1537 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1539 else if (key == KSYM_BackSpace && cursor_pos > 0)
1541 strcpy(text, gi->text.value);
1542 strcpy(&gi->text.value[cursor_pos - 1], &text[cursor_pos]);
1543 gi->text.cursor_position--;
1544 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1546 else if (key == KSYM_Delete && cursor_pos < text_length)
1548 strcpy(text, gi->text.value);
1549 strcpy(&gi->text.value[cursor_pos], &text[cursor_pos + 1]);
1550 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1553 else if (gi->type & GD_TYPE_SELECTBOX) /* only valid for selectbox */
1555 int index = gi->selectbox.current_index;
1556 int num_values = gi->selectbox.num_values;
1558 if (key == KSYM_Up && index > 0)
1560 gi->selectbox.current_index--;
1561 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1563 else if (key == KSYM_Down && index < num_values - 1)
1565 gi->selectbox.current_index++;
1566 DrawGadget(gi, DG_PRESSED, DG_DIRECT);