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)
98 static void default_callback_info(void *ptr)
103 static void default_callback_action(void *ptr)
108 static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
110 int state = (pressed ? 1 : 0);
111 struct GadgetDesign *gd = (gi->checked ?
112 &gi->alt_design[state] :
117 case GD_TYPE_NORMAL_BUTTON:
118 case GD_TYPE_CHECK_BUTTON:
119 case GD_TYPE_RADIO_BUTTON:
120 BlitBitmapOnBackground(gd->bitmap, drawto,
121 gd->x, gd->y, gi->width, gi->height,
123 if (gi->deco.design.bitmap)
124 BlitBitmap(gi->deco.design.bitmap, drawto,
125 gi->deco.design.x, gi->deco.design.y,
126 gi->deco.width, gi->deco.height,
127 gi->x + gi->deco.x + (pressed ? gi->deco.xshift : 0),
128 gi->y + gi->deco.y + (pressed ? gi->deco.yshift : 0));
131 case GD_TYPE_TEXTINPUT_ALPHANUMERIC:
132 case GD_TYPE_TEXTINPUT_NUMERIC:
136 char cursor_string[3];
137 char text[MAX_GADGET_TEXTSIZE + 1];
138 int font_type = gi->text.font_type;
139 int font_width = getFontWidth(font_type);
140 int border = gi->border.size;
142 strcpy(text, gi->text.value);
145 /* left part of gadget */
146 BlitBitmapOnBackground(gd->bitmap, drawto,
147 gd->x, gd->y, border, gi->height, gi->x, gi->y);
149 /* middle part of gadget */
150 for (i=0; i <= gi->text.size; i++)
151 BlitBitmapOnBackground(gd->bitmap, drawto,
152 gd->x + border, gd->y, font_width, gi->height,
153 gi->x + border + i * font_width, gi->y);
155 /* right part of gadget */
156 BlitBitmapOnBackground(gd->bitmap, drawto,
157 gd->x + gi->border.width - border, gd->y,border,
158 gi->height, gi->x + gi->width - border, gi->y);
160 /* gadget text value */
162 gi->x + border, gi->y + border, text,
163 font_type, FONT_MASKED);
165 cursor_letter = gi->text.value[gi->text.cursor_position];
166 cursor_string[0] = '~';
167 cursor_string[1] = (cursor_letter != '\0' ? cursor_letter : ' ');
168 cursor_string[2] = '\0';
170 /* draw cursor, if active */
173 gi->x + border + gi->text.cursor_position * font_width,
174 gi->y + border, cursor_string,
175 font_type, FONT_MASKED);
179 case GD_TYPE_SELECTBOX:
182 char text[MAX_GADGET_TEXTSIZE + 1];
183 int font_type = gi->selectbox.font_type;
184 int font_width = getFontWidth(font_type);
185 int border = gi->border.size;
186 int button = gi->border.size_selectbutton;
187 int width_inner = gi->border.width - button - 2 * border;
189 strncpy(text, gi->selectbox.values[gi->selectbox.index],
190 MAX_GADGET_TEXTSIZE);
191 text[MAX_GADGET_TEXTSIZE] = '\0';
193 /* left part of gadget */
194 BlitBitmapOnBackground(gd->bitmap, drawto,
195 gd->x, gd->y, border, gi->height, gi->x, gi->y);
197 /* middle part of gadget */
198 for (i=0; i <= gi->selectbox.size; i++)
199 BlitBitmapOnBackground(gd->bitmap, drawto,
200 gd->x + border, gd->y, font_width, gi->height,
201 gi->x + border + i * font_width, gi->y);
203 /* button part of gadget */
204 BlitBitmapOnBackground(gd->bitmap, drawto,
205 gd->x + border + width_inner, gd->y,
207 gi->x + gi->width - border - button, gi->y);
209 /* right part of gadget */
210 BlitBitmapOnBackground(gd->bitmap, drawto,
211 gd->x + gi->border.width - border, gd->y,border,
212 gi->height, gi->x + gi->width - border, gi->y);
214 /* gadget text value */
216 gi->x + border, gi->y + border, text,
217 font_type, FONT_MASKED);
221 case GD_TYPE_SCROLLBAR_VERTICAL:
225 int ypos = gi->y + gi->scrollbar.position;
226 int design_full = gi->width;
227 int design_body = design_full - 2 * gi->border.size;
228 int size_full = gi->scrollbar.size;
229 int size_body = size_full - 2 * gi->border.size;
230 int num_steps = size_body / design_body;
231 int step_size_remain = size_body - num_steps * design_body;
233 /* clear scrollbar area */
234 ClearRectangleOnBackground(backbuffer, gi->x, gi->y,
235 gi->width, gi->height);
237 /* upper part of gadget */
238 BlitBitmapOnBackground(gd->bitmap, drawto,
240 gi->width, gi->border.size,
243 /* middle part of gadget */
244 for (i=0; i<num_steps; i++)
245 BlitBitmapOnBackground(gd->bitmap, drawto,
246 gd->x, gd->y + gi->border.size,
247 gi->width, design_body,
249 ypos + gi->border.size + i * design_body);
251 /* remaining middle part of gadget */
252 if (step_size_remain > 0)
253 BlitBitmapOnBackground(gd->bitmap, drawto,
254 gd->x, gd->y + gi->border.size,
255 gi->width, step_size_remain,
257 ypos + gi->border.size
258 + num_steps * design_body);
260 /* lower part of gadget */
261 BlitBitmapOnBackground(gd->bitmap, drawto,
262 gd->x, gd->y + design_full - gi->border.size,
263 gi->width, gi->border.size,
264 xpos, ypos + size_full - gi->border.size);
268 case GD_TYPE_SCROLLBAR_HORIZONTAL:
271 int xpos = gi->x + gi->scrollbar.position;
273 int design_full = gi->height;
274 int design_body = design_full - 2 * gi->border.size;
275 int size_full = gi->scrollbar.size;
276 int size_body = size_full - 2 * gi->border.size;
277 int num_steps = size_body / design_body;
278 int step_size_remain = size_body - num_steps * design_body;
280 /* clear scrollbar area */
281 ClearRectangleOnBackground(backbuffer, gi->x, gi->y,
282 gi->width, gi->height);
284 /* left part of gadget */
285 BlitBitmapOnBackground(gd->bitmap, drawto,
287 gi->border.size, gi->height,
290 /* middle part of gadget */
291 for (i=0; i<num_steps; i++)
292 BlitBitmapOnBackground(gd->bitmap, drawto,
293 gd->x + gi->border.size, gd->y,
294 design_body, gi->height,
295 xpos + gi->border.size + i * design_body,
298 /* remaining middle part of gadget */
299 if (step_size_remain > 0)
300 BlitBitmapOnBackground(gd->bitmap, drawto,
301 gd->x + gi->border.size, gd->y,
302 step_size_remain, gi->height,
303 xpos + gi->border.size
304 + num_steps * design_body,
307 /* right part of gadget */
308 BlitBitmapOnBackground(gd->bitmap, drawto,
309 gd->x + design_full - gi->border.size, gd->y,
310 gi->border.size, gi->height,
311 xpos + size_full - gi->border.size, ypos);
320 BlitBitmap(drawto, window,
321 gi->x, gi->y, gi->width, gi->height, gi->x, gi->y);
323 redraw_mask |= (gi->x < gfx.sx + gfx.sxsize ? REDRAW_FIELD :
324 gi->y < gfx.dy + gfx.dysize ? REDRAW_DOOR_1 :
325 gi->y > gfx.vy ? REDRAW_DOOR_2 : REDRAW_DOOR_3);
328 static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
332 while (tag != GDI_END)
337 gi->custom_id = va_arg(ap, int);
340 case GDI_CUSTOM_TYPE_ID:
341 gi->custom_type_id = va_arg(ap, int);
346 int max_textsize = MAX_INFO_TEXTSIZE - 1;
347 char *text = va_arg(ap, char *);
350 strncpy(gi->info_text, text, max_textsize);
354 gi->info_text[max_textsize] = '\0';
359 gi->x = va_arg(ap, int);
363 gi->y = va_arg(ap, int);
367 gi->width = va_arg(ap, int);
371 gi->height = va_arg(ap, int);
375 gi->type = va_arg(ap, unsigned long);
379 gi->state = va_arg(ap, unsigned long);
383 /* take care here: "boolean" is typedef'ed as "unsigned char",
384 which gets promoted to "int" */
385 gi->checked = (boolean)va_arg(ap, int);
389 gi->radio_nr = va_arg(ap, unsigned long);
392 case GDI_NUMBER_VALUE:
393 gi->text.number_value = va_arg(ap, long);
394 sprintf(gi->text.value, "%d", gi->text.number_value);
395 gi->text.cursor_position = strlen(gi->text.value);
399 gi->text.number_min = va_arg(ap, long);
400 if (gi->text.number_value < gi->text.number_min)
402 gi->text.number_value = gi->text.number_min;
403 sprintf(gi->text.value, "%d", gi->text.number_value);
408 gi->text.number_max = va_arg(ap, long);
409 if (gi->text.number_value > gi->text.number_max)
411 gi->text.number_value = gi->text.number_max;
412 sprintf(gi->text.value, "%d", gi->text.number_value);
418 int max_textsize = MAX_GADGET_TEXTSIZE;
421 max_textsize = MIN(gi->text.size, MAX_GADGET_TEXTSIZE - 1);
423 strncpy(gi->text.value, va_arg(ap, char *), max_textsize);
424 gi->text.value[max_textsize] = '\0';
425 gi->text.cursor_position = strlen(gi->text.value);
431 int tag_value = va_arg(ap, int);
432 int max_textsize = MIN(tag_value, MAX_GADGET_TEXTSIZE - 1);
434 gi->text.size = max_textsize;
435 gi->text.value[max_textsize] = '\0';
437 /* same tag also used for selectbox definition */
438 gi->selectbox.size = gi->text.size;
443 gi->text.font_type = va_arg(ap, int);
445 /* same tag also used for selectbox definition */
446 gi->selectbox.font_type = gi->text.font_type;
449 case GDI_SELECTBOX_VALUES:
450 gi->selectbox.values = va_arg(ap, const char **);
453 case GDI_SELECTBOX_INDEX:
454 gi->selectbox.index = va_arg(ap, int);
457 case GDI_DESIGN_UNPRESSED:
458 gi->design[GD_BUTTON_UNPRESSED].bitmap = va_arg(ap, Bitmap *);
459 gi->design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
460 gi->design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
463 case GDI_DESIGN_PRESSED:
464 gi->design[GD_BUTTON_PRESSED].bitmap = va_arg(ap, Bitmap *);
465 gi->design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
466 gi->design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
469 case GDI_ALT_DESIGN_UNPRESSED:
470 gi->alt_design[GD_BUTTON_UNPRESSED].bitmap= va_arg(ap, Bitmap *);
471 gi->alt_design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
472 gi->alt_design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
475 case GDI_ALT_DESIGN_PRESSED:
476 gi->alt_design[GD_BUTTON_PRESSED].bitmap = va_arg(ap, Bitmap *);
477 gi->alt_design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
478 gi->alt_design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
481 case GDI_BORDER_SIZE:
482 gi->border.size = va_arg(ap, int);
485 case GDI_BORDER_SIZE_SELECTBUTTON:
486 gi->border.size_selectbutton = va_arg(ap, int);
489 case GDI_TEXTINPUT_DESIGN_WIDTH:
490 gi->border.width = va_arg(ap, int);
493 case GDI_DECORATION_DESIGN:
494 gi->deco.design.bitmap = va_arg(ap, Bitmap *);
495 gi->deco.design.x = va_arg(ap, int);
496 gi->deco.design.y = va_arg(ap, int);
499 case GDI_DECORATION_POSITION:
500 gi->deco.x = va_arg(ap, int);
501 gi->deco.y = va_arg(ap, int);
504 case GDI_DECORATION_SIZE:
505 gi->deco.width = va_arg(ap, int);
506 gi->deco.height = va_arg(ap, int);
509 case GDI_DECORATION_SHIFTING:
510 gi->deco.xshift = va_arg(ap, int);
511 gi->deco.yshift = va_arg(ap, int);
515 gi->event_mask = va_arg(ap, unsigned long);
519 gi->drawing.area_xsize = va_arg(ap, int);
520 gi->drawing.area_ysize = va_arg(ap, int);
522 /* determine dependent values for drawing area gadget, if needed */
523 if (gi->width == 0 && gi->height == 0 &&
524 gi->drawing.item_xsize !=0 && gi->drawing.item_ysize !=0)
526 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
527 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
529 else if (gi->drawing.item_xsize == 0 && gi->drawing.item_ysize == 0 &&
530 gi->width != 0 && gi->height != 0)
532 gi->drawing.item_xsize = gi->width / gi->drawing.area_xsize;
533 gi->drawing.item_ysize = gi->height / gi->drawing.area_ysize;
538 gi->drawing.item_xsize = va_arg(ap, int);
539 gi->drawing.item_ysize = va_arg(ap, int);
541 /* determine dependent values for drawing area gadget, if needed */
542 if (gi->width == 0 && gi->height == 0 &&
543 gi->drawing.area_xsize !=0 && gi->drawing.area_ysize !=0)
545 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
546 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
548 else if (gi->drawing.area_xsize == 0 && gi->drawing.area_ysize == 0 &&
549 gi->width != 0 && gi->height != 0)
551 gi->drawing.area_xsize = gi->width / gi->drawing.item_xsize;
552 gi->drawing.area_ysize = gi->height / gi->drawing.item_ysize;
556 case GDI_SCROLLBAR_ITEMS_MAX:
557 gi->scrollbar.items_max = va_arg(ap, int);
560 case GDI_SCROLLBAR_ITEMS_VISIBLE:
561 gi->scrollbar.items_visible = va_arg(ap, int);
564 case GDI_SCROLLBAR_ITEM_POSITION:
565 gi->scrollbar.item_position = va_arg(ap, int);
568 case GDI_CALLBACK_INFO:
569 gi->callback_info = va_arg(ap, gadget_function);
572 case GDI_CALLBACK_ACTION:
573 gi->callback_action = va_arg(ap, gadget_function);
577 Error(ERR_EXIT, "HandleGadgetTags(): unknown tag %d", tag);
580 tag = va_arg(ap, int); /* read next tag */
583 /* check if gadget is complete */
584 if (gi->type != GD_TYPE_DRAWING_AREA &&
585 (!gi->design[GD_BUTTON_UNPRESSED].bitmap ||
586 !gi->design[GD_BUTTON_PRESSED].bitmap))
587 Error(ERR_EXIT, "gadget incomplete (missing Bitmap)");
589 /* adjust gadget values in relation to other gadget values */
591 if (gi->type & GD_TYPE_TEXTINPUT)
593 int font_width = getFontWidth(gi->text.font_type);
594 int font_height = getFontHeight(gi->text.font_type);
595 int border_size = gi->border.size;
597 gi->width = 2 * border_size + (gi->text.size + 1) * font_width;
598 gi->height = 2 * border_size + font_height;
601 if (gi->type & GD_TYPE_SELECTBOX)
603 int font_width = getFontWidth(gi->selectbox.font_type);
604 int font_height = getFontHeight(gi->selectbox.font_type);
605 int border_size = gi->border.size;
606 int button_size = gi->border.size_selectbutton;
608 gi->width = 2 * border_size + gi->text.size * font_width + button_size;
609 gi->height = 2 * border_size + font_height;
612 if (gi->type & GD_TYPE_TEXTINPUT_NUMERIC)
614 struct GadgetTextInput *text = &gi->text;
615 int value = text->number_value;
617 text->number_value = (value < text->number_min ? text->number_min :
618 value > text->number_max ? text->number_max :
621 sprintf(text->value, "%d", text->number_value);
624 if (gi->type & GD_TYPE_SCROLLBAR)
626 struct GadgetScrollbar *gs = &gi->scrollbar;
628 if (gi->width == 0 || gi->height == 0 ||
629 gs->items_max == 0 || gs->items_visible == 0)
630 Error(ERR_EXIT, "scrollbar gadget incomplete (missing tags)");
632 /* calculate internal scrollbar values */
633 gs->size_max = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ?
634 gi->height : gi->width);
635 gs->size = gs->size_max * gs->items_visible / gs->items_max;
636 gs->position = gs->size_max * gs->item_position / gs->items_max;
637 gs->position_max = gs->size_max - gs->size;
638 gs->correction = gs->size_max / gs->items_max / 2;
640 /* finetuning for maximal right/bottom position */
641 if (gs->item_position == gs->items_max - gs->items_visible)
642 gs->position = gs->position_max;
646 void ModifyGadget(struct GadgetInfo *gi, int first_tag, ...)
650 va_start(ap, first_tag);
651 HandleGadgetTags(gi, first_tag, ap);
657 void RedrawGadget(struct GadgetInfo *gi)
660 DrawGadget(gi, gi->state, DG_DIRECT);
663 struct GadgetInfo *CreateGadget(int first_tag, ...)
665 struct GadgetInfo *new_gadget = checked_calloc(sizeof(struct GadgetInfo));
668 /* always start with reliable default values */
669 new_gadget->id = getNewGadgetID();
670 new_gadget->callback_info = default_callback_info;
671 new_gadget->callback_action = default_callback_action;
672 new_gadget->next = NULL;
674 va_start(ap, first_tag);
675 HandleGadgetTags(new_gadget, first_tag, ap);
678 /* insert new gadget into global gadget list */
679 if (gadget_list_last_entry)
681 gadget_list_last_entry->next = new_gadget;
682 gadget_list_last_entry = gadget_list_last_entry->next;
685 gadget_list_first_entry = gadget_list_last_entry = new_gadget;
690 void FreeGadget(struct GadgetInfo *gi)
692 struct GadgetInfo *gi_previous = gadget_list_first_entry;
694 while (gi_previous != NULL && gi_previous->next != gi)
695 gi_previous = gi_previous->next;
697 if (gi == gadget_list_first_entry)
698 gadget_list_first_entry = gi->next;
700 if (gi == gadget_list_last_entry)
701 gadget_list_last_entry = gi_previous;
703 if (gi_previous != NULL)
704 gi_previous->next = gi->next;
709 static void CheckRangeOfNumericInputGadget(struct GadgetInfo *gi)
711 if (gi->type != GD_TYPE_TEXTINPUT_NUMERIC)
714 gi->text.number_value = atoi(gi->text.value);
716 if (gi->text.number_value < gi->text.number_min)
717 gi->text.number_value = gi->text.number_min;
718 if (gi->text.number_value > gi->text.number_max)
719 gi->text.number_value = gi->text.number_max;
721 sprintf(gi->text.value, "%d", gi->text.number_value);
723 if (gi->text.cursor_position < 0)
724 gi->text.cursor_position = 0;
725 else if (gi->text.cursor_position > strlen(gi->text.value))
726 gi->text.cursor_position = strlen(gi->text.value);
729 /* global pointer to gadget actually in use (when mouse button pressed) */
730 static struct GadgetInfo *last_gi = NULL;
732 static void MapGadgetExt(struct GadgetInfo *gi, boolean redraw)
734 if (gi == NULL || gi->mapped)
740 DrawGadget(gi, DG_UNPRESSED, DG_BUFFERED);
743 void MapGadget(struct GadgetInfo *gi)
745 MapGadgetExt(gi, TRUE);
748 void UnmapGadget(struct GadgetInfo *gi)
750 if (gi == NULL || !gi->mapped)
759 #define MAX_NUM_GADGETS 1024
760 #define MULTIMAP_UNMAP (1 << 0)
761 #define MULTIMAP_REMAP (1 << 1)
762 #define MULTIMAP_REDRAW (1 << 2)
763 #define MULTIMAP_PLAYFIELD (1 << 3)
764 #define MULTIMAP_DOOR_1 (1 << 4)
765 #define MULTIMAP_DOOR_2 (1 << 5)
766 #define MULTIMAP_ALL (MULTIMAP_PLAYFIELD | \
770 static void MultiMapGadgets(int mode)
772 struct GadgetInfo *gi = gadget_list_first_entry;
773 static boolean map_state[MAX_NUM_GADGETS];
778 if ((mode & MULTIMAP_PLAYFIELD &&
779 gi->x < gfx.sx + gfx.sxsize) ||
780 (mode & MULTIMAP_DOOR_1 &&
781 gi->x >= gfx.dx && gi->y < gfx.dy + gfx.dysize) ||
782 (mode & MULTIMAP_DOOR_2 &&
783 gi->x >= gfx.dx && gi->y > gfx.dy + gfx.dysize) ||
784 (mode & MULTIMAP_ALL) == MULTIMAP_ALL)
786 if (mode & MULTIMAP_UNMAP)
788 map_state[map_count++ % MAX_NUM_GADGETS] = gi->mapped;
793 if (map_state[map_count++ % MAX_NUM_GADGETS])
794 MapGadgetExt(gi, (mode & MULTIMAP_REDRAW));
802 void UnmapAllGadgets()
804 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_UNMAP);
807 void RemapAllGadgets()
809 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_REMAP);
812 boolean anyTextGadgetActive()
814 return (last_gi && last_gi->type & GD_TYPE_TEXTINPUT && last_gi->mapped);
817 void ClickOnGadget(struct GadgetInfo *gi, int button)
819 /* simulate releasing mouse button over last gadget, if still pressed */
821 HandleGadgets(-1, -1, 0);
823 /* simulate pressing mouse button over specified gadget */
824 HandleGadgets(gi->x, gi->y, button);
826 /* simulate releasing mouse button over specified gadget */
827 HandleGadgets(gi->x, gi->y, 0);
830 void HandleGadgets(int mx, int my, int button)
832 static struct GadgetInfo *last_info_gi = NULL;
833 static unsigned long pressed_delay = 0;
834 static int last_button = 0;
835 static int last_mx = 0, last_my = 0;
836 int scrollbar_mouse_pos = 0;
837 struct GadgetInfo *new_gi, *gi;
839 boolean release_event;
840 boolean mouse_moving;
841 boolean gadget_pressed;
842 boolean gadget_pressed_repeated;
843 boolean gadget_moving;
844 boolean gadget_moving_inside;
845 boolean gadget_moving_off_borders;
846 boolean gadget_released;
847 boolean gadget_released_inside;
848 boolean gadget_released_off_borders;
849 boolean changed_position = FALSE;
851 /* check if there are any gadgets defined */
852 if (gadget_list_first_entry == NULL)
855 /* check which gadget is under the mouse pointer */
856 new_gi = getGadgetInfoFromMousePosition(mx, my);
858 /* check if button state has changed since last invocation */
859 press_event = (button != 0 && last_button == 0);
860 release_event = (button == 0 && last_button != 0);
861 last_button = button;
863 /* check if mouse has been moved since last invocation */
864 mouse_moving = ((mx != last_mx || my != last_my) && motion_status);
868 /* special treatment for text and number input gadgets */
869 if (anyTextGadgetActive() && button != 0 && !motion_status)
871 struct GadgetInfo *gi = last_gi;
873 if (new_gi == last_gi)
875 /* if mouse button pressed inside activated text gadget, set cursor */
876 gi->text.cursor_position =
877 (mx - gi->x - gi->border.size) /
878 getFontWidth(gi->text.font_type);
880 if (gi->text.cursor_position < 0)
881 gi->text.cursor_position = 0;
882 else if (gi->text.cursor_position > strlen(gi->text.value))
883 gi->text.cursor_position = strlen(gi->text.value);
885 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
889 /* if mouse button pressed outside text input gadget, deactivate it */
890 CheckRangeOfNumericInputGadget(gi);
891 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
893 gi->event.type = GD_EVENT_TEXT_LEAVING;
895 if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
896 gi->callback_action(gi);
903 (button != 0 && last_gi == NULL && new_gi != NULL && press_event);
904 gadget_pressed_repeated =
905 (button != 0 && last_gi != NULL && new_gi == last_gi);
907 gadget_released = (release_event && last_gi != NULL);
908 gadget_released_inside = (gadget_released && new_gi == last_gi);
909 gadget_released_off_borders = (gadget_released && new_gi != last_gi);
911 gadget_moving = (button != 0 && last_gi != NULL && mouse_moving);
912 gadget_moving_inside = (gadget_moving && new_gi == last_gi);
913 gadget_moving_off_borders = (gadget_moving && new_gi != last_gi);
915 /* if new gadget pressed, store this gadget */
919 /* 'gi' is actually handled gadget */
922 /* if gadget is scrollbar, choose mouse position value */
923 if (gi && gi->type & GD_TYPE_SCROLLBAR)
924 scrollbar_mouse_pos =
925 (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? mx - gi->x : my - gi->y);
927 /* if mouse button released, no gadget needs to be handled anymore */
928 if (button == 0 && last_gi && !(last_gi->type & GD_TYPE_TEXTINPUT))
931 /* modify event position values even if no gadget is pressed */
932 if (button == 0 && !release_event)
937 int last_x = gi->event.x;
938 int last_y = gi->event.y;
940 gi->event.x = mx - gi->x;
941 gi->event.y = my - gi->y;
943 if (gi->type == GD_TYPE_DRAWING_AREA)
945 gi->event.x /= gi->drawing.item_xsize;
946 gi->event.y /= gi->drawing.item_ysize;
948 if (last_x != gi->event.x || last_y != gi->event.y)
949 changed_position = TRUE;
953 /* handle gadget popup info text */
954 if (last_info_gi != new_gi ||
955 (new_gi && new_gi->type == GD_TYPE_DRAWING_AREA && changed_position))
957 if (new_gi != NULL && (button == 0 || new_gi == last_gi))
959 new_gi->event.type = GD_EVENT_INFO_ENTERING;
960 new_gi->callback_info(new_gi);
962 else if (last_info_gi != NULL)
964 last_info_gi->event.type = GD_EVENT_INFO_LEAVING;
965 last_info_gi->callback_info(last_info_gi);
968 default_callback_info(NULL);
970 printf("It seems that we are leaving gadget [%s]!\n",
971 (last_info_gi != NULL &&
972 last_info_gi->info_text != NULL ?
973 last_info_gi->info_text : ""));
977 last_info_gi = new_gi;
982 if (gi->type == GD_TYPE_CHECK_BUTTON)
984 gi->checked = !gi->checked;
986 else if (gi->type == GD_TYPE_RADIO_BUTTON)
988 struct GadgetInfo *rgi = gadget_list_first_entry;
993 rgi->type == GD_TYPE_RADIO_BUTTON &&
994 rgi->radio_nr == gi->radio_nr &&
997 rgi->checked = FALSE;
998 DrawGadget(rgi, DG_UNPRESSED, DG_DIRECT);
1006 else if (gi->type & GD_TYPE_SCROLLBAR)
1010 if (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL)
1021 if (mpos >= gpos + gi->scrollbar.position &&
1022 mpos < gpos + gi->scrollbar.position + gi->scrollbar.size)
1024 /* drag scrollbar */
1025 gi->scrollbar.drag_position =
1026 scrollbar_mouse_pos - gi->scrollbar.position;
1030 /* click scrollbar one scrollbar length up/left or down/right */
1032 struct GadgetScrollbar *gs = &gi->scrollbar;
1033 int old_item_position = gs->item_position;
1035 changed_position = FALSE;
1037 gs->item_position +=
1038 gs->items_visible * (mpos < gpos + gi->scrollbar.position ? -1 : +1);
1040 if (gs->item_position < 0)
1041 gs->item_position = 0;
1042 if (gs->item_position > gs->items_max - gs->items_visible)
1043 gs->item_position = gs->items_max - gs->items_visible;
1045 if (old_item_position != gs->item_position)
1047 gi->event.item_position = gs->item_position;
1048 changed_position = TRUE;
1051 ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, gs->item_position,
1054 gi->state = GD_BUTTON_UNPRESSED;
1055 gi->event.type = GD_EVENT_MOVING;
1056 gi->event.off_borders = FALSE;
1058 if (gi->event_mask & GD_EVENT_MOVING && changed_position)
1059 gi->callback_action(gi);
1061 /* don't handle this scrollbar anymore while mouse button pressed */
1068 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1070 gi->state = GD_BUTTON_PRESSED;
1071 gi->event.type = GD_EVENT_PRESSED;
1072 gi->event.button = button;
1073 gi->event.off_borders = FALSE;
1075 /* initialize delay counter */
1076 DelayReached(&pressed_delay, 0);
1078 if (gi->event_mask & GD_EVENT_PRESSED)
1079 gi->callback_action(gi);
1082 if (gadget_pressed_repeated)
1084 gi->event.type = GD_EVENT_PRESSED;
1086 if (gi->event_mask & GD_EVENT_REPEATED &&
1087 DelayReached(&pressed_delay, GADGET_FRAME_DELAY))
1088 gi->callback_action(gi);
1093 if (gi->type & GD_TYPE_BUTTON)
1095 if (gadget_moving_inside && gi->state == GD_BUTTON_UNPRESSED)
1096 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1097 else if (gadget_moving_off_borders && gi->state == GD_BUTTON_PRESSED)
1098 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1101 if (gi->type & GD_TYPE_SCROLLBAR)
1103 struct GadgetScrollbar *gs = &gi->scrollbar;
1104 int old_item_position = gs->item_position;
1106 gs->position = scrollbar_mouse_pos - gs->drag_position;
1108 if (gs->position < 0)
1110 if (gs->position > gs->position_max)
1111 gs->position = gs->position_max;
1114 gs->items_max * (gs->position + gs->correction) / gs->size_max;
1116 if (gs->item_position < 0)
1117 gs->item_position = 0;
1118 if (gs->item_position > gs->items_max - 1)
1119 gs->item_position = gs->items_max - 1;
1121 if (old_item_position != gs->item_position)
1123 gi->event.item_position = gs->item_position;
1124 changed_position = TRUE;
1127 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1130 gi->state = (gadget_moving_inside || gi->type & GD_TYPE_SCROLLBAR ?
1131 GD_BUTTON_PRESSED : GD_BUTTON_UNPRESSED);
1132 gi->event.type = GD_EVENT_MOVING;
1133 gi->event.off_borders = gadget_moving_off_borders;
1135 if (gi->event_mask & GD_EVENT_MOVING && changed_position &&
1136 (gadget_moving_inside || gi->event_mask & GD_EVENT_OFF_BORDERS))
1137 gi->callback_action(gi);
1140 if (gadget_released_inside)
1142 if (!(gi->type & GD_TYPE_TEXTINPUT))
1143 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1145 gi->state = GD_BUTTON_UNPRESSED;
1146 gi->event.type = GD_EVENT_RELEASED;
1148 if (gi->event_mask & GD_EVENT_RELEASED)
1149 gi->callback_action(gi);
1152 if (gadget_released_off_borders)
1154 if (gi->type & GD_TYPE_SCROLLBAR)
1155 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1157 gi->event.type = GD_EVENT_RELEASED;
1159 if (gi->event_mask & GD_EVENT_RELEASED &&
1160 gi->event_mask & GD_EVENT_OFF_BORDERS)
1161 gi->callback_action(gi);
1165 void HandleGadgetsKeyInput(Key key)
1167 struct GadgetInfo *gi = last_gi;
1168 char text[MAX_GADGET_TEXTSIZE];
1172 boolean legal_letter;
1174 if (gi == NULL || !(gi->type & GD_TYPE_TEXTINPUT) || !gi->mapped)
1177 text_length = strlen(gi->text.value);
1178 cursor_pos = gi->text.cursor_position;
1179 letter = getCharFromKey(key);
1180 legal_letter = (gi->type == GD_TYPE_TEXTINPUT_NUMERIC ?
1181 letter >= '0' && letter <= '9' :
1184 if (legal_letter && text_length < gi->text.size)
1186 strcpy(text, gi->text.value);
1187 strcpy(&gi->text.value[cursor_pos + 1], &text[cursor_pos]);
1188 gi->text.value[cursor_pos] = letter;
1189 gi->text.cursor_position++;
1190 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1192 else if (key == KSYM_Left && cursor_pos > 0)
1194 gi->text.cursor_position--;
1195 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1197 else if (key == KSYM_Right && cursor_pos < text_length)
1199 gi->text.cursor_position++;
1200 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1202 else if (key == KSYM_BackSpace && cursor_pos > 0)
1204 strcpy(text, gi->text.value);
1205 strcpy(&gi->text.value[cursor_pos - 1], &text[cursor_pos]);
1206 gi->text.cursor_position--;
1207 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1209 else if (key == KSYM_Delete && cursor_pos < text_length)
1211 strcpy(text, gi->text.value);
1212 strcpy(&gi->text.value[cursor_pos], &text[cursor_pos + 1]);
1213 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1215 else if (key == KSYM_Return)
1217 CheckRangeOfNumericInputGadget(gi);
1218 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1220 gi->event.type = GD_EVENT_TEXT_RETURN;
1222 if (gi->event_mask & GD_EVENT_TEXT_RETURN)
1223 gi->callback_action(gi);