1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2001 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");
66 static struct GadgetInfo *getGadgetInfoFromMousePosition(int mx, int my)
68 struct GadgetInfo *gi = gadget_list_first_entry;
73 mx >= gi->x && mx < gi->x + gi->width &&
74 my >= gi->y && my < gi->y + gi->height)
83 static void default_callback_info(void *ptr)
88 static void default_callback_action(void *ptr)
93 static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
95 int state = (pressed ? 1 : 0);
96 struct GadgetDesign *gd = (gi->checked ?
97 &gi->alt_design[state] :
102 case GD_TYPE_NORMAL_BUTTON:
103 case GD_TYPE_CHECK_BUTTON:
104 case GD_TYPE_RADIO_BUTTON:
105 BlitBitmap(gd->bitmap, drawto,
106 gd->x, gd->y, gi->width, gi->height, gi->x, gi->y);
107 if (gi->deco.design.bitmap)
108 BlitBitmap(gi->deco.design.bitmap, drawto,
109 gi->deco.design.x, gi->deco.design.y,
110 gi->deco.width, gi->deco.height,
111 gi->x + gi->deco.x + (pressed ? gi->deco.xshift : 0),
112 gi->y + gi->deco.y + (pressed ? gi->deco.yshift : 0));
115 case GD_TYPE_TEXTINPUT_ALPHANUMERIC:
116 case GD_TYPE_TEXTINPUT_NUMERIC:
120 char cursor_string[3];
121 char text[MAX_GADGET_TEXTSIZE + 1];
122 int font_type = gi->text.font_type;
123 int font_width = getFontWidth(FS_SMALL, font_type);
124 int border = gi->border.size;
125 strcpy(text, gi->text.value);
128 /* left part of gadget */
129 BlitBitmap(gd->bitmap, drawto,
130 gd->x, gd->y, border, gi->height, gi->x, gi->y);
132 /* middle part of gadget */
133 for (i=0; i<=gi->text.size; i++)
134 BlitBitmap(gd->bitmap, drawto,
135 gd->x + border, gd->y, font_width, gi->height,
136 gi->x + border + i * font_width, gi->y);
138 /* right part of gadget */
139 BlitBitmap(gd->bitmap, drawto,
140 gd->x + gi->border.width - border, gd->y,
141 border, gi->height, gi->x + gi->width - border, gi->y);
143 /* gadget text value */
144 DrawText(gi->x + border, gi->y + border, text, FS_SMALL, font_type);
146 cursor_letter = gi->text.value[gi->text.cursor_position];
147 cursor_string[0] = '~';
148 cursor_string[1] = (cursor_letter != '\0' ? cursor_letter : ' ');
149 cursor_string[2] = '\0';
151 /* draw cursor, if active */
153 DrawText(gi->x + border + gi->text.cursor_position * font_width,
154 gi->y + border, cursor_string, FS_SMALL, font_type);
158 case GD_TYPE_SCROLLBAR_VERTICAL:
162 int ypos = gi->y + gi->scrollbar.position;
163 int design_full = gi->width;
164 int design_body = design_full - 2 * gi->border.size;
165 int size_full = gi->scrollbar.size;
166 int size_body = size_full - 2 * gi->border.size;
167 int num_steps = size_body / design_body;
168 int step_size_remain = size_body - num_steps * design_body;
170 /* clear scrollbar area */
171 ClearRectangle(backbuffer, gi->x, gi->y, gi->width, gi->height);
173 /* upper part of gadget */
174 BlitBitmap(gd->bitmap, drawto,
176 gi->width, gi->border.size,
179 /* middle part of gadget */
180 for (i=0; i<num_steps; i++)
181 BlitBitmap(gd->bitmap, drawto,
182 gd->x, gd->y + gi->border.size,
183 gi->width, design_body,
184 xpos, ypos + gi->border.size + i * design_body);
186 /* remaining middle part of gadget */
187 if (step_size_remain > 0)
188 BlitBitmap(gd->bitmap, drawto,
189 gd->x, gd->y + gi->border.size,
190 gi->width, step_size_remain,
191 xpos, ypos + gi->border.size + num_steps * design_body);
193 /* lower part of gadget */
194 BlitBitmap(gd->bitmap, drawto,
195 gd->x, gd->y + design_full - gi->border.size,
196 gi->width, gi->border.size,
197 xpos, ypos + size_full - gi->border.size);
201 case GD_TYPE_SCROLLBAR_HORIZONTAL:
204 int xpos = gi->x + gi->scrollbar.position;
206 int design_full = gi->height;
207 int design_body = design_full - 2 * gi->border.size;
208 int size_full = gi->scrollbar.size;
209 int size_body = size_full - 2 * gi->border.size;
210 int num_steps = size_body / design_body;
211 int step_size_remain = size_body - num_steps * design_body;
213 /* clear scrollbar area */
214 ClearRectangle(backbuffer, gi->x, gi->y, gi->width, gi->height);
216 /* left part of gadget */
217 BlitBitmap(gd->bitmap, drawto,
219 gi->border.size, gi->height,
222 /* middle part of gadget */
223 for (i=0; i<num_steps; i++)
224 BlitBitmap(gd->bitmap, drawto,
225 gd->x + gi->border.size, gd->y,
226 design_body, gi->height,
227 xpos + gi->border.size + i * design_body, ypos);
229 /* remaining middle part of gadget */
230 if (step_size_remain > 0)
231 BlitBitmap(gd->bitmap, drawto,
232 gd->x + gi->border.size, gd->y,
233 step_size_remain, gi->height,
234 xpos + gi->border.size + num_steps * design_body, ypos);
236 /* right part of gadget */
237 BlitBitmap(gd->bitmap, drawto,
238 gd->x + design_full - gi->border.size, gd->y,
239 gi->border.size, gi->height,
240 xpos + size_full - gi->border.size, ypos);
249 BlitBitmap(drawto, window,
250 gi->x, gi->y, gi->width, gi->height, gi->x, gi->y);
252 redraw_mask |= (gi->x < gfx.sx + gfx.sxsize ? REDRAW_FIELD :
253 gi->y < gfx.dy + gfx.dysize ? REDRAW_DOOR_1 :
254 gi->y > gfx.vy ? REDRAW_DOOR_2 : REDRAW_DOOR_3);
257 static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
261 while (tag != GDI_END)
266 gi->custom_id = va_arg(ap, int);
269 case GDI_CUSTOM_TYPE_ID:
270 gi->custom_type_id = va_arg(ap, int);
275 int max_textsize = MAX_INFO_TEXTSIZE - 1;
277 strncpy(gi->info_text, va_arg(ap, char *), max_textsize);
278 gi->info_text[max_textsize] = '\0';
283 gi->x = va_arg(ap, int);
287 gi->y = va_arg(ap, int);
291 gi->width = va_arg(ap, int);
295 gi->height = va_arg(ap, int);
299 gi->type = va_arg(ap, unsigned long);
303 gi->state = va_arg(ap, unsigned long);
307 /* take care here: "boolean" is typedef'ed as "unsigned char",
308 which gets promoted to "int" */
309 gi->checked = (boolean)va_arg(ap, int);
313 gi->radio_nr = va_arg(ap, unsigned long);
316 case GDI_NUMBER_VALUE:
317 gi->text.number_value = va_arg(ap, long);
318 sprintf(gi->text.value, "%d", gi->text.number_value);
319 gi->text.cursor_position = strlen(gi->text.value);
323 gi->text.number_min = va_arg(ap, long);
324 if (gi->text.number_value < gi->text.number_min)
326 gi->text.number_value = gi->text.number_min;
327 sprintf(gi->text.value, "%d", gi->text.number_value);
332 gi->text.number_max = va_arg(ap, long);
333 if (gi->text.number_value > gi->text.number_max)
335 gi->text.number_value = gi->text.number_max;
336 sprintf(gi->text.value, "%d", gi->text.number_value);
342 int max_textsize = MAX_GADGET_TEXTSIZE;
345 max_textsize = MIN(gi->text.size, MAX_GADGET_TEXTSIZE - 1);
347 strncpy(gi->text.value, va_arg(ap, char *), max_textsize);
348 gi->text.value[max_textsize] = '\0';
349 gi->text.cursor_position = strlen(gi->text.value);
355 int tag_value = va_arg(ap, int);
356 int max_textsize = MIN(tag_value, MAX_GADGET_TEXTSIZE - 1);
358 gi->text.size = max_textsize;
359 gi->text.value[max_textsize] = '\0';
364 gi->text.font_type = va_arg(ap, int);
367 case GDI_DESIGN_UNPRESSED:
368 gi->design[GD_BUTTON_UNPRESSED].bitmap = va_arg(ap, Bitmap *);
369 gi->design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
370 gi->design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
373 case GDI_DESIGN_PRESSED:
374 gi->design[GD_BUTTON_PRESSED].bitmap = va_arg(ap, Bitmap *);
375 gi->design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
376 gi->design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
379 case GDI_ALT_DESIGN_UNPRESSED:
380 gi->alt_design[GD_BUTTON_UNPRESSED].bitmap= va_arg(ap, Bitmap *);
381 gi->alt_design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
382 gi->alt_design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
385 case GDI_ALT_DESIGN_PRESSED:
386 gi->alt_design[GD_BUTTON_PRESSED].bitmap = va_arg(ap, Bitmap *);
387 gi->alt_design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
388 gi->alt_design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
391 case GDI_BORDER_SIZE:
392 gi->border.size = va_arg(ap, int);
395 case GDI_TEXTINPUT_DESIGN_WIDTH:
396 gi->border.width = va_arg(ap, int);
399 case GDI_DECORATION_DESIGN:
400 gi->deco.design.bitmap = va_arg(ap, Bitmap *);
401 gi->deco.design.x = va_arg(ap, int);
402 gi->deco.design.y = va_arg(ap, int);
405 case GDI_DECORATION_POSITION:
406 gi->deco.x = va_arg(ap, int);
407 gi->deco.y = va_arg(ap, int);
410 case GDI_DECORATION_SIZE:
411 gi->deco.width = va_arg(ap, int);
412 gi->deco.height = va_arg(ap, int);
415 case GDI_DECORATION_SHIFTING:
416 gi->deco.xshift = va_arg(ap, int);
417 gi->deco.yshift = va_arg(ap, int);
421 gi->event_mask = va_arg(ap, unsigned long);
425 gi->drawing.area_xsize = va_arg(ap, int);
426 gi->drawing.area_ysize = va_arg(ap, int);
428 /* determine dependent values for drawing area gadget, if needed */
429 if (gi->width == 0 && gi->height == 0 &&
430 gi->drawing.item_xsize !=0 && gi->drawing.item_ysize !=0)
432 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
433 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
435 else if (gi->drawing.item_xsize == 0 && gi->drawing.item_ysize == 0 &&
436 gi->width != 0 && gi->height != 0)
438 gi->drawing.item_xsize = gi->width / gi->drawing.area_xsize;
439 gi->drawing.item_ysize = gi->height / gi->drawing.area_ysize;
444 gi->drawing.item_xsize = va_arg(ap, int);
445 gi->drawing.item_ysize = va_arg(ap, int);
447 /* determine dependent values for drawing area gadget, if needed */
448 if (gi->width == 0 && gi->height == 0 &&
449 gi->drawing.area_xsize !=0 && gi->drawing.area_ysize !=0)
451 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
452 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
454 else if (gi->drawing.area_xsize == 0 && gi->drawing.area_ysize == 0 &&
455 gi->width != 0 && gi->height != 0)
457 gi->drawing.area_xsize = gi->width / gi->drawing.item_xsize;
458 gi->drawing.area_ysize = gi->height / gi->drawing.item_ysize;
462 case GDI_SCROLLBAR_ITEMS_MAX:
463 gi->scrollbar.items_max = va_arg(ap, int);
466 case GDI_SCROLLBAR_ITEMS_VISIBLE:
467 gi->scrollbar.items_visible = va_arg(ap, int);
470 case GDI_SCROLLBAR_ITEM_POSITION:
471 gi->scrollbar.item_position = va_arg(ap, int);
474 case GDI_CALLBACK_INFO:
475 gi->callback_info = va_arg(ap, gadget_function);
478 case GDI_CALLBACK_ACTION:
479 gi->callback_action = va_arg(ap, gadget_function);
483 Error(ERR_EXIT, "HandleGadgetTags(): unknown tag %d", tag);
486 tag = va_arg(ap, int); /* read next tag */
489 /* check if gadget complete */
490 if (gi->type != GD_TYPE_DRAWING_AREA &&
491 (!gi->design[GD_BUTTON_UNPRESSED].bitmap ||
492 !gi->design[GD_BUTTON_PRESSED].bitmap))
493 Error(ERR_EXIT, "gadget incomplete (missing Bitmap)");
495 /* adjust gadget values in relation to other gadget values */
497 if (gi->type & GD_TYPE_TEXTINPUT)
499 int font_width = getFontWidth(FS_SMALL, gi->text.font_type);
500 int font_height = getFontHeight(FS_SMALL, gi->text.font_type);
502 gi->width = 2 * gi->border.size + (gi->text.size + 1) * font_width;
503 gi->height = 2 * gi->border.size + font_height;
506 if (gi->type & GD_TYPE_TEXTINPUT_NUMERIC)
508 struct GadgetTextInput *text = &gi->text;
509 int value = text->number_value;
511 text->number_value = (value < text->number_min ? text->number_min :
512 value > text->number_max ? text->number_max :
515 sprintf(text->value, "%d", text->number_value);
518 if (gi->type & GD_TYPE_SCROLLBAR)
520 struct GadgetScrollbar *gs = &gi->scrollbar;
522 if (gi->width == 0 || gi->height == 0 ||
523 gs->items_max == 0 || gs->items_visible == 0)
524 Error(ERR_EXIT, "scrollbar gadget incomplete (missing tags)");
526 /* calculate internal scrollbar values */
527 gs->size_max = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ?
528 gi->height : gi->width);
529 gs->size = gs->size_max * gs->items_visible / gs->items_max;
530 gs->position = gs->size_max * gs->item_position / gs->items_max;
531 gs->position_max = gs->size_max - gs->size;
532 gs->correction = gs->size_max / gs->items_max / 2;
534 /* finetuning for maximal right/bottom position */
535 if (gs->item_position == gs->items_max - gs->items_visible)
536 gs->position = gs->position_max;
540 void ModifyGadget(struct GadgetInfo *gi, int first_tag, ...)
544 va_start(ap, first_tag);
545 HandleGadgetTags(gi, first_tag, ap);
551 void RedrawGadget(struct GadgetInfo *gi)
554 DrawGadget(gi, gi->state, DG_DIRECT);
557 struct GadgetInfo *CreateGadget(int first_tag, ...)
559 struct GadgetInfo *new_gadget = checked_malloc(sizeof(struct GadgetInfo));
562 /* always start with reliable default values */
563 memset(new_gadget, 0, sizeof(struct GadgetInfo)); /* zero all fields */
564 new_gadget->id = getNewGadgetID();
565 new_gadget->callback_info = default_callback_info;
566 new_gadget->callback_action = default_callback_action;
568 va_start(ap, first_tag);
569 HandleGadgetTags(new_gadget, first_tag, ap);
572 /* insert new gadget into global gadget list */
573 if (gadget_list_last_entry)
575 gadget_list_last_entry->next = new_gadget;
576 gadget_list_last_entry = gadget_list_last_entry->next;
579 gadget_list_first_entry = gadget_list_last_entry = new_gadget;
584 void FreeGadget(struct GadgetInfo *gi)
586 struct GadgetInfo *gi_previous = gadget_list_first_entry;
588 while (gi_previous && gi_previous->next != gi)
589 gi_previous = gi_previous->next;
591 if (gi == gadget_list_first_entry)
592 gadget_list_first_entry = gi->next;
594 if (gi == gadget_list_last_entry)
595 gadget_list_last_entry = gi_previous;
597 gi_previous->next = gi->next;
601 static void CheckRangeOfNumericInputGadget(struct GadgetInfo *gi)
603 if (gi->type != GD_TYPE_TEXTINPUT_NUMERIC)
606 gi->text.number_value = atoi(gi->text.value);
608 if (gi->text.number_value < gi->text.number_min)
609 gi->text.number_value = gi->text.number_min;
610 if (gi->text.number_value > gi->text.number_max)
611 gi->text.number_value = gi->text.number_max;
613 sprintf(gi->text.value, "%d", gi->text.number_value);
615 if (gi->text.cursor_position < 0)
616 gi->text.cursor_position = 0;
617 else if (gi->text.cursor_position > strlen(gi->text.value))
618 gi->text.cursor_position = strlen(gi->text.value);
621 /* global pointer to gadget actually in use (when mouse button pressed) */
622 static struct GadgetInfo *last_gi = NULL;
624 static void MapGadgetExt(struct GadgetInfo *gi, boolean redraw)
626 if (gi == NULL || gi->mapped)
632 DrawGadget(gi, DG_UNPRESSED, DG_BUFFERED);
635 void MapGadget(struct GadgetInfo *gi)
637 MapGadgetExt(gi, TRUE);
640 void UnmapGadget(struct GadgetInfo *gi)
642 if (gi == NULL || !gi->mapped)
651 #define MAX_NUM_GADGETS 1024
652 #define MULTIMAP_UNMAP (1 << 0)
653 #define MULTIMAP_REMAP (1 << 1)
654 #define MULTIMAP_REDRAW (1 << 2)
655 #define MULTIMAP_PLAYFIELD (1 << 3)
656 #define MULTIMAP_DOOR_1 (1 << 4)
657 #define MULTIMAP_DOOR_2 (1 << 5)
658 #define MULTIMAP_ALL (MULTIMAP_PLAYFIELD | \
662 static void MultiMapGadgets(int mode)
664 struct GadgetInfo *gi = gadget_list_first_entry;
665 static boolean map_state[MAX_NUM_GADGETS];
670 if ((mode & MULTIMAP_PLAYFIELD &&
671 gi->x < gfx.sx + gfx.sxsize) ||
672 (mode & MULTIMAP_DOOR_1 &&
673 gi->x >= gfx.dx && gi->y < gfx.dy + gfx.dysize) ||
674 (mode & MULTIMAP_DOOR_2 &&
675 gi->x >= gfx.dx && gi->y > gfx.dy + gfx.dysize) ||
676 (mode & MULTIMAP_ALL) == MULTIMAP_ALL)
678 if (mode & MULTIMAP_UNMAP)
680 map_state[map_count++ % MAX_NUM_GADGETS] = gi->mapped;
685 if (map_state[map_count++ % MAX_NUM_GADGETS])
686 MapGadgetExt(gi, (mode & MULTIMAP_REDRAW));
694 void UnmapAllGadgets()
696 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_UNMAP);
699 void RemapAllGadgets()
701 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_REMAP);
704 boolean anyTextGadgetActive()
706 return (last_gi && last_gi->type & GD_TYPE_TEXTINPUT && last_gi->mapped);
709 void ClickOnGadget(struct GadgetInfo *gi, int button)
711 /* simulate releasing mouse button over last gadget, if still pressed */
713 HandleGadgets(-1, -1, 0);
715 /* simulate pressing mouse button over specified gadget */
716 HandleGadgets(gi->x, gi->y, button);
718 /* simulate releasing mouse button over specified gadget */
719 HandleGadgets(gi->x, gi->y, 0);
722 void HandleGadgets(int mx, int my, int button)
724 static struct GadgetInfo *last_info_gi = NULL;
725 static unsigned long pressed_delay = 0;
726 static int last_button = 0;
727 static int last_mx = 0, last_my = 0;
728 int scrollbar_mouse_pos = 0;
729 struct GadgetInfo *new_gi, *gi;
731 boolean release_event;
732 boolean mouse_moving;
733 boolean gadget_pressed;
734 boolean gadget_pressed_repeated;
735 boolean gadget_moving;
736 boolean gadget_moving_inside;
737 boolean gadget_moving_off_borders;
738 boolean gadget_released;
739 boolean gadget_released_inside;
740 boolean gadget_released_off_borders;
741 boolean changed_position = FALSE;
743 /* check if there are any gadgets defined */
744 if (gadget_list_first_entry == NULL)
747 /* check which gadget is under the mouse pointer */
748 new_gi = getGadgetInfoFromMousePosition(mx, my);
750 /* check if button state has changed since last invocation */
751 press_event = (button != 0 && last_button == 0);
752 release_event = (button == 0 && last_button != 0);
753 last_button = button;
755 /* check if mouse has been moved since last invocation */
756 mouse_moving = ((mx != last_mx || my != last_my) && motion_status);
760 /* special treatment for text and number input gadgets */
761 if (anyTextGadgetActive() && button != 0 && !motion_status)
763 struct GadgetInfo *gi = last_gi;
765 if (new_gi == last_gi)
767 /* if mouse button pressed inside activated text gadget, set cursor */
768 gi->text.cursor_position =
769 (mx - gi->x - gi->border.size) /
770 getFontWidth(FS_SMALL, gi->text.font_type);
772 if (gi->text.cursor_position < 0)
773 gi->text.cursor_position = 0;
774 else if (gi->text.cursor_position > strlen(gi->text.value))
775 gi->text.cursor_position = strlen(gi->text.value);
777 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
781 /* if mouse button pressed outside text input gadget, deactivate it */
782 CheckRangeOfNumericInputGadget(gi);
783 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
785 gi->event.type = GD_EVENT_TEXT_LEAVING;
787 if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
788 gi->callback_action(gi);
795 (button != 0 && last_gi == NULL && new_gi != NULL && press_event);
796 gadget_pressed_repeated =
797 (button != 0 && last_gi != NULL && new_gi == last_gi);
799 gadget_released = (release_event && last_gi != NULL);
800 gadget_released_inside = (gadget_released && new_gi == last_gi);
801 gadget_released_off_borders = (gadget_released && new_gi != last_gi);
803 gadget_moving = (button != 0 && last_gi != NULL && mouse_moving);
804 gadget_moving_inside = (gadget_moving && new_gi == last_gi);
805 gadget_moving_off_borders = (gadget_moving && new_gi != last_gi);
807 /* if new gadget pressed, store this gadget */
811 /* 'gi' is actually handled gadget */
814 /* if gadget is scrollbar, choose mouse position value */
815 if (gi && gi->type & GD_TYPE_SCROLLBAR)
816 scrollbar_mouse_pos =
817 (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? mx - gi->x : my - gi->y);
819 /* if mouse button released, no gadget needs to be handled anymore */
820 if (button == 0 && last_gi && !(last_gi->type & GD_TYPE_TEXTINPUT))
823 /* modify event position values even if no gadget is pressed */
824 if (button == 0 && !release_event)
829 int last_x = gi->event.x;
830 int last_y = gi->event.y;
832 gi->event.x = mx - gi->x;
833 gi->event.y = my - gi->y;
835 if (gi->type == GD_TYPE_DRAWING_AREA)
837 gi->event.x /= gi->drawing.item_xsize;
838 gi->event.y /= gi->drawing.item_ysize;
840 if (last_x != gi->event.x || last_y != gi->event.y)
841 changed_position = TRUE;
845 /* handle gadget popup info text */
846 if (last_info_gi != new_gi ||
847 (new_gi && new_gi->type == GD_TYPE_DRAWING_AREA && changed_position))
849 if (new_gi != NULL && (button == 0 || new_gi == last_gi))
851 new_gi->event.type = GD_EVENT_INFO_ENTERING;
852 new_gi->callback_info(new_gi);
854 else if (last_info_gi != NULL)
856 last_info_gi->event.type = GD_EVENT_INFO_LEAVING;
857 last_info_gi->callback_info(last_info_gi);
860 default_callback_info(NULL);
862 printf("It seems that we are leaving gadget [%s]!\n",
863 (last_info_gi != NULL &&
864 last_info_gi->info_text != NULL ?
865 last_info_gi->info_text : ""));
869 last_info_gi = new_gi;
874 if (gi->type == GD_TYPE_CHECK_BUTTON)
876 gi->checked = !gi->checked;
878 else if (gi->type == GD_TYPE_RADIO_BUTTON)
880 struct GadgetInfo *rgi = gadget_list_first_entry;
885 rgi->type == GD_TYPE_RADIO_BUTTON &&
886 rgi->radio_nr == gi->radio_nr &&
889 rgi->checked = FALSE;
890 DrawGadget(rgi, DG_UNPRESSED, DG_DIRECT);
898 else if (gi->type & GD_TYPE_SCROLLBAR)
902 if (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL)
913 if (mpos >= gpos + gi->scrollbar.position &&
914 mpos < gpos + gi->scrollbar.position + gi->scrollbar.size)
917 gi->scrollbar.drag_position =
918 scrollbar_mouse_pos - gi->scrollbar.position;
922 /* click scrollbar one scrollbar length up/left or down/right */
924 struct GadgetScrollbar *gs = &gi->scrollbar;
925 int old_item_position = gs->item_position;
927 changed_position = FALSE;
930 gs->items_visible * (mpos < gpos + gi->scrollbar.position ? -1 : +1);
932 if (gs->item_position < 0)
933 gs->item_position = 0;
934 if (gs->item_position > gs->items_max - gs->items_visible)
935 gs->item_position = gs->items_max - gs->items_visible;
937 if (old_item_position != gs->item_position)
939 gi->event.item_position = gs->item_position;
940 changed_position = TRUE;
943 ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, gs->item_position,
946 gi->state = GD_BUTTON_UNPRESSED;
947 gi->event.type = GD_EVENT_MOVING;
948 gi->event.off_borders = FALSE;
950 if (gi->event_mask & GD_EVENT_MOVING && changed_position)
951 gi->callback_action(gi);
953 /* don't handle this scrollbar anymore while mouse button pressed */
960 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
962 gi->state = GD_BUTTON_PRESSED;
963 gi->event.type = GD_EVENT_PRESSED;
964 gi->event.button = button;
965 gi->event.off_borders = FALSE;
967 /* initialize delay counter */
968 DelayReached(&pressed_delay, 0);
970 if (gi->event_mask & GD_EVENT_PRESSED)
971 gi->callback_action(gi);
974 if (gadget_pressed_repeated)
976 gi->event.type = GD_EVENT_PRESSED;
978 if (gi->event_mask & GD_EVENT_REPEATED &&
979 DelayReached(&pressed_delay, GADGET_FRAME_DELAY))
980 gi->callback_action(gi);
985 if (gi->type & GD_TYPE_BUTTON)
987 if (gadget_moving_inside && gi->state == GD_BUTTON_UNPRESSED)
988 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
989 else if (gadget_moving_off_borders && gi->state == GD_BUTTON_PRESSED)
990 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
993 if (gi->type & GD_TYPE_SCROLLBAR)
995 struct GadgetScrollbar *gs = &gi->scrollbar;
996 int old_item_position = gs->item_position;
998 gs->position = scrollbar_mouse_pos - gs->drag_position;
1000 if (gs->position < 0)
1002 if (gs->position > gs->position_max)
1003 gs->position = gs->position_max;
1006 gs->items_max * (gs->position + gs->correction) / gs->size_max;
1008 if (gs->item_position < 0)
1009 gs->item_position = 0;
1010 if (gs->item_position > gs->items_max - 1)
1011 gs->item_position = gs->items_max - 1;
1013 if (old_item_position != gs->item_position)
1015 gi->event.item_position = gs->item_position;
1016 changed_position = TRUE;
1019 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1022 gi->state = (gadget_moving_inside || gi->type & GD_TYPE_SCROLLBAR ?
1023 GD_BUTTON_PRESSED : GD_BUTTON_UNPRESSED);
1024 gi->event.type = GD_EVENT_MOVING;
1025 gi->event.off_borders = gadget_moving_off_borders;
1027 if (gi->event_mask & GD_EVENT_MOVING && changed_position &&
1028 (gadget_moving_inside || gi->event_mask & GD_EVENT_OFF_BORDERS))
1029 gi->callback_action(gi);
1032 if (gadget_released_inside)
1034 if (!(gi->type & GD_TYPE_TEXTINPUT))
1035 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1037 gi->state = GD_BUTTON_UNPRESSED;
1038 gi->event.type = GD_EVENT_RELEASED;
1040 if (gi->event_mask & GD_EVENT_RELEASED)
1041 gi->callback_action(gi);
1044 if (gadget_released_off_borders)
1046 if (gi->type & GD_TYPE_SCROLLBAR)
1047 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1049 gi->event.type = GD_EVENT_RELEASED;
1051 if (gi->event_mask & GD_EVENT_RELEASED &&
1052 gi->event_mask & GD_EVENT_OFF_BORDERS)
1053 gi->callback_action(gi);
1057 void HandleGadgetsKeyInput(Key key)
1059 struct GadgetInfo *gi = last_gi;
1060 char text[MAX_GADGET_TEXTSIZE];
1064 boolean legal_letter;
1066 if (gi == NULL || !(gi->type & GD_TYPE_TEXTINPUT) || !gi->mapped)
1069 text_length = strlen(gi->text.value);
1070 cursor_pos = gi->text.cursor_position;
1071 letter = getCharFromKey(key);
1072 legal_letter = (gi->type == GD_TYPE_TEXTINPUT_NUMERIC ?
1073 letter >= '0' && letter <= '9' :
1076 if (legal_letter && text_length < gi->text.size)
1078 strcpy(text, gi->text.value);
1079 strcpy(&gi->text.value[cursor_pos + 1], &text[cursor_pos]);
1080 gi->text.value[cursor_pos] = letter;
1081 gi->text.cursor_position++;
1082 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1084 else if (key == KSYM_Left && cursor_pos > 0)
1086 gi->text.cursor_position--;
1087 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1089 else if (key == KSYM_Right && cursor_pos < text_length)
1091 gi->text.cursor_position++;
1092 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1094 else if (key == KSYM_BackSpace && cursor_pos > 0)
1096 strcpy(text, gi->text.value);
1097 strcpy(&gi->text.value[cursor_pos - 1], &text[cursor_pos]);
1098 gi->text.cursor_position--;
1099 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1101 else if (key == KSYM_Delete && cursor_pos < text_length)
1103 strcpy(text, gi->text.value);
1104 strcpy(&gi->text.value[cursor_pos], &text[cursor_pos + 1]);
1105 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1107 else if (key == KSYM_Return)
1109 CheckRangeOfNumericInputGadget(gi);
1110 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1112 gi->event.type = GD_EVENT_TEXT_RETURN;
1114 if (gi->event_mask & GD_EVENT_TEXT_RETURN)
1115 gi->callback_action(gi);