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(FS_SMALL, font_type);
140 int border = gi->border.size;
141 strcpy(text, gi->text.value);
144 /* left part of gadget */
145 BlitBitmap(gd->bitmap, drawto,
146 gd->x, gd->y, border, gi->height, gi->x, gi->y);
148 /* middle part of gadget */
149 for (i=0; i<=gi->text.size; i++)
150 BlitBitmap(gd->bitmap, drawto,
151 gd->x + border, gd->y, font_width, gi->height,
152 gi->x + border + i * font_width, gi->y);
154 /* right part of gadget */
155 BlitBitmap(gd->bitmap, drawto,
156 gd->x + gi->border.width - border, gd->y,
157 border, gi->height, gi->x + gi->width - border, gi->y);
159 /* gadget text value */
161 gi->x + border, gi->y + border, text,
162 FS_SMALL, font_type, FONT_OPAQUE);
164 cursor_letter = gi->text.value[gi->text.cursor_position];
165 cursor_string[0] = '~';
166 cursor_string[1] = (cursor_letter != '\0' ? cursor_letter : ' ');
167 cursor_string[2] = '\0';
169 /* draw cursor, if active */
172 gi->x + border + gi->text.cursor_position * font_width,
173 gi->y + border, cursor_string,
174 FS_SMALL, font_type, FONT_OPAQUE);
178 case GD_TYPE_SCROLLBAR_VERTICAL:
182 int ypos = gi->y + gi->scrollbar.position;
183 int design_full = gi->width;
184 int design_body = design_full - 2 * gi->border.size;
185 int size_full = gi->scrollbar.size;
186 int size_body = size_full - 2 * gi->border.size;
187 int num_steps = size_body / design_body;
188 int step_size_remain = size_body - num_steps * design_body;
190 /* clear scrollbar area */
191 ClearRectangleOnBackground(backbuffer, gi->x, gi->y,
192 gi->width, gi->height);
194 /* upper part of gadget */
195 BlitBitmapOnBackground(gd->bitmap, drawto,
197 gi->width, gi->border.size,
200 /* middle part of gadget */
201 for (i=0; i<num_steps; i++)
202 BlitBitmapOnBackground(gd->bitmap, drawto,
203 gd->x, gd->y + gi->border.size,
204 gi->width, design_body,
206 ypos + gi->border.size + i * design_body);
208 /* remaining middle part of gadget */
209 if (step_size_remain > 0)
210 BlitBitmapOnBackground(gd->bitmap, drawto,
211 gd->x, gd->y + gi->border.size,
212 gi->width, step_size_remain,
214 ypos + gi->border.size
215 + num_steps * design_body);
217 /* lower part of gadget */
218 BlitBitmapOnBackground(gd->bitmap, drawto,
219 gd->x, gd->y + design_full - gi->border.size,
220 gi->width, gi->border.size,
221 xpos, ypos + size_full - gi->border.size);
225 case GD_TYPE_SCROLLBAR_HORIZONTAL:
228 int xpos = gi->x + gi->scrollbar.position;
230 int design_full = gi->height;
231 int design_body = design_full - 2 * gi->border.size;
232 int size_full = gi->scrollbar.size;
233 int size_body = size_full - 2 * gi->border.size;
234 int num_steps = size_body / design_body;
235 int step_size_remain = size_body - num_steps * design_body;
237 /* clear scrollbar area */
238 ClearRectangleOnBackground(backbuffer, gi->x, gi->y,
239 gi->width, gi->height);
241 /* left part of gadget */
242 BlitBitmapOnBackground(gd->bitmap, drawto,
244 gi->border.size, gi->height,
247 /* middle part of gadget */
248 for (i=0; i<num_steps; i++)
249 BlitBitmapOnBackground(gd->bitmap, drawto,
250 gd->x + gi->border.size, gd->y,
251 design_body, gi->height,
252 xpos + gi->border.size + i * design_body,
255 /* remaining middle part of gadget */
256 if (step_size_remain > 0)
257 BlitBitmapOnBackground(gd->bitmap, drawto,
258 gd->x + gi->border.size, gd->y,
259 step_size_remain, gi->height,
260 xpos + gi->border.size
261 + num_steps * design_body,
264 /* right part of gadget */
265 BlitBitmapOnBackground(gd->bitmap, drawto,
266 gd->x + design_full - gi->border.size, gd->y,
267 gi->border.size, gi->height,
268 xpos + size_full - gi->border.size, ypos);
277 BlitBitmap(drawto, window,
278 gi->x, gi->y, gi->width, gi->height, gi->x, gi->y);
280 redraw_mask |= (gi->x < gfx.sx + gfx.sxsize ? REDRAW_FIELD :
281 gi->y < gfx.dy + gfx.dysize ? REDRAW_DOOR_1 :
282 gi->y > gfx.vy ? REDRAW_DOOR_2 : REDRAW_DOOR_3);
285 static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
289 while (tag != GDI_END)
294 gi->custom_id = va_arg(ap, int);
297 case GDI_CUSTOM_TYPE_ID:
298 gi->custom_type_id = va_arg(ap, int);
303 int max_textsize = MAX_INFO_TEXTSIZE - 1;
304 char *text = va_arg(ap, char *);
307 strncpy(gi->info_text, text, max_textsize);
311 gi->info_text[max_textsize] = '\0';
316 gi->x = va_arg(ap, int);
320 gi->y = va_arg(ap, int);
324 gi->width = va_arg(ap, int);
328 gi->height = va_arg(ap, int);
332 gi->type = va_arg(ap, unsigned long);
336 gi->state = va_arg(ap, unsigned long);
340 /* take care here: "boolean" is typedef'ed as "unsigned char",
341 which gets promoted to "int" */
342 gi->checked = (boolean)va_arg(ap, int);
346 gi->radio_nr = va_arg(ap, unsigned long);
349 case GDI_NUMBER_VALUE:
350 gi->text.number_value = va_arg(ap, long);
351 sprintf(gi->text.value, "%d", gi->text.number_value);
352 gi->text.cursor_position = strlen(gi->text.value);
356 gi->text.number_min = va_arg(ap, long);
357 if (gi->text.number_value < gi->text.number_min)
359 gi->text.number_value = gi->text.number_min;
360 sprintf(gi->text.value, "%d", gi->text.number_value);
365 gi->text.number_max = va_arg(ap, long);
366 if (gi->text.number_value > gi->text.number_max)
368 gi->text.number_value = gi->text.number_max;
369 sprintf(gi->text.value, "%d", gi->text.number_value);
375 int max_textsize = MAX_GADGET_TEXTSIZE;
378 max_textsize = MIN(gi->text.size, MAX_GADGET_TEXTSIZE - 1);
380 strncpy(gi->text.value, va_arg(ap, char *), max_textsize);
381 gi->text.value[max_textsize] = '\0';
382 gi->text.cursor_position = strlen(gi->text.value);
388 int tag_value = va_arg(ap, int);
389 int max_textsize = MIN(tag_value, MAX_GADGET_TEXTSIZE - 1);
391 gi->text.size = max_textsize;
392 gi->text.value[max_textsize] = '\0';
397 gi->text.font_type = va_arg(ap, int);
400 case GDI_DESIGN_UNPRESSED:
401 gi->design[GD_BUTTON_UNPRESSED].bitmap = va_arg(ap, Bitmap *);
402 gi->design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
403 gi->design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
406 case GDI_DESIGN_PRESSED:
407 gi->design[GD_BUTTON_PRESSED].bitmap = va_arg(ap, Bitmap *);
408 gi->design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
409 gi->design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
412 case GDI_ALT_DESIGN_UNPRESSED:
413 gi->alt_design[GD_BUTTON_UNPRESSED].bitmap= va_arg(ap, Bitmap *);
414 gi->alt_design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
415 gi->alt_design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
418 case GDI_ALT_DESIGN_PRESSED:
419 gi->alt_design[GD_BUTTON_PRESSED].bitmap = va_arg(ap, Bitmap *);
420 gi->alt_design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
421 gi->alt_design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
424 case GDI_BORDER_SIZE:
425 gi->border.size = va_arg(ap, int);
428 case GDI_TEXTINPUT_DESIGN_WIDTH:
429 gi->border.width = va_arg(ap, int);
432 case GDI_DECORATION_DESIGN:
433 gi->deco.design.bitmap = va_arg(ap, Bitmap *);
434 gi->deco.design.x = va_arg(ap, int);
435 gi->deco.design.y = va_arg(ap, int);
438 case GDI_DECORATION_POSITION:
439 gi->deco.x = va_arg(ap, int);
440 gi->deco.y = va_arg(ap, int);
443 case GDI_DECORATION_SIZE:
444 gi->deco.width = va_arg(ap, int);
445 gi->deco.height = va_arg(ap, int);
448 case GDI_DECORATION_SHIFTING:
449 gi->deco.xshift = va_arg(ap, int);
450 gi->deco.yshift = va_arg(ap, int);
454 gi->event_mask = va_arg(ap, unsigned long);
458 gi->drawing.area_xsize = va_arg(ap, int);
459 gi->drawing.area_ysize = va_arg(ap, int);
461 /* determine dependent values for drawing area gadget, if needed */
462 if (gi->width == 0 && gi->height == 0 &&
463 gi->drawing.item_xsize !=0 && gi->drawing.item_ysize !=0)
465 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
466 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
468 else if (gi->drawing.item_xsize == 0 && gi->drawing.item_ysize == 0 &&
469 gi->width != 0 && gi->height != 0)
471 gi->drawing.item_xsize = gi->width / gi->drawing.area_xsize;
472 gi->drawing.item_ysize = gi->height / gi->drawing.area_ysize;
477 gi->drawing.item_xsize = va_arg(ap, int);
478 gi->drawing.item_ysize = va_arg(ap, int);
480 /* determine dependent values for drawing area gadget, if needed */
481 if (gi->width == 0 && gi->height == 0 &&
482 gi->drawing.area_xsize !=0 && gi->drawing.area_ysize !=0)
484 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
485 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
487 else if (gi->drawing.area_xsize == 0 && gi->drawing.area_ysize == 0 &&
488 gi->width != 0 && gi->height != 0)
490 gi->drawing.area_xsize = gi->width / gi->drawing.item_xsize;
491 gi->drawing.area_ysize = gi->height / gi->drawing.item_ysize;
495 case GDI_SCROLLBAR_ITEMS_MAX:
496 gi->scrollbar.items_max = va_arg(ap, int);
499 case GDI_SCROLLBAR_ITEMS_VISIBLE:
500 gi->scrollbar.items_visible = va_arg(ap, int);
503 case GDI_SCROLLBAR_ITEM_POSITION:
504 gi->scrollbar.item_position = va_arg(ap, int);
507 case GDI_CALLBACK_INFO:
508 gi->callback_info = va_arg(ap, gadget_function);
511 case GDI_CALLBACK_ACTION:
512 gi->callback_action = va_arg(ap, gadget_function);
516 Error(ERR_EXIT, "HandleGadgetTags(): unknown tag %d", tag);
519 tag = va_arg(ap, int); /* read next tag */
522 /* check if gadget complete */
523 if (gi->type != GD_TYPE_DRAWING_AREA &&
524 (!gi->design[GD_BUTTON_UNPRESSED].bitmap ||
525 !gi->design[GD_BUTTON_PRESSED].bitmap))
526 Error(ERR_EXIT, "gadget incomplete (missing Bitmap)");
528 /* adjust gadget values in relation to other gadget values */
530 if (gi->type & GD_TYPE_TEXTINPUT)
532 int font_width = getFontWidth(FS_SMALL, gi->text.font_type);
533 int font_height = getFontHeight(FS_SMALL, gi->text.font_type);
535 gi->width = 2 * gi->border.size + (gi->text.size + 1) * font_width;
536 gi->height = 2 * gi->border.size + font_height;
539 if (gi->type & GD_TYPE_TEXTINPUT_NUMERIC)
541 struct GadgetTextInput *text = &gi->text;
542 int value = text->number_value;
544 text->number_value = (value < text->number_min ? text->number_min :
545 value > text->number_max ? text->number_max :
548 sprintf(text->value, "%d", text->number_value);
551 if (gi->type & GD_TYPE_SCROLLBAR)
553 struct GadgetScrollbar *gs = &gi->scrollbar;
555 if (gi->width == 0 || gi->height == 0 ||
556 gs->items_max == 0 || gs->items_visible == 0)
557 Error(ERR_EXIT, "scrollbar gadget incomplete (missing tags)");
559 /* calculate internal scrollbar values */
560 gs->size_max = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ?
561 gi->height : gi->width);
562 gs->size = gs->size_max * gs->items_visible / gs->items_max;
563 gs->position = gs->size_max * gs->item_position / gs->items_max;
564 gs->position_max = gs->size_max - gs->size;
565 gs->correction = gs->size_max / gs->items_max / 2;
567 /* finetuning for maximal right/bottom position */
568 if (gs->item_position == gs->items_max - gs->items_visible)
569 gs->position = gs->position_max;
573 void ModifyGadget(struct GadgetInfo *gi, int first_tag, ...)
577 va_start(ap, first_tag);
578 HandleGadgetTags(gi, first_tag, ap);
584 void RedrawGadget(struct GadgetInfo *gi)
587 DrawGadget(gi, gi->state, DG_DIRECT);
590 struct GadgetInfo *CreateGadget(int first_tag, ...)
592 struct GadgetInfo *new_gadget = checked_calloc(sizeof(struct GadgetInfo));
595 /* always start with reliable default values */
596 new_gadget->id = getNewGadgetID();
597 new_gadget->callback_info = default_callback_info;
598 new_gadget->callback_action = default_callback_action;
599 new_gadget->next = NULL;
601 va_start(ap, first_tag);
602 HandleGadgetTags(new_gadget, first_tag, ap);
605 /* insert new gadget into global gadget list */
606 if (gadget_list_last_entry)
608 gadget_list_last_entry->next = new_gadget;
609 gadget_list_last_entry = gadget_list_last_entry->next;
612 gadget_list_first_entry = gadget_list_last_entry = new_gadget;
617 void FreeGadget(struct GadgetInfo *gi)
619 struct GadgetInfo *gi_previous = gadget_list_first_entry;
621 while (gi_previous && gi_previous->next != gi)
622 gi_previous = gi_previous->next;
624 if (gi == gadget_list_first_entry)
625 gadget_list_first_entry = gi->next;
627 if (gi == gadget_list_last_entry)
628 gadget_list_last_entry = gi_previous;
631 gi_previous->next = gi->next;
636 static void CheckRangeOfNumericInputGadget(struct GadgetInfo *gi)
638 if (gi->type != GD_TYPE_TEXTINPUT_NUMERIC)
641 gi->text.number_value = atoi(gi->text.value);
643 if (gi->text.number_value < gi->text.number_min)
644 gi->text.number_value = gi->text.number_min;
645 if (gi->text.number_value > gi->text.number_max)
646 gi->text.number_value = gi->text.number_max;
648 sprintf(gi->text.value, "%d", gi->text.number_value);
650 if (gi->text.cursor_position < 0)
651 gi->text.cursor_position = 0;
652 else if (gi->text.cursor_position > strlen(gi->text.value))
653 gi->text.cursor_position = strlen(gi->text.value);
656 /* global pointer to gadget actually in use (when mouse button pressed) */
657 static struct GadgetInfo *last_gi = NULL;
659 static void MapGadgetExt(struct GadgetInfo *gi, boolean redraw)
661 if (gi == NULL || gi->mapped)
667 DrawGadget(gi, DG_UNPRESSED, DG_BUFFERED);
670 void MapGadget(struct GadgetInfo *gi)
672 MapGadgetExt(gi, TRUE);
675 void UnmapGadget(struct GadgetInfo *gi)
677 if (gi == NULL || !gi->mapped)
686 #define MAX_NUM_GADGETS 1024
687 #define MULTIMAP_UNMAP (1 << 0)
688 #define MULTIMAP_REMAP (1 << 1)
689 #define MULTIMAP_REDRAW (1 << 2)
690 #define MULTIMAP_PLAYFIELD (1 << 3)
691 #define MULTIMAP_DOOR_1 (1 << 4)
692 #define MULTIMAP_DOOR_2 (1 << 5)
693 #define MULTIMAP_ALL (MULTIMAP_PLAYFIELD | \
697 static void MultiMapGadgets(int mode)
699 struct GadgetInfo *gi = gadget_list_first_entry;
700 static boolean map_state[MAX_NUM_GADGETS];
705 if ((mode & MULTIMAP_PLAYFIELD &&
706 gi->x < gfx.sx + gfx.sxsize) ||
707 (mode & MULTIMAP_DOOR_1 &&
708 gi->x >= gfx.dx && gi->y < gfx.dy + gfx.dysize) ||
709 (mode & MULTIMAP_DOOR_2 &&
710 gi->x >= gfx.dx && gi->y > gfx.dy + gfx.dysize) ||
711 (mode & MULTIMAP_ALL) == MULTIMAP_ALL)
713 if (mode & MULTIMAP_UNMAP)
715 map_state[map_count++ % MAX_NUM_GADGETS] = gi->mapped;
720 if (map_state[map_count++ % MAX_NUM_GADGETS])
721 MapGadgetExt(gi, (mode & MULTIMAP_REDRAW));
729 void UnmapAllGadgets()
731 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_UNMAP);
734 void RemapAllGadgets()
736 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_REMAP);
739 boolean anyTextGadgetActive()
741 return (last_gi && last_gi->type & GD_TYPE_TEXTINPUT && last_gi->mapped);
744 void ClickOnGadget(struct GadgetInfo *gi, int button)
746 /* simulate releasing mouse button over last gadget, if still pressed */
748 HandleGadgets(-1, -1, 0);
750 /* simulate pressing mouse button over specified gadget */
751 HandleGadgets(gi->x, gi->y, button);
753 /* simulate releasing mouse button over specified gadget */
754 HandleGadgets(gi->x, gi->y, 0);
757 void HandleGadgets(int mx, int my, int button)
759 static struct GadgetInfo *last_info_gi = NULL;
760 static unsigned long pressed_delay = 0;
761 static int last_button = 0;
762 static int last_mx = 0, last_my = 0;
763 int scrollbar_mouse_pos = 0;
764 struct GadgetInfo *new_gi, *gi;
766 boolean release_event;
767 boolean mouse_moving;
768 boolean gadget_pressed;
769 boolean gadget_pressed_repeated;
770 boolean gadget_moving;
771 boolean gadget_moving_inside;
772 boolean gadget_moving_off_borders;
773 boolean gadget_released;
774 boolean gadget_released_inside;
775 boolean gadget_released_off_borders;
776 boolean changed_position = FALSE;
778 /* check if there are any gadgets defined */
779 if (gadget_list_first_entry == NULL)
782 /* check which gadget is under the mouse pointer */
783 new_gi = getGadgetInfoFromMousePosition(mx, my);
785 /* check if button state has changed since last invocation */
786 press_event = (button != 0 && last_button == 0);
787 release_event = (button == 0 && last_button != 0);
788 last_button = button;
790 /* check if mouse has been moved since last invocation */
791 mouse_moving = ((mx != last_mx || my != last_my) && motion_status);
795 /* special treatment for text and number input gadgets */
796 if (anyTextGadgetActive() && button != 0 && !motion_status)
798 struct GadgetInfo *gi = last_gi;
800 if (new_gi == last_gi)
802 /* if mouse button pressed inside activated text gadget, set cursor */
803 gi->text.cursor_position =
804 (mx - gi->x - gi->border.size) /
805 getFontWidth(FS_SMALL, gi->text.font_type);
807 if (gi->text.cursor_position < 0)
808 gi->text.cursor_position = 0;
809 else if (gi->text.cursor_position > strlen(gi->text.value))
810 gi->text.cursor_position = strlen(gi->text.value);
812 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
816 /* if mouse button pressed outside text input gadget, deactivate it */
817 CheckRangeOfNumericInputGadget(gi);
818 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
820 gi->event.type = GD_EVENT_TEXT_LEAVING;
822 if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
823 gi->callback_action(gi);
830 (button != 0 && last_gi == NULL && new_gi != NULL && press_event);
831 gadget_pressed_repeated =
832 (button != 0 && last_gi != NULL && new_gi == last_gi);
834 gadget_released = (release_event && last_gi != NULL);
835 gadget_released_inside = (gadget_released && new_gi == last_gi);
836 gadget_released_off_borders = (gadget_released && new_gi != last_gi);
838 gadget_moving = (button != 0 && last_gi != NULL && mouse_moving);
839 gadget_moving_inside = (gadget_moving && new_gi == last_gi);
840 gadget_moving_off_borders = (gadget_moving && new_gi != last_gi);
842 /* if new gadget pressed, store this gadget */
846 /* 'gi' is actually handled gadget */
849 /* if gadget is scrollbar, choose mouse position value */
850 if (gi && gi->type & GD_TYPE_SCROLLBAR)
851 scrollbar_mouse_pos =
852 (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? mx - gi->x : my - gi->y);
854 /* if mouse button released, no gadget needs to be handled anymore */
855 if (button == 0 && last_gi && !(last_gi->type & GD_TYPE_TEXTINPUT))
858 /* modify event position values even if no gadget is pressed */
859 if (button == 0 && !release_event)
864 int last_x = gi->event.x;
865 int last_y = gi->event.y;
867 gi->event.x = mx - gi->x;
868 gi->event.y = my - gi->y;
870 if (gi->type == GD_TYPE_DRAWING_AREA)
872 gi->event.x /= gi->drawing.item_xsize;
873 gi->event.y /= gi->drawing.item_ysize;
875 if (last_x != gi->event.x || last_y != gi->event.y)
876 changed_position = TRUE;
880 /* handle gadget popup info text */
881 if (last_info_gi != new_gi ||
882 (new_gi && new_gi->type == GD_TYPE_DRAWING_AREA && changed_position))
884 if (new_gi != NULL && (button == 0 || new_gi == last_gi))
886 new_gi->event.type = GD_EVENT_INFO_ENTERING;
887 new_gi->callback_info(new_gi);
889 else if (last_info_gi != NULL)
891 last_info_gi->event.type = GD_EVENT_INFO_LEAVING;
892 last_info_gi->callback_info(last_info_gi);
895 default_callback_info(NULL);
897 printf("It seems that we are leaving gadget [%s]!\n",
898 (last_info_gi != NULL &&
899 last_info_gi->info_text != NULL ?
900 last_info_gi->info_text : ""));
904 last_info_gi = new_gi;
909 if (gi->type == GD_TYPE_CHECK_BUTTON)
911 gi->checked = !gi->checked;
913 else if (gi->type == GD_TYPE_RADIO_BUTTON)
915 struct GadgetInfo *rgi = gadget_list_first_entry;
920 rgi->type == GD_TYPE_RADIO_BUTTON &&
921 rgi->radio_nr == gi->radio_nr &&
924 rgi->checked = FALSE;
925 DrawGadget(rgi, DG_UNPRESSED, DG_DIRECT);
933 else if (gi->type & GD_TYPE_SCROLLBAR)
937 if (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL)
948 if (mpos >= gpos + gi->scrollbar.position &&
949 mpos < gpos + gi->scrollbar.position + gi->scrollbar.size)
952 gi->scrollbar.drag_position =
953 scrollbar_mouse_pos - gi->scrollbar.position;
957 /* click scrollbar one scrollbar length up/left or down/right */
959 struct GadgetScrollbar *gs = &gi->scrollbar;
960 int old_item_position = gs->item_position;
962 changed_position = FALSE;
965 gs->items_visible * (mpos < gpos + gi->scrollbar.position ? -1 : +1);
967 if (gs->item_position < 0)
968 gs->item_position = 0;
969 if (gs->item_position > gs->items_max - gs->items_visible)
970 gs->item_position = gs->items_max - gs->items_visible;
972 if (old_item_position != gs->item_position)
974 gi->event.item_position = gs->item_position;
975 changed_position = TRUE;
978 ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, gs->item_position,
981 gi->state = GD_BUTTON_UNPRESSED;
982 gi->event.type = GD_EVENT_MOVING;
983 gi->event.off_borders = FALSE;
985 if (gi->event_mask & GD_EVENT_MOVING && changed_position)
986 gi->callback_action(gi);
988 /* don't handle this scrollbar anymore while mouse button pressed */
995 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
997 gi->state = GD_BUTTON_PRESSED;
998 gi->event.type = GD_EVENT_PRESSED;
999 gi->event.button = button;
1000 gi->event.off_borders = FALSE;
1002 /* initialize delay counter */
1003 DelayReached(&pressed_delay, 0);
1005 if (gi->event_mask & GD_EVENT_PRESSED)
1006 gi->callback_action(gi);
1009 if (gadget_pressed_repeated)
1011 gi->event.type = GD_EVENT_PRESSED;
1013 if (gi->event_mask & GD_EVENT_REPEATED &&
1014 DelayReached(&pressed_delay, GADGET_FRAME_DELAY))
1015 gi->callback_action(gi);
1020 if (gi->type & GD_TYPE_BUTTON)
1022 if (gadget_moving_inside && gi->state == GD_BUTTON_UNPRESSED)
1023 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1024 else if (gadget_moving_off_borders && gi->state == GD_BUTTON_PRESSED)
1025 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1028 if (gi->type & GD_TYPE_SCROLLBAR)
1030 struct GadgetScrollbar *gs = &gi->scrollbar;
1031 int old_item_position = gs->item_position;
1033 gs->position = scrollbar_mouse_pos - gs->drag_position;
1035 if (gs->position < 0)
1037 if (gs->position > gs->position_max)
1038 gs->position = gs->position_max;
1041 gs->items_max * (gs->position + gs->correction) / gs->size_max;
1043 if (gs->item_position < 0)
1044 gs->item_position = 0;
1045 if (gs->item_position > gs->items_max - 1)
1046 gs->item_position = gs->items_max - 1;
1048 if (old_item_position != gs->item_position)
1050 gi->event.item_position = gs->item_position;
1051 changed_position = TRUE;
1054 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1057 gi->state = (gadget_moving_inside || gi->type & GD_TYPE_SCROLLBAR ?
1058 GD_BUTTON_PRESSED : GD_BUTTON_UNPRESSED);
1059 gi->event.type = GD_EVENT_MOVING;
1060 gi->event.off_borders = gadget_moving_off_borders;
1062 if (gi->event_mask & GD_EVENT_MOVING && changed_position &&
1063 (gadget_moving_inside || gi->event_mask & GD_EVENT_OFF_BORDERS))
1064 gi->callback_action(gi);
1067 if (gadget_released_inside)
1069 if (!(gi->type & GD_TYPE_TEXTINPUT))
1070 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1072 gi->state = GD_BUTTON_UNPRESSED;
1073 gi->event.type = GD_EVENT_RELEASED;
1075 if (gi->event_mask & GD_EVENT_RELEASED)
1076 gi->callback_action(gi);
1079 if (gadget_released_off_borders)
1081 if (gi->type & GD_TYPE_SCROLLBAR)
1082 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1084 gi->event.type = GD_EVENT_RELEASED;
1086 if (gi->event_mask & GD_EVENT_RELEASED &&
1087 gi->event_mask & GD_EVENT_OFF_BORDERS)
1088 gi->callback_action(gi);
1092 void HandleGadgetsKeyInput(Key key)
1094 struct GadgetInfo *gi = last_gi;
1095 char text[MAX_GADGET_TEXTSIZE];
1099 boolean legal_letter;
1101 if (gi == NULL || !(gi->type & GD_TYPE_TEXTINPUT) || !gi->mapped)
1104 text_length = strlen(gi->text.value);
1105 cursor_pos = gi->text.cursor_position;
1106 letter = getCharFromKey(key);
1107 legal_letter = (gi->type == GD_TYPE_TEXTINPUT_NUMERIC ?
1108 letter >= '0' && letter <= '9' :
1111 if (legal_letter && text_length < gi->text.size)
1113 strcpy(text, gi->text.value);
1114 strcpy(&gi->text.value[cursor_pos + 1], &text[cursor_pos]);
1115 gi->text.value[cursor_pos] = letter;
1116 gi->text.cursor_position++;
1117 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1119 else if (key == KSYM_Left && cursor_pos > 0)
1121 gi->text.cursor_position--;
1122 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1124 else if (key == KSYM_Right && cursor_pos < text_length)
1126 gi->text.cursor_position++;
1127 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1129 else if (key == KSYM_BackSpace && cursor_pos > 0)
1131 strcpy(text, gi->text.value);
1132 strcpy(&gi->text.value[cursor_pos - 1], &text[cursor_pos]);
1133 gi->text.cursor_position--;
1134 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1136 else if (key == KSYM_Delete && cursor_pos < text_length)
1138 strcpy(text, gi->text.value);
1139 strcpy(&gi->text.value[cursor_pos], &text[cursor_pos + 1]);
1140 DrawGadget(gi, DG_PRESSED, DG_DIRECT);
1142 else if (key == KSYM_Return)
1144 CheckRangeOfNumericInputGadget(gi);
1145 DrawGadget(gi, DG_UNPRESSED, DG_DIRECT);
1147 gi->event.type = GD_EVENT_TEXT_RETURN;
1149 if (gi->event_mask & GD_EVENT_TEXT_RETURN)
1150 gi->callback_action(gi);