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, FONT_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 /* draw cursor, if active */
178 gi->x + border + gi->text.cursor_position * font_width,
179 gi->y + border, cursor_string,
180 font_type, FONT_MASKED);
184 case GD_TYPE_SELECTBOX:
187 char text[MAX_GADGET_TEXTSIZE + 1];
188 int font_type = gi->selectbox.font_type;
189 int font_width = getFontWidth(font_type);
190 int font_height = getFontHeight(font_type);
191 int border = gi->border.size;
192 int button = gi->border.size_selectbutton;
193 int width_inner = gi->border.width - button - 2 * border;
194 int box_width = gi->selectbox.width;
195 int box_height = gi->selectbox.height;
197 /* left part of gadget */
198 BlitBitmapOnBackground(gd->bitmap, drawto,
199 gd->x, gd->y, border, gi->height, gi->x, gi->y);
201 /* middle part of gadget */
202 for (i=0; i < gi->selectbox.size; i++)
203 BlitBitmapOnBackground(gd->bitmap, drawto,
204 gd->x + border, gd->y, font_width, gi->height,
205 gi->x + border + i * font_width, gi->y);
207 /* button part of gadget */
208 BlitBitmapOnBackground(gd->bitmap, drawto,
209 gd->x + border + width_inner, gd->y,
211 gi->x + gi->width - border - button, gi->y);
213 /* right part of gadget */
214 BlitBitmapOnBackground(gd->bitmap, drawto,
215 gd->x + gi->border.width - border, gd->y,border,
216 gi->height, gi->x + gi->width - border, gi->y);
219 strncpy(text, gi->selectbox.values[gi->selectbox.index],
221 text[gi->selectbox.size] = '\0';
223 /* gadget text value */
224 DrawTextExt(drawto, gi->x + border, gi->y + border, text,
225 font_type, FONT_MASKED);
229 if (!gi->selectbox.open)
231 gi->selectbox.open = TRUE;
232 gi->selectbox.stay_open = FALSE;
233 gi->selectbox.current_index = gi->selectbox.index;
235 /* save background under selectbox */
236 BlitBitmap(drawto, gfx.field_save_buffer,
237 gi->selectbox.x, gi->selectbox.y,
238 gi->selectbox.width, gi->selectbox.height,
239 gi->selectbox.x, gi->selectbox.y);
242 /* draw open selectbox */
244 /* top left part of gadget border */
245 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
247 gi->selectbox.x, gi->selectbox.y);
249 /* top middle part of gadget border */
250 for (i=0; i < gi->selectbox.size; i++)
251 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border, gd->y,
253 gi->selectbox.x + border + i * font_width,
256 /* top button part of gadget border */
257 BlitBitmapOnBackground(gd->bitmap, drawto,
258 gd->x + border + width_inner, gd->y,
260 gi->selectbox.x + box_width - border - button,
263 /* top right part of gadget border */
264 BlitBitmapOnBackground(gd->bitmap, drawto,
265 gd->x + gi->border.width - border, gd->y,
267 gi->selectbox.x + box_width - border,
270 /* left and right part of gadget border for each row */
271 for (i=0; i < gi->selectbox.num_values; i++)
273 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y + border,
276 gi->selectbox.y + border + i * font_height);
277 BlitBitmapOnBackground(gd->bitmap, drawto,
278 gd->x + gi->border.width - border,
281 gi->selectbox.x + box_width - border,
282 gi->selectbox.y + border + i * font_height);
285 /* bottom left part of gadget border */
286 BlitBitmapOnBackground(gd->bitmap, drawto,
287 gd->x, gd->y + gi->height - border,
290 gi->selectbox.y + box_height - border);
292 /* bottom middle part of gadget border */
293 for (i=0; i < gi->selectbox.size; i++)
294 BlitBitmapOnBackground(gd->bitmap, drawto,
295 gd->x + border, gd->y + gi->height - border,
297 gi->selectbox.x + border + i * font_width,
298 gi->selectbox.y + box_height - border);
300 /* bottom button part of gadget border */
301 BlitBitmapOnBackground(gd->bitmap, drawto,
302 gd->x + border + width_inner,
303 gd->y + gi->height - border,
305 gi->selectbox.x + box_width - border - button,
306 gi->selectbox.y + box_height - border);
308 /* bottom right part of gadget border */
309 BlitBitmapOnBackground(gd->bitmap, drawto,
310 gd->x + gi->border.width - border,
311 gd->y + gi->height - border,
313 gi->selectbox.x + box_width - border,
314 gi->selectbox.y + box_height - border);
316 ClearRectangleOnBackground(drawto,
317 gi->selectbox.x + border,
318 gi->selectbox.y + border,
319 gi->selectbox.width - 2 * border,
320 gi->selectbox.height - 2 * border);
322 /* selectbox text values */
323 for (i=0; i < gi->selectbox.num_values; i++)
325 if (i == gi->selectbox.current_index)
327 DrawRectangle(drawto,
328 gi->selectbox.x + border,
329 gi->selectbox.y + border + i * font_height,
330 gi->selectbox.width - 2 * border, font_height,
331 gi->selectbox.reverse_color);
334 strncpy(&text[1], gi->selectbox.values[i], gi->selectbox.size);
335 text[1 + gi->selectbox.size] = '\0';
339 strncpy(text, gi->selectbox.values[i], gi->selectbox.size);
340 text[gi->selectbox.size] = '\0';
344 gi->selectbox.x + border,
345 gi->selectbox.y + border + i * font_height, text,
346 font_type, FONT_MASKED);
349 redraw_selectbox = TRUE;
351 else if (gi->selectbox.open)
353 gi->selectbox.open = FALSE;
355 /* redraw closed selectbox */
356 DrawGadget(gi, FALSE, FALSE);
358 /* restore background under selectbox */
359 BlitBitmap(gfx.field_save_buffer, drawto,
360 gi->selectbox.x, gi->selectbox.y,
361 gi->selectbox.width, gi->selectbox.height,
362 gi->selectbox.x, gi->selectbox.y);
364 redraw_selectbox = TRUE;
369 case GD_TYPE_SCROLLBAR_VERTICAL:
373 int ypos = gi->y + gi->scrollbar.position;
374 int design_full = gi->width;
375 int design_body = design_full - 2 * gi->border.size;
376 int size_full = gi->scrollbar.size;
377 int size_body = size_full - 2 * gi->border.size;
378 int num_steps = size_body / design_body;
379 int step_size_remain = size_body - num_steps * design_body;
381 /* clear scrollbar area */
382 ClearRectangleOnBackground(backbuffer, gi->x, gi->y,
383 gi->width, gi->height);
385 /* upper part of gadget */
386 BlitBitmapOnBackground(gd->bitmap, drawto,
388 gi->width, gi->border.size,
391 /* middle part of gadget */
392 for (i=0; i<num_steps; i++)
393 BlitBitmapOnBackground(gd->bitmap, drawto,
394 gd->x, gd->y + gi->border.size,
395 gi->width, design_body,
397 ypos + gi->border.size + i * design_body);
399 /* remaining middle part of gadget */
400 if (step_size_remain > 0)
401 BlitBitmapOnBackground(gd->bitmap, drawto,
402 gd->x, gd->y + gi->border.size,
403 gi->width, step_size_remain,
405 ypos + gi->border.size
406 + num_steps * design_body);
408 /* lower part of gadget */
409 BlitBitmapOnBackground(gd->bitmap, drawto,
410 gd->x, gd->y + design_full - gi->border.size,
411 gi->width, gi->border.size,
412 xpos, ypos + size_full - gi->border.size);
416 case GD_TYPE_SCROLLBAR_HORIZONTAL:
419 int xpos = gi->x + gi->scrollbar.position;
421 int design_full = gi->height;
422 int design_body = design_full - 2 * gi->border.size;
423 int size_full = gi->scrollbar.size;
424 int size_body = size_full - 2 * gi->border.size;
425 int num_steps = size_body / design_body;
426 int step_size_remain = size_body - num_steps * design_body;
428 /* clear scrollbar area */
429 ClearRectangleOnBackground(backbuffer, gi->x, gi->y,
430 gi->width, gi->height);
432 /* left part of gadget */
433 BlitBitmapOnBackground(gd->bitmap, drawto,
435 gi->border.size, gi->height,
438 /* middle part of gadget */
439 for (i=0; i<num_steps; i++)
440 BlitBitmapOnBackground(gd->bitmap, drawto,
441 gd->x + gi->border.size, gd->y,
442 design_body, gi->height,
443 xpos + gi->border.size + i * design_body,
446 /* remaining middle part of gadget */
447 if (step_size_remain > 0)
448 BlitBitmapOnBackground(gd->bitmap, drawto,
449 gd->x + gi->border.size, gd->y,
450 step_size_remain, gi->height,
451 xpos + gi->border.size
452 + num_steps * design_body,
455 /* right part of gadget */
456 BlitBitmapOnBackground(gd->bitmap, drawto,
457 gd->x + design_full - gi->border.size, gd->y,
458 gi->border.size, gi->height,
459 xpos + size_full - gi->border.size, ypos);
469 BlitBitmap(drawto, window,
470 gi->x, gi->y, gi->width, gi->height, gi->x, gi->y);
472 if (gi->type == GD_TYPE_SELECTBOX && redraw_selectbox)
473 BlitBitmap(drawto, window,
474 gi->selectbox.x, gi->selectbox.y,
475 gi->selectbox.width, gi->selectbox.height,
476 gi->selectbox.x, gi->selectbox.y);
479 redraw_mask |= (gi->x < gfx.sx + gfx.sxsize ? REDRAW_FIELD :
480 gi->y < gfx.dy + gfx.dysize ? REDRAW_DOOR_1 :
481 gi->y > gfx.vy ? REDRAW_DOOR_2 : REDRAW_DOOR_3);
484 static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
488 while (tag != GDI_END)
493 gi->custom_id = va_arg(ap, int);
496 case GDI_CUSTOM_TYPE_ID:
497 gi->custom_type_id = va_arg(ap, int);
502 int max_textsize = MAX_INFO_TEXTSIZE - 1;
503 char *text = va_arg(ap, char *);
506 strncpy(gi->info_text, text, max_textsize);
510 gi->info_text[max_textsize] = '\0';
515 gi->x = va_arg(ap, int);
519 gi->y = va_arg(ap, int);
523 gi->width = va_arg(ap, int);
527 gi->height = va_arg(ap, int);
531 gi->type = va_arg(ap, unsigned long);
535 gi->state = va_arg(ap, unsigned long);
539 /* take care here: "boolean" is typedef'ed as "unsigned char",
540 which gets promoted to "int" */
541 gi->checked = (boolean)va_arg(ap, int);
545 gi->radio_nr = va_arg(ap, unsigned long);
548 case GDI_NUMBER_VALUE:
549 gi->text.number_value = va_arg(ap, long);
550 sprintf(gi->text.value, "%d", gi->text.number_value);
551 gi->text.cursor_position = strlen(gi->text.value);
555 gi->text.number_min = va_arg(ap, long);
556 if (gi->text.number_value < gi->text.number_min)
558 gi->text.number_value = gi->text.number_min;
559 sprintf(gi->text.value, "%d", gi->text.number_value);
564 gi->text.number_max = va_arg(ap, long);
565 if (gi->text.number_value > gi->text.number_max)
567 gi->text.number_value = gi->text.number_max;
568 sprintf(gi->text.value, "%d", gi->text.number_value);
574 int max_textsize = MAX_GADGET_TEXTSIZE;
577 max_textsize = MIN(gi->text.size, MAX_GADGET_TEXTSIZE - 1);
579 strncpy(gi->text.value, va_arg(ap, char *), max_textsize);
580 gi->text.value[max_textsize] = '\0';
581 gi->text.cursor_position = strlen(gi->text.value);
587 int tag_value = va_arg(ap, int);
588 int max_textsize = MIN(tag_value, MAX_GADGET_TEXTSIZE - 1);
590 gi->text.size = max_textsize;
591 gi->text.value[max_textsize] = '\0';
593 /* same tag also used for selectbox definition */
594 gi->selectbox.size = gi->text.size;
599 gi->text.font_type = va_arg(ap, int);
601 /* same tag also used for selectbox definition */
602 gi->selectbox.font_type = gi->text.font_type;
605 case GDI_SELECTBOX_VALUES:
606 gi->selectbox.values = va_arg(ap, const char **);
609 case GDI_SELECTBOX_INDEX:
610 gi->selectbox.index = va_arg(ap, int);
613 case GDI_DESIGN_UNPRESSED:
614 gi->design[GD_BUTTON_UNPRESSED].bitmap = va_arg(ap, Bitmap *);
615 gi->design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
616 gi->design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
619 case GDI_DESIGN_PRESSED:
620 gi->design[GD_BUTTON_PRESSED].bitmap = va_arg(ap, Bitmap *);
621 gi->design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
622 gi->design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
625 case GDI_ALT_DESIGN_UNPRESSED:
626 gi->alt_design[GD_BUTTON_UNPRESSED].bitmap= va_arg(ap, Bitmap *);
627 gi->alt_design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
628 gi->alt_design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
631 case GDI_ALT_DESIGN_PRESSED:
632 gi->alt_design[GD_BUTTON_PRESSED].bitmap = va_arg(ap, Bitmap *);
633 gi->alt_design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
634 gi->alt_design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
637 case GDI_BORDER_SIZE:
638 gi->border.size = va_arg(ap, int);
641 case GDI_BORDER_SIZE_SELECTBUTTON:
642 gi->border.size_selectbutton = va_arg(ap, int);
645 case GDI_TEXTINPUT_DESIGN_WIDTH:
646 gi->border.width = va_arg(ap, int);
649 case GDI_DECORATION_DESIGN:
650 gi->deco.design.bitmap = va_arg(ap, Bitmap *);
651 gi->deco.design.x = va_arg(ap, int);
652 gi->deco.design.y = va_arg(ap, int);
655 case GDI_DECORATION_POSITION:
656 gi->deco.x = va_arg(ap, int);
657 gi->deco.y = va_arg(ap, int);
660 case GDI_DECORATION_SIZE:
661 gi->deco.width = va_arg(ap, int);
662 gi->deco.height = va_arg(ap, int);
665 case GDI_DECORATION_SHIFTING:
666 gi->deco.xshift = va_arg(ap, int);
667 gi->deco.yshift = va_arg(ap, int);
671 gi->event_mask = va_arg(ap, unsigned long);
675 gi->drawing.area_xsize = va_arg(ap, int);
676 gi->drawing.area_ysize = va_arg(ap, int);
678 /* determine dependent values for drawing area gadget, if needed */
679 if (gi->width == 0 && gi->height == 0 &&
680 gi->drawing.item_xsize !=0 && gi->drawing.item_ysize !=0)
682 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
683 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
685 else if (gi->drawing.item_xsize == 0 && gi->drawing.item_ysize == 0 &&
686 gi->width != 0 && gi->height != 0)
688 gi->drawing.item_xsize = gi->width / gi->drawing.area_xsize;
689 gi->drawing.item_ysize = gi->height / gi->drawing.area_ysize;
694 gi->drawing.item_xsize = va_arg(ap, int);
695 gi->drawing.item_ysize = va_arg(ap, int);
697 /* determine dependent values for drawing area gadget, if needed */
698 if (gi->width == 0 && gi->height == 0 &&
699 gi->drawing.area_xsize !=0 && gi->drawing.area_ysize !=0)
701 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
702 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
704 else if (gi->drawing.area_xsize == 0 && gi->drawing.area_ysize == 0 &&
705 gi->width != 0 && gi->height != 0)
707 gi->drawing.area_xsize = gi->width / gi->drawing.item_xsize;
708 gi->drawing.area_ysize = gi->height / gi->drawing.item_ysize;
712 case GDI_SCROLLBAR_ITEMS_MAX:
713 gi->scrollbar.items_max = va_arg(ap, int);
716 case GDI_SCROLLBAR_ITEMS_VISIBLE:
717 gi->scrollbar.items_visible = va_arg(ap, int);
720 case GDI_SCROLLBAR_ITEM_POSITION:
721 gi->scrollbar.item_position = va_arg(ap, int);
724 case GDI_CALLBACK_INFO:
725 gi->callback_info = va_arg(ap, gadget_function);
728 case GDI_CALLBACK_ACTION:
729 gi->callback_action = va_arg(ap, gadget_function);
733 Error(ERR_EXIT, "HandleGadgetTags(): unknown tag %d", tag);
736 tag = va_arg(ap, int); /* read next tag */
739 /* check if gadget is complete */
740 if (gi->type != GD_TYPE_DRAWING_AREA &&
741 (!gi->design[GD_BUTTON_UNPRESSED].bitmap ||
742 !gi->design[GD_BUTTON_PRESSED].bitmap))
743 Error(ERR_EXIT, "gadget incomplete (missing Bitmap)");
745 /* adjust gadget values in relation to other gadget values */
747 if (gi->type & GD_TYPE_TEXTINPUT)
749 int font_width = getFontWidth(gi->text.font_type);
750 int font_height = getFontHeight(gi->text.font_type);
751 int border_size = gi->border.size;
753 gi->width = 2 * border_size + (gi->text.size + 1) * font_width;
754 gi->height = 2 * border_size + font_height;
757 if (gi->type & GD_TYPE_SELECTBOX)
759 int font_nr = gi->selectbox.font_type;
760 int font_bitmap_id = gfx.select_font_function(font_nr);
761 struct FontBitmapInfo *font = &gfx.font_bitmap_info[font_bitmap_id];
762 int font_width = getFontWidth(gi->selectbox.font_type);
763 int font_height = getFontHeight(gi->selectbox.font_type);
764 int border_size = gi->border.size;
765 int button_size = gi->border.size_selectbutton;
768 gi->width = 2 * border_size + gi->text.size * font_width + button_size;
769 gi->height = 2 * border_size + font_height;
771 if (gi->selectbox.values == NULL)
772 Error(ERR_EXIT, "selectbox gadget incomplete (missing values array)");
774 gi->selectbox.num_values = 0;
775 while (gi->selectbox.values[gi->selectbox.num_values] != NULL)
776 gi->selectbox.num_values++;
778 /* calculate values for open selectbox */
779 gi->selectbox.width = gi->width;
780 gi->selectbox.height =
781 2 * border_size + gi->selectbox.num_values * font_height;
783 gi->selectbox.x = gi->x;
784 gi->selectbox.y = gi->y + gi->height;
785 if (gi->selectbox.y + gi->selectbox.height > gfx.real_sy + gfx.full_sysize)
786 gi->selectbox.y = gi->y - gi->selectbox.height;
787 if (gi->selectbox.y < 0)
788 gi->selectbox.y = gfx.real_sy + gfx.full_sysize - gi->selectbox.height;
790 if (!getFontChar(font_nr, '|', &src_x, &src_y))
791 Error(ERR_EXIT, "selectbox gadget incomplete (cannot get cursor)");
793 gi->selectbox.reverse_color = GetPixel(font->bitmap, src_x, src_y);
795 /* always start with closed selectbox */
796 gi->selectbox.open = FALSE;
799 if (gi->type & GD_TYPE_TEXTINPUT_NUMERIC)
801 struct GadgetTextInput *text = &gi->text;
802 int value = text->number_value;
804 text->number_value = (value < text->number_min ? text->number_min :
805 value > text->number_max ? text->number_max :
808 sprintf(text->value, "%d", text->number_value);
811 if (gi->type & GD_TYPE_SCROLLBAR)
813 struct GadgetScrollbar *gs = &gi->scrollbar;
815 if (gi->width == 0 || gi->height == 0 ||
816 gs->items_max == 0 || gs->items_visible == 0)
817 Error(ERR_EXIT, "scrollbar gadget incomplete (missing tags)");
819 /* calculate internal scrollbar values */
820 gs->size_max = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ?
821 gi->height : gi->width);
822 gs->size = gs->size_max * gs->items_visible / gs->items_max;
823 gs->position = gs->size_max * gs->item_position / gs->items_max;
824 gs->position_max = gs->size_max - gs->size;
825 gs->correction = gs->size_max / gs->items_max / 2;
827 /* finetuning for maximal right/bottom position */
828 if (gs->item_position == gs->items_max - gs->items_visible)
829 gs->position = gs->position_max;
833 void ModifyGadget(struct GadgetInfo *gi, int first_tag, ...)
837 va_start(ap, first_tag);
838 HandleGadgetTags(gi, first_tag, ap);
844 void RedrawGadget(struct GadgetInfo *gi)
847 DrawGadget(gi, gi->state, DG_DIRECT);
850 struct GadgetInfo *CreateGadget(int first_tag, ...)
852 struct GadgetInfo *new_gadget = checked_calloc(sizeof(struct GadgetInfo));
855 /* always start with reliable default values */
856 new_gadget->id = getNewGadgetID();
857 new_gadget->callback_info = default_callback_info;
858 new_gadget->callback_action = default_callback_action;
859 new_gadget->next = NULL;
861 va_start(ap, first_tag);
862 HandleGadgetTags(new_gadget, first_tag, ap);
865 /* insert new gadget into global gadget list */
866 if (gadget_list_last_entry)
868 gadget_list_last_entry->next = new_gadget;
869 gadget_list_last_entry = gadget_list_last_entry->next;
872 gadget_list_first_entry = gadget_list_last_entry = new_gadget;
877 void FreeGadget(struct GadgetInfo *gi)
879 struct GadgetInfo *gi_previous = gadget_list_first_entry;
881 while (gi_previous != NULL && gi_previous->next != gi)
882 gi_previous = gi_previous->next;
884 if (gi == gadget_list_first_entry)
885 gadget_list_first_entry = gi->next;
887 if (gi == gadget_list_last_entry)
888 gadget_list_last_entry = gi_previous;
890 if (gi_previous != NULL)
891 gi_previous->next = gi->next;
896 static void CheckRangeOfNumericInputGadget(struct GadgetInfo *gi)
898 if (gi->type != GD_TYPE_TEXTINPUT_NUMERIC)
901 gi->text.number_value = atoi(gi->text.value);
903 if (gi->text.number_value < gi->text.number_min)
904 gi->text.number_value = gi->text.number_min;
905 if (gi->text.number_value > gi->text.number_max)
906 gi->text.number_value = gi->text.number_max;
908 sprintf(gi->text.value, "%d", gi->text.number_value);
910 if (gi->text.cursor_position < 0)
911 gi->text.cursor_position = 0;
912 else if (gi->text.cursor_position > strlen(gi->text.value))
913 gi->text.cursor_position = strlen(gi->text.value);
916 /* global pointer to gadget actually in use (when mouse button pressed) */
917 static struct GadgetInfo *last_gi = NULL;
919 static void MapGadgetExt(struct GadgetInfo *gi, boolean redraw)
921 if (gi == NULL || gi->mapped)
927 DrawGadget(gi, DG_UNPRESSED, DG_BUFFERED);
930 void MapGadget(struct GadgetInfo *gi)
932 MapGadgetExt(gi, TRUE);
935 void UnmapGadget(struct GadgetInfo *gi)
937 if (gi == NULL || !gi->mapped)
946 #define MAX_NUM_GADGETS 1024
947 #define MULTIMAP_UNMAP (1 << 0)
948 #define MULTIMAP_REMAP (1 << 1)
949 #define MULTIMAP_REDRAW (1 << 2)
950 #define MULTIMAP_PLAYFIELD (1 << 3)
951 #define MULTIMAP_DOOR_1 (1 << 4)
952 #define MULTIMAP_DOOR_2 (1 << 5)
953 #define MULTIMAP_ALL (MULTIMAP_PLAYFIELD | \
957 static void MultiMapGadgets(int mode)
959 struct GadgetInfo *gi = gadget_list_first_entry;
960 static boolean map_state[MAX_NUM_GADGETS];
965 if ((mode & MULTIMAP_PLAYFIELD &&
966 gi->x < gfx.sx + gfx.sxsize) ||
967 (mode & MULTIMAP_DOOR_1 &&
968 gi->x >= gfx.dx && gi->y < gfx.dy + gfx.dysize) ||
969 (mode & MULTIMAP_DOOR_2 &&
970 gi->x >= gfx.dx && gi->y > gfx.dy + gfx.dysize) ||
971 (mode & MULTIMAP_ALL) == MULTIMAP_ALL)
973 if (mode & MULTIMAP_UNMAP)
975 map_state[map_count++ % MAX_NUM_GADGETS] = gi->mapped;
980 if (map_state[map_count++ % MAX_NUM_GADGETS])
981 MapGadgetExt(gi, (mode & MULTIMAP_REDRAW));
989 void UnmapAllGadgets()
991 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_UNMAP);
994 void RemapAllGadgets()
996 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_REMAP);
999 boolean anyTextInputGadgetActive()
1001 return (last_gi && (last_gi->type & GD_TYPE_TEXTINPUT) && last_gi->mapped);
1004 boolean anySelectboxGadgetActive()
1006 return (last_gi && (last_gi->type & GD_TYPE_SELECTBOX) && last_gi->mapped);
1009 boolean anyTextGadgetActive()
1011 return (anyTextInputGadgetActive() || anySelectboxGadgetActive());
1014 void ClickOnGadget(struct GadgetInfo *gi, int button)
1016 /* simulate releasing mouse button over last gadget, if still pressed */
1018 HandleGadgets(-1, -1, 0);
1020 /* simulate pressing mouse button over specified gadget */
1021 HandleGadgets(gi->x, gi->y, button);
1023 /* simulate releasing mouse button over specified gadget */
1024 HandleGadgets(gi->x, gi->y, 0);
1027 void HandleGadgets(int mx, int my, int button)
1029 static struct GadgetInfo *last_info_gi = NULL;
1030 static unsigned long pressed_delay = 0;
1031 static int last_button = 0;
1032 static int last_mx = 0, last_my = 0;
1033 int scrollbar_mouse_pos = 0;
1034 struct GadgetInfo *new_gi, *gi;
1035 boolean press_event;
1036 boolean release_event;
1037 boolean mouse_moving;
1038 boolean gadget_pressed;
1039 boolean gadget_pressed_repeated;
1040 boolean gadget_moving;
1041 boolean gadget_moving_inside;
1042 boolean gadget_moving_off_borders;
1043 boolean gadget_released;
1044 boolean gadget_released_inside;
1045 boolean gadget_released_inside_select_line;
1046 boolean gadget_released_inside_select_area;
1047 boolean gadget_released_off_borders;
1048 boolean changed_position = FALSE;
1050 /* check if there are any gadgets defined */
1051 if (gadget_list_first_entry == NULL)
1054 /* check which gadget is under the mouse pointer */
1055 new_gi = getGadgetInfoFromMousePosition(mx, my);
1057 /* check if button state has changed since last invocation */
1058 press_event = (button != 0 && last_button == 0);
1059 release_event = (button == 0 && last_button != 0);
1060 last_button = button;
1062 /* check if mouse has been moved since last invocation */
1063 mouse_moving = ((mx != last_mx || my != last_my) && motion_status);
1067 /* special treatment for text and number input gadgets */
1068 if (anyTextInputGadgetActive() && button != 0 && !motion_status)
1070 struct GadgetInfo *gi = last_gi;
1072 if (new_gi == last_gi)
1074 int old_cursor_position = gi->text.cursor_position;
1076 /* if mouse button pressed inside activated text gadget, set cursor */
1077 gi->text.cursor_position =
1078 (mx - gi->x - gi->border.size) / getFontWidth(gi->text.font_type);
1080 if (gi->text.cursor_position < 0)
1081 gi->text.cursor_position = 0;
1082 else if (gi->text.cursor_position > strlen(gi->text.value))
1083 gi->text.cursor_position = strlen(gi->text.value);
1085 if (gi->text.cursor_position != old_cursor_position)
1086 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1090 /* if mouse button pressed outside text input gadget, deactivate it */
1091 CheckRangeOfNumericInputGadget(gi);
1092 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1094 gi->event.type = GD_EVENT_TEXT_LEAVING;
1096 if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
1097 gi->callback_action(gi);
1103 /* special treatment for selectbox gadgets */
1104 if (anySelectboxGadgetActive() && button != 0 && !motion_status)
1106 struct GadgetInfo *gi = last_gi;
1108 if (new_gi == last_gi)
1110 int old_index = gi->selectbox.current_index;
1112 /* if mouse button pressed inside activated selectbox, select value */
1113 if (my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
1114 gi->selectbox.current_index =
1115 (my - gi->selectbox.y - gi->border.size) /
1116 getFontWidth(gi->selectbox.font_type);
1118 if (gi->selectbox.current_index < 0)
1119 gi->selectbox.current_index = 0;
1120 else if (gi->selectbox.current_index > gi->selectbox.num_values - 1)
1121 gi->selectbox.current_index = gi->selectbox.num_values - 1;
1123 if (gi->selectbox.current_index != old_index)
1124 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1128 /* if mouse button pressed outside selectbox gadget, deactivate it */
1129 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1131 gi->event.type = GD_EVENT_TEXT_LEAVING;
1133 if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
1134 gi->callback_action(gi);
1141 (button != 0 && last_gi == NULL && new_gi != NULL && press_event);
1142 gadget_pressed_repeated =
1143 (button != 0 && last_gi != NULL && new_gi == last_gi);
1145 gadget_released = (release_event && last_gi != NULL);
1146 gadget_released_inside = (gadget_released && new_gi == last_gi);
1147 gadget_released_off_borders = (gadget_released && new_gi != last_gi);
1149 gadget_moving = (button != 0 && last_gi != NULL && mouse_moving);
1150 gadget_moving_inside = (gadget_moving && new_gi == last_gi);
1151 gadget_moving_off_borders = (gadget_moving && new_gi != last_gi);
1153 /* when handling selectbox, set additional state values */
1154 if (gadget_released_inside && (last_gi->type & GD_TYPE_SELECTBOX))
1156 struct GadgetInfo *gi = last_gi;
1158 gadget_released_inside_select_line =
1159 (mx >= gi->x && mx < gi->x + gi->width &&
1160 my >= gi->y && my < gi->y + gi->height);
1161 gadget_released_inside_select_area =
1162 (mx >= gi->selectbox.x && mx < gi->selectbox.x+gi->selectbox.width &&
1163 my >= gi->selectbox.y && my < gi->selectbox.y+gi->selectbox.height);
1167 gadget_released_inside_select_line = FALSE;
1168 gadget_released_inside_select_area = FALSE;
1171 /* if new gadget pressed, store this gadget */
1175 /* 'gi' is actually handled gadget */
1178 /* if gadget is scrollbar, choose mouse position value */
1179 if (gi && gi->type & GD_TYPE_SCROLLBAR)
1180 scrollbar_mouse_pos =
1181 (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? mx - gi->x : my - gi->y);
1183 /* if mouse button released, no gadget needs to be handled anymore */
1184 if (gadget_released)
1186 if ((last_gi->type & GD_TYPE_SELECTBOX) &&
1187 (gadget_released_inside_select_line ||
1188 gadget_released_off_borders)) /* selectbox stays open */
1189 gi->selectbox.stay_open = TRUE;
1190 else if (!(last_gi->type & GD_TYPE_TEXTINPUT)) /* text input stays open */
1194 /* modify event position values even if no gadget is pressed */
1195 if (button == 0 && !release_event)
1200 int last_x = gi->event.x;
1201 int last_y = gi->event.y;
1203 gi->event.x = mx - gi->x;
1204 gi->event.y = my - gi->y;
1206 if (gi->type == GD_TYPE_DRAWING_AREA)
1208 gi->event.x /= gi->drawing.item_xsize;
1209 gi->event.y /= gi->drawing.item_ysize;
1211 if (last_x != gi->event.x || last_y != gi->event.y)
1212 changed_position = TRUE;
1216 /* handle gadget popup info text */
1217 if (last_info_gi != new_gi ||
1218 (new_gi && new_gi->type == GD_TYPE_DRAWING_AREA && changed_position))
1220 if (new_gi != NULL && (button == 0 || new_gi == last_gi))
1222 new_gi->event.type = GD_EVENT_INFO_ENTERING;
1223 new_gi->callback_info(new_gi);
1225 else if (last_info_gi != NULL)
1227 last_info_gi->event.type = GD_EVENT_INFO_LEAVING;
1228 last_info_gi->callback_info(last_info_gi);
1231 default_callback_info(NULL);
1233 printf("It seems that we are leaving gadget [%s]!\n",
1234 (last_info_gi != NULL &&
1235 last_info_gi->info_text != NULL ?
1236 last_info_gi->info_text : ""));
1240 last_info_gi = new_gi;
1245 if (gi->type == GD_TYPE_CHECK_BUTTON)
1247 gi->checked = !gi->checked;
1249 else if (gi->type == GD_TYPE_RADIO_BUTTON)
1251 struct GadgetInfo *rgi = gadget_list_first_entry;
1256 rgi->type == GD_TYPE_RADIO_BUTTON &&
1257 rgi->radio_nr == gi->radio_nr &&
1260 rgi->checked = FALSE;
1261 DrawGadget(rgi, DG_UNPRESSED, DG_DIRECT);
1269 else if (gi->type & GD_TYPE_SCROLLBAR)
1273 if (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL)
1284 if (mpos >= gpos + gi->scrollbar.position &&
1285 mpos < gpos + gi->scrollbar.position + gi->scrollbar.size)
1287 /* drag scrollbar */
1288 gi->scrollbar.drag_position =
1289 scrollbar_mouse_pos - gi->scrollbar.position;
1293 /* click scrollbar one scrollbar length up/left or down/right */
1295 struct GadgetScrollbar *gs = &gi->scrollbar;
1296 int old_item_position = gs->item_position;
1298 changed_position = FALSE;
1300 gs->item_position +=
1301 gs->items_visible * (mpos < gpos + gi->scrollbar.position ? -1 : +1);
1303 if (gs->item_position < 0)
1304 gs->item_position = 0;
1305 if (gs->item_position > gs->items_max - gs->items_visible)
1306 gs->item_position = gs->items_max - gs->items_visible;
1308 if (old_item_position != gs->item_position)
1310 gi->event.item_position = gs->item_position;
1311 changed_position = TRUE;
1314 ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, gs->item_position,
1317 gi->state = GD_BUTTON_UNPRESSED;
1318 gi->event.type = GD_EVENT_MOVING;
1319 gi->event.off_borders = FALSE;
1321 if (gi->event_mask & GD_EVENT_MOVING && changed_position)
1322 gi->callback_action(gi);
1324 /* don't handle this scrollbar anymore while mouse button pressed */
1331 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1333 gi->state = GD_BUTTON_PRESSED;
1334 gi->event.type = GD_EVENT_PRESSED;
1335 gi->event.button = button;
1336 gi->event.off_borders = FALSE;
1338 /* initialize delay counter */
1339 DelayReached(&pressed_delay, 0);
1341 if (gi->event_mask & GD_EVENT_PRESSED)
1342 gi->callback_action(gi);
1345 if (gadget_pressed_repeated)
1347 gi->event.type = GD_EVENT_PRESSED;
1349 if (gi->event_mask & GD_EVENT_REPEATED &&
1350 DelayReached(&pressed_delay, GADGET_FRAME_DELAY))
1351 gi->callback_action(gi);
1356 if (gi->type & GD_TYPE_BUTTON)
1358 if (gadget_moving_inside && gi->state == GD_BUTTON_UNPRESSED)
1359 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1360 else if (gadget_moving_off_borders && gi->state == GD_BUTTON_PRESSED)
1361 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1363 else if (gi->type & GD_TYPE_SELECTBOX)
1365 int old_index = gi->selectbox.current_index;
1367 /* if mouse moving inside activated selectbox, select value */
1368 if (my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
1369 gi->selectbox.current_index =
1370 (my - gi->selectbox.y - gi->border.size) /
1371 getFontWidth(gi->selectbox.font_type);
1373 if (gi->selectbox.current_index < 0)
1374 gi->selectbox.current_index = 0;
1375 else if (gi->selectbox.current_index > gi->selectbox.num_values - 1)
1376 gi->selectbox.current_index = gi->selectbox.num_values - 1;
1378 if (gi->selectbox.current_index != old_index)
1379 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1381 else if (gi->type & GD_TYPE_SCROLLBAR)
1383 struct GadgetScrollbar *gs = &gi->scrollbar;
1384 int old_item_position = gs->item_position;
1386 gs->position = scrollbar_mouse_pos - gs->drag_position;
1388 if (gs->position < 0)
1390 if (gs->position > gs->position_max)
1391 gs->position = gs->position_max;
1394 gs->items_max * (gs->position + gs->correction) / gs->size_max;
1396 if (gs->item_position < 0)
1397 gs->item_position = 0;
1398 if (gs->item_position > gs->items_max - 1)
1399 gs->item_position = gs->items_max - 1;
1401 if (old_item_position != gs->item_position)
1403 gi->event.item_position = gs->item_position;
1404 changed_position = TRUE;
1407 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1410 gi->state = (gadget_moving_inside || gi->type & GD_TYPE_SCROLLBAR ?
1411 GD_BUTTON_PRESSED : GD_BUTTON_UNPRESSED);
1412 gi->event.type = GD_EVENT_MOVING;
1413 gi->event.off_borders = gadget_moving_off_borders;
1415 if (gi->event_mask & GD_EVENT_MOVING && changed_position &&
1416 (gadget_moving_inside || gi->event_mask & GD_EVENT_OFF_BORDERS))
1417 gi->callback_action(gi);
1420 if (gadget_released_inside)
1422 boolean deactivate_gadget = TRUE;
1424 if (gi->type & GD_TYPE_SELECTBOX)
1426 if (gadget_released_inside_select_line ||
1427 gadget_released_off_borders) /* selectbox stays open */
1428 deactivate_gadget = FALSE;
1430 gi->selectbox.index = gi->selectbox.current_index;
1433 if (deactivate_gadget &&
1434 !(gi->type & GD_TYPE_TEXTINPUT)) /* text input stays open */
1435 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1437 gi->state = GD_BUTTON_UNPRESSED;
1438 gi->event.type = GD_EVENT_RELEASED;
1440 if ((gi->event_mask & GD_EVENT_RELEASED) && deactivate_gadget)
1441 gi->callback_action(gi);
1444 if (gadget_released_off_borders)
1446 if (gi->type & GD_TYPE_SCROLLBAR)
1447 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1449 gi->event.type = GD_EVENT_RELEASED;
1451 if (gi->event_mask & GD_EVENT_RELEASED &&
1452 gi->event_mask & GD_EVENT_OFF_BORDERS)
1453 gi->callback_action(gi);
1457 void HandleGadgetsKeyInput(Key key)
1459 struct GadgetInfo *gi = last_gi;
1461 if (gi == NULL || !gi->mapped ||
1462 !((gi->type & GD_TYPE_TEXTINPUT) || (gi->type & GD_TYPE_SELECTBOX)))
1465 if (key == KSYM_Return) /* valid for both text input and selectbox */
1467 if (gi->type & GD_TYPE_TEXTINPUT)
1468 CheckRangeOfNumericInputGadget(gi);
1469 else if (gi->type & GD_TYPE_SELECTBOX)
1470 gi->selectbox.index = gi->selectbox.current_index;
1472 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1474 gi->event.type = GD_EVENT_TEXT_RETURN;
1476 if (gi->event_mask & GD_EVENT_TEXT_RETURN)
1477 gi->callback_action(gi);
1481 else if (gi->type & GD_TYPE_TEXTINPUT) /* only valid for text input */
1483 char text[MAX_GADGET_TEXTSIZE];
1484 int text_length = strlen(gi->text.value);
1485 int cursor_pos = gi->text.cursor_position;
1486 char letter = getCharFromKey(key);
1487 boolean legal_letter = (gi->type == GD_TYPE_TEXTINPUT_NUMERIC ?
1488 letter >= '0' && letter <= '9' :
1491 if (legal_letter && text_length < gi->text.size)
1493 strcpy(text, gi->text.value);
1494 strcpy(&gi->text.value[cursor_pos + 1], &text[cursor_pos]);
1495 gi->text.value[cursor_pos] = letter;
1496 gi->text.cursor_position++;
1498 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1500 else if (key == KSYM_Left && cursor_pos > 0)
1502 gi->text.cursor_position--;
1503 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1505 else if (key == KSYM_Right && cursor_pos < text_length)
1507 gi->text.cursor_position++;
1508 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1510 else if (key == KSYM_BackSpace && cursor_pos > 0)
1512 strcpy(text, gi->text.value);
1513 strcpy(&gi->text.value[cursor_pos - 1], &text[cursor_pos]);
1514 gi->text.cursor_position--;
1515 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1517 else if (key == KSYM_Delete && cursor_pos < text_length)
1519 strcpy(text, gi->text.value);
1520 strcpy(&gi->text.value[cursor_pos], &text[cursor_pos + 1]);
1521 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1524 else if (gi->type & GD_TYPE_SELECTBOX) /* only valid for selectbox */
1526 int index = gi->selectbox.current_index;
1527 int num_values = gi->selectbox.num_values;
1529 if (key == KSYM_Up && index > 0)
1531 gi->selectbox.current_index--;
1532 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1534 else if (key == KSYM_Down && index < num_values - 1)
1536 gi->selectbox.current_index++;
1537 DrawGadget(gi, DG_PRESSED, DG_DIRECT);