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 int next_free_gadget_id = 1;
32 static boolean gadget_id_wrapped = FALSE;
34 static struct GadgetInfo *getGadgetInfoFromGadgetID(int id)
36 struct GadgetInfo *gi = gadget_list_first_entry;
38 while (gi && gi->id != id)
44 static int getNewGadgetID()
46 int id = next_free_gadget_id++;
48 if (next_free_gadget_id <= 0) /* counter overrun */
50 gadget_id_wrapped = TRUE; /* now we must check each ID */
51 next_free_gadget_id = 0;
54 if (gadget_id_wrapped)
56 next_free_gadget_id++;
57 while (getGadgetInfoFromGadgetID(next_free_gadget_id) != NULL)
58 next_free_gadget_id++;
61 if (next_free_gadget_id <= 0) /* cannot get new gadget id */
62 Error(ERR_EXIT, "too much gadgets -- this should not happen");
67 static struct GadgetInfo *getGadgetInfoFromMousePosition(int mx, int my)
69 struct GadgetInfo *gi;
71 /* open selectboxes may overlap other active gadgets, so check them first */
72 for (gi = gadget_list_first_entry; gi != NULL; gi = gi->next)
74 if (gi->mapped && gi->active &&
75 gi->type & GD_TYPE_SELECTBOX && gi->selectbox.open &&
76 mx >= gi->selectbox.x && mx < gi->selectbox.x + gi->selectbox.width &&
77 my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
81 /* check all other gadgets */
82 for (gi = gadget_list_first_entry; gi != NULL; gi = gi->next)
84 if (gi->mapped && gi->active &&
85 mx >= gi->x && mx < gi->x + gi->width &&
86 my >= gi->y && my < gi->y + gi->height)
93 static void setTextAreaCursorExt(struct GadgetInfo *gi, boolean set_cursor_pos)
95 char *text = gi->textarea.value;
96 int area_xsize = gi->textarea.xsize;
97 int area_ysize = gi->textarea.ysize;
98 int cursor_position = gi->textarea.cursor_position;
99 int cursor_x = gi->textarea.cursor_x;
100 int cursor_y = gi->textarea.cursor_y;
107 if (set_cursor_pos) /* x/y => position */
109 if (y == cursor_y && (x == cursor_x || (x < cursor_x && *text == '\n')))
112 else /* position => x/y */
114 if (pos == cursor_position)
118 if (x + 1 >= area_xsize || *text == '\n')
120 if (y + 1 >= area_ysize)
133 gi->textarea.cursor_x = x;
134 gi->textarea.cursor_y = y;
135 gi->textarea.cursor_x_preferred = x;
136 gi->textarea.cursor_position = pos;
139 static void setTextAreaCursorXY(struct GadgetInfo *gi, int x, int y)
141 gi->textarea.cursor_x = x;
142 gi->textarea.cursor_y = y;
144 setTextAreaCursorExt(gi, TRUE);
147 static void setTextAreaCursorPosition(struct GadgetInfo *gi, int pos)
149 gi->textarea.cursor_position = pos;
151 setTextAreaCursorExt(gi, FALSE);
154 static void default_callback_info(void *ptr)
159 static void default_callback_action(void *ptr)
164 static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
166 int state = (pressed ? GD_BUTTON_PRESSED : GD_BUTTON_UNPRESSED);
167 struct GadgetDesign *gd = (!gi->active ? &gi->alt_design[state] :
168 gi->checked ? &gi->alt_design[state] :
170 boolean redraw_selectbox = FALSE;
174 case GD_TYPE_NORMAL_BUTTON:
175 case GD_TYPE_CHECK_BUTTON:
176 case GD_TYPE_RADIO_BUTTON:
177 BlitBitmapOnBackground(gd->bitmap, drawto,
178 gd->x, gd->y, gi->width, gi->height,
180 if (gi->deco.design.bitmap)
181 BlitBitmap(gi->deco.design.bitmap, drawto,
182 gi->deco.design.x, gi->deco.design.y,
183 gi->deco.width, gi->deco.height,
184 gi->x + gi->deco.x + (pressed ? gi->deco.xshift : 0),
185 gi->y + gi->deco.y + (pressed ? gi->deco.yshift : 0));
188 case GD_TYPE_TEXT_BUTTON:
191 int font_nr = (gi->active ? gi->font_active : gi->font);
192 int font_width = getFontWidth(font_nr);
193 int border_x = gi->border.xsize;
194 int border_y = gi->border.ysize;
195 int text_size = strlen(gi->textbutton.value);
196 int text_start = (gi->width - text_size * font_width) / 2;
198 /* left part of gadget */
199 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
200 border_x, gi->height, gi->x, gi->y);
202 /* middle part of gadget */
203 for (i=0; i < gi->textbutton.size; i++)
204 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
205 font_width, gi->height,
206 gi->x + border_x + i * font_width, gi->y);
208 /* right part of gadget */
209 BlitBitmapOnBackground(gd->bitmap, drawto,
210 gd->x + gi->border.width - border_x, gd->y,
211 border_x, gi->height,
212 gi->x + gi->width - border_x, gi->y);
214 /* gadget text value */
216 gi->x + text_start + (pressed ? gi->deco.xshift : 0),
217 gi->y + border_y + (pressed ? gi->deco.yshift : 0),
218 gi->textbutton.value, font_nr, BLIT_MASKED);
222 case GD_TYPE_TEXT_INPUT_ALPHANUMERIC:
223 case GD_TYPE_TEXT_INPUT_NUMERIC:
227 char cursor_string[2];
228 char text[MAX_GADGET_TEXTSIZE + 1];
229 int font_nr = (pressed ? gi->font_active : gi->font);
230 int font_width = getFontWidth(font_nr);
231 int border_x = gi->border.xsize;
232 int border_y = gi->border.ysize;
234 /* left part of gadget */
235 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
236 border_x, gi->height, gi->x, gi->y);
238 /* middle part of gadget */
239 for (i=0; i < gi->textinput.size + 1; i++)
240 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
241 font_width, gi->height,
242 gi->x + border_x + i * font_width, gi->y);
244 /* right part of gadget */
245 BlitBitmapOnBackground(gd->bitmap, drawto,
246 gd->x + gi->border.width - border_x, gd->y,
247 border_x, gi->height,
248 gi->x + gi->width - border_x, gi->y);
251 strcpy(text, gi->textinput.value);
254 /* gadget text value */
256 gi->x + border_x, gi->y + border_y, text,
257 font_nr, BLIT_MASKED);
259 cursor_letter = gi->textinput.value[gi->textinput.cursor_position];
260 cursor_string[0] = (cursor_letter != '\0' ? cursor_letter : ' ');
261 cursor_string[1] = '\0';
263 /* draw cursor, if active */
267 gi->textinput.cursor_position * font_width,
268 gi->y + border_y, cursor_string,
269 font_nr, BLIT_INVERSE);
273 case GD_TYPE_TEXT_AREA:
277 char cursor_string[2];
278 int font_nr = (pressed ? gi->font_active : gi->font);
279 int font_width = getFontWidth(font_nr);
280 int font_height = getFontHeight(font_nr);
281 int border_x = gi->border.xsize;
282 int border_y = gi->border.ysize;
283 int gd_height = 2 * border_y + font_height;
285 /* top left part of gadget border */
286 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
287 border_x, border_y, gi->x, gi->y);
289 /* top middle part of gadget border */
290 for (i=0; i < gi->textarea.xsize; i++)
291 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
292 font_width, border_y,
293 gi->x + border_x + i * font_width, gi->y);
295 /* top right part of gadget border */
296 BlitBitmapOnBackground(gd->bitmap, drawto,
297 gd->x + gi->border.width - border_x, gd->y,
299 gi->x + gi->width - border_x, gi->y);
301 /* left and right part of gadget border for each row */
302 for (i=0; i < gi->textarea.ysize; i++)
304 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y + border_y,
305 border_x, font_height,
306 gi->x, gi->y + border_y + i * font_height);
307 BlitBitmapOnBackground(gd->bitmap, drawto,
308 gd->x + gi->border.width - border_x,
310 border_x, font_height,
311 gi->x + gi->width - border_x,
312 gi->y + border_y + i * font_height);
315 /* bottom left part of gadget border */
316 BlitBitmapOnBackground(gd->bitmap, drawto,
317 gd->x, gd->y + gd_height - border_y,
319 gi->x, gi->y + gi->height - border_y);
321 /* bottom middle part of gadget border */
322 for (i=0; i < gi->textarea.xsize; i++)
323 BlitBitmapOnBackground(gd->bitmap, drawto,
325 gd->y + gd_height - border_y,
326 font_width, border_y,
327 gi->x + border_x + i * font_width,
328 gi->y + gi->height - border_y);
330 /* bottom right part of gadget border */
331 BlitBitmapOnBackground(gd->bitmap, drawto,
332 gd->x + gi->border.width - border_x,
333 gd->y + gd_height - border_y,
335 gi->x + gi->width - border_x,
336 gi->y + gi->height - border_y);
338 ClearRectangleOnBackground(drawto,
341 gi->width - 2 * border_x,
342 gi->height - 2 * border_y);
344 /* gadget text value */
345 DrawTextToTextArea(gi->x + border_x, gi->y + border_y,
346 gi->textarea.value, font_nr, gi->textarea.xsize,
347 gi->textarea.xsize, gi->textarea.ysize,
350 cursor_letter = gi->textarea.value[gi->textarea.cursor_position];
351 cursor_string[0] = (cursor_letter != '\0' ? cursor_letter : ' ');
352 cursor_string[1] = '\0';
354 /* draw cursor, if active */
357 gi->x + border_x + gi->textarea.cursor_x * font_width,
358 gi->y + border_y + gi->textarea.cursor_y * font_height,
360 font_nr, BLIT_INVERSE);
364 case GD_TYPE_SELECTBOX:
367 char text[MAX_GADGET_TEXTSIZE + 1];
368 int font_nr = (pressed ? gi->font_active : gi->font);
369 int font_width = getFontWidth(font_nr);
370 int font_height = getFontHeight(font_nr);
371 int border_x = gi->border.xsize;
372 int border_y = gi->border.ysize;
373 int button = gi->border.xsize_selectbutton;
374 int width_inner = gi->border.width - button - 2 * border_x;
375 int box_width = gi->selectbox.width;
376 int box_height = gi->selectbox.height;
378 /* left part of gadget */
379 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
380 border_x, gi->height, gi->x, gi->y);
382 /* middle part of gadget */
383 for (i=0; i < gi->selectbox.size; i++)
384 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
385 font_width, gi->height,
386 gi->x + border_x + i * font_width, gi->y);
388 /* button part of gadget */
389 BlitBitmapOnBackground(gd->bitmap, drawto,
390 gd->x + border_x + width_inner, gd->y,
392 gi->x + gi->width - border_x - button, gi->y);
394 /* right part of gadget */
395 BlitBitmapOnBackground(gd->bitmap, drawto,
396 gd->x + gi->border.width - border_x, gd->y,
397 border_x, gi->height,
398 gi->x + gi->width - border_x, gi->y);
401 strncpy(text, gi->selectbox.options[gi->selectbox.index].text,
403 text[gi->selectbox.size] = '\0';
405 /* gadget text value */
406 DrawTextExt(drawto, gi->x + border_x, gi->y + border_y, text,
407 font_nr, BLIT_MASKED);
411 if (!gi->selectbox.open)
413 gi->selectbox.open = TRUE;
414 gi->selectbox.stay_open = FALSE;
415 gi->selectbox.current_index = gi->selectbox.index;
417 /* save background under selectbox */
418 BlitBitmap(drawto, gfx.field_save_buffer,
419 gi->selectbox.x, gi->selectbox.y,
420 gi->selectbox.width, gi->selectbox.height,
421 gi->selectbox.x, gi->selectbox.y);
424 /* draw open selectbox */
426 /* top left part of gadget border */
427 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
429 gi->selectbox.x, gi->selectbox.y);
431 /* top middle part of gadget border */
432 for (i=0; i < gi->selectbox.size; i++)
433 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
434 font_width, border_y,
435 gi->selectbox.x + border_x + i * font_width,
438 /* top button part of gadget border */
439 BlitBitmapOnBackground(gd->bitmap, drawto,
440 gd->x + border_x + width_inner, gd->y,
442 gi->selectbox.x + box_width -border_x -button,
445 /* top right part of gadget border */
446 BlitBitmapOnBackground(gd->bitmap, drawto,
447 gd->x + gi->border.width - border_x, gd->y,
449 gi->selectbox.x + box_width - border_x,
452 /* left and right part of gadget border for each row */
453 for (i=0; i < gi->selectbox.num_values; i++)
455 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y + border_y,
456 border_x, font_height,
458 gi->selectbox.y + border_y + i*font_height);
459 BlitBitmapOnBackground(gd->bitmap, drawto,
460 gd->x + gi->border.width - border_x,
462 border_x, font_height,
463 gi->selectbox.x + box_width - border_x,
464 gi->selectbox.y + border_y + i*font_height);
467 /* bottom left part of gadget border */
468 BlitBitmapOnBackground(gd->bitmap, drawto,
469 gd->x, gd->y + gi->height - border_y,
472 gi->selectbox.y + box_height - border_y);
474 /* bottom middle part of gadget border */
475 for (i=0; i < gi->selectbox.size; i++)
476 BlitBitmapOnBackground(gd->bitmap, drawto,
478 gd->y + gi->height - border_y,
479 font_width, border_y,
480 gi->selectbox.x + border_x + i * font_width,
481 gi->selectbox.y + box_height - border_y);
483 /* bottom button part of gadget border */
484 BlitBitmapOnBackground(gd->bitmap, drawto,
485 gd->x + border_x + width_inner,
486 gd->y + gi->height - border_y,
488 gi->selectbox.x + box_width -border_x -button,
489 gi->selectbox.y + box_height - border_y);
491 /* bottom right part of gadget border */
492 BlitBitmapOnBackground(gd->bitmap, drawto,
493 gd->x + gi->border.width - border_x,
494 gd->y + gi->height - border_y,
496 gi->selectbox.x + box_width - border_x,
497 gi->selectbox.y + box_height - border_y);
499 ClearRectangleOnBackground(drawto,
500 gi->selectbox.x + border_x,
501 gi->selectbox.y + border_y,
502 gi->selectbox.width - 2 * border_x,
503 gi->selectbox.height - 2 * border_y);
505 /* selectbox text values */
506 for (i=0; i < gi->selectbox.num_values; i++)
508 int mask_mode = BLIT_MASKED;
510 strncpy(text, gi->selectbox.options[i].text, gi->selectbox.size);
511 text[gi->selectbox.size] = '\0';
513 if (i == gi->selectbox.current_index)
515 FillRectangle(drawto,
516 gi->selectbox.x + border_x,
517 gi->selectbox.y + border_y + i * font_height,
518 gi->selectbox.width - 2 * border_x, font_height,
519 gi->selectbox.inverse_color);
521 /* prevent use of cursor graphic by drawing at least two chars */
523 text[gi->selectbox.size] = '\0';
525 mask_mode = BLIT_INVERSE;
529 gi->selectbox.x + border_x,
530 gi->selectbox.y + border_y + i * font_height, text,
534 redraw_selectbox = TRUE;
536 else if (gi->selectbox.open)
538 gi->selectbox.open = FALSE;
540 /* restore background under selectbox */
541 BlitBitmap(gfx.field_save_buffer, drawto,
542 gi->selectbox.x, gi->selectbox.y,
543 gi->selectbox.width, gi->selectbox.height,
544 gi->selectbox.x, gi->selectbox.y);
546 /* redraw closed selectbox */
547 DrawGadget(gi, FALSE, FALSE);
549 redraw_selectbox = TRUE;
554 case GD_TYPE_SCROLLBAR_VERTICAL:
558 int ypos = gi->y + gi->scrollbar.position;
559 int design_full = gi->width;
560 int design_body = design_full - 2 * gi->border.ysize;
561 int size_full = gi->scrollbar.size;
562 int size_body = size_full - 2 * gi->border.ysize;
563 int num_steps = size_body / design_body;
564 int step_size_remain = size_body - num_steps * design_body;
566 /* clear scrollbar area */
567 ClearRectangleOnBackground(backbuffer, gi->x, gi->y,
568 gi->width, gi->height);
570 /* upper part of gadget */
571 BlitBitmapOnBackground(gd->bitmap, drawto,
573 gi->width, gi->border.ysize,
576 /* middle part of gadget */
577 for (i=0; i < num_steps; i++)
578 BlitBitmapOnBackground(gd->bitmap, drawto,
579 gd->x, gd->y + gi->border.ysize,
580 gi->width, design_body,
582 ypos + gi->border.ysize + i * design_body);
584 /* remaining middle part of gadget */
585 if (step_size_remain > 0)
586 BlitBitmapOnBackground(gd->bitmap, drawto,
587 gd->x, gd->y + gi->border.ysize,
588 gi->width, step_size_remain,
590 ypos + gi->border.ysize
591 + num_steps * design_body);
593 /* lower part of gadget */
594 BlitBitmapOnBackground(gd->bitmap, drawto,
595 gd->x, gd->y + design_full - gi->border.ysize,
596 gi->width, gi->border.ysize,
597 xpos, ypos + size_full - gi->border.ysize);
601 case GD_TYPE_SCROLLBAR_HORIZONTAL:
604 int xpos = gi->x + gi->scrollbar.position;
606 int design_full = gi->height;
607 int design_body = design_full - 2 * gi->border.xsize;
608 int size_full = gi->scrollbar.size;
609 int size_body = size_full - 2 * gi->border.xsize;
610 int num_steps = size_body / design_body;
611 int step_size_remain = size_body - num_steps * design_body;
613 /* clear scrollbar area */
614 ClearRectangleOnBackground(backbuffer, gi->x, gi->y,
615 gi->width, gi->height);
617 /* left part of gadget */
618 BlitBitmapOnBackground(gd->bitmap, drawto,
620 gi->border.xsize, gi->height,
623 /* middle part of gadget */
624 for (i=0; i < num_steps; i++)
625 BlitBitmapOnBackground(gd->bitmap, drawto,
626 gd->x + gi->border.xsize, gd->y,
627 design_body, gi->height,
628 xpos + gi->border.xsize + i * design_body,
631 /* remaining middle part of gadget */
632 if (step_size_remain > 0)
633 BlitBitmapOnBackground(gd->bitmap, drawto,
634 gd->x + gi->border.xsize, gd->y,
635 step_size_remain, gi->height,
636 xpos + gi->border.xsize
637 + num_steps * design_body,
640 /* right part of gadget */
641 BlitBitmapOnBackground(gd->bitmap, drawto,
642 gd->x + design_full - gi->border.xsize, gd->y,
643 gi->border.xsize, gi->height,
644 xpos + size_full - gi->border.xsize, ypos);
654 BlitBitmap(drawto, window,
655 gi->x, gi->y, gi->width, gi->height, gi->x, gi->y);
657 if (gi->type == GD_TYPE_SELECTBOX && redraw_selectbox)
658 BlitBitmap(drawto, window,
659 gi->selectbox.x, gi->selectbox.y,
660 gi->selectbox.width, gi->selectbox.height,
661 gi->selectbox.x, gi->selectbox.y);
664 redraw_mask |= (gi->x < gfx.sx + gfx.sxsize ? REDRAW_FIELD :
665 gi->y < gfx.dy + gfx.dysize ? REDRAW_DOOR_1 :
666 gi->y > gfx.vy ? REDRAW_DOOR_2 : REDRAW_DOOR_3);
669 static int get_minimal_size_for_numeric_input(int minmax_value)
671 int min_size = 1; /* value needs at least one digit */
674 /* add number of digits needed for absolute value */
675 for (i = 10; i <= ABS(minmax_value); i *= 10)
678 /* if min/max value is negative, add one digit for minus sign */
679 if (minmax_value < 0)
685 static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
689 while (tag != GDI_END)
694 gi->custom_id = va_arg(ap, int);
697 case GDI_CUSTOM_TYPE_ID:
698 gi->custom_type_id = va_arg(ap, int);
703 int max_textsize = MAX_INFO_TEXTSIZE - 1;
704 char *text = va_arg(ap, char *);
707 strncpy(gi->info_text, text, max_textsize);
711 gi->info_text[max_textsize] = '\0';
716 gi->x = va_arg(ap, int);
720 gi->y = va_arg(ap, int);
724 gi->width = va_arg(ap, int);
728 gi->height = va_arg(ap, int);
732 gi->type = va_arg(ap, unsigned int);
736 gi->state = va_arg(ap, unsigned int);
740 /* take care here: "boolean" is typedef'ed as "unsigned char",
741 which gets promoted to "int" */
742 gi->active = (boolean)va_arg(ap, int);
745 case GDI_DIRECT_DRAW:
746 /* take care here: "boolean" is typedef'ed as "unsigned char",
747 which gets promoted to "int" */
748 gi->direct_draw = (boolean)va_arg(ap, int);
752 /* take care here: "boolean" is typedef'ed as "unsigned char",
753 which gets promoted to "int" */
754 gi->checked = (boolean)va_arg(ap, int);
758 gi->radio_nr = va_arg(ap, unsigned int);
761 case GDI_NUMBER_VALUE:
762 gi->textinput.number_value = va_arg(ap, int);
763 sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
764 strcpy(gi->textinput.last_value, gi->textinput.value);
765 gi->textinput.cursor_position = strlen(gi->textinput.value);
769 gi->textinput.number_min = va_arg(ap, int);
770 if (gi->textinput.number_value < gi->textinput.number_min)
772 gi->textinput.number_value = gi->textinput.number_min;
773 sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
774 strcpy(gi->textinput.last_value, gi->textinput.value);
779 gi->textinput.number_max = va_arg(ap, int);
780 if (gi->textinput.number_value > gi->textinput.number_max)
782 gi->textinput.number_value = gi->textinput.number_max;
783 sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
784 strcpy(gi->textinput.last_value, gi->textinput.value);
790 int max_textsize = MAX_GADGET_TEXTSIZE;
792 if (gi->textinput.size)
793 max_textsize = MIN(gi->textinput.size, MAX_GADGET_TEXTSIZE - 1);
795 strncpy(gi->textinput.value, va_arg(ap, char *), max_textsize);
796 strcpy(gi->textinput.last_value, gi->textinput.value);
798 gi->textinput.value[max_textsize] = '\0';
799 gi->textinput.cursor_position = strlen(gi->textinput.value);
801 /* same tag also used for other gadget definitions */
802 strcpy(gi->textbutton.value, gi->textinput.value);
803 strcpy(gi->textarea.value, gi->textinput.value);
804 strcpy(gi->textarea.last_value, gi->textinput.value);
810 int tag_value = va_arg(ap, int);
811 int max_textsize = MIN(tag_value, MAX_GADGET_TEXTSIZE - 1);
813 gi->textinput.size = max_textsize;
814 gi->textinput.value[max_textsize] = '\0';
815 strcpy(gi->textinput.last_value, gi->textinput.value);
817 /* same tag also used for other gadget definitions */
819 gi->textarea.size = max_textsize;
820 gi->textarea.value[max_textsize] = '\0';
821 strcpy(gi->textarea.last_value, gi->textinput.value);
823 gi->textbutton.size = max_textsize;
824 gi->textbutton.value[max_textsize] = '\0';
826 gi->selectbox.size = gi->textinput.size;
831 gi->font = va_arg(ap, int);
832 if (gi->font_active == 0)
833 gi->font_active = gi->font;
836 case GDI_TEXT_FONT_ACTIVE:
837 gi->font_active = va_arg(ap, int);
840 case GDI_SELECTBOX_OPTIONS:
841 gi->selectbox.options = va_arg(ap, struct ValueTextInfo *);
844 case GDI_SELECTBOX_INDEX:
845 gi->selectbox.index = va_arg(ap, int);
848 case GDI_DESIGN_UNPRESSED:
849 gi->design[GD_BUTTON_UNPRESSED].bitmap = va_arg(ap, Bitmap *);
850 gi->design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
851 gi->design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
854 case GDI_DESIGN_PRESSED:
855 gi->design[GD_BUTTON_PRESSED].bitmap = va_arg(ap, Bitmap *);
856 gi->design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
857 gi->design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
860 case GDI_ALT_DESIGN_UNPRESSED:
861 gi->alt_design[GD_BUTTON_UNPRESSED].bitmap= va_arg(ap, Bitmap *);
862 gi->alt_design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
863 gi->alt_design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
866 case GDI_ALT_DESIGN_PRESSED:
867 gi->alt_design[GD_BUTTON_PRESSED].bitmap = va_arg(ap, Bitmap *);
868 gi->alt_design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
869 gi->alt_design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
872 case GDI_BORDER_SIZE:
873 gi->border.xsize = va_arg(ap, int);
874 gi->border.ysize = va_arg(ap, int);
877 case GDI_BORDER_SIZE_SELECTBUTTON:
878 gi->border.xsize_selectbutton = va_arg(ap, int);
881 case GDI_DESIGN_WIDTH:
882 gi->border.width = va_arg(ap, int);
885 case GDI_DECORATION_DESIGN:
886 gi->deco.design.bitmap = va_arg(ap, Bitmap *);
887 gi->deco.design.x = va_arg(ap, int);
888 gi->deco.design.y = va_arg(ap, int);
891 case GDI_DECORATION_POSITION:
892 gi->deco.x = va_arg(ap, int);
893 gi->deco.y = va_arg(ap, int);
896 case GDI_DECORATION_SIZE:
897 gi->deco.width = va_arg(ap, int);
898 gi->deco.height = va_arg(ap, int);
901 case GDI_DECORATION_SHIFTING:
902 gi->deco.xshift = va_arg(ap, int);
903 gi->deco.yshift = va_arg(ap, int);
907 gi->event_mask = va_arg(ap, unsigned int);
911 gi->drawing.area_xsize = va_arg(ap, int);
912 gi->drawing.area_ysize = va_arg(ap, int);
914 /* determine dependent values for drawing area gadget, if needed */
915 if (gi->width == 0 && gi->height == 0 &&
916 gi->drawing.item_xsize !=0 && gi->drawing.item_ysize !=0)
918 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
919 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
921 else if (gi->drawing.item_xsize == 0 && gi->drawing.item_ysize == 0 &&
922 gi->width != 0 && gi->height != 0)
924 gi->drawing.item_xsize = gi->width / gi->drawing.area_xsize;
925 gi->drawing.item_ysize = gi->height / gi->drawing.area_ysize;
928 /* same tag also used for other gadget definitions */
929 gi->textarea.xsize = gi->drawing.area_xsize;
930 gi->textarea.ysize = gi->drawing.area_ysize;
932 if (gi->type & GD_TYPE_TEXT_AREA) /* force recalculation */
941 gi->drawing.item_xsize = va_arg(ap, int);
942 gi->drawing.item_ysize = va_arg(ap, int);
944 /* determine dependent values for drawing area gadget, if needed */
945 if (gi->width == 0 && gi->height == 0 &&
946 gi->drawing.area_xsize !=0 && gi->drawing.area_ysize !=0)
948 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
949 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
951 else if (gi->drawing.area_xsize == 0 && gi->drawing.area_ysize == 0 &&
952 gi->width != 0 && gi->height != 0)
954 gi->drawing.area_xsize = gi->width / gi->drawing.item_xsize;
955 gi->drawing.area_ysize = gi->height / gi->drawing.item_ysize;
959 case GDI_SCROLLBAR_ITEMS_MAX:
960 gi->scrollbar.items_max = va_arg(ap, int);
963 case GDI_SCROLLBAR_ITEMS_VISIBLE:
964 gi->scrollbar.items_visible = va_arg(ap, int);
967 case GDI_SCROLLBAR_ITEM_POSITION:
968 gi->scrollbar.item_position = va_arg(ap, int);
971 case GDI_CALLBACK_INFO:
972 gi->callback_info = va_arg(ap, gadget_function);
975 case GDI_CALLBACK_ACTION:
976 gi->callback_action = va_arg(ap, gadget_function);
980 Error(ERR_EXIT, "HandleGadgetTags(): unknown tag %d", tag);
983 tag = va_arg(ap, int); /* read next tag */
986 /* check if gadget is complete */
987 if (gi->type != GD_TYPE_DRAWING_AREA &&
988 (!gi->design[GD_BUTTON_UNPRESSED].bitmap ||
989 !gi->design[GD_BUTTON_PRESSED].bitmap))
990 Error(ERR_EXIT, "gadget incomplete (missing Bitmap)");
992 /* adjust gadget values in relation to other gadget values */
994 if (gi->type & GD_TYPE_TEXT_INPUT)
996 int font_nr = gi->font_active;
997 int font_width = getFontWidth(font_nr);
998 int font_height = getFontHeight(font_nr);
999 int border_xsize = gi->border.xsize;
1000 int border_ysize = gi->border.ysize;
1002 if (gi->type == GD_TYPE_TEXT_INPUT_NUMERIC)
1004 int number_min = gi->textinput.number_min;
1005 int number_max = gi->textinput.number_max;
1006 int min_size_min = get_minimal_size_for_numeric_input(number_min);
1007 int min_size_max = get_minimal_size_for_numeric_input(number_max);
1008 int min_size = MAX(min_size_min, min_size_max);
1010 /* expand gadget text input size, if maximal value is too large */
1011 if (gi->textinput.size < min_size)
1012 gi->textinput.size = min_size;
1015 gi->width = 2 * border_xsize + (gi->textinput.size + 1) * font_width;
1016 gi->height = 2 * border_ysize + font_height;
1019 if (gi->type & GD_TYPE_SELECTBOX)
1021 int font_nr = gi->font_active;
1022 int font_width = getFontWidth(font_nr);
1023 int font_height = getFontHeight(font_nr);
1024 int border_xsize = gi->border.xsize;
1025 int border_ysize = gi->border.ysize;
1026 int button_size = gi->border.xsize_selectbutton;
1027 int bottom_screen_border = gfx.sy + gfx.sysize - font_height;
1031 gi->width = 2 * border_xsize + gi->textinput.size*font_width +button_size;
1032 gi->height = 2 * border_ysize + font_height;
1034 if (gi->selectbox.options == NULL)
1035 Error(ERR_EXIT, "selectbox gadget incomplete (missing options array)");
1037 gi->selectbox.num_values = 0;
1038 while (gi->selectbox.options[gi->selectbox.num_values].text != NULL)
1039 gi->selectbox.num_values++;
1041 /* calculate values for open selectbox */
1042 gi->selectbox.width = gi->width;
1043 gi->selectbox.height =
1044 2 * border_ysize + gi->selectbox.num_values * font_height;
1046 gi->selectbox.x = gi->x;
1047 gi->selectbox.y = gi->y + gi->height;
1048 if (gi->selectbox.y + gi->selectbox.height > bottom_screen_border)
1049 gi->selectbox.y = gi->y - gi->selectbox.height;
1050 if (gi->selectbox.y < 0)
1051 gi->selectbox.y = bottom_screen_border - gi->selectbox.height;
1053 getFontCharSource(font_nr, FONT_ASCII_CURSOR, &src_bitmap, &src_x, &src_y);
1054 src_x += font_width / 2;
1055 src_y += font_height / 2;
1057 /* there may be esoteric cases with missing or too small font bitmap */
1058 if (src_bitmap != NULL &&
1059 src_x < src_bitmap->width && src_y < src_bitmap->height)
1060 gi->selectbox.inverse_color = GetPixel(src_bitmap, src_x, src_y);
1062 /* always start with closed selectbox */
1063 gi->selectbox.open = FALSE;
1066 if (gi->type & GD_TYPE_TEXT_INPUT_NUMERIC)
1068 struct GadgetTextInput *text = &gi->textinput;
1069 int value = text->number_value;
1071 text->number_value = (value < text->number_min ? text->number_min :
1072 value > text->number_max ? text->number_max :
1075 sprintf(text->value, "%d", text->number_value);
1078 if (gi->type & GD_TYPE_TEXT_BUTTON)
1080 int font_nr = gi->font_active;
1081 int font_width = getFontWidth(font_nr);
1082 int font_height = getFontHeight(font_nr);
1083 int border_xsize = gi->border.xsize;
1084 int border_ysize = gi->border.ysize;
1086 gi->width = 2 * border_xsize + gi->textbutton.size * font_width;
1087 gi->height = 2 * border_ysize + font_height;
1090 if (gi->type & GD_TYPE_SCROLLBAR)
1092 struct GadgetScrollbar *gs = &gi->scrollbar;
1093 int scrollbar_size_cmp;
1095 if (gi->width == 0 || gi->height == 0 ||
1096 gs->items_max == 0 || gs->items_visible == 0)
1097 Error(ERR_EXIT, "scrollbar gadget incomplete (missing tags)");
1099 /* calculate internal scrollbar values */
1100 gs->size_min = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ?
1101 gi->width : gi->height);
1102 gs->size_max = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ?
1103 gi->height : gi->width);
1105 scrollbar_size_cmp = gs->size_max * gs->items_visible / gs->items_max;
1106 gs->size = MAX(scrollbar_size_cmp, gs->size_min);
1107 gs->size_max_cmp = (gs->size_max - (gs->size - scrollbar_size_cmp));
1109 gs->position = gs->size_max_cmp * gs->item_position / gs->items_max;
1110 gs->position_max = gs->size_max - gs->size;
1111 gs->correction = gs->size_max / gs->items_max / 2;
1113 /* finetuning for maximal right/bottom position */
1114 if (gs->item_position == gs->items_max - gs->items_visible)
1115 gs->position = gs->position_max;
1118 if (gi->type & GD_TYPE_TEXT_AREA)
1120 int font_nr = gi->font_active;
1121 int font_width = getFontWidth(font_nr);
1122 int font_height = getFontHeight(font_nr);
1123 int border_xsize = gi->border.xsize;
1124 int border_ysize = gi->border.ysize;
1126 if (gi->width == 0 || gi->height == 0)
1128 gi->width = 2 * border_xsize + gi->textarea.xsize * font_width;
1129 gi->height = 2 * border_ysize + gi->textarea.ysize * font_height;
1133 gi->textarea.xsize = (gi->width - 2 * border_xsize) / font_width;
1134 gi->textarea.ysize = (gi->height - 2 * border_ysize) / font_height;
1139 void ModifyGadget(struct GadgetInfo *gi, int first_tag, ...)
1143 va_start(ap, first_tag);
1144 HandleGadgetTags(gi, first_tag, ap);
1150 void RedrawGadget(struct GadgetInfo *gi)
1153 DrawGadget(gi, gi->state, gi->direct_draw);
1156 struct GadgetInfo *CreateGadget(int first_tag, ...)
1158 struct GadgetInfo *new_gadget = checked_calloc(sizeof(struct GadgetInfo));
1161 /* always start with reliable default values */
1162 new_gadget->id = getNewGadgetID();
1163 new_gadget->callback_info = default_callback_info;
1164 new_gadget->callback_action = default_callback_action;
1165 new_gadget->active = TRUE;
1166 new_gadget->direct_draw = TRUE;
1168 new_gadget->next = NULL;
1170 va_start(ap, first_tag);
1171 HandleGadgetTags(new_gadget, first_tag, ap);
1174 /* insert new gadget into global gadget list */
1175 if (gadget_list_last_entry)
1177 gadget_list_last_entry->next = new_gadget;
1178 gadget_list_last_entry = gadget_list_last_entry->next;
1181 gadget_list_first_entry = gadget_list_last_entry = new_gadget;
1186 void FreeGadget(struct GadgetInfo *gi)
1188 struct GadgetInfo *gi_previous = gadget_list_first_entry;
1190 while (gi_previous != NULL && gi_previous->next != gi)
1191 gi_previous = gi_previous->next;
1193 if (gi == gadget_list_first_entry)
1194 gadget_list_first_entry = gi->next;
1196 if (gi == gadget_list_last_entry)
1197 gadget_list_last_entry = gi_previous;
1199 if (gi_previous != NULL)
1200 gi_previous->next = gi->next;
1205 static void CheckRangeOfNumericInputGadget(struct GadgetInfo *gi)
1207 if (gi->type != GD_TYPE_TEXT_INPUT_NUMERIC)
1210 gi->textinput.number_value = atoi(gi->textinput.value);
1212 if (gi->textinput.number_value < gi->textinput.number_min)
1213 gi->textinput.number_value = gi->textinput.number_min;
1214 if (gi->textinput.number_value > gi->textinput.number_max)
1215 gi->textinput.number_value = gi->textinput.number_max;
1217 sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
1219 if (gi->textinput.cursor_position < 0)
1220 gi->textinput.cursor_position = 0;
1221 else if (gi->textinput.cursor_position > strlen(gi->textinput.value))
1222 gi->textinput.cursor_position = strlen(gi->textinput.value);
1225 /* global pointer to gadget actually in use (when mouse button pressed) */
1226 static struct GadgetInfo *last_gi = NULL;
1228 static void MapGadgetExt(struct GadgetInfo *gi, boolean redraw)
1230 if (gi == NULL || gi->mapped)
1236 DrawGadget(gi, DG_UNPRESSED, DG_BUFFERED);
1239 void MapGadget(struct GadgetInfo *gi)
1241 MapGadgetExt(gi, TRUE);
1244 void UnmapGadget(struct GadgetInfo *gi)
1246 if (gi == NULL || !gi->mapped)
1255 #define MAX_NUM_GADGETS 1024
1256 #define MULTIMAP_UNMAP (1 << 0)
1257 #define MULTIMAP_REMAP (1 << 1)
1258 #define MULTIMAP_REDRAW (1 << 2)
1259 #define MULTIMAP_PLAYFIELD (1 << 3)
1260 #define MULTIMAP_DOOR_1 (1 << 4)
1261 #define MULTIMAP_DOOR_2 (1 << 5)
1262 #define MULTIMAP_ALL (MULTIMAP_PLAYFIELD | \
1266 static void MultiMapGadgets(int mode)
1268 struct GadgetInfo *gi = gadget_list_first_entry;
1269 static boolean map_state[MAX_NUM_GADGETS];
1274 if ((mode & MULTIMAP_PLAYFIELD &&
1275 gi->x < gfx.sx + gfx.sxsize) ||
1276 (mode & MULTIMAP_DOOR_1 &&
1277 gi->x >= gfx.dx && gi->y < gfx.dy + gfx.dysize) ||
1278 (mode & MULTIMAP_DOOR_2 &&
1279 gi->x >= gfx.dx && gi->y > gfx.dy + gfx.dysize) ||
1280 (mode & MULTIMAP_ALL) == MULTIMAP_ALL)
1282 if (mode & MULTIMAP_UNMAP)
1284 map_state[map_count++ % MAX_NUM_GADGETS] = gi->mapped;
1289 if (map_state[map_count++ % MAX_NUM_GADGETS])
1290 MapGadgetExt(gi, (mode & MULTIMAP_REDRAW));
1298 void UnmapAllGadgets()
1300 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_UNMAP);
1303 void RemapAllGadgets()
1305 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_REMAP);
1308 boolean anyTextInputGadgetActive()
1310 return (last_gi && (last_gi->type & GD_TYPE_TEXT_INPUT) && last_gi->mapped);
1313 boolean anyTextAreaGadgetActive()
1315 return (last_gi && (last_gi->type & GD_TYPE_TEXT_AREA) && last_gi->mapped);
1318 boolean anySelectboxGadgetActive()
1320 return (last_gi && (last_gi->type & GD_TYPE_SELECTBOX) && last_gi->mapped);
1323 boolean anyScrollbarGadgetActive()
1325 return (last_gi && (last_gi->type & GD_TYPE_SCROLLBAR) && last_gi->mapped);
1328 boolean anyTextGadgetActive()
1330 return (anyTextInputGadgetActive() ||
1331 anyTextAreaGadgetActive() ||
1332 anySelectboxGadgetActive());
1335 static boolean insideSelectboxLine(struct GadgetInfo *gi, int mx, int my)
1337 return(gi != NULL &&
1338 gi->type & GD_TYPE_SELECTBOX &&
1339 mx >= gi->x && mx < gi->x + gi->width &&
1340 my >= gi->y && my < gi->y + gi->height);
1343 static boolean insideSelectboxArea(struct GadgetInfo *gi, int mx, int my)
1345 return(gi != NULL &&
1346 gi->type & GD_TYPE_SELECTBOX &&
1347 mx >= gi->selectbox.x && mx < gi->selectbox.x + gi->selectbox.width &&
1348 my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height);
1351 void ClickOnGadget(struct GadgetInfo *gi, int button)
1356 /* simulate releasing mouse button over last gadget, if still pressed */
1358 HandleGadgets(-1, -1, 0);
1360 /* simulate pressing mouse button over specified gadget */
1361 HandleGadgets(gi->x, gi->y, button);
1363 /* simulate releasing mouse button over specified gadget */
1364 HandleGadgets(gi->x, gi->y, 0);
1367 boolean HandleGadgets(int mx, int my, int button)
1369 static struct GadgetInfo *last_info_gi = NULL;
1370 static unsigned long pressed_delay = 0;
1371 static int last_button = 0;
1372 static int last_mx = 0, last_my = 0;
1373 static int pressed_mx = 0, pressed_my = 0;
1374 int scrollbar_mouse_pos = 0;
1375 struct GadgetInfo *new_gi, *gi;
1376 boolean press_event;
1377 boolean release_event;
1378 boolean mouse_moving;
1379 boolean mouse_inside_select_line;
1380 boolean mouse_inside_select_area;
1381 boolean mouse_released_where_pressed;
1382 boolean gadget_pressed;
1383 boolean gadget_pressed_repeated;
1384 boolean gadget_pressed_off_borders;
1385 boolean gadget_pressed_inside_select_line;
1386 boolean gadget_moving;
1387 boolean gadget_moving_inside;
1388 boolean gadget_moving_off_borders;
1389 boolean gadget_released;
1390 boolean gadget_released_inside;
1391 boolean gadget_released_inside_select_line;
1392 boolean gadget_released_inside_select_area;
1393 boolean gadget_released_off_borders;
1394 boolean changed_position = FALSE;
1396 /* check if there are any gadgets defined */
1397 if (gadget_list_first_entry == NULL)
1400 /* simulated release of mouse button over last gadget */
1401 if (mx == -1 && my == -1 && button == 0)
1407 /* check which gadget is under the mouse pointer */
1408 new_gi = getGadgetInfoFromMousePosition(mx, my);
1410 /* check if button state has changed since last invocation */
1411 press_event = (button != 0 && last_button == 0);
1412 release_event = (button == 0 && last_button != 0);
1413 last_button = button;
1415 /* check if mouse has been moved since last invocation */
1416 mouse_moving = ((mx != last_mx || my != last_my) && motion_status);
1420 if (press_event && new_gi != last_gi)
1426 mouse_released_where_pressed =
1427 (release_event && mx == pressed_mx && my == pressed_my);
1429 mouse_inside_select_line = insideSelectboxLine(new_gi, mx, my);
1430 mouse_inside_select_area = insideSelectboxArea(new_gi, mx, my);
1432 gadget_pressed_off_borders = (press_event && new_gi != last_gi);
1434 gadget_pressed_inside_select_line =
1435 (press_event && new_gi != NULL &&
1436 new_gi->type & GD_TYPE_SELECTBOX && new_gi->selectbox.open &&
1437 insideSelectboxLine(new_gi, mx, my));
1439 /* if mouse button pressed outside text or selectbox gadget, deactivate it */
1440 if (anyTextGadgetActive() &&
1441 (gadget_pressed_off_borders ||
1442 (gadget_pressed_inside_select_line && !mouse_inside_select_area)))
1444 struct GadgetInfo *gi = last_gi;
1445 boolean gadget_changed = (gi->event_mask & GD_EVENT_TEXT_LEAVING);
1447 /* check if text gadget has changed its value */
1448 if (gi->type & GD_TYPE_TEXT_INPUT)
1450 CheckRangeOfNumericInputGadget(gi);
1452 if (strcmp(gi->textinput.value, gi->textinput.last_value) != 0)
1453 strcpy(gi->textinput.last_value, gi->textinput.value);
1455 gadget_changed = FALSE;
1458 /* selectbox does not change its value when closed by clicking outside */
1459 if (gi->type & GD_TYPE_SELECTBOX)
1460 gadget_changed = FALSE;
1462 DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
1464 gi->event.type = GD_EVENT_TEXT_LEAVING;
1466 if (gadget_changed && !(gi->type & GD_TYPE_SELECTBOX))
1467 gi->callback_action(gi);
1471 if (gadget_pressed_inside_select_line)
1476 (button != 0 && last_gi == NULL && new_gi != NULL && press_event);
1477 gadget_pressed_repeated =
1478 (button != 0 && last_gi != NULL && new_gi == last_gi);
1480 gadget_released = (release_event && last_gi != NULL);
1481 gadget_released_inside = (gadget_released && new_gi == last_gi);
1482 gadget_released_off_borders = (gadget_released && new_gi != last_gi);
1484 gadget_moving = (button != 0 && last_gi != NULL && mouse_moving);
1485 gadget_moving_inside = (gadget_moving && new_gi == last_gi);
1486 gadget_moving_off_borders = (gadget_moving && new_gi != last_gi);
1488 /* when handling selectbox, set additional state values */
1489 if (gadget_released_inside && (last_gi->type & GD_TYPE_SELECTBOX))
1491 gadget_released_inside_select_line = insideSelectboxLine(last_gi, mx, my);
1492 gadget_released_inside_select_area = insideSelectboxArea(last_gi, mx, my);
1496 gadget_released_inside_select_line = FALSE;
1497 gadget_released_inside_select_area = FALSE;
1500 /* if new gadget pressed, store this gadget */
1504 /* 'gi' is actually handled gadget */
1507 /* if gadget is scrollbar, choose mouse position value */
1508 if (gi && gi->type & GD_TYPE_SCROLLBAR)
1509 scrollbar_mouse_pos =
1510 (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? mx - gi->x : my - gi->y);
1512 /* if mouse button released, no gadget needs to be handled anymore */
1513 if (gadget_released)
1515 if (gi->type & GD_TYPE_SELECTBOX &&
1516 (mouse_released_where_pressed ||
1517 !gadget_released_inside_select_area)) /* selectbox stays open */
1519 gi->selectbox.stay_open = TRUE;
1523 else if (!(gi->type & GD_TYPE_TEXT_INPUT ||
1524 gi->type & GD_TYPE_TEXT_AREA)) /* text input stays open */
1528 /* modify event position values even if no gadget is pressed */
1529 if (button == 0 && !release_event)
1534 int last_x = gi->event.x;
1535 int last_y = gi->event.y;
1537 gi->event.x = mx - gi->x;
1538 gi->event.y = my - gi->y;
1540 if (gi->type == GD_TYPE_DRAWING_AREA)
1542 gi->event.x /= gi->drawing.item_xsize;
1543 gi->event.y /= gi->drawing.item_ysize;
1545 if (last_x != gi->event.x || last_y != gi->event.y)
1546 changed_position = TRUE;
1548 else if (gi->type & GD_TYPE_TEXT_INPUT && button != 0 && !motion_status)
1550 int old_cursor_position = gi->textinput.cursor_position;
1552 /* if mouse button pressed inside activated text gadget, set cursor */
1553 gi->textinput.cursor_position =
1554 (mx - gi->x - gi->border.xsize) / getFontWidth(gi->font);
1556 if (gi->textinput.cursor_position < 0)
1557 gi->textinput.cursor_position = 0;
1558 else if (gi->textinput.cursor_position > strlen(gi->textinput.value))
1559 gi->textinput.cursor_position = strlen(gi->textinput.value);
1561 if (gi->textinput.cursor_position != old_cursor_position)
1562 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1564 else if (gi->type & GD_TYPE_TEXT_AREA && button != 0 && !motion_status)
1566 int old_cursor_position = gi->textarea.cursor_position;
1567 int x = (mx - gi->x - gi->border.xsize) / getFontWidth(gi->font);
1568 int y = (my - gi->y - gi->border.ysize) / getFontHeight(gi->font);
1570 x = (x < 0 ? 0 : x >= gi->textarea.xsize ? gi->textarea.xsize - 1 : x);
1571 y = (y < 0 ? 0 : y >= gi->textarea.ysize ? gi->textarea.ysize - 1 : y);
1573 setTextAreaCursorXY(gi, x, y);
1575 if (gi->textarea.cursor_position != old_cursor_position)
1576 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1578 else if (gi->type & GD_TYPE_SELECTBOX && gi->selectbox.open)
1580 int old_index = gi->selectbox.current_index;
1582 /* if mouse moving inside activated selectbox, select value */
1583 if (my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
1584 gi->selectbox.current_index =
1585 (my - gi->selectbox.y - gi->border.ysize) / getFontHeight(gi->font);
1587 if (gi->selectbox.current_index < 0)
1588 gi->selectbox.current_index = 0;
1589 else if (gi->selectbox.current_index > gi->selectbox.num_values - 1)
1590 gi->selectbox.current_index = gi->selectbox.num_values - 1;
1592 if (gi->selectbox.current_index != old_index)
1593 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1597 /* handle gadget popup info text */
1598 if (last_info_gi != new_gi ||
1599 (new_gi && new_gi->type == GD_TYPE_DRAWING_AREA && changed_position))
1601 if (new_gi != NULL && (button == 0 || new_gi == last_gi))
1603 new_gi->event.type = GD_EVENT_INFO_ENTERING;
1604 new_gi->callback_info(new_gi);
1606 else if (last_info_gi != NULL)
1608 last_info_gi->event.type = GD_EVENT_INFO_LEAVING;
1609 last_info_gi->callback_info(last_info_gi);
1612 last_info_gi = new_gi;
1617 if (gi->type == GD_TYPE_CHECK_BUTTON)
1619 gi->checked = !gi->checked;
1621 else if (gi->type == GD_TYPE_RADIO_BUTTON)
1623 struct GadgetInfo *rgi = gadget_list_first_entry;
1628 rgi->type == GD_TYPE_RADIO_BUTTON &&
1629 rgi->radio_nr == gi->radio_nr &&
1632 rgi->checked = FALSE;
1633 DrawGadget(rgi, DG_UNPRESSED, rgi->direct_draw);
1641 else if (gi->type & GD_TYPE_SCROLLBAR)
1645 if (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL)
1656 if (mpos >= gpos + gi->scrollbar.position &&
1657 mpos < gpos + gi->scrollbar.position + gi->scrollbar.size)
1659 /* drag scrollbar */
1660 gi->scrollbar.drag_position =
1661 scrollbar_mouse_pos - gi->scrollbar.position;
1665 /* click scrollbar one scrollbar length up/left or down/right */
1667 struct GadgetScrollbar *gs = &gi->scrollbar;
1668 int old_item_position = gs->item_position;
1670 changed_position = FALSE;
1672 gs->item_position +=
1673 gs->items_visible * (mpos < gpos + gi->scrollbar.position ? -1 : +1);
1675 if (gs->item_position < 0)
1676 gs->item_position = 0;
1677 else if (gs->item_position > gs->items_max - gs->items_visible)
1678 gs->item_position = gs->items_max - gs->items_visible;
1680 if (old_item_position != gs->item_position)
1682 gi->event.item_position = gs->item_position;
1683 changed_position = TRUE;
1686 ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, gs->item_position,
1689 gi->state = GD_BUTTON_UNPRESSED;
1690 gi->event.type = GD_EVENT_MOVING;
1691 gi->event.off_borders = FALSE;
1693 if (gi->event_mask & GD_EVENT_MOVING && changed_position)
1694 gi->callback_action(gi);
1696 /* don't handle this scrollbar anymore while mouse button pressed */
1703 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1705 gi->state = GD_BUTTON_PRESSED;
1706 gi->event.type = GD_EVENT_PRESSED;
1707 gi->event.button = button;
1708 gi->event.off_borders = FALSE;
1710 /* initialize delay counter */
1711 DelayReached(&pressed_delay, 0);
1713 if (gi->event_mask & GD_EVENT_PRESSED)
1714 gi->callback_action(gi);
1717 if (gadget_pressed_repeated)
1719 gi->event.type = GD_EVENT_PRESSED;
1721 if (gi->event_mask & GD_EVENT_REPEATED &&
1722 DelayReached(&pressed_delay, GADGET_FRAME_DELAY))
1723 gi->callback_action(gi);
1728 if (gi->type & GD_TYPE_BUTTON)
1730 if (gadget_moving_inside && gi->state == GD_BUTTON_UNPRESSED)
1731 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1732 else if (gadget_moving_off_borders && gi->state == GD_BUTTON_PRESSED)
1733 DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
1735 else if (gi->type & GD_TYPE_SELECTBOX)
1737 int old_index = gi->selectbox.current_index;
1739 /* if mouse moving inside activated selectbox, select value */
1740 if (my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
1741 gi->selectbox.current_index =
1742 (my - gi->selectbox.y - gi->border.ysize) / getFontHeight(gi->font);
1744 if (gi->selectbox.current_index < 0)
1745 gi->selectbox.current_index = 0;
1746 else if (gi->selectbox.current_index > gi->selectbox.num_values - 1)
1747 gi->selectbox.current_index = gi->selectbox.num_values - 1;
1749 if (gi->selectbox.current_index != old_index)
1750 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1752 else if (gi->type & GD_TYPE_SCROLLBAR)
1754 struct GadgetScrollbar *gs = &gi->scrollbar;
1755 int old_item_position = gs->item_position;
1757 gs->position = scrollbar_mouse_pos - gs->drag_position;
1759 /* make sure to always precisely reach end positions when dragging */
1760 if (gs->position <= 0)
1763 gs->item_position = 0;
1765 else if (gs->position >= gs->position_max)
1767 gs->position = gs->position_max;
1768 gs->item_position = gs->items_max - gs->items_visible;
1773 gs->items_max * (gs->position + gs->correction) / gs->size_max_cmp;
1776 if (gs->item_position < 0)
1777 gs->item_position = 0;
1778 if (gs->item_position > gs->items_max - 1)
1779 gs->item_position = gs->items_max - 1;
1781 if (old_item_position != gs->item_position)
1783 gi->event.item_position = gs->item_position;
1784 changed_position = TRUE;
1787 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1790 gi->state = (gadget_moving_inside || gi->type & GD_TYPE_SCROLLBAR ?
1791 GD_BUTTON_PRESSED : GD_BUTTON_UNPRESSED);
1792 gi->event.type = GD_EVENT_MOVING;
1793 gi->event.off_borders = gadget_moving_off_borders;
1795 if (gi->event_mask & GD_EVENT_MOVING && changed_position &&
1796 (gadget_moving_inside || gi->event_mask & GD_EVENT_OFF_BORDERS))
1797 gi->callback_action(gi);
1800 if (gadget_released_inside)
1802 boolean deactivate_gadget = TRUE;
1803 boolean gadget_changed = TRUE;
1805 if (gi->type & GD_TYPE_SELECTBOX)
1807 if (mouse_released_where_pressed ||
1808 !gadget_released_inside_select_area) /* selectbox stays open */
1810 deactivate_gadget = FALSE;
1811 gadget_changed = FALSE;
1813 else if (gi->selectbox.index != gi->selectbox.current_index)
1814 gi->selectbox.index = gi->selectbox.current_index;
1816 gadget_changed = FALSE;
1819 if (deactivate_gadget &&
1820 !(gi->type & GD_TYPE_TEXT_INPUT ||
1821 gi->type & GD_TYPE_TEXT_AREA)) /* text input stays open */
1822 DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
1824 gi->state = GD_BUTTON_UNPRESSED;
1825 gi->event.type = GD_EVENT_RELEASED;
1827 if ((gi->event_mask & GD_EVENT_RELEASED) && gadget_changed)
1829 gi->callback_action(gi);
1833 if (gadget_released_off_borders)
1835 if (gi->type & GD_TYPE_SCROLLBAR)
1836 DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
1838 gi->event.type = GD_EVENT_RELEASED;
1840 if (gi->event_mask & GD_EVENT_RELEASED &&
1841 gi->event_mask & GD_EVENT_OFF_BORDERS)
1842 gi->callback_action(gi);
1845 /* handle gadgets unmapped/mapped between pressing and releasing */
1846 if (release_event && !gadget_released && new_gi)
1847 new_gi->state = GD_BUTTON_UNPRESSED;
1849 return (gadget_pressed || gadget_pressed_repeated ||
1850 gadget_released || gadget_moving);
1853 static void insertCharIntoTextArea(struct GadgetInfo *gi, char c)
1855 char text[MAX_GADGET_TEXTSIZE];
1856 int cursor_position = gi->textarea.cursor_position;
1858 if (strlen(gi->textarea.value) == MAX_GADGET_TEXTSIZE) /* no space left */
1861 strcpy(text, gi->textarea.value);
1862 strcpy(&gi->textarea.value[cursor_position + 1], &text[cursor_position]);
1863 gi->textarea.value[cursor_position] = c;
1865 setTextAreaCursorPosition(gi, gi->textarea.cursor_position + 1);
1868 boolean HandleGadgetsKeyInput(Key key)
1870 struct GadgetInfo *gi = last_gi;
1872 if (gi == NULL || !gi->mapped ||
1873 !(gi->type & GD_TYPE_TEXT_INPUT ||
1874 gi->type & GD_TYPE_TEXT_AREA ||
1875 gi->type & GD_TYPE_SELECTBOX))
1878 if (key == KSYM_Return) /* valid for both text input and selectbox */
1880 boolean gadget_changed = (gi->event_mask & GD_EVENT_TEXT_RETURN);
1882 if (gi->type & GD_TYPE_TEXT_INPUT)
1884 CheckRangeOfNumericInputGadget(gi);
1886 if (strcmp(gi->textinput.value, gi->textinput.last_value) != 0)
1887 strcpy(gi->textinput.last_value, gi->textinput.value);
1889 gadget_changed = FALSE;
1891 else if (gi->type & GD_TYPE_SELECTBOX)
1893 if (gi->selectbox.index != gi->selectbox.current_index)
1894 gi->selectbox.index = gi->selectbox.current_index;
1896 gadget_changed = FALSE;
1899 if (gi->type & GD_TYPE_TEXT_AREA)
1901 insertCharIntoTextArea(gi, '\n');
1903 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1907 DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
1909 gi->event.type = GD_EVENT_TEXT_RETURN;
1915 gi->callback_action(gi);
1917 else if (gi->type & GD_TYPE_TEXT_INPUT) /* only valid for text input */
1919 char text[MAX_GADGET_TEXTSIZE];
1920 int text_length = strlen(gi->textinput.value);
1921 int cursor_pos = gi->textinput.cursor_position;
1922 char letter = getCharFromKey(key);
1923 boolean legal_letter = (gi->type == GD_TYPE_TEXT_INPUT_NUMERIC ?
1924 letter >= '0' && letter <= '9' :
1927 if (legal_letter && text_length < gi->textinput.size)
1929 strcpy(text, gi->textinput.value);
1930 strcpy(&gi->textinput.value[cursor_pos + 1], &text[cursor_pos]);
1931 gi->textinput.value[cursor_pos] = letter;
1932 gi->textinput.cursor_position++;
1934 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1936 else if (key == KSYM_Left && cursor_pos > 0)
1938 gi->textinput.cursor_position--;
1940 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1942 else if (key == KSYM_Right && cursor_pos < text_length)
1944 gi->textinput.cursor_position++;
1946 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1948 else if (key == KSYM_BackSpace && cursor_pos > 0)
1950 strcpy(text, gi->textinput.value);
1951 strcpy(&gi->textinput.value[cursor_pos - 1], &text[cursor_pos]);
1952 gi->textinput.cursor_position--;
1954 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1956 else if (key == KSYM_Delete && cursor_pos < text_length)
1958 strcpy(text, gi->textinput.value);
1959 strcpy(&gi->textinput.value[cursor_pos], &text[cursor_pos + 1]);
1961 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1964 else if (gi->type & GD_TYPE_TEXT_AREA) /* only valid for text area */
1966 char text[MAX_GADGET_TEXTSIZE];
1967 int text_length = strlen(gi->textarea.value);
1968 int area_ysize = gi->textarea.ysize;
1969 int cursor_x_pref = gi->textarea.cursor_x_preferred;
1970 int cursor_y = gi->textarea.cursor_y;
1971 int cursor_pos = gi->textarea.cursor_position;
1972 char letter = getCharFromKey(key);
1973 boolean legal_letter = (letter != 0);
1977 insertCharIntoTextArea(gi, letter);
1979 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1981 else if (key == KSYM_Left && cursor_pos > 0)
1983 setTextAreaCursorPosition(gi, gi->textarea.cursor_position - 1);
1985 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1987 else if (key == KSYM_Right && cursor_pos < text_length)
1989 setTextAreaCursorPosition(gi, gi->textarea.cursor_position + 1);
1991 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1993 else if (key == KSYM_Up && cursor_y > 0)
1995 setTextAreaCursorXY(gi, cursor_x_pref, cursor_y - 1);
1996 gi->textarea.cursor_x_preferred = cursor_x_pref;
1998 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2000 else if (key == KSYM_Down && cursor_y < area_ysize - 1)
2002 setTextAreaCursorXY(gi, cursor_x_pref, cursor_y + 1);
2003 gi->textarea.cursor_x_preferred = cursor_x_pref;
2005 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2007 else if (key == KSYM_BackSpace && cursor_pos > 0)
2009 strcpy(text, gi->textarea.value);
2010 strcpy(&gi->textarea.value[cursor_pos - 1], &text[cursor_pos]);
2012 setTextAreaCursorPosition(gi, gi->textarea.cursor_position - 1);
2014 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2016 else if (key == KSYM_Delete && cursor_pos < text_length)
2018 strcpy(text, gi->textarea.value);
2019 strcpy(&gi->textarea.value[cursor_pos], &text[cursor_pos + 1]);
2021 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2024 else if (gi->type & GD_TYPE_SELECTBOX) /* only valid for selectbox */
2026 int index = gi->selectbox.current_index;
2027 int num_values = gi->selectbox.num_values;
2029 if (key == KSYM_Up && index > 0)
2031 gi->selectbox.current_index--;
2033 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2035 else if (key == KSYM_Down && index < num_values - 1)
2037 gi->selectbox.current_index++;
2039 DrawGadget(gi, DG_PRESSED, gi->direct_draw);