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
29 static struct GadgetInfo *gadget_list_first_entry = NULL;
30 static struct GadgetInfo *gadget_list_last_entry = NULL;
31 static struct GadgetInfo *last_info_gi = NULL;
32 static int next_free_gadget_id = 1;
33 static boolean gadget_id_wrapped = FALSE;
35 static struct GadgetInfo *getGadgetInfoFromGadgetID(int id)
37 struct GadgetInfo *gi = gadget_list_first_entry;
39 while (gi && gi->id != id)
45 static int getNewGadgetID()
47 int id = next_free_gadget_id++;
49 if (next_free_gadget_id <= 0) /* counter overrun */
51 gadget_id_wrapped = TRUE; /* now we must check each ID */
52 next_free_gadget_id = 0;
55 if (gadget_id_wrapped)
57 next_free_gadget_id++;
58 while (getGadgetInfoFromGadgetID(next_free_gadget_id) != NULL)
59 next_free_gadget_id++;
62 if (next_free_gadget_id <= 0) /* cannot get new gadget id */
63 Error(ERR_EXIT, "too much gadgets -- this should not happen");
68 static struct GadgetInfo *getGadgetInfoFromMousePosition(int mx, int my,
71 struct GadgetInfo *gi;
73 /* first check for scrollbars in case of mouse scroll wheel button events */
74 if (button == 4 || button == 5)
76 /* check for the first active scrollbar with matching mouse wheel area */
77 for (gi = gadget_list_first_entry; gi != NULL; gi = gi->next)
79 if (gi->mapped && gi->active &&
80 gi->type & GD_TYPE_SCROLLBAR &&
81 mx >= gi->wheelarea.x && mx < gi->wheelarea.x + gi->wheelarea.width &&
82 my >= gi->wheelarea.y && my < gi->wheelarea.y + gi->wheelarea.height)
86 /* no active scrollbar found -- ignore this scroll wheel button event */
90 /* open selectboxes may overlap other active gadgets, so check them first */
91 for (gi = gadget_list_first_entry; gi != NULL; gi = gi->next)
93 if (gi->mapped && gi->active &&
94 gi->type & GD_TYPE_SELECTBOX && gi->selectbox.open &&
95 mx >= gi->selectbox.x && mx < gi->selectbox.x + gi->selectbox.width &&
96 my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
100 /* check all other gadgets */
101 for (gi = gadget_list_first_entry; gi != NULL; gi = gi->next)
103 if (gi->mapped && gi->active &&
104 mx >= gi->x && mx < gi->x + gi->width &&
105 my >= gi->y && my < gi->y + gi->height)
112 static void setTextAreaCursorExt(struct GadgetInfo *gi, boolean set_cursor_pos)
114 char *text = gi->textarea.value;
115 int area_xsize = gi->textarea.xsize;
116 int area_ysize = gi->textarea.ysize;
117 int cursor_position = gi->textarea.cursor_position;
118 int cursor_x = gi->textarea.cursor_x;
119 int cursor_y = gi->textarea.cursor_y;
126 if (set_cursor_pos) /* x/y => position */
128 if (y == cursor_y && (x == cursor_x || (x < cursor_x && *text == '\n')))
131 else /* position => x/y */
133 if (pos == cursor_position)
137 if (x + 1 >= area_xsize || *text == '\n')
139 if (y + 1 >= area_ysize)
152 gi->textarea.cursor_x = x;
153 gi->textarea.cursor_y = y;
154 gi->textarea.cursor_x_preferred = x;
155 gi->textarea.cursor_position = pos;
158 static void setTextAreaCursorXY(struct GadgetInfo *gi, int x, int y)
160 gi->textarea.cursor_x = x;
161 gi->textarea.cursor_y = y;
163 setTextAreaCursorExt(gi, TRUE);
166 static void setTextAreaCursorPosition(struct GadgetInfo *gi, int pos)
168 gi->textarea.cursor_position = pos;
170 setTextAreaCursorExt(gi, FALSE);
173 static void default_callback_info(void *ptr)
178 static void default_callback_action(void *ptr)
183 static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
185 struct GadgetDesign *gd;
186 int state = (pressed ? GD_BUTTON_PRESSED : GD_BUTTON_UNPRESSED);
187 boolean redraw_selectbox = FALSE;
192 gd = (!gi->active ? &gi->alt_design[state] :
193 gi->checked ? &gi->alt_design[state] : &gi->design[state]);
197 case GD_TYPE_NORMAL_BUTTON:
198 case GD_TYPE_CHECK_BUTTON:
199 case GD_TYPE_RADIO_BUTTON:
200 BlitBitmapOnBackground(gd->bitmap, drawto,
201 gd->x, gd->y, gi->width, gi->height,
203 if (gi->deco.design.bitmap)
204 BlitBitmap(gi->deco.design.bitmap, drawto,
205 gi->deco.design.x, gi->deco.design.y,
206 gi->deco.width, gi->deco.height,
207 gi->x + gi->deco.x + (pressed ? gi->deco.xshift : 0),
208 gi->y + gi->deco.y + (pressed ? gi->deco.yshift : 0));
211 case GD_TYPE_TEXT_BUTTON:
214 int font_nr = (gi->active ? gi->font_active : gi->font);
215 int font_width = getFontWidth(font_nr);
216 int border_x = gi->border.xsize;
217 int border_y = gi->border.ysize;
218 int text_size = strlen(gi->textbutton.value);
219 int text_start = (gi->width - text_size * font_width) / 2;
221 /* left part of gadget */
222 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
223 border_x, gi->height, gi->x, gi->y);
225 /* middle part of gadget */
226 for (i=0; i < gi->textbutton.size; i++)
227 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
228 font_width, gi->height,
229 gi->x + border_x + i * font_width, gi->y);
231 /* right part of gadget */
232 BlitBitmapOnBackground(gd->bitmap, drawto,
233 gd->x + gi->border.width - border_x, gd->y,
234 border_x, gi->height,
235 gi->x + gi->width - border_x, gi->y);
237 /* gadget text value */
239 gi->x + text_start + (pressed ? gi->deco.xshift : 0),
240 gi->y + border_y + (pressed ? gi->deco.yshift : 0),
241 gi->textbutton.value, font_nr, BLIT_MASKED);
245 case GD_TYPE_TEXT_INPUT_ALPHANUMERIC:
246 case GD_TYPE_TEXT_INPUT_NUMERIC:
250 char cursor_string[2];
251 char text[MAX_GADGET_TEXTSIZE + 1];
252 int font_nr = (pressed ? gi->font_active : gi->font);
253 int font_width = getFontWidth(font_nr);
254 int border_x = gi->border.xsize;
255 int border_y = gi->border.ysize;
257 /* left part of gadget */
258 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
259 border_x, gi->height, gi->x, gi->y);
261 /* middle part of gadget */
262 for (i=0; i < gi->textinput.size + 1; i++)
263 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
264 font_width, gi->height,
265 gi->x + border_x + i * font_width, gi->y);
267 /* right part of gadget */
268 BlitBitmapOnBackground(gd->bitmap, drawto,
269 gd->x + gi->border.width - border_x, gd->y,
270 border_x, gi->height,
271 gi->x + gi->width - border_x, gi->y);
274 strcpy(text, gi->textinput.value);
277 /* gadget text value */
279 gi->x + border_x, gi->y + border_y, text,
280 font_nr, BLIT_MASKED);
282 cursor_letter = gi->textinput.value[gi->textinput.cursor_position];
283 cursor_string[0] = (cursor_letter != '\0' ? cursor_letter : ' ');
284 cursor_string[1] = '\0';
286 /* draw cursor, if active */
290 gi->textinput.cursor_position * font_width,
291 gi->y + border_y, cursor_string,
292 font_nr, BLIT_INVERSE);
296 case GD_TYPE_TEXT_AREA:
300 char cursor_string[2];
301 int font_nr = (pressed ? gi->font_active : gi->font);
302 int font_width = getFontWidth(font_nr);
303 int font_height = getFontHeight(font_nr);
304 int border_x = gi->border.xsize;
305 int border_y = gi->border.ysize;
306 int gd_height = 2 * border_y + font_height;
308 /* top left part of gadget border */
309 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
310 border_x, border_y, gi->x, gi->y);
312 /* top middle part of gadget border */
313 for (i=0; i < gi->textarea.xsize; i++)
314 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
315 font_width, border_y,
316 gi->x + border_x + i * font_width, gi->y);
318 /* top right part of gadget border */
319 BlitBitmapOnBackground(gd->bitmap, drawto,
320 gd->x + gi->border.width - border_x, gd->y,
322 gi->x + gi->width - border_x, gi->y);
324 /* left and right part of gadget border for each row */
325 for (i=0; i < gi->textarea.ysize; i++)
327 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y + border_y,
328 border_x, font_height,
329 gi->x, gi->y + border_y + i * font_height);
330 BlitBitmapOnBackground(gd->bitmap, drawto,
331 gd->x + gi->border.width - border_x,
333 border_x, font_height,
334 gi->x + gi->width - border_x,
335 gi->y + border_y + i * font_height);
338 /* bottom left part of gadget border */
339 BlitBitmapOnBackground(gd->bitmap, drawto,
340 gd->x, gd->y + gd_height - border_y,
342 gi->x, gi->y + gi->height - border_y);
344 /* bottom middle part of gadget border */
345 for (i=0; i < gi->textarea.xsize; i++)
346 BlitBitmapOnBackground(gd->bitmap, drawto,
348 gd->y + gd_height - border_y,
349 font_width, border_y,
350 gi->x + border_x + i * font_width,
351 gi->y + gi->height - border_y);
353 /* bottom right part of gadget border */
354 BlitBitmapOnBackground(gd->bitmap, drawto,
355 gd->x + gi->border.width - border_x,
356 gd->y + gd_height - border_y,
358 gi->x + gi->width - border_x,
359 gi->y + gi->height - border_y);
361 ClearRectangleOnBackground(drawto,
364 gi->width - 2 * border_x,
365 gi->height - 2 * border_y);
367 /* gadget text value */
368 DrawTextToTextArea(gi->x + border_x, gi->y + border_y,
369 gi->textarea.value, font_nr, gi->textarea.xsize,
370 gi->textarea.xsize, gi->textarea.ysize,
373 cursor_letter = gi->textarea.value[gi->textarea.cursor_position];
374 cursor_string[0] = (cursor_letter != '\0' ? cursor_letter : ' ');
375 cursor_string[1] = '\0';
377 /* draw cursor, if active */
380 gi->x + border_x + gi->textarea.cursor_x * font_width,
381 gi->y + border_y + gi->textarea.cursor_y * font_height,
383 font_nr, BLIT_INVERSE);
387 case GD_TYPE_SELECTBOX:
390 char text[MAX_GADGET_TEXTSIZE + 1];
391 int font_nr = (pressed ? gi->font_active : gi->font);
392 int font_width = getFontWidth(font_nr);
393 int font_height = getFontHeight(font_nr);
394 int border_x = gi->border.xsize;
395 int border_y = gi->border.ysize;
396 int button = gi->border.xsize_selectbutton;
397 int width_inner = gi->border.width - button - 2 * border_x;
398 int box_width = gi->selectbox.width;
399 int box_height = gi->selectbox.height;
401 /* left part of gadget */
402 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
403 border_x, gi->height, gi->x, gi->y);
405 /* middle part of gadget */
406 for (i=0; i < gi->selectbox.size; i++)
407 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
408 font_width, gi->height,
409 gi->x + border_x + i * font_width, gi->y);
411 /* button part of gadget */
412 BlitBitmapOnBackground(gd->bitmap, drawto,
413 gd->x + border_x + width_inner, gd->y,
415 gi->x + gi->width - border_x - button, gi->y);
417 /* right part of gadget */
418 BlitBitmapOnBackground(gd->bitmap, drawto,
419 gd->x + gi->border.width - border_x, gd->y,
420 border_x, gi->height,
421 gi->x + gi->width - border_x, gi->y);
424 strncpy(text, gi->selectbox.options[gi->selectbox.index].text,
426 text[gi->selectbox.size] = '\0';
428 /* gadget text value */
429 DrawTextExt(drawto, gi->x + border_x, gi->y + border_y, text,
430 font_nr, BLIT_MASKED);
434 if (!gi->selectbox.open)
436 gi->selectbox.open = TRUE;
437 gi->selectbox.stay_open = FALSE;
438 gi->selectbox.current_index = gi->selectbox.index;
440 /* save background under selectbox */
441 BlitBitmap(drawto, gfx.field_save_buffer,
442 gi->selectbox.x, gi->selectbox.y,
443 gi->selectbox.width, gi->selectbox.height,
444 gi->selectbox.x, gi->selectbox.y);
447 /* draw open selectbox */
449 /* top left part of gadget border */
450 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
452 gi->selectbox.x, gi->selectbox.y);
454 /* top middle part of gadget border */
455 for (i=0; i < gi->selectbox.size; i++)
456 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
457 font_width, border_y,
458 gi->selectbox.x + border_x + i * font_width,
461 /* top button part of gadget border */
462 BlitBitmapOnBackground(gd->bitmap, drawto,
463 gd->x + border_x + width_inner, gd->y,
465 gi->selectbox.x + box_width -border_x -button,
468 /* top right part of gadget border */
469 BlitBitmapOnBackground(gd->bitmap, drawto,
470 gd->x + gi->border.width - border_x, gd->y,
472 gi->selectbox.x + box_width - border_x,
475 /* left and right part of gadget border for each row */
476 for (i=0; i < gi->selectbox.num_values; i++)
478 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y + border_y,
479 border_x, font_height,
481 gi->selectbox.y + border_y + i*font_height);
482 BlitBitmapOnBackground(gd->bitmap, drawto,
483 gd->x + gi->border.width - border_x,
485 border_x, font_height,
486 gi->selectbox.x + box_width - border_x,
487 gi->selectbox.y + border_y + i*font_height);
490 /* bottom left part of gadget border */
491 BlitBitmapOnBackground(gd->bitmap, drawto,
492 gd->x, gd->y + gi->height - border_y,
495 gi->selectbox.y + box_height - border_y);
497 /* bottom middle part of gadget border */
498 for (i=0; i < gi->selectbox.size; i++)
499 BlitBitmapOnBackground(gd->bitmap, drawto,
501 gd->y + gi->height - border_y,
502 font_width, border_y,
503 gi->selectbox.x + border_x + i * font_width,
504 gi->selectbox.y + box_height - border_y);
506 /* bottom button part of gadget border */
507 BlitBitmapOnBackground(gd->bitmap, drawto,
508 gd->x + border_x + width_inner,
509 gd->y + gi->height - border_y,
511 gi->selectbox.x + box_width -border_x -button,
512 gi->selectbox.y + box_height - border_y);
514 /* bottom right part of gadget border */
515 BlitBitmapOnBackground(gd->bitmap, drawto,
516 gd->x + gi->border.width - border_x,
517 gd->y + gi->height - border_y,
519 gi->selectbox.x + box_width - border_x,
520 gi->selectbox.y + box_height - border_y);
522 ClearRectangleOnBackground(drawto,
523 gi->selectbox.x + border_x,
524 gi->selectbox.y + border_y,
525 gi->selectbox.width - 2 * border_x,
526 gi->selectbox.height - 2 * border_y);
528 /* selectbox text values */
529 for (i=0; i < gi->selectbox.num_values; i++)
531 int mask_mode = BLIT_MASKED;
533 strncpy(text, gi->selectbox.options[i].text, gi->selectbox.size);
534 text[gi->selectbox.size] = '\0';
536 if (i == gi->selectbox.current_index)
538 FillRectangle(drawto,
539 gi->selectbox.x + border_x,
540 gi->selectbox.y + border_y + i * font_height,
541 gi->selectbox.width - 2 * border_x, font_height,
542 gi->selectbox.inverse_color);
544 /* prevent use of cursor graphic by drawing at least two chars */
546 text[gi->selectbox.size] = '\0';
548 mask_mode = BLIT_INVERSE;
552 gi->selectbox.x + border_x,
553 gi->selectbox.y + border_y + i * font_height, text,
557 redraw_selectbox = TRUE;
559 else if (gi->selectbox.open)
561 gi->selectbox.open = FALSE;
563 /* restore background under selectbox */
564 BlitBitmap(gfx.field_save_buffer, drawto,
565 gi->selectbox.x, gi->selectbox.y,
566 gi->selectbox.width, gi->selectbox.height,
567 gi->selectbox.x, gi->selectbox.y);
569 /* redraw closed selectbox */
570 DrawGadget(gi, FALSE, FALSE);
572 redraw_selectbox = TRUE;
577 case GD_TYPE_SCROLLBAR_VERTICAL:
581 int ypos = gi->y + gi->scrollbar.position;
582 int design_full = gi->width;
583 int design_body = design_full - 2 * gi->border.ysize;
584 int size_full = gi->scrollbar.size;
585 int size_body = size_full - 2 * gi->border.ysize;
586 int num_steps = size_body / design_body;
587 int step_size_remain = size_body - num_steps * design_body;
589 /* clear scrollbar area */
590 ClearRectangleOnBackground(backbuffer, gi->x, gi->y,
591 gi->width, gi->height);
593 /* upper part of gadget */
594 BlitBitmapOnBackground(gd->bitmap, drawto,
596 gi->width, gi->border.ysize,
599 /* middle part of gadget */
600 for (i=0; i < num_steps; i++)
601 BlitBitmapOnBackground(gd->bitmap, drawto,
602 gd->x, gd->y + gi->border.ysize,
603 gi->width, design_body,
605 ypos + gi->border.ysize + i * design_body);
607 /* remaining middle part of gadget */
608 if (step_size_remain > 0)
609 BlitBitmapOnBackground(gd->bitmap, drawto,
610 gd->x, gd->y + gi->border.ysize,
611 gi->width, step_size_remain,
613 ypos + gi->border.ysize
614 + num_steps * design_body);
616 /* lower part of gadget */
617 BlitBitmapOnBackground(gd->bitmap, drawto,
618 gd->x, gd->y + design_full - gi->border.ysize,
619 gi->width, gi->border.ysize,
620 xpos, ypos + size_full - gi->border.ysize);
624 case GD_TYPE_SCROLLBAR_HORIZONTAL:
627 int xpos = gi->x + gi->scrollbar.position;
629 int design_full = gi->height;
630 int design_body = design_full - 2 * gi->border.xsize;
631 int size_full = gi->scrollbar.size;
632 int size_body = size_full - 2 * gi->border.xsize;
633 int num_steps = size_body / design_body;
634 int step_size_remain = size_body - num_steps * design_body;
636 /* clear scrollbar area */
637 ClearRectangleOnBackground(backbuffer, gi->x, gi->y,
638 gi->width, gi->height);
640 /* left part of gadget */
641 BlitBitmapOnBackground(gd->bitmap, drawto,
643 gi->border.xsize, gi->height,
646 /* middle part of gadget */
647 for (i=0; i < num_steps; i++)
648 BlitBitmapOnBackground(gd->bitmap, drawto,
649 gd->x + gi->border.xsize, gd->y,
650 design_body, gi->height,
651 xpos + gi->border.xsize + i * design_body,
654 /* remaining middle part of gadget */
655 if (step_size_remain > 0)
656 BlitBitmapOnBackground(gd->bitmap, drawto,
657 gd->x + gi->border.xsize, gd->y,
658 step_size_remain, gi->height,
659 xpos + gi->border.xsize
660 + num_steps * design_body,
663 /* right part of gadget */
664 BlitBitmapOnBackground(gd->bitmap, drawto,
665 gd->x + design_full - gi->border.xsize, gd->y,
666 gi->border.xsize, gi->height,
667 xpos + size_full - gi->border.xsize, ypos);
677 BlitBitmap(drawto, window,
678 gi->x, gi->y, gi->width, gi->height, gi->x, gi->y);
680 if (gi->type == GD_TYPE_SELECTBOX && redraw_selectbox)
681 BlitBitmap(drawto, window,
682 gi->selectbox.x, gi->selectbox.y,
683 gi->selectbox.width, gi->selectbox.height,
684 gi->selectbox.x, gi->selectbox.y);
687 redraw_mask |= (gi->x < gfx.sx + gfx.sxsize ? REDRAW_FIELD :
688 gi->y < gfx.dy + gfx.dysize ? REDRAW_DOOR_1 :
689 gi->y > gfx.vy ? REDRAW_DOOR_2 : REDRAW_DOOR_3);
692 static int get_minimal_size_for_numeric_input(int minmax_value)
694 int min_size = 1; /* value needs at least one digit */
697 /* add number of digits needed for absolute value */
698 for (i = 10; i <= ABS(minmax_value); i *= 10)
701 /* if min/max value is negative, add one digit for minus sign */
702 if (minmax_value < 0)
708 static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
715 while (tag != GDI_END)
720 gi->custom_id = va_arg(ap, int);
723 case GDI_CUSTOM_TYPE_ID:
724 gi->custom_type_id = va_arg(ap, int);
729 int max_textsize = MAX_INFO_TEXTSIZE - 1;
730 char *text = va_arg(ap, char *);
733 strncpy(gi->info_text, text, max_textsize);
737 gi->info_text[max_textsize] = '\0';
742 gi->x = va_arg(ap, int);
746 gi->y = va_arg(ap, int);
750 gi->width = va_arg(ap, int);
754 gi->height = va_arg(ap, int);
758 gi->type = va_arg(ap, unsigned int);
762 gi->state = va_arg(ap, unsigned int);
766 /* take care here: "boolean" is typedef'ed as "unsigned char",
767 which gets promoted to "int" */
768 gi->active = (boolean)va_arg(ap, int);
771 case GDI_DIRECT_DRAW:
772 /* take care here: "boolean" is typedef'ed as "unsigned char",
773 which gets promoted to "int" */
774 gi->direct_draw = (boolean)va_arg(ap, int);
778 /* take care here: "boolean" is typedef'ed as "unsigned char",
779 which gets promoted to "int" */
780 gi->checked = (boolean)va_arg(ap, int);
784 gi->radio_nr = va_arg(ap, unsigned int);
787 case GDI_NUMBER_VALUE:
788 gi->textinput.number_value = va_arg(ap, int);
789 sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
790 strcpy(gi->textinput.last_value, gi->textinput.value);
791 gi->textinput.cursor_position = strlen(gi->textinput.value);
795 gi->textinput.number_min = va_arg(ap, int);
796 if (gi->textinput.number_value < gi->textinput.number_min)
798 gi->textinput.number_value = gi->textinput.number_min;
799 sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
800 strcpy(gi->textinput.last_value, gi->textinput.value);
805 gi->textinput.number_max = va_arg(ap, int);
806 if (gi->textinput.number_value > gi->textinput.number_max)
808 gi->textinput.number_value = gi->textinput.number_max;
809 sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
810 strcpy(gi->textinput.last_value, gi->textinput.value);
816 int max_textsize = MAX_GADGET_TEXTSIZE;
818 if (gi->textinput.size)
819 max_textsize = MIN(gi->textinput.size, MAX_GADGET_TEXTSIZE - 1);
821 strncpy(gi->textinput.value, va_arg(ap, char *), max_textsize);
822 strcpy(gi->textinput.last_value, gi->textinput.value);
824 gi->textinput.value[max_textsize] = '\0';
825 gi->textinput.cursor_position = strlen(gi->textinput.value);
827 /* same tag also used for other gadget definitions */
828 strcpy(gi->textbutton.value, gi->textinput.value);
829 strcpy(gi->textarea.value, gi->textinput.value);
830 strcpy(gi->textarea.last_value, gi->textinput.value);
836 int tag_value = va_arg(ap, int);
837 int max_textsize = MIN(tag_value, MAX_GADGET_TEXTSIZE - 1);
839 gi->textinput.size = max_textsize;
840 gi->textinput.value[max_textsize] = '\0';
841 strcpy(gi->textinput.last_value, gi->textinput.value);
843 /* same tag also used for other gadget definitions */
845 gi->textarea.size = max_textsize;
846 gi->textarea.value[max_textsize] = '\0';
847 strcpy(gi->textarea.last_value, gi->textinput.value);
849 gi->textbutton.size = max_textsize;
850 gi->textbutton.value[max_textsize] = '\0';
852 gi->selectbox.size = gi->textinput.size;
857 gi->font = va_arg(ap, int);
858 if (gi->font_active == 0)
859 gi->font_active = gi->font;
862 case GDI_TEXT_FONT_ACTIVE:
863 gi->font_active = va_arg(ap, int);
866 case GDI_SELECTBOX_OPTIONS:
867 gi->selectbox.options = va_arg(ap, struct ValueTextInfo *);
870 case GDI_SELECTBOX_INDEX:
871 gi->selectbox.index = va_arg(ap, int);
874 case GDI_DESIGN_UNPRESSED:
875 gi->design[GD_BUTTON_UNPRESSED].bitmap = va_arg(ap, Bitmap *);
876 gi->design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
877 gi->design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
880 case GDI_DESIGN_PRESSED:
881 gi->design[GD_BUTTON_PRESSED].bitmap = va_arg(ap, Bitmap *);
882 gi->design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
883 gi->design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
886 case GDI_ALT_DESIGN_UNPRESSED:
887 gi->alt_design[GD_BUTTON_UNPRESSED].bitmap= va_arg(ap, Bitmap *);
888 gi->alt_design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
889 gi->alt_design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
892 case GDI_ALT_DESIGN_PRESSED:
893 gi->alt_design[GD_BUTTON_PRESSED].bitmap = va_arg(ap, Bitmap *);
894 gi->alt_design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
895 gi->alt_design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
898 case GDI_BORDER_SIZE:
899 gi->border.xsize = va_arg(ap, int);
900 gi->border.ysize = va_arg(ap, int);
903 case GDI_BORDER_SIZE_SELECTBUTTON:
904 gi->border.xsize_selectbutton = va_arg(ap, int);
907 case GDI_DESIGN_WIDTH:
908 gi->border.width = va_arg(ap, int);
911 case GDI_DECORATION_DESIGN:
912 gi->deco.design.bitmap = va_arg(ap, Bitmap *);
913 gi->deco.design.x = va_arg(ap, int);
914 gi->deco.design.y = va_arg(ap, int);
917 case GDI_DECORATION_POSITION:
918 gi->deco.x = va_arg(ap, int);
919 gi->deco.y = va_arg(ap, int);
922 case GDI_DECORATION_SIZE:
923 gi->deco.width = va_arg(ap, int);
924 gi->deco.height = va_arg(ap, int);
927 case GDI_DECORATION_SHIFTING:
928 gi->deco.xshift = va_arg(ap, int);
929 gi->deco.yshift = va_arg(ap, int);
933 gi->event_mask = va_arg(ap, unsigned int);
937 gi->drawing.area_xsize = va_arg(ap, int);
938 gi->drawing.area_ysize = va_arg(ap, int);
940 /* determine dependent values for drawing area gadget, if needed */
941 if (gi->width == 0 && gi->height == 0 &&
942 gi->drawing.item_xsize !=0 && gi->drawing.item_ysize !=0)
944 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
945 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
947 else if (gi->drawing.item_xsize == 0 && gi->drawing.item_ysize == 0 &&
948 gi->width != 0 && gi->height != 0)
950 gi->drawing.item_xsize = gi->width / gi->drawing.area_xsize;
951 gi->drawing.item_ysize = gi->height / gi->drawing.area_ysize;
954 /* same tag also used for other gadget definitions */
955 gi->textarea.xsize = gi->drawing.area_xsize;
956 gi->textarea.ysize = gi->drawing.area_ysize;
958 if (gi->type & GD_TYPE_TEXT_AREA) /* force recalculation */
967 gi->drawing.item_xsize = va_arg(ap, int);
968 gi->drawing.item_ysize = va_arg(ap, int);
970 /* determine dependent values for drawing area gadget, if needed */
971 if (gi->width == 0 && gi->height == 0 &&
972 gi->drawing.area_xsize !=0 && gi->drawing.area_ysize !=0)
974 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
975 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
977 else if (gi->drawing.area_xsize == 0 && gi->drawing.area_ysize == 0 &&
978 gi->width != 0 && gi->height != 0)
980 gi->drawing.area_xsize = gi->width / gi->drawing.item_xsize;
981 gi->drawing.area_ysize = gi->height / gi->drawing.item_ysize;
985 case GDI_SCROLLBAR_ITEMS_MAX:
986 gi->scrollbar.items_max = va_arg(ap, int);
989 case GDI_SCROLLBAR_ITEMS_VISIBLE:
990 gi->scrollbar.items_visible = va_arg(ap, int);
993 case GDI_SCROLLBAR_ITEM_POSITION:
994 gi->scrollbar.item_position = va_arg(ap, int);
997 case GDI_WHEEL_AREA_X:
998 gi->wheelarea.x = va_arg(ap, int);
1001 case GDI_WHEEL_AREA_Y:
1002 gi->wheelarea.y = va_arg(ap, int);
1005 case GDI_WHEEL_AREA_WIDTH:
1006 gi->wheelarea.width = va_arg(ap, int);
1009 case GDI_WHEEL_AREA_HEIGHT:
1010 gi->wheelarea.height = va_arg(ap, int);
1013 case GDI_CALLBACK_INFO:
1014 gi->callback_info = va_arg(ap, gadget_function);
1017 case GDI_CALLBACK_ACTION:
1018 gi->callback_action = va_arg(ap, gadget_function);
1022 Error(ERR_EXIT, "HandleGadgetTags(): unknown tag %d", tag);
1025 tag = va_arg(ap, int); /* read next tag */
1028 /* check if gadget is complete */
1029 if (gi->type != GD_TYPE_DRAWING_AREA &&
1030 (!gi->design[GD_BUTTON_UNPRESSED].bitmap ||
1031 !gi->design[GD_BUTTON_PRESSED].bitmap))
1032 Error(ERR_EXIT, "gadget incomplete (missing Bitmap)");
1034 /* adjust gadget values in relation to other gadget values */
1036 if (gi->type & GD_TYPE_TEXT_INPUT)
1038 int font_nr = gi->font_active;
1039 int font_width = getFontWidth(font_nr);
1040 int font_height = getFontHeight(font_nr);
1041 int border_xsize = gi->border.xsize;
1042 int border_ysize = gi->border.ysize;
1044 if (gi->type == GD_TYPE_TEXT_INPUT_NUMERIC)
1046 int number_min = gi->textinput.number_min;
1047 int number_max = gi->textinput.number_max;
1048 int min_size_min = get_minimal_size_for_numeric_input(number_min);
1049 int min_size_max = get_minimal_size_for_numeric_input(number_max);
1050 int min_size = MAX(min_size_min, min_size_max);
1052 /* expand gadget text input size, if maximal value is too large */
1053 if (gi->textinput.size < min_size)
1054 gi->textinput.size = min_size;
1057 gi->width = 2 * border_xsize + (gi->textinput.size + 1) * font_width;
1058 gi->height = 2 * border_ysize + font_height;
1061 if (gi->type & GD_TYPE_SELECTBOX)
1063 int font_nr = gi->font_active;
1064 int font_width = getFontWidth(font_nr);
1065 int font_height = getFontHeight(font_nr);
1066 int border_xsize = gi->border.xsize;
1067 int border_ysize = gi->border.ysize;
1068 int button_size = gi->border.xsize_selectbutton;
1069 int bottom_screen_border = gfx.sy + gfx.sysize - font_height;
1073 gi->width = 2 * border_xsize + gi->textinput.size*font_width +button_size;
1074 gi->height = 2 * border_ysize + font_height;
1076 if (gi->selectbox.options == NULL)
1077 Error(ERR_EXIT, "selectbox gadget incomplete (missing options array)");
1079 gi->selectbox.num_values = 0;
1080 while (gi->selectbox.options[gi->selectbox.num_values].text != NULL)
1081 gi->selectbox.num_values++;
1083 /* calculate values for open selectbox */
1084 gi->selectbox.width = gi->width;
1085 gi->selectbox.height =
1086 2 * border_ysize + gi->selectbox.num_values * font_height;
1088 gi->selectbox.x = gi->x;
1089 gi->selectbox.y = gi->y + gi->height;
1090 if (gi->selectbox.y + gi->selectbox.height > bottom_screen_border)
1091 gi->selectbox.y = gi->y - gi->selectbox.height;
1092 if (gi->selectbox.y < 0)
1093 gi->selectbox.y = bottom_screen_border - gi->selectbox.height;
1095 getFontCharSource(font_nr, FONT_ASCII_CURSOR, &src_bitmap, &src_x, &src_y);
1096 src_x += font_width / 2;
1097 src_y += font_height / 2;
1099 /* there may be esoteric cases with missing or too small font bitmap */
1100 if (src_bitmap != NULL &&
1101 src_x < src_bitmap->width && src_y < src_bitmap->height)
1102 gi->selectbox.inverse_color = GetPixel(src_bitmap, src_x, src_y);
1104 /* always start with closed selectbox */
1105 gi->selectbox.open = FALSE;
1108 if (gi->type & GD_TYPE_TEXT_INPUT_NUMERIC)
1110 struct GadgetTextInput *text = &gi->textinput;
1111 int value = text->number_value;
1113 text->number_value = (value < text->number_min ? text->number_min :
1114 value > text->number_max ? text->number_max :
1117 sprintf(text->value, "%d", text->number_value);
1120 if (gi->type & GD_TYPE_TEXT_BUTTON)
1122 int font_nr = gi->font_active;
1123 int font_width = getFontWidth(font_nr);
1124 int font_height = getFontHeight(font_nr);
1125 int border_xsize = gi->border.xsize;
1126 int border_ysize = gi->border.ysize;
1128 gi->width = 2 * border_xsize + gi->textbutton.size * font_width;
1129 gi->height = 2 * border_ysize + font_height;
1132 if (gi->type & GD_TYPE_SCROLLBAR)
1134 struct GadgetScrollbar *gs = &gi->scrollbar;
1135 int scrollbar_size_cmp;
1137 if (gi->width == 0 || gi->height == 0 ||
1138 gs->items_max == 0 || gs->items_visible == 0)
1139 Error(ERR_EXIT, "scrollbar gadget incomplete (missing tags)");
1141 /* calculate internal scrollbar values */
1142 gs->size_min = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ?
1143 gi->width : gi->height);
1144 gs->size_max = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ?
1145 gi->height : gi->width);
1147 scrollbar_size_cmp = gs->size_max * gs->items_visible / gs->items_max;
1148 gs->size = MAX(scrollbar_size_cmp, gs->size_min);
1149 gs->size_max_cmp = (gs->size_max - (gs->size - scrollbar_size_cmp));
1151 gs->position = gs->size_max_cmp * gs->item_position / gs->items_max;
1152 gs->position_max = gs->size_max - gs->size;
1153 gs->correction = gs->size_max / gs->items_max / 2;
1155 /* finetuning for maximal right/bottom position */
1156 if (gs->item_position == gs->items_max - gs->items_visible)
1157 gs->position = gs->position_max;
1160 if (gi->type & GD_TYPE_TEXT_AREA)
1162 int font_nr = gi->font_active;
1163 int font_width = getFontWidth(font_nr);
1164 int font_height = getFontHeight(font_nr);
1165 int border_xsize = gi->border.xsize;
1166 int border_ysize = gi->border.ysize;
1168 if (gi->width == 0 || gi->height == 0)
1170 gi->width = 2 * border_xsize + gi->textarea.xsize * font_width;
1171 gi->height = 2 * border_ysize + gi->textarea.ysize * font_height;
1175 gi->textarea.xsize = (gi->width - 2 * border_xsize) / font_width;
1176 gi->textarea.ysize = (gi->height - 2 * border_ysize) / font_height;
1181 void ModifyGadget(struct GadgetInfo *gi, int first_tag, ...)
1185 va_start(ap, first_tag);
1186 HandleGadgetTags(gi, first_tag, ap);
1192 void RedrawGadget(struct GadgetInfo *gi)
1198 DrawGadget(gi, gi->state, gi->direct_draw);
1201 struct GadgetInfo *CreateGadget(int first_tag, ...)
1203 struct GadgetInfo *new_gadget = checked_calloc(sizeof(struct GadgetInfo));
1206 /* always start with reliable default values */
1207 new_gadget->id = getNewGadgetID();
1208 new_gadget->callback_info = default_callback_info;
1209 new_gadget->callback_action = default_callback_action;
1210 new_gadget->active = TRUE;
1211 new_gadget->direct_draw = TRUE;
1213 new_gadget->next = NULL;
1215 va_start(ap, first_tag);
1216 HandleGadgetTags(new_gadget, first_tag, ap);
1219 /* insert new gadget into global gadget list */
1220 if (gadget_list_last_entry)
1222 gadget_list_last_entry->next = new_gadget;
1223 gadget_list_last_entry = gadget_list_last_entry->next;
1226 gadget_list_first_entry = gadget_list_last_entry = new_gadget;
1231 void FreeGadget(struct GadgetInfo *gi)
1233 struct GadgetInfo *gi_previous = gadget_list_first_entry;
1235 /* prevent "last_info_gi" from pointing to memory that will be freed */
1236 if (last_info_gi == gi)
1237 last_info_gi = NULL;
1239 while (gi_previous != NULL && gi_previous->next != gi)
1240 gi_previous = gi_previous->next;
1242 if (gi == gadget_list_first_entry)
1243 gadget_list_first_entry = gi->next;
1245 if (gi == gadget_list_last_entry)
1246 gadget_list_last_entry = gi_previous;
1248 if (gi_previous != NULL)
1249 gi_previous->next = gi->next;
1254 static void CheckRangeOfNumericInputGadget(struct GadgetInfo *gi)
1256 if (gi->type != GD_TYPE_TEXT_INPUT_NUMERIC)
1259 gi->textinput.number_value = atoi(gi->textinput.value);
1261 if (gi->textinput.number_value < gi->textinput.number_min)
1262 gi->textinput.number_value = gi->textinput.number_min;
1263 if (gi->textinput.number_value > gi->textinput.number_max)
1264 gi->textinput.number_value = gi->textinput.number_max;
1266 sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
1268 if (gi->textinput.cursor_position < 0)
1269 gi->textinput.cursor_position = 0;
1270 else if (gi->textinput.cursor_position > strlen(gi->textinput.value))
1271 gi->textinput.cursor_position = strlen(gi->textinput.value);
1274 /* global pointer to gadget actually in use (when mouse button pressed) */
1275 static struct GadgetInfo *last_gi = NULL;
1277 static void MapGadgetExt(struct GadgetInfo *gi, boolean redraw)
1279 if (gi == NULL || gi->mapped)
1285 DrawGadget(gi, DG_UNPRESSED, DG_BUFFERED);
1288 void MapGadget(struct GadgetInfo *gi)
1290 MapGadgetExt(gi, TRUE);
1293 void UnmapGadget(struct GadgetInfo *gi)
1295 if (gi == NULL || !gi->mapped)
1304 #define MAX_NUM_GADGETS 1024
1305 #define MULTIMAP_UNMAP (1 << 0)
1306 #define MULTIMAP_REMAP (1 << 1)
1307 #define MULTIMAP_REDRAW (1 << 2)
1308 #define MULTIMAP_PLAYFIELD (1 << 3)
1309 #define MULTIMAP_DOOR_1 (1 << 4)
1310 #define MULTIMAP_DOOR_2 (1 << 5)
1311 #define MULTIMAP_ALL (MULTIMAP_PLAYFIELD | \
1315 static void MultiMapGadgets(int mode)
1317 struct GadgetInfo *gi = gadget_list_first_entry;
1318 static boolean map_state[MAX_NUM_GADGETS];
1323 if ((mode & MULTIMAP_PLAYFIELD &&
1324 gi->x < gfx.sx + gfx.sxsize) ||
1325 (mode & MULTIMAP_DOOR_1 &&
1326 gi->x >= gfx.dx && gi->y < gfx.dy + gfx.dysize) ||
1327 (mode & MULTIMAP_DOOR_2 &&
1328 gi->x >= gfx.dx && gi->y > gfx.dy + gfx.dysize) ||
1329 (mode & MULTIMAP_ALL) == MULTIMAP_ALL)
1331 if (mode & MULTIMAP_UNMAP)
1333 map_state[map_count++ % MAX_NUM_GADGETS] = gi->mapped;
1338 if (map_state[map_count++ % MAX_NUM_GADGETS])
1339 MapGadgetExt(gi, (mode & MULTIMAP_REDRAW));
1347 void UnmapAllGadgets()
1349 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_UNMAP);
1352 void RemapAllGadgets()
1354 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_REMAP);
1357 boolean anyTextInputGadgetActive()
1359 return (last_gi && (last_gi->type & GD_TYPE_TEXT_INPUT) && last_gi->mapped);
1362 boolean anyTextAreaGadgetActive()
1364 return (last_gi && (last_gi->type & GD_TYPE_TEXT_AREA) && last_gi->mapped);
1367 boolean anySelectboxGadgetActive()
1369 return (last_gi && (last_gi->type & GD_TYPE_SELECTBOX) && last_gi->mapped);
1372 boolean anyScrollbarGadgetActive()
1374 return (last_gi && (last_gi->type & GD_TYPE_SCROLLBAR) && last_gi->mapped);
1377 boolean anyTextGadgetActive()
1379 return (anyTextInputGadgetActive() ||
1380 anyTextAreaGadgetActive() ||
1381 anySelectboxGadgetActive());
1384 static boolean insideSelectboxLine(struct GadgetInfo *gi, int mx, int my)
1386 return(gi != NULL &&
1387 gi->type & GD_TYPE_SELECTBOX &&
1388 mx >= gi->x && mx < gi->x + gi->width &&
1389 my >= gi->y && my < gi->y + gi->height);
1392 static boolean insideSelectboxArea(struct GadgetInfo *gi, int mx, int my)
1394 return(gi != NULL &&
1395 gi->type & GD_TYPE_SELECTBOX &&
1396 mx >= gi->selectbox.x && mx < gi->selectbox.x + gi->selectbox.width &&
1397 my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height);
1400 void ClickOnGadget(struct GadgetInfo *gi, int button)
1405 /* simulate releasing mouse button over last gadget, if still pressed */
1407 HandleGadgets(-1, -1, 0);
1409 /* simulate pressing mouse button over specified gadget */
1410 HandleGadgets(gi->x, gi->y, button);
1412 /* simulate releasing mouse button over specified gadget */
1413 HandleGadgets(gi->x, gi->y, 0);
1416 boolean HandleGadgets(int mx, int my, int button)
1418 static unsigned long pressed_delay = 0;
1419 static unsigned long pressed_delay_value = GADGET_FRAME_DELAY;
1420 static int last_button = 0;
1421 static int last_mx = 0, last_my = 0;
1422 static int pressed_mx = 0, pressed_my = 0;
1423 static boolean keep_selectbox_open = FALSE;
1424 static boolean gadget_stopped = FALSE;
1425 int scrollbar_mouse_pos = 0;
1426 struct GadgetInfo *new_gi, *gi;
1427 boolean press_event;
1428 boolean release_event;
1429 boolean mouse_moving;
1430 boolean mouse_inside_select_line;
1431 boolean mouse_inside_select_area;
1432 boolean mouse_released_where_pressed;
1433 boolean gadget_pressed;
1434 boolean gadget_pressed_repeated;
1435 boolean gadget_pressed_off_borders;
1436 boolean gadget_pressed_inside_select_line;
1437 boolean gadget_pressed_delay_reached;
1438 boolean gadget_moving;
1439 boolean gadget_moving_inside;
1440 boolean gadget_moving_off_borders;
1441 boolean gadget_draggable;
1442 boolean gadget_dragging;
1443 boolean gadget_released;
1444 boolean gadget_released_inside;
1445 boolean gadget_released_inside_select_line;
1446 boolean gadget_released_inside_select_area;
1447 boolean gadget_released_off_borders;
1448 boolean changed_position = FALSE;
1450 /* check if there are any gadgets defined */
1451 if (gadget_list_first_entry == NULL)
1454 /* simulated release of mouse button over last gadget */
1455 if (mx == -1 && my == -1 && button == 0)
1461 /* check which gadget is under the mouse pointer */
1462 new_gi = getGadgetInfoFromMousePosition(mx, my, button);
1464 /* check if button state has changed since last invocation */
1465 press_event = (button != 0 && last_button == 0);
1466 release_event = (button == 0 && last_button != 0);
1467 last_button = button;
1469 /* check if mouse has been moved since last invocation */
1470 mouse_moving = ((mx != last_mx || my != last_my) && motion_status);
1474 if (press_event && new_gi != last_gi)
1480 mouse_released_where_pressed =
1481 (release_event && mx == pressed_mx && my == pressed_my);
1483 mouse_inside_select_line = insideSelectboxLine(new_gi, mx, my);
1484 mouse_inside_select_area = insideSelectboxArea(new_gi, mx, my);
1486 gadget_pressed_off_borders = (press_event && new_gi != last_gi);
1488 gadget_pressed_inside_select_line =
1489 (press_event && new_gi != NULL &&
1490 new_gi->type & GD_TYPE_SELECTBOX && new_gi->selectbox.open &&
1491 insideSelectboxLine(new_gi, mx, my));
1493 /* if mouse button pressed outside text or selectbox gadget, deactivate it */
1494 if (anyTextGadgetActive() &&
1495 (gadget_pressed_off_borders ||
1496 (gadget_pressed_inside_select_line && !mouse_inside_select_area)))
1498 struct GadgetInfo *gi = last_gi;
1499 boolean gadget_changed = (gi->event_mask & GD_EVENT_TEXT_LEAVING);
1501 /* check if text gadget has changed its value */
1502 if (gi->type & GD_TYPE_TEXT_INPUT)
1504 CheckRangeOfNumericInputGadget(gi);
1506 if (!strEqual(gi->textinput.last_value, gi->textinput.value))
1507 strcpy(gi->textinput.last_value, gi->textinput.value);
1509 gadget_changed = FALSE;
1512 /* selectbox does not change its value when closed by clicking outside */
1513 if (gi->type & GD_TYPE_SELECTBOX)
1514 gadget_changed = FALSE;
1516 DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
1518 gi->event.type = GD_EVENT_TEXT_LEAVING;
1520 if (gadget_changed && !(gi->type & GD_TYPE_SELECTBOX))
1521 gi->callback_action(gi);
1525 if (gadget_pressed_inside_select_line)
1530 (button != 0 && last_gi == NULL && new_gi != NULL && press_event);
1531 gadget_pressed_repeated =
1532 (button != 0 && last_gi != NULL && new_gi == last_gi);
1534 gadget_pressed_delay_reached =
1535 DelayReached(&pressed_delay, pressed_delay_value);
1537 gadget_released = (release_event && last_gi != NULL);
1538 gadget_released_inside = (gadget_released && new_gi == last_gi);
1539 gadget_released_off_borders = (gadget_released && new_gi != last_gi);
1541 gadget_moving = (button != 0 && last_gi != NULL && mouse_moving);
1542 gadget_moving_inside = (gadget_moving && new_gi == last_gi);
1543 gadget_moving_off_borders = (gadget_moving && new_gi != last_gi);
1545 /* when handling selectbox, set additional state values */
1546 if (gadget_released_inside && (last_gi->type & GD_TYPE_SELECTBOX))
1548 gadget_released_inside_select_line = insideSelectboxLine(last_gi, mx, my);
1549 gadget_released_inside_select_area = insideSelectboxArea(last_gi, mx, my);
1553 gadget_released_inside_select_line = FALSE;
1554 gadget_released_inside_select_area = FALSE;
1557 /* setting state for handling over-large selectbox */
1558 if (keep_selectbox_open && (press_event || !mouse_inside_select_line))
1559 keep_selectbox_open = FALSE;
1561 /* if new gadget pressed, store this gadget */
1565 /* 'gi' is actually handled gadget */
1568 /* if gadget is scrollbar, choose mouse position value */
1569 if (gi && gi->type & GD_TYPE_SCROLLBAR)
1570 scrollbar_mouse_pos =
1571 (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? mx - gi->x : my - gi->y);
1573 /* if mouse button released, no gadget needs to be handled anymore */
1574 if (gadget_released)
1576 if (gi->type & GD_TYPE_SELECTBOX &&
1577 (keep_selectbox_open ||
1578 mouse_released_where_pressed ||
1579 !gadget_released_inside_select_area)) /* selectbox stays open */
1581 gi->selectbox.stay_open = TRUE;
1585 else if (!(gi->type & GD_TYPE_TEXT_INPUT ||
1586 gi->type & GD_TYPE_TEXT_AREA)) /* text input stays open */
1590 /* modify event position values even if no gadget is pressed */
1591 if (button == 0 && !release_event)
1594 /* if new gadget or if no gadget was pressed, release stopped processing */
1595 if (gadget_pressed || new_gi == NULL)
1596 gadget_stopped = FALSE;
1598 /* if gadget was stopped while being handled, stop gadget processing here */
1604 int last_x = gi->event.x;
1605 int last_y = gi->event.y;
1607 gi->event.x = mx - gi->x;
1608 gi->event.y = my - gi->y;
1610 if (gi->type == GD_TYPE_DRAWING_AREA)
1612 gi->event.x /= gi->drawing.item_xsize;
1613 gi->event.y /= gi->drawing.item_ysize;
1615 if (last_x != gi->event.x || last_y != gi->event.y)
1616 changed_position = TRUE;
1618 else if (gi->type & GD_TYPE_TEXT_INPUT && button != 0 && !motion_status)
1620 int old_cursor_position = gi->textinput.cursor_position;
1622 /* if mouse button pressed inside activated text gadget, set cursor */
1623 gi->textinput.cursor_position =
1624 (mx - gi->x - gi->border.xsize) / getFontWidth(gi->font);
1626 if (gi->textinput.cursor_position < 0)
1627 gi->textinput.cursor_position = 0;
1628 else if (gi->textinput.cursor_position > strlen(gi->textinput.value))
1629 gi->textinput.cursor_position = strlen(gi->textinput.value);
1631 if (gi->textinput.cursor_position != old_cursor_position)
1632 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1634 else if (gi->type & GD_TYPE_TEXT_AREA && button != 0 && !motion_status)
1636 int old_cursor_position = gi->textarea.cursor_position;
1637 int x = (mx - gi->x - gi->border.xsize) / getFontWidth(gi->font);
1638 int y = (my - gi->y - gi->border.ysize) / getFontHeight(gi->font);
1640 x = (x < 0 ? 0 : x >= gi->textarea.xsize ? gi->textarea.xsize - 1 : x);
1641 y = (y < 0 ? 0 : y >= gi->textarea.ysize ? gi->textarea.ysize - 1 : y);
1643 setTextAreaCursorXY(gi, x, y);
1645 if (gi->textarea.cursor_position != old_cursor_position)
1646 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1648 else if (gi->type & GD_TYPE_SELECTBOX && gi->selectbox.open &&
1649 !keep_selectbox_open)
1651 int old_index = gi->selectbox.current_index;
1653 /* if mouse moving inside activated selectbox, select value */
1654 if (my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
1655 gi->selectbox.current_index =
1656 (my - gi->selectbox.y - gi->border.ysize) / getFontHeight(gi->font);
1658 if (gi->selectbox.current_index < 0)
1659 gi->selectbox.current_index = 0;
1660 else if (gi->selectbox.current_index > gi->selectbox.num_values - 1)
1661 gi->selectbox.current_index = gi->selectbox.num_values - 1;
1663 if (gi->selectbox.current_index != old_index)
1664 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1668 /* handle gadget popup info text */
1669 if (last_info_gi != new_gi ||
1670 (new_gi && new_gi->type == GD_TYPE_DRAWING_AREA && changed_position))
1672 if (new_gi != NULL && (button == 0 || new_gi == last_gi))
1674 new_gi->event.type = GD_EVENT_INFO_ENTERING;
1675 new_gi->callback_info(new_gi);
1677 else if (last_info_gi != NULL)
1679 last_info_gi->event.type = GD_EVENT_INFO_LEAVING;
1680 last_info_gi->callback_info(last_info_gi);
1683 last_info_gi = new_gi;
1688 gadget_draggable = (gi && gi->type & GD_TYPE_SCROLLBAR);
1690 /* reset drag position for newly pressed scrollbar to "not dragging" */
1691 if (gadget_pressed && gadget_draggable)
1692 gi->scrollbar.drag_position = -1;
1694 gadget_dragging = (gadget_draggable && gi->scrollbar.drag_position != -1);
1696 /* clicking next to a scrollbar to move it is not considered "moving" */
1697 if (gadget_draggable && !gadget_dragging)
1698 gadget_moving = FALSE;
1700 /* when leaving scrollbar area when jump-scrolling, stop gadget processing */
1701 if (gadget_draggable && !gadget_dragging && gadget_moving_off_borders)
1702 gadget_stopped = TRUE;
1704 if ((gadget_pressed) ||
1705 (gadget_pressed_repeated && gadget_pressed_delay_reached))
1707 if (gadget_pressed) /* gadget pressed the first time */
1709 /* initialize delay counter */
1710 DelayReached(&pressed_delay, 0);
1712 /* start gadget delay with longer delay after first click on gadget */
1713 pressed_delay_value = GADGET_FRAME_DELAY_FIRST;
1715 else /* gadget hold pressed for some time */
1717 /* after first repeated gadget click, continue with shorter delay value */
1718 pressed_delay_value = GADGET_FRAME_DELAY;
1721 if (gi->type & GD_TYPE_SCROLLBAR && !gadget_dragging)
1723 int mpos = (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? mx : my);
1724 int gpos = (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? gi->x : gi->y);
1727 mpos < gpos + gi->scrollbar.position ||
1728 mpos >= gpos + gi->scrollbar.position + gi->scrollbar.size)
1730 /* click scrollbar one scrollbar length up/left or down/right */
1732 struct GadgetScrollbar *gs = &gi->scrollbar;
1733 int old_item_position = gs->item_position;
1734 int item_steps = gs->items_visible - 1;
1735 int item_direction = (mpos < gpos + gi->scrollbar.position ? -1 : +1);
1740 item_direction = (button == 4 ? -1 : +1);
1743 changed_position = FALSE;
1745 gs->item_position += item_steps * item_direction;
1747 if (gs->item_position < 0)
1748 gs->item_position = 0;
1749 else if (gs->item_position > gs->items_max - gs->items_visible)
1750 gs->item_position = gs->items_max - gs->items_visible;
1752 if (old_item_position != gs->item_position)
1754 gi->event.item_position = gs->item_position;
1755 changed_position = TRUE;
1758 ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, gs->item_position,
1761 gi->state = GD_BUTTON_UNPRESSED;
1762 gi->event.type = GD_EVENT_MOVING;
1763 gi->event.off_borders = FALSE;
1765 if (gi->event_mask & GD_EVENT_MOVING && changed_position)
1766 gi->callback_action(gi);
1772 /* don't handle this scrollbar anymore when mouse position reached */
1773 if (gadget_pressed_repeated)
1775 gadget_stopped = TRUE;
1787 if (gi->type == GD_TYPE_CHECK_BUTTON)
1789 gi->checked = !gi->checked;
1791 else if (gi->type == GD_TYPE_RADIO_BUTTON)
1793 struct GadgetInfo *rgi = gadget_list_first_entry;
1798 rgi->type == GD_TYPE_RADIO_BUTTON &&
1799 rgi->radio_nr == gi->radio_nr &&
1802 rgi->checked = FALSE;
1803 DrawGadget(rgi, DG_UNPRESSED, rgi->direct_draw);
1811 else if (gi->type & GD_TYPE_SCROLLBAR)
1813 int mpos = (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? mx : my);
1814 int gpos = (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? gi->x : gi->y);
1816 if (button >= 1 && button <= 3 &&
1817 mpos >= gpos + gi->scrollbar.position &&
1818 mpos < gpos + gi->scrollbar.position + gi->scrollbar.size)
1820 /* start dragging scrollbar */
1821 gi->scrollbar.drag_position =
1822 scrollbar_mouse_pos - gi->scrollbar.position;
1825 else if (gi->type & GD_TYPE_SELECTBOX)
1827 /* keep selectbox open in case of over-large selectbox */
1828 keep_selectbox_open = (mouse_inside_select_line &&
1829 mouse_inside_select_area);
1832 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1834 gi->state = GD_BUTTON_PRESSED;
1835 gi->event.type = GD_EVENT_PRESSED;
1836 gi->event.button = button;
1837 gi->event.off_borders = FALSE;
1839 if (gi->event_mask & GD_EVENT_PRESSED)
1840 gi->callback_action(gi);
1843 if (gadget_pressed_repeated)
1845 gi->event.type = GD_EVENT_PRESSED;
1847 if (gi->event_mask & GD_EVENT_REPEATED && gadget_pressed_delay_reached)
1848 gi->callback_action(gi);
1853 if (gi->type & GD_TYPE_BUTTON)
1855 if (gadget_moving_inside && gi->state == GD_BUTTON_UNPRESSED)
1856 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1857 else if (gadget_moving_off_borders && gi->state == GD_BUTTON_PRESSED)
1858 DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
1860 else if (gi->type & GD_TYPE_SELECTBOX && !keep_selectbox_open)
1862 int old_index = gi->selectbox.current_index;
1864 /* if mouse moving inside activated selectbox, select value */
1865 if (my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
1866 gi->selectbox.current_index =
1867 (my - gi->selectbox.y - gi->border.ysize) / getFontHeight(gi->font);
1869 if (gi->selectbox.current_index < 0)
1870 gi->selectbox.current_index = 0;
1871 else if (gi->selectbox.current_index > gi->selectbox.num_values - 1)
1872 gi->selectbox.current_index = gi->selectbox.num_values - 1;
1874 if (gi->selectbox.current_index != old_index)
1875 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1877 else if (gi->type & GD_TYPE_SCROLLBAR)
1879 struct GadgetScrollbar *gs = &gi->scrollbar;
1880 int old_item_position = gs->item_position;
1882 gs->position = scrollbar_mouse_pos - gs->drag_position;
1884 /* make sure to always precisely reach end positions when dragging */
1885 if (gs->position <= 0)
1888 gs->item_position = 0;
1890 else if (gs->position >= gs->position_max)
1892 gs->position = gs->position_max;
1893 gs->item_position = gs->items_max - gs->items_visible;
1898 gs->items_max * (gs->position + gs->correction) / gs->size_max_cmp;
1901 if (gs->item_position < 0)
1902 gs->item_position = 0;
1903 if (gs->item_position > gs->items_max - 1)
1904 gs->item_position = gs->items_max - 1;
1906 if (old_item_position != gs->item_position)
1908 gi->event.item_position = gs->item_position;
1909 changed_position = TRUE;
1912 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1915 gi->state = (gadget_moving_inside || gadget_draggable ?
1916 GD_BUTTON_PRESSED : GD_BUTTON_UNPRESSED);
1917 gi->event.type = GD_EVENT_MOVING;
1918 gi->event.off_borders = gadget_moving_off_borders;
1920 if (gi->event_mask & GD_EVENT_MOVING && changed_position &&
1921 (gadget_moving_inside || gi->event_mask & GD_EVENT_OFF_BORDERS))
1922 gi->callback_action(gi);
1925 if (gadget_released_inside)
1927 boolean deactivate_gadget = TRUE;
1928 boolean gadget_changed = TRUE;
1930 if (gi->type & GD_TYPE_SELECTBOX)
1932 if (keep_selectbox_open ||
1933 mouse_released_where_pressed ||
1934 !gadget_released_inside_select_area) /* selectbox stays open */
1936 deactivate_gadget = FALSE;
1937 gadget_changed = FALSE;
1939 else if (gi->selectbox.index != gi->selectbox.current_index)
1940 gi->selectbox.index = gi->selectbox.current_index;
1942 gadget_changed = FALSE;
1945 if (deactivate_gadget &&
1946 !(gi->type & GD_TYPE_TEXT_INPUT ||
1947 gi->type & GD_TYPE_TEXT_AREA)) /* text input stays open */
1948 DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
1950 gi->state = GD_BUTTON_UNPRESSED;
1951 gi->event.type = GD_EVENT_RELEASED;
1953 if ((gi->event_mask & GD_EVENT_RELEASED) && gadget_changed)
1955 gi->callback_action(gi);
1959 if (gadget_released_off_borders)
1961 if (gi->type & GD_TYPE_SCROLLBAR)
1962 DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
1965 gi->state = GD_BUTTON_UNPRESSED;
1967 gi->event.type = GD_EVENT_RELEASED;
1969 if (gi->event_mask & GD_EVENT_RELEASED &&
1970 gi->event_mask & GD_EVENT_OFF_BORDERS)
1971 gi->callback_action(gi);
1974 /* handle gadgets unmapped/mapped between pressing and releasing */
1975 if (release_event && !gadget_released && new_gi)
1976 new_gi->state = GD_BUTTON_UNPRESSED;
1978 return (gadget_pressed || gadget_pressed_repeated ||
1979 gadget_released || gadget_moving);
1982 static void insertCharIntoTextArea(struct GadgetInfo *gi, char c)
1984 char text[MAX_GADGET_TEXTSIZE];
1985 int cursor_position = gi->textarea.cursor_position;
1987 if (strlen(gi->textarea.value) == MAX_GADGET_TEXTSIZE) /* no space left */
1990 strcpy(text, gi->textarea.value);
1991 strcpy(&gi->textarea.value[cursor_position + 1], &text[cursor_position]);
1992 gi->textarea.value[cursor_position] = c;
1994 setTextAreaCursorPosition(gi, gi->textarea.cursor_position + 1);
1997 boolean HandleGadgetsKeyInput(Key key)
1999 struct GadgetInfo *gi = last_gi;
2001 if (gi == NULL || !gi->mapped ||
2002 !(gi->type & GD_TYPE_TEXT_INPUT ||
2003 gi->type & GD_TYPE_TEXT_AREA ||
2004 gi->type & GD_TYPE_SELECTBOX))
2007 if (key == KSYM_Return) /* valid for both text input and selectbox */
2009 boolean gadget_changed = (gi->event_mask & GD_EVENT_TEXT_RETURN);
2011 if (gi->type & GD_TYPE_TEXT_INPUT)
2013 CheckRangeOfNumericInputGadget(gi);
2015 if (!strEqual(gi->textinput.last_value, gi->textinput.value))
2016 strcpy(gi->textinput.last_value, gi->textinput.value);
2018 gadget_changed = FALSE;
2020 else if (gi->type & GD_TYPE_SELECTBOX)
2022 if (gi->selectbox.index != gi->selectbox.current_index)
2023 gi->selectbox.index = gi->selectbox.current_index;
2025 gadget_changed = FALSE;
2028 if (gi->type & GD_TYPE_TEXT_AREA)
2030 insertCharIntoTextArea(gi, '\n');
2032 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2036 DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
2038 gi->event.type = GD_EVENT_TEXT_RETURN;
2044 gi->callback_action(gi);
2046 else if (gi->type & GD_TYPE_TEXT_INPUT) /* only valid for text input */
2048 char text[MAX_GADGET_TEXTSIZE];
2049 int text_length = strlen(gi->textinput.value);
2050 int cursor_pos = gi->textinput.cursor_position;
2051 char letter = getCharFromKey(key);
2052 boolean legal_letter = (gi->type == GD_TYPE_TEXT_INPUT_NUMERIC ?
2053 letter >= '0' && letter <= '9' :
2056 if (legal_letter && text_length < gi->textinput.size)
2058 strcpy(text, gi->textinput.value);
2059 strcpy(&gi->textinput.value[cursor_pos + 1], &text[cursor_pos]);
2060 gi->textinput.value[cursor_pos] = letter;
2061 gi->textinput.cursor_position++;
2063 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2065 else if (key == KSYM_Left && cursor_pos > 0)
2067 gi->textinput.cursor_position--;
2069 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2071 else if (key == KSYM_Right && cursor_pos < text_length)
2073 gi->textinput.cursor_position++;
2075 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2077 else if (key == KSYM_BackSpace && cursor_pos > 0)
2079 strcpy(text, gi->textinput.value);
2080 strcpy(&gi->textinput.value[cursor_pos - 1], &text[cursor_pos]);
2081 gi->textinput.cursor_position--;
2083 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2085 else if (key == KSYM_Delete && cursor_pos < text_length)
2087 strcpy(text, gi->textinput.value);
2088 strcpy(&gi->textinput.value[cursor_pos], &text[cursor_pos + 1]);
2090 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2093 else if (gi->type & GD_TYPE_TEXT_AREA) /* only valid for text area */
2095 char text[MAX_GADGET_TEXTSIZE];
2096 int text_length = strlen(gi->textarea.value);
2097 int area_ysize = gi->textarea.ysize;
2098 int cursor_x_pref = gi->textarea.cursor_x_preferred;
2099 int cursor_y = gi->textarea.cursor_y;
2100 int cursor_pos = gi->textarea.cursor_position;
2101 char letter = getCharFromKey(key);
2102 boolean legal_letter = (letter != 0);
2106 insertCharIntoTextArea(gi, letter);
2108 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2110 else if (key == KSYM_Left && cursor_pos > 0)
2112 setTextAreaCursorPosition(gi, gi->textarea.cursor_position - 1);
2114 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2116 else if (key == KSYM_Right && cursor_pos < text_length)
2118 setTextAreaCursorPosition(gi, gi->textarea.cursor_position + 1);
2120 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2122 else if (key == KSYM_Up && cursor_y > 0)
2124 setTextAreaCursorXY(gi, cursor_x_pref, cursor_y - 1);
2125 gi->textarea.cursor_x_preferred = cursor_x_pref;
2127 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2129 else if (key == KSYM_Down && cursor_y < area_ysize - 1)
2131 setTextAreaCursorXY(gi, cursor_x_pref, cursor_y + 1);
2132 gi->textarea.cursor_x_preferred = cursor_x_pref;
2134 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2136 else if (key == KSYM_BackSpace && cursor_pos > 0)
2138 strcpy(text, gi->textarea.value);
2139 strcpy(&gi->textarea.value[cursor_pos - 1], &text[cursor_pos]);
2141 setTextAreaCursorPosition(gi, gi->textarea.cursor_position - 1);
2143 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2145 else if (key == KSYM_Delete && cursor_pos < text_length)
2147 strcpy(text, gi->textarea.value);
2148 strcpy(&gi->textarea.value[cursor_pos], &text[cursor_pos + 1]);
2150 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2153 else if (gi->type & GD_TYPE_SELECTBOX) /* only valid for selectbox */
2155 int index = gi->selectbox.current_index;
2156 int num_values = gi->selectbox.num_values;
2158 if (key == KSYM_Up && index > 0)
2160 gi->selectbox.current_index--;
2162 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2164 else if (key == KSYM_Down && index < num_values - 1)
2166 gi->selectbox.current_index++;
2168 DrawGadget(gi, DG_PRESSED, gi->direct_draw);