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[2];
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] = (cursor_letter != '\0' ? cursor_letter : ' ');
172 cursor_string[1] = '\0';
174 SetInverseTextColor(gi->text.inverse_color);
176 /* draw cursor, if active */
179 gi->x + border + gi->text.cursor_position * font_width,
180 gi->y + border, cursor_string,
181 font_type, BLIT_INVERSE);
185 case GD_TYPE_SELECTBOX:
188 char text[MAX_GADGET_TEXTSIZE + 1];
189 int font_type = gi->selectbox.font_type;
190 int font_width = getFontWidth(font_type);
191 int font_height = getFontHeight(font_type);
192 int border = gi->border.size;
193 int button = gi->border.size_selectbutton;
194 int width_inner = gi->border.width - button - 2 * border;
195 int box_width = gi->selectbox.width;
196 int box_height = gi->selectbox.height;
198 /* left part of gadget */
199 BlitBitmapOnBackground(gd->bitmap, drawto,
200 gd->x, gd->y, border, gi->height, gi->x, gi->y);
202 /* middle part of gadget */
203 for (i=0; i < gi->selectbox.size; i++)
204 BlitBitmapOnBackground(gd->bitmap, drawto,
205 gd->x + border, gd->y, font_width, gi->height,
206 gi->x + border + i * font_width, gi->y);
208 /* button part of gadget */
209 BlitBitmapOnBackground(gd->bitmap, drawto,
210 gd->x + border + width_inner, gd->y,
212 gi->x + gi->width - border - button, gi->y);
214 /* right part of gadget */
215 BlitBitmapOnBackground(gd->bitmap, drawto,
216 gd->x + gi->border.width - border, gd->y,border,
217 gi->height, gi->x + gi->width - border, gi->y);
220 strncpy(text, gi->selectbox.values[gi->selectbox.index],
222 text[gi->selectbox.size] = '\0';
224 /* gadget text value */
225 DrawTextExt(drawto, gi->x + border, gi->y + border, text,
226 font_type, BLIT_MASKED);
230 if (!gi->selectbox.open)
232 gi->selectbox.open = TRUE;
233 gi->selectbox.stay_open = FALSE;
234 gi->selectbox.current_index = gi->selectbox.index;
236 /* save background under selectbox */
237 BlitBitmap(drawto, gfx.field_save_buffer,
238 gi->selectbox.x, gi->selectbox.y,
239 gi->selectbox.width, gi->selectbox.height,
240 gi->selectbox.x, gi->selectbox.y);
243 /* draw open selectbox */
245 /* top left part of gadget border */
246 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
248 gi->selectbox.x, gi->selectbox.y);
250 /* top middle part of gadget border */
251 for (i=0; i < gi->selectbox.size; i++)
252 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border, gd->y,
254 gi->selectbox.x + border + i * font_width,
257 /* top button part of gadget border */
258 BlitBitmapOnBackground(gd->bitmap, drawto,
259 gd->x + border + width_inner, gd->y,
261 gi->selectbox.x + box_width - border - button,
264 /* top right part of gadget border */
265 BlitBitmapOnBackground(gd->bitmap, drawto,
266 gd->x + gi->border.width - border, gd->y,
268 gi->selectbox.x + box_width - border,
271 /* left and right part of gadget border for each row */
272 for (i=0; i < gi->selectbox.num_values; i++)
274 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y + border,
277 gi->selectbox.y + border + i * font_height);
278 BlitBitmapOnBackground(gd->bitmap, drawto,
279 gd->x + gi->border.width - border,
282 gi->selectbox.x + box_width - border,
283 gi->selectbox.y + border + i * font_height);
286 /* bottom left part of gadget border */
287 BlitBitmapOnBackground(gd->bitmap, drawto,
288 gd->x, gd->y + gi->height - border,
291 gi->selectbox.y + box_height - border);
293 /* bottom middle part of gadget border */
294 for (i=0; i < gi->selectbox.size; i++)
295 BlitBitmapOnBackground(gd->bitmap, drawto,
296 gd->x + border, gd->y + gi->height - border,
298 gi->selectbox.x + border + i * font_width,
299 gi->selectbox.y + box_height - border);
301 /* bottom button part of gadget border */
302 BlitBitmapOnBackground(gd->bitmap, drawto,
303 gd->x + border + width_inner,
304 gd->y + gi->height - border,
306 gi->selectbox.x + box_width - border - button,
307 gi->selectbox.y + box_height - border);
309 /* bottom right part of gadget border */
310 BlitBitmapOnBackground(gd->bitmap, drawto,
311 gd->x + gi->border.width - border,
312 gd->y + gi->height - border,
314 gi->selectbox.x + box_width - border,
315 gi->selectbox.y + box_height - border);
317 ClearRectangleOnBackground(drawto,
318 gi->selectbox.x + border,
319 gi->selectbox.y + border,
320 gi->selectbox.width - 2 * border,
321 gi->selectbox.height - 2 * border);
323 /* selectbox text values */
324 for (i=0; i < gi->selectbox.num_values; i++)
328 if (i == gi->selectbox.current_index)
330 FillRectangle(drawto,
331 gi->selectbox.x + border,
332 gi->selectbox.y + border + i * font_height,
333 gi->selectbox.width - 2 * border, font_height,
334 gi->selectbox.inverse_color);
336 strncpy(text, gi->selectbox.values[i], gi->selectbox.size);
337 text[1 + gi->selectbox.size] = '\0';
339 SetInverseTextColor(gi->selectbox.inverse_color);
341 mask_mode = BLIT_INVERSE;
345 strncpy(text, gi->selectbox.values[i], gi->selectbox.size);
346 text[gi->selectbox.size] = '\0';
348 mask_mode = BLIT_MASKED;
352 gi->selectbox.x + border,
353 gi->selectbox.y + border + i * font_height, text,
354 font_type, mask_mode);
357 redraw_selectbox = TRUE;
359 else if (gi->selectbox.open)
361 gi->selectbox.open = FALSE;
363 /* redraw closed selectbox */
364 DrawGadget(gi, FALSE, FALSE);
366 /* restore background under selectbox */
367 BlitBitmap(gfx.field_save_buffer, drawto,
368 gi->selectbox.x, gi->selectbox.y,
369 gi->selectbox.width, gi->selectbox.height,
370 gi->selectbox.x, gi->selectbox.y);
372 redraw_selectbox = TRUE;
377 case GD_TYPE_SCROLLBAR_VERTICAL:
381 int ypos = gi->y + gi->scrollbar.position;
382 int design_full = gi->width;
383 int design_body = design_full - 2 * gi->border.size;
384 int size_full = gi->scrollbar.size;
385 int size_body = size_full - 2 * gi->border.size;
386 int num_steps = size_body / design_body;
387 int step_size_remain = size_body - num_steps * design_body;
389 /* clear scrollbar area */
390 ClearRectangleOnBackground(backbuffer, gi->x, gi->y,
391 gi->width, gi->height);
393 /* upper part of gadget */
394 BlitBitmapOnBackground(gd->bitmap, drawto,
396 gi->width, gi->border.size,
399 /* middle part of gadget */
400 for (i=0; i<num_steps; i++)
401 BlitBitmapOnBackground(gd->bitmap, drawto,
402 gd->x, gd->y + gi->border.size,
403 gi->width, design_body,
405 ypos + gi->border.size + i * design_body);
407 /* remaining middle part of gadget */
408 if (step_size_remain > 0)
409 BlitBitmapOnBackground(gd->bitmap, drawto,
410 gd->x, gd->y + gi->border.size,
411 gi->width, step_size_remain,
413 ypos + gi->border.size
414 + num_steps * design_body);
416 /* lower part of gadget */
417 BlitBitmapOnBackground(gd->bitmap, drawto,
418 gd->x, gd->y + design_full - gi->border.size,
419 gi->width, gi->border.size,
420 xpos, ypos + size_full - gi->border.size);
424 case GD_TYPE_SCROLLBAR_HORIZONTAL:
427 int xpos = gi->x + gi->scrollbar.position;
429 int design_full = gi->height;
430 int design_body = design_full - 2 * gi->border.size;
431 int size_full = gi->scrollbar.size;
432 int size_body = size_full - 2 * gi->border.size;
433 int num_steps = size_body / design_body;
434 int step_size_remain = size_body - num_steps * design_body;
436 /* clear scrollbar area */
437 ClearRectangleOnBackground(backbuffer, gi->x, gi->y,
438 gi->width, gi->height);
440 /* left part of gadget */
441 BlitBitmapOnBackground(gd->bitmap, drawto,
443 gi->border.size, gi->height,
446 /* middle part of gadget */
447 for (i=0; i<num_steps; i++)
448 BlitBitmapOnBackground(gd->bitmap, drawto,
449 gd->x + gi->border.size, gd->y,
450 design_body, gi->height,
451 xpos + gi->border.size + i * design_body,
454 /* remaining middle part of gadget */
455 if (step_size_remain > 0)
456 BlitBitmapOnBackground(gd->bitmap, drawto,
457 gd->x + gi->border.size, gd->y,
458 step_size_remain, gi->height,
459 xpos + gi->border.size
460 + num_steps * design_body,
463 /* right part of gadget */
464 BlitBitmapOnBackground(gd->bitmap, drawto,
465 gd->x + design_full - gi->border.size, gd->y,
466 gi->border.size, gi->height,
467 xpos + size_full - gi->border.size, ypos);
477 BlitBitmap(drawto, window,
478 gi->x, gi->y, gi->width, gi->height, gi->x, gi->y);
480 if (gi->type == GD_TYPE_SELECTBOX && redraw_selectbox)
481 BlitBitmap(drawto, window,
482 gi->selectbox.x, gi->selectbox.y,
483 gi->selectbox.width, gi->selectbox.height,
484 gi->selectbox.x, gi->selectbox.y);
487 redraw_mask |= (gi->x < gfx.sx + gfx.sxsize ? REDRAW_FIELD :
488 gi->y < gfx.dy + gfx.dysize ? REDRAW_DOOR_1 :
489 gi->y > gfx.vy ? REDRAW_DOOR_2 : REDRAW_DOOR_3);
492 static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
496 while (tag != GDI_END)
501 gi->custom_id = va_arg(ap, int);
504 case GDI_CUSTOM_TYPE_ID:
505 gi->custom_type_id = va_arg(ap, int);
510 int max_textsize = MAX_INFO_TEXTSIZE - 1;
511 char *text = va_arg(ap, char *);
514 strncpy(gi->info_text, text, max_textsize);
518 gi->info_text[max_textsize] = '\0';
523 gi->x = va_arg(ap, int);
527 gi->y = va_arg(ap, int);
531 gi->width = va_arg(ap, int);
535 gi->height = va_arg(ap, int);
539 gi->type = va_arg(ap, unsigned long);
543 gi->state = va_arg(ap, unsigned long);
547 /* take care here: "boolean" is typedef'ed as "unsigned char",
548 which gets promoted to "int" */
549 gi->checked = (boolean)va_arg(ap, int);
553 gi->radio_nr = va_arg(ap, unsigned long);
556 case GDI_NUMBER_VALUE:
557 gi->text.number_value = va_arg(ap, long);
558 sprintf(gi->text.value, "%d", gi->text.number_value);
559 gi->text.cursor_position = strlen(gi->text.value);
563 gi->text.number_min = va_arg(ap, long);
564 if (gi->text.number_value < gi->text.number_min)
566 gi->text.number_value = gi->text.number_min;
567 sprintf(gi->text.value, "%d", gi->text.number_value);
572 gi->text.number_max = va_arg(ap, long);
573 if (gi->text.number_value > gi->text.number_max)
575 gi->text.number_value = gi->text.number_max;
576 sprintf(gi->text.value, "%d", gi->text.number_value);
582 int max_textsize = MAX_GADGET_TEXTSIZE;
585 max_textsize = MIN(gi->text.size, MAX_GADGET_TEXTSIZE - 1);
587 strncpy(gi->text.value, va_arg(ap, char *), max_textsize);
588 gi->text.value[max_textsize] = '\0';
589 gi->text.cursor_position = strlen(gi->text.value);
595 int tag_value = va_arg(ap, int);
596 int max_textsize = MIN(tag_value, MAX_GADGET_TEXTSIZE - 1);
598 gi->text.size = max_textsize;
599 gi->text.value[max_textsize] = '\0';
601 /* same tag also used for selectbox definition */
602 gi->selectbox.size = gi->text.size;
607 gi->text.font_type = va_arg(ap, int);
609 /* same tag also used for selectbox definition */
610 gi->selectbox.font_type = gi->text.font_type;
613 case GDI_SELECTBOX_VALUES:
614 gi->selectbox.values = va_arg(ap, const char **);
617 case GDI_SELECTBOX_INDEX:
618 gi->selectbox.index = va_arg(ap, int);
621 case GDI_DESIGN_UNPRESSED:
622 gi->design[GD_BUTTON_UNPRESSED].bitmap = va_arg(ap, Bitmap *);
623 gi->design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
624 gi->design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
627 case GDI_DESIGN_PRESSED:
628 gi->design[GD_BUTTON_PRESSED].bitmap = va_arg(ap, Bitmap *);
629 gi->design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
630 gi->design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
633 case GDI_ALT_DESIGN_UNPRESSED:
634 gi->alt_design[GD_BUTTON_UNPRESSED].bitmap= va_arg(ap, Bitmap *);
635 gi->alt_design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
636 gi->alt_design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
639 case GDI_ALT_DESIGN_PRESSED:
640 gi->alt_design[GD_BUTTON_PRESSED].bitmap = va_arg(ap, Bitmap *);
641 gi->alt_design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
642 gi->alt_design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
645 case GDI_BORDER_SIZE:
646 gi->border.size = va_arg(ap, int);
649 case GDI_BORDER_SIZE_SELECTBUTTON:
650 gi->border.size_selectbutton = va_arg(ap, int);
653 case GDI_TEXTINPUT_DESIGN_WIDTH:
654 gi->border.width = va_arg(ap, int);
657 case GDI_DECORATION_DESIGN:
658 gi->deco.design.bitmap = va_arg(ap, Bitmap *);
659 gi->deco.design.x = va_arg(ap, int);
660 gi->deco.design.y = va_arg(ap, int);
663 case GDI_DECORATION_POSITION:
664 gi->deco.x = va_arg(ap, int);
665 gi->deco.y = va_arg(ap, int);
668 case GDI_DECORATION_SIZE:
669 gi->deco.width = va_arg(ap, int);
670 gi->deco.height = va_arg(ap, int);
673 case GDI_DECORATION_SHIFTING:
674 gi->deco.xshift = va_arg(ap, int);
675 gi->deco.yshift = va_arg(ap, int);
679 gi->event_mask = va_arg(ap, unsigned long);
683 gi->drawing.area_xsize = va_arg(ap, int);
684 gi->drawing.area_ysize = va_arg(ap, int);
686 /* determine dependent values for drawing area gadget, if needed */
687 if (gi->width == 0 && gi->height == 0 &&
688 gi->drawing.item_xsize !=0 && gi->drawing.item_ysize !=0)
690 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
691 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
693 else if (gi->drawing.item_xsize == 0 && gi->drawing.item_ysize == 0 &&
694 gi->width != 0 && gi->height != 0)
696 gi->drawing.item_xsize = gi->width / gi->drawing.area_xsize;
697 gi->drawing.item_ysize = gi->height / gi->drawing.area_ysize;
702 gi->drawing.item_xsize = va_arg(ap, int);
703 gi->drawing.item_ysize = va_arg(ap, int);
705 /* determine dependent values for drawing area gadget, if needed */
706 if (gi->width == 0 && gi->height == 0 &&
707 gi->drawing.area_xsize !=0 && gi->drawing.area_ysize !=0)
709 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
710 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
712 else if (gi->drawing.area_xsize == 0 && gi->drawing.area_ysize == 0 &&
713 gi->width != 0 && gi->height != 0)
715 gi->drawing.area_xsize = gi->width / gi->drawing.item_xsize;
716 gi->drawing.area_ysize = gi->height / gi->drawing.item_ysize;
720 case GDI_SCROLLBAR_ITEMS_MAX:
721 gi->scrollbar.items_max = va_arg(ap, int);
724 case GDI_SCROLLBAR_ITEMS_VISIBLE:
725 gi->scrollbar.items_visible = va_arg(ap, int);
728 case GDI_SCROLLBAR_ITEM_POSITION:
729 gi->scrollbar.item_position = va_arg(ap, int);
732 case GDI_CALLBACK_INFO:
733 gi->callback_info = va_arg(ap, gadget_function);
736 case GDI_CALLBACK_ACTION:
737 gi->callback_action = va_arg(ap, gadget_function);
741 Error(ERR_EXIT, "HandleGadgetTags(): unknown tag %d", tag);
744 tag = va_arg(ap, int); /* read next tag */
747 /* check if gadget is complete */
748 if (gi->type != GD_TYPE_DRAWING_AREA &&
749 (!gi->design[GD_BUTTON_UNPRESSED].bitmap ||
750 !gi->design[GD_BUTTON_PRESSED].bitmap))
751 Error(ERR_EXIT, "gadget incomplete (missing Bitmap)");
753 /* adjust gadget values in relation to other gadget values */
755 if (gi->type & GD_TYPE_TEXTINPUT)
757 int font_nr = gi->text.font_type;
758 int font_width = getFontWidth(font_nr);
759 int font_height = getFontHeight(font_nr);
760 int border_size = gi->border.size;
764 gi->width = 2 * border_size + (gi->text.size + 1) * font_width;
765 gi->height = 2 * border_size + font_height;
767 getFontCharSource(font_nr, FONT_ASCII_CURSOR, &src_bitmap, &src_x, &src_y);
768 src_x += font_width / 2;
769 src_y += font_height / 2;
770 gi->text.inverse_color = GetPixel(src_bitmap, src_x, src_y);
773 if (gi->type & GD_TYPE_SELECTBOX)
775 int font_nr = gi->selectbox.font_type;
776 int font_width = getFontWidth(font_nr);
777 int font_height = getFontHeight(font_nr);
778 int border_size = gi->border.size;
779 int button_size = gi->border.size_selectbutton;
783 gi->width = 2 * border_size + gi->text.size * font_width + button_size;
784 gi->height = 2 * border_size + font_height;
786 if (gi->selectbox.values == NULL)
787 Error(ERR_EXIT, "selectbox gadget incomplete (missing values array)");
789 gi->selectbox.num_values = 0;
790 while (gi->selectbox.values[gi->selectbox.num_values] != NULL)
791 gi->selectbox.num_values++;
793 /* calculate values for open selectbox */
794 gi->selectbox.width = gi->width;
795 gi->selectbox.height =
796 2 * border_size + gi->selectbox.num_values * font_height;
798 gi->selectbox.x = gi->x;
799 gi->selectbox.y = gi->y + gi->height;
800 if (gi->selectbox.y + gi->selectbox.height > gfx.real_sy + gfx.full_sysize)
801 gi->selectbox.y = gi->y - gi->selectbox.height;
802 if (gi->selectbox.y < 0)
803 gi->selectbox.y = gfx.real_sy + gfx.full_sysize - gi->selectbox.height;
805 getFontCharSource(font_nr, FONT_ASCII_CURSOR, &src_bitmap, &src_x, &src_y);
806 src_x += font_width / 2;
807 src_y += font_height / 2;
808 gi->selectbox.inverse_color = GetPixel(src_bitmap, src_x, src_y);
810 /* always start with closed selectbox */
811 gi->selectbox.open = FALSE;
814 if (gi->type & GD_TYPE_TEXTINPUT_NUMERIC)
816 struct GadgetTextInput *text = &gi->text;
817 int value = text->number_value;
819 text->number_value = (value < text->number_min ? text->number_min :
820 value > text->number_max ? text->number_max :
823 sprintf(text->value, "%d", text->number_value);
826 if (gi->type & GD_TYPE_SCROLLBAR)
828 struct GadgetScrollbar *gs = &gi->scrollbar;
830 if (gi->width == 0 || gi->height == 0 ||
831 gs->items_max == 0 || gs->items_visible == 0)
832 Error(ERR_EXIT, "scrollbar gadget incomplete (missing tags)");
834 /* calculate internal scrollbar values */
835 gs->size_max = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ?
836 gi->height : gi->width);
837 gs->size = gs->size_max * gs->items_visible / gs->items_max;
838 gs->position = gs->size_max * gs->item_position / gs->items_max;
839 gs->position_max = gs->size_max - gs->size;
840 gs->correction = gs->size_max / gs->items_max / 2;
842 /* finetuning for maximal right/bottom position */
843 if (gs->item_position == gs->items_max - gs->items_visible)
844 gs->position = gs->position_max;
848 void ModifyGadget(struct GadgetInfo *gi, int first_tag, ...)
852 va_start(ap, first_tag);
853 HandleGadgetTags(gi, first_tag, ap);
859 void RedrawGadget(struct GadgetInfo *gi)
862 DrawGadget(gi, gi->state, DG_DIRECT);
865 struct GadgetInfo *CreateGadget(int first_tag, ...)
867 struct GadgetInfo *new_gadget = checked_calloc(sizeof(struct GadgetInfo));
870 /* always start with reliable default values */
871 new_gadget->id = getNewGadgetID();
872 new_gadget->callback_info = default_callback_info;
873 new_gadget->callback_action = default_callback_action;
874 new_gadget->next = NULL;
876 va_start(ap, first_tag);
877 HandleGadgetTags(new_gadget, first_tag, ap);
880 /* insert new gadget into global gadget list */
881 if (gadget_list_last_entry)
883 gadget_list_last_entry->next = new_gadget;
884 gadget_list_last_entry = gadget_list_last_entry->next;
887 gadget_list_first_entry = gadget_list_last_entry = new_gadget;
892 void FreeGadget(struct GadgetInfo *gi)
894 struct GadgetInfo *gi_previous = gadget_list_first_entry;
896 while (gi_previous != NULL && gi_previous->next != gi)
897 gi_previous = gi_previous->next;
899 if (gi == gadget_list_first_entry)
900 gadget_list_first_entry = gi->next;
902 if (gi == gadget_list_last_entry)
903 gadget_list_last_entry = gi_previous;
905 if (gi_previous != NULL)
906 gi_previous->next = gi->next;
911 static void CheckRangeOfNumericInputGadget(struct GadgetInfo *gi)
913 if (gi->type != GD_TYPE_TEXTINPUT_NUMERIC)
916 gi->text.number_value = atoi(gi->text.value);
918 if (gi->text.number_value < gi->text.number_min)
919 gi->text.number_value = gi->text.number_min;
920 if (gi->text.number_value > gi->text.number_max)
921 gi->text.number_value = gi->text.number_max;
923 sprintf(gi->text.value, "%d", gi->text.number_value);
925 if (gi->text.cursor_position < 0)
926 gi->text.cursor_position = 0;
927 else if (gi->text.cursor_position > strlen(gi->text.value))
928 gi->text.cursor_position = strlen(gi->text.value);
931 /* global pointer to gadget actually in use (when mouse button pressed) */
932 static struct GadgetInfo *last_gi = NULL;
934 static void MapGadgetExt(struct GadgetInfo *gi, boolean redraw)
936 if (gi == NULL || gi->mapped)
942 DrawGadget(gi, DG_UNPRESSED, DG_BUFFERED);
945 void MapGadget(struct GadgetInfo *gi)
947 MapGadgetExt(gi, TRUE);
950 void UnmapGadget(struct GadgetInfo *gi)
952 if (gi == NULL || !gi->mapped)
961 #define MAX_NUM_GADGETS 1024
962 #define MULTIMAP_UNMAP (1 << 0)
963 #define MULTIMAP_REMAP (1 << 1)
964 #define MULTIMAP_REDRAW (1 << 2)
965 #define MULTIMAP_PLAYFIELD (1 << 3)
966 #define MULTIMAP_DOOR_1 (1 << 4)
967 #define MULTIMAP_DOOR_2 (1 << 5)
968 #define MULTIMAP_ALL (MULTIMAP_PLAYFIELD | \
972 static void MultiMapGadgets(int mode)
974 struct GadgetInfo *gi = gadget_list_first_entry;
975 static boolean map_state[MAX_NUM_GADGETS];
980 if ((mode & MULTIMAP_PLAYFIELD &&
981 gi->x < gfx.sx + gfx.sxsize) ||
982 (mode & MULTIMAP_DOOR_1 &&
983 gi->x >= gfx.dx && gi->y < gfx.dy + gfx.dysize) ||
984 (mode & MULTIMAP_DOOR_2 &&
985 gi->x >= gfx.dx && gi->y > gfx.dy + gfx.dysize) ||
986 (mode & MULTIMAP_ALL) == MULTIMAP_ALL)
988 if (mode & MULTIMAP_UNMAP)
990 map_state[map_count++ % MAX_NUM_GADGETS] = gi->mapped;
995 if (map_state[map_count++ % MAX_NUM_GADGETS])
996 MapGadgetExt(gi, (mode & MULTIMAP_REDRAW));
1004 void UnmapAllGadgets()
1006 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_UNMAP);
1009 void RemapAllGadgets()
1011 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_REMAP);
1014 boolean anyTextInputGadgetActive()
1016 return (last_gi && (last_gi->type & GD_TYPE_TEXTINPUT) && last_gi->mapped);
1019 boolean anySelectboxGadgetActive()
1021 return (last_gi && (last_gi->type & GD_TYPE_SELECTBOX) && last_gi->mapped);
1024 boolean anyTextGadgetActive()
1026 return (anyTextInputGadgetActive() || anySelectboxGadgetActive());
1029 void ClickOnGadget(struct GadgetInfo *gi, int button)
1031 /* simulate releasing mouse button over last gadget, if still pressed */
1033 HandleGadgets(-1, -1, 0);
1035 /* simulate pressing mouse button over specified gadget */
1036 HandleGadgets(gi->x, gi->y, button);
1038 /* simulate releasing mouse button over specified gadget */
1039 HandleGadgets(gi->x, gi->y, 0);
1042 void HandleGadgets(int mx, int my, int button)
1044 static struct GadgetInfo *last_info_gi = NULL;
1045 static unsigned long pressed_delay = 0;
1046 static int last_button = 0;
1047 static int last_mx = 0, last_my = 0;
1048 int scrollbar_mouse_pos = 0;
1049 struct GadgetInfo *new_gi, *gi;
1050 boolean press_event;
1051 boolean release_event;
1052 boolean mouse_moving;
1053 boolean gadget_pressed;
1054 boolean gadget_pressed_repeated;
1055 boolean gadget_moving;
1056 boolean gadget_moving_inside;
1057 boolean gadget_moving_off_borders;
1058 boolean gadget_released;
1059 boolean gadget_released_inside;
1060 boolean gadget_released_inside_select_line;
1061 boolean gadget_released_inside_select_area;
1062 boolean gadget_released_off_borders;
1063 boolean changed_position = FALSE;
1065 /* check if there are any gadgets defined */
1066 if (gadget_list_first_entry == NULL)
1069 /* check which gadget is under the mouse pointer */
1070 new_gi = getGadgetInfoFromMousePosition(mx, my);
1072 /* check if button state has changed since last invocation */
1073 press_event = (button != 0 && last_button == 0);
1074 release_event = (button == 0 && last_button != 0);
1075 last_button = button;
1077 /* check if mouse has been moved since last invocation */
1078 mouse_moving = ((mx != last_mx || my != last_my) && motion_status);
1082 /* special treatment for text and number input gadgets */
1083 if (anyTextInputGadgetActive() && button != 0 && !motion_status)
1085 struct GadgetInfo *gi = last_gi;
1087 if (new_gi == last_gi)
1089 int old_cursor_position = gi->text.cursor_position;
1091 /* if mouse button pressed inside activated text gadget, set cursor */
1092 gi->text.cursor_position =
1093 (mx - gi->x - gi->border.size) / getFontWidth(gi->text.font_type);
1095 if (gi->text.cursor_position < 0)
1096 gi->text.cursor_position = 0;
1097 else if (gi->text.cursor_position > strlen(gi->text.value))
1098 gi->text.cursor_position = strlen(gi->text.value);
1100 if (gi->text.cursor_position != old_cursor_position)
1101 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1105 /* if mouse button pressed outside text input gadget, deactivate it */
1106 CheckRangeOfNumericInputGadget(gi);
1107 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1109 gi->event.type = GD_EVENT_TEXT_LEAVING;
1111 if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
1112 gi->callback_action(gi);
1118 /* special treatment for selectbox gadgets */
1119 if (anySelectboxGadgetActive() && button != 0 && !motion_status)
1121 struct GadgetInfo *gi = last_gi;
1123 if (new_gi == last_gi)
1125 int old_index = gi->selectbox.current_index;
1127 /* if mouse button pressed inside activated selectbox, select value */
1128 if (my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
1129 gi->selectbox.current_index =
1130 (my - gi->selectbox.y - gi->border.size) /
1131 getFontWidth(gi->selectbox.font_type);
1133 if (gi->selectbox.current_index < 0)
1134 gi->selectbox.current_index = 0;
1135 else if (gi->selectbox.current_index > gi->selectbox.num_values - 1)
1136 gi->selectbox.current_index = gi->selectbox.num_values - 1;
1138 if (gi->selectbox.current_index != old_index)
1139 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1143 /* if mouse button pressed outside selectbox gadget, deactivate it */
1144 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1146 gi->event.type = GD_EVENT_TEXT_LEAVING;
1148 if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
1149 gi->callback_action(gi);
1156 (button != 0 && last_gi == NULL && new_gi != NULL && press_event);
1157 gadget_pressed_repeated =
1158 (button != 0 && last_gi != NULL && new_gi == last_gi);
1160 gadget_released = (release_event && last_gi != NULL);
1161 gadget_released_inside = (gadget_released && new_gi == last_gi);
1162 gadget_released_off_borders = (gadget_released && new_gi != last_gi);
1164 gadget_moving = (button != 0 && last_gi != NULL && mouse_moving);
1165 gadget_moving_inside = (gadget_moving && new_gi == last_gi);
1166 gadget_moving_off_borders = (gadget_moving && new_gi != last_gi);
1168 /* when handling selectbox, set additional state values */
1169 if (gadget_released_inside && (last_gi->type & GD_TYPE_SELECTBOX))
1171 struct GadgetInfo *gi = last_gi;
1173 gadget_released_inside_select_line =
1174 (mx >= gi->x && mx < gi->x + gi->width &&
1175 my >= gi->y && my < gi->y + gi->height);
1176 gadget_released_inside_select_area =
1177 (mx >= gi->selectbox.x && mx < gi->selectbox.x+gi->selectbox.width &&
1178 my >= gi->selectbox.y && my < gi->selectbox.y+gi->selectbox.height);
1182 gadget_released_inside_select_line = FALSE;
1183 gadget_released_inside_select_area = FALSE;
1186 /* if new gadget pressed, store this gadget */
1190 /* 'gi' is actually handled gadget */
1193 /* if gadget is scrollbar, choose mouse position value */
1194 if (gi && gi->type & GD_TYPE_SCROLLBAR)
1195 scrollbar_mouse_pos =
1196 (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? mx - gi->x : my - gi->y);
1198 /* if mouse button released, no gadget needs to be handled anymore */
1199 if (gadget_released)
1201 if ((last_gi->type & GD_TYPE_SELECTBOX) &&
1202 (gadget_released_inside_select_line ||
1203 gadget_released_off_borders)) /* selectbox stays open */
1204 gi->selectbox.stay_open = TRUE;
1205 else if (!(last_gi->type & GD_TYPE_TEXTINPUT)) /* text input stays open */
1209 /* modify event position values even if no gadget is pressed */
1210 if (button == 0 && !release_event)
1215 int last_x = gi->event.x;
1216 int last_y = gi->event.y;
1218 gi->event.x = mx - gi->x;
1219 gi->event.y = my - gi->y;
1221 if (gi->type == GD_TYPE_DRAWING_AREA)
1223 gi->event.x /= gi->drawing.item_xsize;
1224 gi->event.y /= gi->drawing.item_ysize;
1226 if (last_x != gi->event.x || last_y != gi->event.y)
1227 changed_position = TRUE;
1229 else if (gi->type & GD_TYPE_SELECTBOX)
1231 int old_index = gi->selectbox.current_index;
1233 /* if mouse moving inside activated selectbox, select value */
1234 if (my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
1235 gi->selectbox.current_index =
1236 (my - gi->selectbox.y - gi->border.size) /
1237 getFontWidth(gi->selectbox.font_type);
1239 if (gi->selectbox.current_index < 0)
1240 gi->selectbox.current_index = 0;
1241 else if (gi->selectbox.current_index > gi->selectbox.num_values - 1)
1242 gi->selectbox.current_index = gi->selectbox.num_values - 1;
1244 if (gi->selectbox.current_index != old_index)
1245 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1249 /* handle gadget popup info text */
1250 if (last_info_gi != new_gi ||
1251 (new_gi && new_gi->type == GD_TYPE_DRAWING_AREA && changed_position))
1253 if (new_gi != NULL && (button == 0 || new_gi == last_gi))
1255 new_gi->event.type = GD_EVENT_INFO_ENTERING;
1256 new_gi->callback_info(new_gi);
1258 else if (last_info_gi != NULL)
1260 last_info_gi->event.type = GD_EVENT_INFO_LEAVING;
1261 last_info_gi->callback_info(last_info_gi);
1264 default_callback_info(NULL);
1266 printf("It seems that we are leaving gadget [%s]!\n",
1267 (last_info_gi != NULL &&
1268 last_info_gi->info_text != NULL ?
1269 last_info_gi->info_text : ""));
1273 last_info_gi = new_gi;
1278 if (gi->type == GD_TYPE_CHECK_BUTTON)
1280 gi->checked = !gi->checked;
1282 else if (gi->type == GD_TYPE_RADIO_BUTTON)
1284 struct GadgetInfo *rgi = gadget_list_first_entry;
1289 rgi->type == GD_TYPE_RADIO_BUTTON &&
1290 rgi->radio_nr == gi->radio_nr &&
1293 rgi->checked = FALSE;
1294 DrawGadget(rgi, DG_UNPRESSED, DG_DIRECT);
1302 else if (gi->type & GD_TYPE_SCROLLBAR)
1306 if (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL)
1317 if (mpos >= gpos + gi->scrollbar.position &&
1318 mpos < gpos + gi->scrollbar.position + gi->scrollbar.size)
1320 /* drag scrollbar */
1321 gi->scrollbar.drag_position =
1322 scrollbar_mouse_pos - gi->scrollbar.position;
1326 /* click scrollbar one scrollbar length up/left or down/right */
1328 struct GadgetScrollbar *gs = &gi->scrollbar;
1329 int old_item_position = gs->item_position;
1331 changed_position = FALSE;
1333 gs->item_position +=
1334 gs->items_visible * (mpos < gpos + gi->scrollbar.position ? -1 : +1);
1336 if (gs->item_position < 0)
1337 gs->item_position = 0;
1338 if (gs->item_position > gs->items_max - gs->items_visible)
1339 gs->item_position = gs->items_max - gs->items_visible;
1341 if (old_item_position != gs->item_position)
1343 gi->event.item_position = gs->item_position;
1344 changed_position = TRUE;
1347 ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, gs->item_position,
1350 gi->state = GD_BUTTON_UNPRESSED;
1351 gi->event.type = GD_EVENT_MOVING;
1352 gi->event.off_borders = FALSE;
1354 if (gi->event_mask & GD_EVENT_MOVING && changed_position)
1355 gi->callback_action(gi);
1357 /* don't handle this scrollbar anymore while mouse button pressed */
1364 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1366 gi->state = GD_BUTTON_PRESSED;
1367 gi->event.type = GD_EVENT_PRESSED;
1368 gi->event.button = button;
1369 gi->event.off_borders = FALSE;
1371 /* initialize delay counter */
1372 DelayReached(&pressed_delay, 0);
1374 if (gi->event_mask & GD_EVENT_PRESSED)
1375 gi->callback_action(gi);
1378 if (gadget_pressed_repeated)
1380 gi->event.type = GD_EVENT_PRESSED;
1382 if (gi->event_mask & GD_EVENT_REPEATED &&
1383 DelayReached(&pressed_delay, GADGET_FRAME_DELAY))
1384 gi->callback_action(gi);
1389 if (gi->type & GD_TYPE_BUTTON)
1391 if (gadget_moving_inside && gi->state == GD_BUTTON_UNPRESSED)
1392 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1393 else if (gadget_moving_off_borders && gi->state == GD_BUTTON_PRESSED)
1394 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1396 else if (gi->type & GD_TYPE_SELECTBOX)
1398 int old_index = gi->selectbox.current_index;
1400 /* if mouse moving inside activated selectbox, select value */
1401 if (my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
1402 gi->selectbox.current_index =
1403 (my - gi->selectbox.y - gi->border.size) /
1404 getFontWidth(gi->selectbox.font_type);
1406 if (gi->selectbox.current_index < 0)
1407 gi->selectbox.current_index = 0;
1408 else if (gi->selectbox.current_index > gi->selectbox.num_values - 1)
1409 gi->selectbox.current_index = gi->selectbox.num_values - 1;
1411 if (gi->selectbox.current_index != old_index)
1412 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1414 else if (gi->type & GD_TYPE_SCROLLBAR)
1416 struct GadgetScrollbar *gs = &gi->scrollbar;
1417 int old_item_position = gs->item_position;
1419 gs->position = scrollbar_mouse_pos - gs->drag_position;
1421 if (gs->position < 0)
1423 if (gs->position > gs->position_max)
1424 gs->position = gs->position_max;
1427 gs->items_max * (gs->position + gs->correction) / gs->size_max;
1429 if (gs->item_position < 0)
1430 gs->item_position = 0;
1431 if (gs->item_position > gs->items_max - 1)
1432 gs->item_position = gs->items_max - 1;
1434 if (old_item_position != gs->item_position)
1436 gi->event.item_position = gs->item_position;
1437 changed_position = TRUE;
1440 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1443 gi->state = (gadget_moving_inside || gi->type & GD_TYPE_SCROLLBAR ?
1444 GD_BUTTON_PRESSED : GD_BUTTON_UNPRESSED);
1445 gi->event.type = GD_EVENT_MOVING;
1446 gi->event.off_borders = gadget_moving_off_borders;
1448 if (gi->event_mask & GD_EVENT_MOVING && changed_position &&
1449 (gadget_moving_inside || gi->event_mask & GD_EVENT_OFF_BORDERS))
1450 gi->callback_action(gi);
1453 if (gadget_released_inside)
1455 boolean deactivate_gadget = TRUE;
1457 if (gi->type & GD_TYPE_SELECTBOX)
1459 if (gadget_released_inside_select_line ||
1460 gadget_released_off_borders) /* selectbox stays open */
1461 deactivate_gadget = FALSE;
1463 gi->selectbox.index = gi->selectbox.current_index;
1466 if (deactivate_gadget &&
1467 !(gi->type & GD_TYPE_TEXTINPUT)) /* text input stays open */
1468 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1470 gi->state = GD_BUTTON_UNPRESSED;
1471 gi->event.type = GD_EVENT_RELEASED;
1473 if ((gi->event_mask & GD_EVENT_RELEASED) && deactivate_gadget)
1474 gi->callback_action(gi);
1477 if (gadget_released_off_borders)
1479 if (gi->type & GD_TYPE_SCROLLBAR)
1480 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1482 gi->event.type = GD_EVENT_RELEASED;
1484 if (gi->event_mask & GD_EVENT_RELEASED &&
1485 gi->event_mask & GD_EVENT_OFF_BORDERS)
1486 gi->callback_action(gi);
1490 void HandleGadgetsKeyInput(Key key)
1492 struct GadgetInfo *gi = last_gi;
1494 if (gi == NULL || !gi->mapped ||
1495 !((gi->type & GD_TYPE_TEXTINPUT) || (gi->type & GD_TYPE_SELECTBOX)))
1498 if (key == KSYM_Return) /* valid for both text input and selectbox */
1500 if (gi->type & GD_TYPE_TEXTINPUT)
1501 CheckRangeOfNumericInputGadget(gi);
1502 else if (gi->type & GD_TYPE_SELECTBOX)
1503 gi->selectbox.index = gi->selectbox.current_index;
1505 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1507 gi->event.type = GD_EVENT_TEXT_RETURN;
1509 if (gi->event_mask & GD_EVENT_TEXT_RETURN)
1510 gi->callback_action(gi);
1514 else if (gi->type & GD_TYPE_TEXTINPUT) /* only valid for text input */
1516 char text[MAX_GADGET_TEXTSIZE];
1517 int text_length = strlen(gi->text.value);
1518 int cursor_pos = gi->text.cursor_position;
1519 char letter = getCharFromKey(key);
1520 boolean legal_letter = (gi->type == GD_TYPE_TEXTINPUT_NUMERIC ?
1521 letter >= '0' && letter <= '9' :
1524 if (legal_letter && text_length < gi->text.size)
1526 strcpy(text, gi->text.value);
1527 strcpy(&gi->text.value[cursor_pos + 1], &text[cursor_pos]);
1528 gi->text.value[cursor_pos] = letter;
1529 gi->text.cursor_position++;
1531 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1533 else if (key == KSYM_Left && cursor_pos > 0)
1535 gi->text.cursor_position--;
1536 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1538 else if (key == KSYM_Right && cursor_pos < text_length)
1540 gi->text.cursor_position++;
1541 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1543 else if (key == KSYM_BackSpace && cursor_pos > 0)
1545 strcpy(text, gi->text.value);
1546 strcpy(&gi->text.value[cursor_pos - 1], &text[cursor_pos]);
1547 gi->text.cursor_position--;
1548 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1550 else if (key == KSYM_Delete && cursor_pos < text_length)
1552 strcpy(text, gi->text.value);
1553 strcpy(&gi->text.value[cursor_pos], &text[cursor_pos + 1]);
1554 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1557 else if (gi->type & GD_TYPE_SELECTBOX) /* only valid for selectbox */
1559 int index = gi->selectbox.current_index;
1560 int num_values = gi->selectbox.num_values;
1562 if (key == KSYM_Up && index > 0)
1564 gi->selectbox.current_index--;
1565 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1567 else if (key == KSYM_Down && index < num_values - 1)
1569 gi->selectbox.current_index++;
1570 DrawGadget(gi, DG_PRESSED, DG_DIRECT);