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");
68 void DUMP_GADGET_MAP_STATE()
70 struct GadgetInfo *gi = gadget_list_first_entry;
74 printf("-XXX-1-> '%s': %s\n",
75 gi->info_text, (gi->mapped ? "mapped" : "not mapped"));
82 static struct GadgetInfo *getGadgetInfoFromMousePosition(int mx, int my)
84 struct GadgetInfo *gi;
86 /* open selectboxes may overlap other active gadgets, so check them first */
87 for (gi = gadget_list_first_entry; gi != NULL; gi = gi->next)
89 if (gi->mapped && gi->active &&
90 gi->type & GD_TYPE_SELECTBOX && gi->selectbox.open &&
91 mx >= gi->selectbox.x && mx < gi->selectbox.x + gi->selectbox.width &&
92 my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
96 /* check all other gadgets */
97 for (gi = gadget_list_first_entry; gi != NULL; gi = gi->next)
99 if (gi->mapped && gi->active &&
100 mx >= gi->x && mx < gi->x + gi->width &&
101 my >= gi->y && my < gi->y + gi->height)
110 static void setTextAreaCursorExt(struct GadgetInfo *gi, boolean set_cursor_pos)
112 char *text = gi->textarea.value;
113 int area_xsize = gi->textarea.xsize;
114 int area_ysize = gi->textarea.ysize;
115 int cursor_position = gi->textarea.cursor_position;
116 int cursor_x = gi->textarea.cursor_x;
117 int cursor_y = gi->textarea.cursor_y;
124 if (set_cursor_pos) /* x/y => position */
126 if (y == cursor_y && (x == cursor_x || (x < cursor_x && *text == '\n')))
129 else /* position => x/y */
131 if (pos == cursor_position)
135 if (x + 1 >= area_xsize || *text == '\n')
137 if (y + 1 >= area_ysize)
156 printf("::: %d, %d [%d]\n", cursor_x, cursor_y, cursor_position);
159 gi->textarea.cursor_x = x;
160 gi->textarea.cursor_y = y;
161 gi->textarea.cursor_x_preferred = x;
162 gi->textarea.cursor_position = pos;
165 static void setTextAreaCursorXY(struct GadgetInfo *gi, int x, int y)
167 gi->textarea.cursor_x = x;
168 gi->textarea.cursor_y = y;
170 setTextAreaCursorExt(gi, TRUE);
173 static void setTextAreaCursorPosition(struct GadgetInfo *gi, int pos)
175 gi->textarea.cursor_position = pos;
177 setTextAreaCursorExt(gi, FALSE);
182 static void setTextAreaCursorPosition(struct GadgetInfo *gi, int x, int y)
184 char *text = gi->textarea.value;
185 int area_xsize = gi->textarea.xsize;
186 int area_ysize = gi->textarea.ysize;
189 int cursor_position = 0;
191 while (*text && cursor_y < area_ysize)
193 char buffer[MAX_OUTPUT_LINESIZE + 1];
196 for (i=0; i < area_xsize && *text && *text != '\n'; i++)
201 if (i == 0 && *text == '\n')
208 if (x == -1 && y == -1) /* get x/y from cursor position */
210 if (cursor_position + i >= gi->textarea.cursor_position)
213 printf("::: cursor: %d + %d >= %d\n", cursor_position, i,
214 gi->textarea.cursor_position);
217 cursor_x = gi->textarea.cursor_position - cursor_position;
218 cursor_position = gi->textarea.cursor_position;
224 if (cursor_y == y || !*text) /* correct y position found */
226 cursor_x = MIN(i, x);
227 cursor_position += cursor_x;
232 cursor_position += i;
248 if (cursor_x >= area_xsize)
254 if (cursor_y >= area_ysize)
256 cursor_x = area_xsize - 1;
257 cursor_y = area_ysize - 1;
261 printf("::: %d, %d [%d]\n", cursor_x, cursor_y, cursor_position);
264 gi->textarea.cursor_x = cursor_x;
265 gi->textarea.cursor_y = cursor_y;
266 gi->textarea.cursor_x_preferred = cursor_x;
267 gi->textarea.cursor_position = cursor_position;
272 static void default_callback_info(void *ptr)
277 static void default_callback_action(void *ptr)
282 static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
284 int state = (pressed ? GD_BUTTON_PRESSED : GD_BUTTON_UNPRESSED);
285 struct GadgetDesign *gd = (!gi->active ? &gi->alt_design[state] :
286 gi->checked ? &gi->alt_design[state] :
288 boolean redraw_selectbox = FALSE;
292 case GD_TYPE_NORMAL_BUTTON:
293 case GD_TYPE_CHECK_BUTTON:
294 case GD_TYPE_RADIO_BUTTON:
295 BlitBitmapOnBackground(gd->bitmap, drawto,
296 gd->x, gd->y, gi->width, gi->height,
298 if (gi->deco.design.bitmap)
299 BlitBitmap(gi->deco.design.bitmap, drawto,
300 gi->deco.design.x, gi->deco.design.y,
301 gi->deco.width, gi->deco.height,
302 gi->x + gi->deco.x + (pressed ? gi->deco.xshift : 0),
303 gi->y + gi->deco.y + (pressed ? gi->deco.yshift : 0));
306 case GD_TYPE_TEXT_BUTTON:
309 int font_nr = (gi->active ? gi->font_active : gi->font);
310 int font_width = getFontWidth(font_nr);
311 int border_x = gi->border.xsize;
312 int border_y = gi->border.ysize;
313 int text_size = strlen(gi->textbutton.value);
314 int text_start = (gi->width - text_size * font_width) / 2;
316 /* left part of gadget */
317 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
318 border_x, gi->height, gi->x, gi->y);
320 /* middle part of gadget */
321 for (i=0; i < gi->textbutton.size; i++)
322 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
323 font_width, gi->height,
324 gi->x + border_x + i * font_width, gi->y);
326 /* right part of gadget */
327 BlitBitmapOnBackground(gd->bitmap, drawto,
328 gd->x + gi->border.width - border_x, gd->y,
329 border_x, gi->height,
330 gi->x + gi->width - border_x, gi->y);
332 /* gadget text value */
334 gi->x + text_start + (pressed ? gi->deco.xshift : 0),
335 gi->y + border_y + (pressed ? gi->deco.yshift : 0),
336 gi->textbutton.value, font_nr, BLIT_MASKED);
340 case GD_TYPE_TEXT_INPUT_ALPHANUMERIC:
341 case GD_TYPE_TEXT_INPUT_NUMERIC:
345 char cursor_string[2];
346 char text[MAX_GADGET_TEXTSIZE + 1];
347 int font_nr = (pressed ? gi->font_active : gi->font);
348 int font_width = getFontWidth(font_nr);
349 int border_x = gi->border.xsize;
350 int border_y = gi->border.ysize;
352 /* left part of gadget */
353 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
354 border_x, gi->height, gi->x, gi->y);
356 /* middle part of gadget */
357 for (i=0; i < gi->textinput.size + 1; i++)
358 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
359 font_width, gi->height,
360 gi->x + border_x + i * font_width, gi->y);
362 /* right part of gadget */
363 BlitBitmapOnBackground(gd->bitmap, drawto,
364 gd->x + gi->border.width - border_x, gd->y,
365 border_x, gi->height,
366 gi->x + gi->width - border_x, gi->y);
369 strcpy(text, gi->textinput.value);
372 /* gadget text value */
374 gi->x + border_x, gi->y + border_y, text,
375 font_nr, BLIT_MASKED);
377 cursor_letter = gi->textinput.value[gi->textinput.cursor_position];
378 cursor_string[0] = (cursor_letter != '\0' ? cursor_letter : ' ');
379 cursor_string[1] = '\0';
383 printf("::: PRESSED!\n");
386 /* draw cursor, if active */
390 gi->textinput.cursor_position * font_width,
391 gi->y + border_y, cursor_string,
392 font_nr, BLIT_INVERSE);
396 case GD_TYPE_TEXT_AREA:
400 char cursor_string[2];
401 int font_nr = (pressed ? gi->font_active : gi->font);
402 int font_width = getFontWidth(font_nr);
403 int font_height = getFontHeight(font_nr);
404 int border_x = gi->border.xsize;
405 int border_y = gi->border.ysize;
406 int gd_height = 2 * border_y + font_height;
408 /* top left part of gadget border */
409 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
410 border_x, border_y, gi->x, gi->y);
412 /* top middle part of gadget border */
413 for (i=0; i < gi->textarea.xsize; i++)
414 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
415 font_width, border_y,
416 gi->x + border_x + i * font_width, gi->y);
418 /* top right part of gadget border */
419 BlitBitmapOnBackground(gd->bitmap, drawto,
420 gd->x + gi->border.width - border_x, gd->y,
422 gi->x + gi->width - border_x, gi->y);
424 /* left and right part of gadget border for each row */
425 for (i=0; i < gi->textarea.ysize; i++)
427 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y + border_y,
428 border_x, font_height,
429 gi->x, gi->y + border_y + i * font_height);
430 BlitBitmapOnBackground(gd->bitmap, drawto,
431 gd->x + gi->border.width - border_x,
433 border_x, font_height,
434 gi->x + gi->width - border_x,
435 gi->y + border_y + i * font_height);
438 /* bottom left part of gadget border */
439 BlitBitmapOnBackground(gd->bitmap, drawto,
440 gd->x, gd->y + gd_height - border_y,
442 gi->x, gi->y + gi->height - border_y);
444 /* bottom middle part of gadget border */
445 for (i=0; i < gi->textarea.xsize; i++)
446 BlitBitmapOnBackground(gd->bitmap, drawto,
448 gd->y + gd_height - border_y,
449 font_width, border_y,
450 gi->x + border_x + i * font_width,
451 gi->y + gi->height - border_y);
453 /* bottom right part of gadget border */
454 BlitBitmapOnBackground(gd->bitmap, drawto,
455 gd->x + gi->border.width - border_x,
456 gd->y + gd_height - border_y,
458 gi->x + gi->width - border_x,
459 gi->y + gi->height - border_y);
461 ClearRectangleOnBackground(drawto,
464 gi->width - 2 * border_x,
465 gi->height - 2 * border_y);
467 /* gadget text value */
468 DrawTextToTextArea(gi->x + border_x, gi->y + border_y,
469 gi->textarea.value, font_nr,
470 gi->textarea.xsize, gi->textarea.ysize);
472 cursor_letter = gi->textarea.value[gi->textarea.cursor_position];
473 cursor_string[0] = (cursor_letter != '\0' ? cursor_letter : ' ');
474 cursor_string[1] = '\0';
477 printf("::: '%s' [%d, %d] [%d] [%d -> '%s']\n",
479 gi->textarea.cursor_x, gi->textarea.cursor_y,
480 gi->textarea.cursor_position,
481 pressed, cursor_string);
484 /* draw cursor, if active */
487 gi->x + border_x + gi->textarea.cursor_x * font_width,
488 gi->y + border_y + gi->textarea.cursor_y * font_height,
490 font_nr, BLIT_INVERSE);
494 case GD_TYPE_SELECTBOX:
497 char text[MAX_GADGET_TEXTSIZE + 1];
498 int font_nr = (pressed ? gi->font_active : gi->font);
499 int font_width = getFontWidth(font_nr);
500 int font_height = getFontHeight(font_nr);
501 int border_x = gi->border.xsize;
502 int border_y = gi->border.ysize;
503 int button = gi->border.xsize_selectbutton;
504 int width_inner = gi->border.width - button - 2 * border_x;
505 int box_width = gi->selectbox.width;
506 int box_height = gi->selectbox.height;
508 /* left part of gadget */
509 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
510 border_x, gi->height, gi->x, gi->y);
512 /* middle part of gadget */
513 for (i=0; i < gi->selectbox.size; i++)
514 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
515 font_width, gi->height,
516 gi->x + border_x + i * font_width, gi->y);
518 /* button part of gadget */
519 BlitBitmapOnBackground(gd->bitmap, drawto,
520 gd->x + border_x + width_inner, gd->y,
522 gi->x + gi->width - border_x - button, gi->y);
524 /* right part of gadget */
525 BlitBitmapOnBackground(gd->bitmap, drawto,
526 gd->x + gi->border.width - border_x, gd->y,
527 border_x, gi->height,
528 gi->x + gi->width - border_x, gi->y);
531 strncpy(text, gi->selectbox.options[gi->selectbox.index].text,
533 text[gi->selectbox.size] = '\0';
535 /* gadget text value */
536 DrawTextExt(drawto, gi->x + border_x, gi->y + border_y, text,
537 font_nr, BLIT_MASKED);
541 if (!gi->selectbox.open)
543 gi->selectbox.open = TRUE;
544 gi->selectbox.stay_open = FALSE;
545 gi->selectbox.current_index = gi->selectbox.index;
547 /* save background under selectbox */
548 BlitBitmap(drawto, gfx.field_save_buffer,
549 gi->selectbox.x, gi->selectbox.y,
550 gi->selectbox.width, gi->selectbox.height,
551 gi->selectbox.x, gi->selectbox.y);
554 /* draw open selectbox */
556 /* top left part of gadget border */
557 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
559 gi->selectbox.x, gi->selectbox.y);
561 /* top middle part of gadget border */
562 for (i=0; i < gi->selectbox.size; i++)
563 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
564 font_width, border_y,
565 gi->selectbox.x + border_x + i * font_width,
568 /* top button part of gadget border */
569 BlitBitmapOnBackground(gd->bitmap, drawto,
570 gd->x + border_x + width_inner, gd->y,
572 gi->selectbox.x + box_width -border_x -button,
575 /* top right part of gadget border */
576 BlitBitmapOnBackground(gd->bitmap, drawto,
577 gd->x + gi->border.width - border_x, gd->y,
579 gi->selectbox.x + box_width - border_x,
582 /* left and right part of gadget border for each row */
583 for (i=0; i < gi->selectbox.num_values; i++)
585 BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y + border_y,
586 border_x, font_height,
588 gi->selectbox.y + border_y + i*font_height);
589 BlitBitmapOnBackground(gd->bitmap, drawto,
590 gd->x + gi->border.width - border_x,
592 border_x, font_height,
593 gi->selectbox.x + box_width - border_x,
594 gi->selectbox.y + border_y + i*font_height);
597 /* bottom left part of gadget border */
598 BlitBitmapOnBackground(gd->bitmap, drawto,
599 gd->x, gd->y + gi->height - border_y,
602 gi->selectbox.y + box_height - border_y);
604 /* bottom middle part of gadget border */
605 for (i=0; i < gi->selectbox.size; i++)
606 BlitBitmapOnBackground(gd->bitmap, drawto,
608 gd->y + gi->height - border_y,
609 font_width, border_y,
610 gi->selectbox.x + border_x + i * font_width,
611 gi->selectbox.y + box_height - border_y);
613 /* bottom button part of gadget border */
614 BlitBitmapOnBackground(gd->bitmap, drawto,
615 gd->x + border_x + width_inner,
616 gd->y + gi->height - border_y,
618 gi->selectbox.x + box_width -border_x -button,
619 gi->selectbox.y + box_height - border_y);
621 /* bottom right part of gadget border */
622 BlitBitmapOnBackground(gd->bitmap, drawto,
623 gd->x + gi->border.width - border_x,
624 gd->y + gi->height - border_y,
626 gi->selectbox.x + box_width - border_x,
627 gi->selectbox.y + box_height - border_y);
629 ClearRectangleOnBackground(drawto,
630 gi->selectbox.x + border_x,
631 gi->selectbox.y + border_y,
632 gi->selectbox.width - 2 * border_x,
633 gi->selectbox.height - 2 * border_y);
635 /* selectbox text values */
636 for (i=0; i < gi->selectbox.num_values; i++)
640 if (i == gi->selectbox.current_index)
642 FillRectangle(drawto,
643 gi->selectbox.x + border_x,
644 gi->selectbox.y + border_y + i * font_height,
645 gi->selectbox.width - 2 * border_x, font_height,
646 gi->selectbox.inverse_color);
648 strncpy(text, gi->selectbox.options[i].text, gi->selectbox.size);
649 text[1 + gi->selectbox.size] = '\0';
651 mask_mode = BLIT_INVERSE;
655 strncpy(text, gi->selectbox.options[i].text, gi->selectbox.size);
656 text[gi->selectbox.size] = '\0';
658 mask_mode = BLIT_MASKED;
662 gi->selectbox.x + border_x,
663 gi->selectbox.y + border_y + i * font_height, text,
667 redraw_selectbox = TRUE;
669 else if (gi->selectbox.open)
671 gi->selectbox.open = FALSE;
673 /* redraw closed selectbox */
674 DrawGadget(gi, FALSE, FALSE);
676 /* restore background under selectbox */
677 BlitBitmap(gfx.field_save_buffer, drawto,
678 gi->selectbox.x, gi->selectbox.y,
679 gi->selectbox.width, gi->selectbox.height,
680 gi->selectbox.x, gi->selectbox.y);
682 redraw_selectbox = TRUE;
687 case GD_TYPE_SCROLLBAR_VERTICAL:
691 int ypos = gi->y + gi->scrollbar.position;
692 int design_full = gi->width;
693 int design_body = design_full - 2 * gi->border.ysize;
694 int size_full = gi->scrollbar.size;
695 int size_body = size_full - 2 * gi->border.ysize;
696 int num_steps = size_body / design_body;
697 int step_size_remain = size_body - num_steps * design_body;
699 /* clear scrollbar area */
700 ClearRectangleOnBackground(backbuffer, gi->x, gi->y,
701 gi->width, gi->height);
703 /* upper part of gadget */
704 BlitBitmapOnBackground(gd->bitmap, drawto,
706 gi->width, gi->border.ysize,
709 /* middle part of gadget */
710 for (i=0; i<num_steps; i++)
711 BlitBitmapOnBackground(gd->bitmap, drawto,
712 gd->x, gd->y + gi->border.ysize,
713 gi->width, design_body,
715 ypos + gi->border.ysize + i * design_body);
717 /* remaining middle part of gadget */
718 if (step_size_remain > 0)
719 BlitBitmapOnBackground(gd->bitmap, drawto,
720 gd->x, gd->y + gi->border.ysize,
721 gi->width, step_size_remain,
723 ypos + gi->border.ysize
724 + num_steps * design_body);
726 /* lower part of gadget */
727 BlitBitmapOnBackground(gd->bitmap, drawto,
728 gd->x, gd->y + design_full - gi->border.ysize,
729 gi->width, gi->border.ysize,
730 xpos, ypos + size_full - gi->border.ysize);
734 case GD_TYPE_SCROLLBAR_HORIZONTAL:
737 int xpos = gi->x + gi->scrollbar.position;
739 int design_full = gi->height;
740 int design_body = design_full - 2 * gi->border.xsize;
741 int size_full = gi->scrollbar.size;
742 int size_body = size_full - 2 * gi->border.xsize;
743 int num_steps = size_body / design_body;
744 int step_size_remain = size_body - num_steps * design_body;
746 /* clear scrollbar area */
747 ClearRectangleOnBackground(backbuffer, gi->x, gi->y,
748 gi->width, gi->height);
750 /* left part of gadget */
751 BlitBitmapOnBackground(gd->bitmap, drawto,
753 gi->border.xsize, gi->height,
756 /* middle part of gadget */
757 for (i=0; i<num_steps; i++)
758 BlitBitmapOnBackground(gd->bitmap, drawto,
759 gd->x + gi->border.xsize, gd->y,
760 design_body, gi->height,
761 xpos + gi->border.xsize + i * design_body,
764 /* remaining middle part of gadget */
765 if (step_size_remain > 0)
766 BlitBitmapOnBackground(gd->bitmap, drawto,
767 gd->x + gi->border.xsize, gd->y,
768 step_size_remain, gi->height,
769 xpos + gi->border.xsize
770 + num_steps * design_body,
773 /* right part of gadget */
774 BlitBitmapOnBackground(gd->bitmap, drawto,
775 gd->x + design_full - gi->border.xsize, gd->y,
776 gi->border.xsize, gi->height,
777 xpos + size_full - gi->border.xsize, ypos);
787 BlitBitmap(drawto, window,
788 gi->x, gi->y, gi->width, gi->height, gi->x, gi->y);
790 if (gi->type == GD_TYPE_SELECTBOX && redraw_selectbox)
791 BlitBitmap(drawto, window,
792 gi->selectbox.x, gi->selectbox.y,
793 gi->selectbox.width, gi->selectbox.height,
794 gi->selectbox.x, gi->selectbox.y);
797 redraw_mask |= (gi->x < gfx.sx + gfx.sxsize ? REDRAW_FIELD :
798 gi->y < gfx.dy + gfx.dysize ? REDRAW_DOOR_1 :
799 gi->y > gfx.vy ? REDRAW_DOOR_2 : REDRAW_DOOR_3);
802 static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
806 while (tag != GDI_END)
811 gi->custom_id = va_arg(ap, int);
814 case GDI_CUSTOM_TYPE_ID:
815 gi->custom_type_id = va_arg(ap, int);
820 int max_textsize = MAX_INFO_TEXTSIZE - 1;
821 char *text = va_arg(ap, char *);
824 strncpy(gi->info_text, text, max_textsize);
828 gi->info_text[max_textsize] = '\0';
833 gi->x = va_arg(ap, int);
837 gi->y = va_arg(ap, int);
841 gi->width = va_arg(ap, int);
845 gi->height = va_arg(ap, int);
849 gi->type = va_arg(ap, unsigned long);
853 gi->state = va_arg(ap, unsigned long);
857 /* take care here: "boolean" is typedef'ed as "unsigned char",
858 which gets promoted to "int" */
859 gi->active = (boolean)va_arg(ap, int);
862 case GDI_DIRECT_DRAW:
863 /* take care here: "boolean" is typedef'ed as "unsigned char",
864 which gets promoted to "int" */
865 gi->direct_draw = (boolean)va_arg(ap, int);
869 /* take care here: "boolean" is typedef'ed as "unsigned char",
870 which gets promoted to "int" */
871 gi->checked = (boolean)va_arg(ap, int);
875 gi->radio_nr = va_arg(ap, unsigned long);
878 case GDI_NUMBER_VALUE:
879 gi->textinput.number_value = va_arg(ap, long);
880 sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
881 gi->textinput.cursor_position = strlen(gi->textinput.value);
885 gi->textinput.number_min = va_arg(ap, long);
886 if (gi->textinput.number_value < gi->textinput.number_min)
888 gi->textinput.number_value = gi->textinput.number_min;
889 sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
894 gi->textinput.number_max = va_arg(ap, long);
895 if (gi->textinput.number_value > gi->textinput.number_max)
897 gi->textinput.number_value = gi->textinput.number_max;
898 sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
904 int max_textsize = MAX_GADGET_TEXTSIZE;
906 if (gi->textinput.size)
907 max_textsize = MIN(gi->textinput.size, MAX_GADGET_TEXTSIZE - 1);
909 strncpy(gi->textinput.value, va_arg(ap, char *), max_textsize);
910 gi->textinput.value[max_textsize] = '\0';
911 gi->textinput.cursor_position = strlen(gi->textinput.value);
913 /* same tag also used for other gadget definitions */
914 strcpy(gi->textbutton.value, gi->textinput.value);
915 strcpy(gi->textarea.value, gi->textinput.value);
921 int tag_value = va_arg(ap, int);
922 int max_textsize = MIN(tag_value, MAX_GADGET_TEXTSIZE - 1);
924 gi->textinput.size = max_textsize;
925 gi->textinput.value[max_textsize] = '\0';
927 /* same tag also used for other gadget definitions */
928 strcpy(gi->textbutton.value, gi->textinput.value);
929 gi->textbutton.size = gi->textinput.size;
930 gi->selectbox.size = gi->textinput.size;
935 gi->font = va_arg(ap, int);
936 if (gi->font_active == 0)
937 gi->font_active = gi->font;
940 case GDI_TEXT_FONT_ACTIVE:
941 gi->font_active = va_arg(ap, int);
944 case GDI_SELECTBOX_OPTIONS:
945 gi->selectbox.options = va_arg(ap, struct ValueTextInfo *);
948 case GDI_SELECTBOX_INDEX:
949 gi->selectbox.index = va_arg(ap, int);
952 case GDI_DESIGN_UNPRESSED:
953 gi->design[GD_BUTTON_UNPRESSED].bitmap = va_arg(ap, Bitmap *);
954 gi->design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
955 gi->design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
958 case GDI_DESIGN_PRESSED:
959 gi->design[GD_BUTTON_PRESSED].bitmap = va_arg(ap, Bitmap *);
960 gi->design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
961 gi->design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
964 case GDI_ALT_DESIGN_UNPRESSED:
965 gi->alt_design[GD_BUTTON_UNPRESSED].bitmap= va_arg(ap, Bitmap *);
966 gi->alt_design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
967 gi->alt_design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
970 case GDI_ALT_DESIGN_PRESSED:
971 gi->alt_design[GD_BUTTON_PRESSED].bitmap = va_arg(ap, Bitmap *);
972 gi->alt_design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
973 gi->alt_design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
976 case GDI_BORDER_SIZE:
977 gi->border.xsize = va_arg(ap, int);
978 gi->border.ysize = va_arg(ap, int);
981 case GDI_BORDER_SIZE_SELECTBUTTON:
982 gi->border.xsize_selectbutton = va_arg(ap, int);
985 case GDI_DESIGN_WIDTH:
986 gi->border.width = va_arg(ap, int);
989 case GDI_DECORATION_DESIGN:
990 gi->deco.design.bitmap = va_arg(ap, Bitmap *);
991 gi->deco.design.x = va_arg(ap, int);
992 gi->deco.design.y = va_arg(ap, int);
995 case GDI_DECORATION_POSITION:
996 gi->deco.x = va_arg(ap, int);
997 gi->deco.y = va_arg(ap, int);
1000 case GDI_DECORATION_SIZE:
1001 gi->deco.width = va_arg(ap, int);
1002 gi->deco.height = va_arg(ap, int);
1005 case GDI_DECORATION_SHIFTING:
1006 gi->deco.xshift = va_arg(ap, int);
1007 gi->deco.yshift = va_arg(ap, int);
1010 case GDI_EVENT_MASK:
1011 gi->event_mask = va_arg(ap, unsigned long);
1015 gi->drawing.area_xsize = va_arg(ap, int);
1016 gi->drawing.area_ysize = va_arg(ap, int);
1018 /* determine dependent values for drawing area gadget, if needed */
1019 if (gi->width == 0 && gi->height == 0 &&
1020 gi->drawing.item_xsize !=0 && gi->drawing.item_ysize !=0)
1022 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
1023 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
1025 else if (gi->drawing.item_xsize == 0 && gi->drawing.item_ysize == 0 &&
1026 gi->width != 0 && gi->height != 0)
1028 gi->drawing.item_xsize = gi->width / gi->drawing.area_xsize;
1029 gi->drawing.item_ysize = gi->height / gi->drawing.area_ysize;
1032 /* same tag also used for other gadget definitions */
1033 gi->textarea.xsize = gi->drawing.area_xsize;
1034 gi->textarea.ysize = gi->drawing.area_ysize;
1036 if (gi->type & GD_TYPE_TEXT_AREA) /* force recalculation */
1045 gi->drawing.item_xsize = va_arg(ap, int);
1046 gi->drawing.item_ysize = va_arg(ap, int);
1048 /* determine dependent values for drawing area gadget, if needed */
1049 if (gi->width == 0 && gi->height == 0 &&
1050 gi->drawing.area_xsize !=0 && gi->drawing.area_ysize !=0)
1052 gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
1053 gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
1055 else if (gi->drawing.area_xsize == 0 && gi->drawing.area_ysize == 0 &&
1056 gi->width != 0 && gi->height != 0)
1058 gi->drawing.area_xsize = gi->width / gi->drawing.item_xsize;
1059 gi->drawing.area_ysize = gi->height / gi->drawing.item_ysize;
1063 case GDI_SCROLLBAR_ITEMS_MAX:
1064 gi->scrollbar.items_max = va_arg(ap, int);
1067 case GDI_SCROLLBAR_ITEMS_VISIBLE:
1068 gi->scrollbar.items_visible = va_arg(ap, int);
1071 case GDI_SCROLLBAR_ITEM_POSITION:
1072 gi->scrollbar.item_position = va_arg(ap, int);
1075 case GDI_CALLBACK_INFO:
1076 gi->callback_info = va_arg(ap, gadget_function);
1079 case GDI_CALLBACK_ACTION:
1080 gi->callback_action = va_arg(ap, gadget_function);
1084 Error(ERR_EXIT, "HandleGadgetTags(): unknown tag %d", tag);
1087 tag = va_arg(ap, int); /* read next tag */
1090 /* check if gadget is complete */
1091 if (gi->type != GD_TYPE_DRAWING_AREA &&
1092 (!gi->design[GD_BUTTON_UNPRESSED].bitmap ||
1093 !gi->design[GD_BUTTON_PRESSED].bitmap))
1094 Error(ERR_EXIT, "gadget incomplete (missing Bitmap)");
1096 /* adjust gadget values in relation to other gadget values */
1098 if (gi->type & GD_TYPE_TEXT_INPUT)
1100 int font_nr = gi->font_active;
1101 int font_width = getFontWidth(font_nr);
1102 int font_height = getFontHeight(font_nr);
1103 int border_xsize = gi->border.xsize;
1104 int border_ysize = gi->border.ysize;
1106 gi->width = 2 * border_xsize + (gi->textinput.size + 1) * font_width;
1107 gi->height = 2 * border_ysize + font_height;
1110 if (gi->type & GD_TYPE_SELECTBOX)
1112 int font_nr = gi->font_active;
1113 int font_width = getFontWidth(font_nr);
1114 int font_height = getFontHeight(font_nr);
1115 int border_xsize = gi->border.xsize;
1116 int border_ysize = gi->border.ysize;
1117 int button_size = gi->border.xsize_selectbutton;
1118 int bottom_screen_border = gfx.sy + gfx.sysize - font_height;
1122 gi->width = 2 * border_xsize + gi->textinput.size*font_width +button_size;
1123 gi->height = 2 * border_ysize + font_height;
1125 if (gi->selectbox.options == NULL)
1126 Error(ERR_EXIT, "selectbox gadget incomplete (missing options array)");
1128 gi->selectbox.num_values = 0;
1129 while (gi->selectbox.options[gi->selectbox.num_values].text != NULL)
1130 gi->selectbox.num_values++;
1132 /* calculate values for open selectbox */
1133 gi->selectbox.width = gi->width;
1134 gi->selectbox.height =
1135 2 * border_ysize + gi->selectbox.num_values * font_height;
1137 gi->selectbox.x = gi->x;
1138 gi->selectbox.y = gi->y + gi->height;
1139 if (gi->selectbox.y + gi->selectbox.height > bottom_screen_border)
1140 gi->selectbox.y = gi->y - gi->selectbox.height;
1141 if (gi->selectbox.y < 0)
1142 gi->selectbox.y = bottom_screen_border - gi->selectbox.height;
1144 getFontCharSource(font_nr, FONT_ASCII_CURSOR, &src_bitmap, &src_x, &src_y);
1145 src_x += font_width / 2;
1146 src_y += font_height / 2;
1147 gi->selectbox.inverse_color = GetPixel(src_bitmap, src_x, src_y);
1149 /* always start with closed selectbox */
1150 gi->selectbox.open = FALSE;
1153 if (gi->type & GD_TYPE_TEXT_INPUT_NUMERIC)
1155 struct GadgetTextInput *text = &gi->textinput;
1156 int value = text->number_value;
1158 text->number_value = (value < text->number_min ? text->number_min :
1159 value > text->number_max ? text->number_max :
1162 sprintf(text->value, "%d", text->number_value);
1165 if (gi->type & GD_TYPE_TEXT_BUTTON)
1167 int font_nr = gi->font_active;
1168 int font_width = getFontWidth(font_nr);
1169 int font_height = getFontHeight(font_nr);
1170 int border_xsize = gi->border.xsize;
1171 int border_ysize = gi->border.ysize;
1173 gi->width = 2 * border_xsize + gi->textbutton.size * font_width;
1174 gi->height = 2 * border_ysize + font_height;
1177 if (gi->type & GD_TYPE_SCROLLBAR)
1179 struct GadgetScrollbar *gs = &gi->scrollbar;
1181 if (gi->width == 0 || gi->height == 0 ||
1182 gs->items_max == 0 || gs->items_visible == 0)
1183 Error(ERR_EXIT, "scrollbar gadget incomplete (missing tags)");
1185 /* calculate internal scrollbar values */
1186 gs->size_max = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ?
1187 gi->height : gi->width);
1188 gs->size = gs->size_max * gs->items_visible / gs->items_max;
1189 gs->position = gs->size_max * gs->item_position / gs->items_max;
1190 gs->position_max = gs->size_max - gs->size;
1191 gs->correction = gs->size_max / gs->items_max / 2;
1193 /* finetuning for maximal right/bottom position */
1194 if (gs->item_position == gs->items_max - gs->items_visible)
1195 gs->position = gs->position_max;
1198 if (gi->type & GD_TYPE_TEXT_AREA)
1200 int font_nr = gi->font_active;
1201 int font_width = getFontWidth(font_nr);
1202 int font_height = getFontHeight(font_nr);
1203 int border_xsize = gi->border.xsize;
1204 int border_ysize = gi->border.ysize;
1206 if (gi->width == 0 || gi->height == 0)
1208 gi->width = 2 * border_xsize + gi->textarea.xsize * font_width;
1209 gi->height = 2 * border_ysize + gi->textarea.ysize * font_height;
1213 gi->textarea.xsize = (gi->width - 2 * border_xsize) / font_width;
1214 gi->textarea.ysize = (gi->height - 2 * border_ysize) / font_height;
1219 void ModifyGadget(struct GadgetInfo *gi, int first_tag, ...)
1223 va_start(ap, first_tag);
1224 HandleGadgetTags(gi, first_tag, ap);
1230 void RedrawGadget(struct GadgetInfo *gi)
1233 DrawGadget(gi, gi->state, gi->direct_draw);
1236 struct GadgetInfo *CreateGadget(int first_tag, ...)
1238 struct GadgetInfo *new_gadget = checked_calloc(sizeof(struct GadgetInfo));
1241 /* always start with reliable default values */
1242 new_gadget->id = getNewGadgetID();
1243 new_gadget->callback_info = default_callback_info;
1244 new_gadget->callback_action = default_callback_action;
1245 new_gadget->active = TRUE;
1246 new_gadget->direct_draw = TRUE;
1248 new_gadget->next = NULL;
1250 va_start(ap, first_tag);
1251 HandleGadgetTags(new_gadget, first_tag, ap);
1254 /* insert new gadget into global gadget list */
1255 if (gadget_list_last_entry)
1257 gadget_list_last_entry->next = new_gadget;
1258 gadget_list_last_entry = gadget_list_last_entry->next;
1261 gadget_list_first_entry = gadget_list_last_entry = new_gadget;
1266 void FreeGadget(struct GadgetInfo *gi)
1268 struct GadgetInfo *gi_previous = gadget_list_first_entry;
1270 while (gi_previous != NULL && gi_previous->next != gi)
1271 gi_previous = gi_previous->next;
1273 if (gi == gadget_list_first_entry)
1274 gadget_list_first_entry = gi->next;
1276 if (gi == gadget_list_last_entry)
1277 gadget_list_last_entry = gi_previous;
1279 if (gi_previous != NULL)
1280 gi_previous->next = gi->next;
1285 static void CheckRangeOfNumericInputGadget(struct GadgetInfo *gi)
1287 if (gi->type != GD_TYPE_TEXT_INPUT_NUMERIC)
1290 gi->textinput.number_value = atoi(gi->textinput.value);
1292 if (gi->textinput.number_value < gi->textinput.number_min)
1293 gi->textinput.number_value = gi->textinput.number_min;
1294 if (gi->textinput.number_value > gi->textinput.number_max)
1295 gi->textinput.number_value = gi->textinput.number_max;
1297 sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
1299 if (gi->textinput.cursor_position < 0)
1300 gi->textinput.cursor_position = 0;
1301 else if (gi->textinput.cursor_position > strlen(gi->textinput.value))
1302 gi->textinput.cursor_position = strlen(gi->textinput.value);
1305 /* global pointer to gadget actually in use (when mouse button pressed) */
1306 static struct GadgetInfo *last_gi = NULL;
1308 static void MapGadgetExt(struct GadgetInfo *gi, boolean redraw)
1310 if (gi == NULL || gi->mapped)
1316 DrawGadget(gi, DG_UNPRESSED, DG_BUFFERED);
1319 void MapGadget(struct GadgetInfo *gi)
1321 MapGadgetExt(gi, TRUE);
1324 void UnmapGadget(struct GadgetInfo *gi)
1326 if (gi == NULL || !gi->mapped)
1335 #define MAX_NUM_GADGETS 1024
1336 #define MULTIMAP_UNMAP (1 << 0)
1337 #define MULTIMAP_REMAP (1 << 1)
1338 #define MULTIMAP_REDRAW (1 << 2)
1339 #define MULTIMAP_PLAYFIELD (1 << 3)
1340 #define MULTIMAP_DOOR_1 (1 << 4)
1341 #define MULTIMAP_DOOR_2 (1 << 5)
1342 #define MULTIMAP_ALL (MULTIMAP_PLAYFIELD | \
1346 static void MultiMapGadgets(int mode)
1348 struct GadgetInfo *gi = gadget_list_first_entry;
1349 static boolean map_state[MAX_NUM_GADGETS];
1354 if ((mode & MULTIMAP_PLAYFIELD &&
1355 gi->x < gfx.sx + gfx.sxsize) ||
1356 (mode & MULTIMAP_DOOR_1 &&
1357 gi->x >= gfx.dx && gi->y < gfx.dy + gfx.dysize) ||
1358 (mode & MULTIMAP_DOOR_2 &&
1359 gi->x >= gfx.dx && gi->y > gfx.dy + gfx.dysize) ||
1360 (mode & MULTIMAP_ALL) == MULTIMAP_ALL)
1362 if (mode & MULTIMAP_UNMAP)
1364 map_state[map_count++ % MAX_NUM_GADGETS] = gi->mapped;
1369 if (map_state[map_count++ % MAX_NUM_GADGETS])
1370 MapGadgetExt(gi, (mode & MULTIMAP_REDRAW));
1378 void UnmapAllGadgets()
1380 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_UNMAP);
1383 void RemapAllGadgets()
1385 MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_REMAP);
1388 static boolean anyTextInputGadgetActive()
1390 return (last_gi && (last_gi->type & GD_TYPE_TEXT_INPUT) && last_gi->mapped);
1393 static boolean anyTextAreaGadgetActive()
1395 return (last_gi && (last_gi->type & GD_TYPE_TEXT_AREA) && last_gi->mapped);
1398 static boolean anySelectboxGadgetActive()
1400 return (last_gi && (last_gi->type & GD_TYPE_SELECTBOX) && last_gi->mapped);
1403 boolean anyTextGadgetActive()
1405 return (anyTextInputGadgetActive() ||
1406 anyTextAreaGadgetActive() ||
1407 anySelectboxGadgetActive());
1410 void ClickOnGadget(struct GadgetInfo *gi, int button)
1412 /* simulate releasing mouse button over last gadget, if still pressed */
1414 HandleGadgets(-1, -1, 0);
1416 /* simulate pressing mouse button over specified gadget */
1417 HandleGadgets(gi->x, gi->y, button);
1419 /* simulate releasing mouse button over specified gadget */
1420 HandleGadgets(gi->x, gi->y, 0);
1423 void HandleGadgets(int mx, int my, int button)
1425 static struct GadgetInfo *last_info_gi = NULL;
1426 static unsigned long pressed_delay = 0;
1427 static int last_button = 0;
1428 static int last_mx = 0, last_my = 0;
1429 int scrollbar_mouse_pos = 0;
1430 struct GadgetInfo *new_gi, *gi;
1431 boolean press_event;
1432 boolean release_event;
1433 boolean mouse_moving;
1434 boolean gadget_pressed;
1435 boolean gadget_pressed_repeated;
1436 boolean gadget_moving;
1437 boolean gadget_moving_inside;
1438 boolean gadget_moving_off_borders;
1439 boolean gadget_released;
1440 boolean gadget_released_inside;
1441 boolean gadget_released_inside_select_line;
1442 boolean gadget_released_inside_select_area;
1443 boolean gadget_released_off_borders;
1444 boolean changed_position = FALSE;
1446 /* check if there are any gadgets defined */
1447 if (gadget_list_first_entry == NULL)
1450 /* simulated release of mouse button over last gadget */
1451 if (mx == -1 && my == -1 && button == 0)
1457 /* check which gadget is under the mouse pointer */
1458 new_gi = getGadgetInfoFromMousePosition(mx, my);
1460 /* check if button state has changed since last invocation */
1461 press_event = (button != 0 && last_button == 0);
1462 release_event = (button == 0 && last_button != 0);
1463 last_button = button;
1465 /* check if mouse has been moved since last invocation */
1466 mouse_moving = ((mx != last_mx || my != last_my) && motion_status);
1474 /* if mouse button pressed outside text or selectbox gadget, deactivate it */
1475 if (anyTextGadgetActive() &&
1476 button != 0 && !motion_status && new_gi != last_gi)
1478 CheckRangeOfNumericInputGadget(last_gi); /* in case of numeric gadget */
1480 DrawGadget(last_gi, DG_UNPRESSED, last_gi->direct_draw);
1482 last_gi->event.type = GD_EVENT_TEXT_LEAVING;
1484 if (last_gi->event_mask & GD_EVENT_TEXT_LEAVING)
1485 last_gi->callback_action(last_gi);
1492 /* special treatment for leaving text and number input gadgets */
1493 if (anyTextInputGadgetActive() &&
1494 button != 0 && !motion_status && new_gi != last_gi)
1496 /* if mouse button pressed outside text input gadget, deactivate it */
1497 CheckRangeOfNumericInputGadget(last_gi);
1498 DrawGadget(last_gi, DG_UNPRESSED, last_gi->direct_draw);
1500 last_gi->event.type = GD_EVENT_TEXT_LEAVING;
1502 if (last_gi->event_mask & GD_EVENT_TEXT_LEAVING)
1503 last_gi->callback_action(last_gi);
1508 /* special treatment for leaving text area gadgets */
1509 if (anyTextAreaGadgetActive() &&
1510 button != 0 && !motion_status && new_gi != last_gi)
1512 /* if mouse button pressed outside text input gadget, deactivate it */
1513 DrawGadget(last_gi, DG_UNPRESSED, last_gi->direct_draw);
1515 last_gi->event.type = GD_EVENT_TEXT_LEAVING;
1517 if (last_gi->event_mask & GD_EVENT_TEXT_LEAVING)
1518 last_gi->callback_action(last_gi);
1523 /* special treatment for leaving selectbox gadgets */
1524 if (anySelectboxGadgetActive() &&
1525 button != 0 && !motion_status && new_gi != last_gi)
1527 /* if mouse button pressed outside selectbox gadget, deactivate it */
1528 DrawGadget(last_gi, DG_UNPRESSED, last_gi->direct_draw);
1530 last_gi->event.type = GD_EVENT_TEXT_LEAVING;
1532 if (last_gi->event_mask & GD_EVENT_TEXT_LEAVING)
1533 last_gi->callback_action(last_gi);
1542 /* special treatment for text and number input gadgets */
1543 if (anyTextInputGadgetActive() && button != 0 && !motion_status)
1545 struct GadgetInfo *gi = last_gi;
1547 if (new_gi == last_gi)
1549 int old_cursor_position = gi->textinput.cursor_position;
1551 /* if mouse button pressed inside activated text gadget, set cursor */
1552 gi->textinput.cursor_position =
1553 (mx - gi->x - gi->border.xsize) / getFontWidth(gi->font);
1555 if (gi->textinput.cursor_position < 0)
1556 gi->textinput.cursor_position = 0;
1557 else if (gi->textinput.cursor_position > strlen(gi->textinput.value))
1558 gi->textinput.cursor_position = strlen(gi->textinput.value);
1560 if (gi->textinput.cursor_position != old_cursor_position)
1561 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1565 /* if mouse button pressed outside text input gadget, deactivate it */
1566 CheckRangeOfNumericInputGadget(gi);
1567 DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
1569 gi->event.type = GD_EVENT_TEXT_LEAVING;
1571 if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
1572 gi->callback_action(gi);
1578 /* special treatment for text area gadgets */
1579 if (anyTextAreaGadgetActive() && button != 0 && !motion_status)
1581 struct GadgetInfo *gi = last_gi;
1583 if (new_gi == last_gi)
1585 int old_cursor_position = gi->textarea.cursor_position;
1586 int x = (mx - gi->x - gi->border.xsize) / getFontWidth(gi->font);
1587 int y = (my - gi->y - gi->border.ysize) / getFontHeight(gi->font);
1589 x = (x < 0 ? 0 : x >= gi->textarea.xsize ? gi->textarea.xsize - 1 : x);
1590 y = (y < 0 ? 0 : y >= gi->textarea.ysize ? gi->textarea.ysize - 1 : y);
1592 setTextAreaCursorXY(gi, x, y);
1595 printf("::: %d -----> %d\n",
1596 old_cursor_position,
1597 gi->textarea.cursor_position);
1600 if (gi->textarea.cursor_position != old_cursor_position)
1601 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1605 /* if mouse button pressed outside text input gadget, deactivate it */
1606 DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
1608 gi->event.type = GD_EVENT_TEXT_LEAVING;
1610 if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
1611 gi->callback_action(gi);
1617 /* special treatment for selectbox gadgets */
1618 if (anySelectboxGadgetActive() && button != 0 && !motion_status)
1620 struct GadgetInfo *gi = last_gi;
1622 if (new_gi == last_gi)
1624 int old_index = gi->selectbox.current_index;
1626 /* if mouse button pressed inside activated selectbox, select value */
1627 if (my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
1628 gi->selectbox.current_index =
1629 (my - gi->selectbox.y - gi->border.ysize) / getFontHeight(gi->font);
1631 if (gi->selectbox.current_index < 0)
1632 gi->selectbox.current_index = 0;
1633 else if (gi->selectbox.current_index > gi->selectbox.num_values - 1)
1634 gi->selectbox.current_index = gi->selectbox.num_values - 1;
1636 if (gi->selectbox.current_index != old_index)
1637 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1641 /* if mouse button pressed outside selectbox gadget, deactivate it */
1642 DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
1644 gi->event.type = GD_EVENT_TEXT_LEAVING;
1646 if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
1647 gi->callback_action(gi);
1656 (button != 0 && last_gi == NULL && new_gi != NULL && press_event);
1657 gadget_pressed_repeated =
1658 (button != 0 && last_gi != NULL && new_gi == last_gi);
1660 gadget_released = (release_event && last_gi != NULL);
1661 gadget_released_inside = (gadget_released && new_gi == last_gi);
1662 gadget_released_off_borders = (gadget_released && new_gi != last_gi);
1664 gadget_moving = (button != 0 && last_gi != NULL && mouse_moving);
1665 gadget_moving_inside = (gadget_moving && new_gi == last_gi);
1666 gadget_moving_off_borders = (gadget_moving && new_gi != last_gi);
1668 /* when handling selectbox, set additional state values */
1669 if (gadget_released_inside && (last_gi->type & GD_TYPE_SELECTBOX))
1671 struct GadgetInfo *gi = last_gi;
1673 gadget_released_inside_select_line =
1674 (mx >= gi->x && mx < gi->x + gi->width &&
1675 my >= gi->y && my < gi->y + gi->height);
1676 gadget_released_inside_select_area =
1677 (mx >= gi->selectbox.x && mx < gi->selectbox.x + gi->selectbox.width &&
1678 my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height);
1682 gadget_released_inside_select_line = FALSE;
1683 gadget_released_inside_select_area = FALSE;
1686 /* if new gadget pressed, store this gadget */
1690 /* 'gi' is actually handled gadget */
1693 /* if gadget is scrollbar, choose mouse position value */
1694 if (gi && gi->type & GD_TYPE_SCROLLBAR)
1695 scrollbar_mouse_pos =
1696 (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? mx - gi->x : my - gi->y);
1698 /* if mouse button released, no gadget needs to be handled anymore */
1699 if (gadget_released)
1701 if (last_gi->type & GD_TYPE_SELECTBOX &&
1702 (gadget_released_inside_select_line ||
1703 gadget_released_off_borders)) /* selectbox stays open */
1704 gi->selectbox.stay_open = TRUE;
1705 else if (!(last_gi->type & GD_TYPE_TEXT_INPUT ||
1706 last_gi->type & GD_TYPE_TEXT_AREA)) /* text input stays open */
1710 /* modify event position values even if no gadget is pressed */
1711 if (button == 0 && !release_event)
1716 int last_x = gi->event.x;
1717 int last_y = gi->event.y;
1719 gi->event.x = mx - gi->x;
1720 gi->event.y = my - gi->y;
1722 if (gi->type == GD_TYPE_DRAWING_AREA)
1724 gi->event.x /= gi->drawing.item_xsize;
1725 gi->event.y /= gi->drawing.item_ysize;
1727 if (last_x != gi->event.x || last_y != gi->event.y)
1728 changed_position = TRUE;
1730 else if (gi->type & GD_TYPE_TEXT_INPUT && button != 0 && !motion_status)
1732 int old_cursor_position = gi->textinput.cursor_position;
1734 /* if mouse button pressed inside activated text gadget, set cursor */
1735 gi->textinput.cursor_position =
1736 (mx - gi->x - gi->border.xsize) / getFontWidth(gi->font);
1738 if (gi->textinput.cursor_position < 0)
1739 gi->textinput.cursor_position = 0;
1740 else if (gi->textinput.cursor_position > strlen(gi->textinput.value))
1741 gi->textinput.cursor_position = strlen(gi->textinput.value);
1743 if (gi->textinput.cursor_position != old_cursor_position)
1744 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1746 else if (gi->type & GD_TYPE_TEXT_AREA && button != 0 && !motion_status)
1748 int old_cursor_position = gi->textarea.cursor_position;
1749 int x = (mx - gi->x - gi->border.xsize) / getFontWidth(gi->font);
1750 int y = (my - gi->y - gi->border.ysize) / getFontHeight(gi->font);
1752 x = (x < 0 ? 0 : x >= gi->textarea.xsize ? gi->textarea.xsize - 1 : x);
1753 y = (y < 0 ? 0 : y >= gi->textarea.ysize ? gi->textarea.ysize - 1 : y);
1755 setTextAreaCursorXY(gi, x, y);
1757 if (gi->textarea.cursor_position != old_cursor_position)
1758 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1760 else if (gi->type & GD_TYPE_SELECTBOX)
1762 int old_index = gi->selectbox.current_index;
1764 /* if mouse moving inside activated selectbox, select value */
1765 if (my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
1766 gi->selectbox.current_index =
1767 (my - gi->selectbox.y - gi->border.ysize) / getFontHeight(gi->font);
1769 if (gi->selectbox.current_index < 0)
1770 gi->selectbox.current_index = 0;
1771 else if (gi->selectbox.current_index > gi->selectbox.num_values - 1)
1772 gi->selectbox.current_index = gi->selectbox.num_values - 1;
1774 if (gi->selectbox.current_index != old_index)
1775 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1779 /* handle gadget popup info text */
1780 if (last_info_gi != new_gi ||
1781 (new_gi && new_gi->type == GD_TYPE_DRAWING_AREA && changed_position))
1783 if (new_gi != NULL && (button == 0 || new_gi == last_gi))
1785 new_gi->event.type = GD_EVENT_INFO_ENTERING;
1786 new_gi->callback_info(new_gi);
1788 else if (last_info_gi != NULL)
1790 last_info_gi->event.type = GD_EVENT_INFO_LEAVING;
1791 last_info_gi->callback_info(last_info_gi);
1794 default_callback_info(NULL);
1796 printf("It seems that we are leaving gadget [%s]!\n",
1797 (last_info_gi != NULL &&
1798 last_info_gi->info_text != NULL ?
1799 last_info_gi->info_text : ""));
1803 last_info_gi = new_gi;
1808 if (gi->type == GD_TYPE_CHECK_BUTTON)
1810 gi->checked = !gi->checked;
1812 else if (gi->type == GD_TYPE_RADIO_BUTTON)
1814 struct GadgetInfo *rgi = gadget_list_first_entry;
1819 rgi->type == GD_TYPE_RADIO_BUTTON &&
1820 rgi->radio_nr == gi->radio_nr &&
1823 rgi->checked = FALSE;
1824 DrawGadget(rgi, DG_UNPRESSED, rgi->direct_draw);
1832 else if (gi->type & GD_TYPE_SCROLLBAR)
1836 if (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL)
1847 if (mpos >= gpos + gi->scrollbar.position &&
1848 mpos < gpos + gi->scrollbar.position + gi->scrollbar.size)
1850 /* drag scrollbar */
1851 gi->scrollbar.drag_position =
1852 scrollbar_mouse_pos - gi->scrollbar.position;
1856 /* click scrollbar one scrollbar length up/left or down/right */
1858 struct GadgetScrollbar *gs = &gi->scrollbar;
1859 int old_item_position = gs->item_position;
1861 changed_position = FALSE;
1863 gs->item_position +=
1864 gs->items_visible * (mpos < gpos + gi->scrollbar.position ? -1 : +1);
1866 if (gs->item_position < 0)
1867 gs->item_position = 0;
1868 if (gs->item_position > gs->items_max - gs->items_visible)
1869 gs->item_position = gs->items_max - gs->items_visible;
1871 if (old_item_position != gs->item_position)
1873 gi->event.item_position = gs->item_position;
1874 changed_position = TRUE;
1877 ModifyGadget(gi, GDI_SCROLLBAR_ITEM_POSITION, gs->item_position,
1880 gi->state = GD_BUTTON_UNPRESSED;
1881 gi->event.type = GD_EVENT_MOVING;
1882 gi->event.off_borders = FALSE;
1884 if (gi->event_mask & GD_EVENT_MOVING && changed_position)
1885 gi->callback_action(gi);
1887 /* don't handle this scrollbar anymore while mouse button pressed */
1894 /* !!! bad for TEXT_INPUT ... !!! */
1895 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1897 gi->state = GD_BUTTON_PRESSED;
1898 gi->event.type = GD_EVENT_PRESSED;
1899 gi->event.button = button;
1900 gi->event.off_borders = FALSE;
1902 /* initialize delay counter */
1903 DelayReached(&pressed_delay, 0);
1905 if (gi->event_mask & GD_EVENT_PRESSED)
1906 gi->callback_action(gi);
1911 printf("::: PRESSED...?\n");
1914 if (gadget_pressed_repeated)
1916 gi->event.type = GD_EVENT_PRESSED;
1918 if (gi->event_mask & GD_EVENT_REPEATED &&
1919 DelayReached(&pressed_delay, GADGET_FRAME_DELAY))
1920 gi->callback_action(gi);
1925 if (gi->type & GD_TYPE_BUTTON)
1927 if (gadget_moving_inside && gi->state == GD_BUTTON_UNPRESSED)
1928 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1929 else if (gadget_moving_off_borders && gi->state == GD_BUTTON_PRESSED)
1930 DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
1932 else if (gi->type & GD_TYPE_SELECTBOX)
1934 int old_index = gi->selectbox.current_index;
1936 /* if mouse moving inside activated selectbox, select value */
1937 if (my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
1938 gi->selectbox.current_index =
1939 (my - gi->selectbox.y - gi->border.ysize) / getFontHeight(gi->font);
1941 if (gi->selectbox.current_index < 0)
1942 gi->selectbox.current_index = 0;
1943 else if (gi->selectbox.current_index > gi->selectbox.num_values - 1)
1944 gi->selectbox.current_index = gi->selectbox.num_values - 1;
1946 if (gi->selectbox.current_index != old_index)
1947 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1949 else if (gi->type & GD_TYPE_SCROLLBAR)
1951 struct GadgetScrollbar *gs = &gi->scrollbar;
1952 int old_item_position = gs->item_position;
1954 gs->position = scrollbar_mouse_pos - gs->drag_position;
1956 if (gs->position < 0)
1958 if (gs->position > gs->position_max)
1959 gs->position = gs->position_max;
1962 gs->items_max * (gs->position + gs->correction) / gs->size_max;
1964 if (gs->item_position < 0)
1965 gs->item_position = 0;
1966 if (gs->item_position > gs->items_max - 1)
1967 gs->item_position = gs->items_max - 1;
1969 if (old_item_position != gs->item_position)
1971 gi->event.item_position = gs->item_position;
1972 changed_position = TRUE;
1975 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
1978 gi->state = (gadget_moving_inside || gi->type & GD_TYPE_SCROLLBAR ?
1979 GD_BUTTON_PRESSED : GD_BUTTON_UNPRESSED);
1980 gi->event.type = GD_EVENT_MOVING;
1981 gi->event.off_borders = gadget_moving_off_borders;
1983 if (gi->event_mask & GD_EVENT_MOVING && changed_position &&
1984 (gadget_moving_inside || gi->event_mask & GD_EVENT_OFF_BORDERS))
1985 gi->callback_action(gi);
1988 if (gadget_released_inside)
1990 boolean deactivate_gadget = TRUE;
1992 if (gi->type & GD_TYPE_SELECTBOX)
1994 if (gadget_released_inside_select_line ||
1995 gadget_released_off_borders) /* selectbox stays open */
1996 deactivate_gadget = FALSE;
1998 gi->selectbox.index = gi->selectbox.current_index;
2001 if (deactivate_gadget &&
2002 !(gi->type & GD_TYPE_TEXT_INPUT ||
2003 gi->type & GD_TYPE_TEXT_AREA)) /* text input stays open */
2004 DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
2006 gi->state = GD_BUTTON_UNPRESSED;
2007 gi->event.type = GD_EVENT_RELEASED;
2009 if ((gi->event_mask & GD_EVENT_RELEASED) && deactivate_gadget)
2010 gi->callback_action(gi);
2013 if (gadget_released_off_borders)
2015 if (gi->type & GD_TYPE_SCROLLBAR)
2016 DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
2018 gi->event.type = GD_EVENT_RELEASED;
2020 if (gi->event_mask & GD_EVENT_RELEASED &&
2021 gi->event_mask & GD_EVENT_OFF_BORDERS)
2022 gi->callback_action(gi);
2025 /* handle gadgets unmapped/mapped between pressing and releasing */
2026 if (release_event && !gadget_released && new_gi)
2027 new_gi->state = GD_BUTTON_UNPRESSED;
2030 static void insertCharIntoTextArea(struct GadgetInfo *gi, char c)
2032 char text[MAX_GADGET_TEXTSIZE];
2033 int cursor_position = gi->textarea.cursor_position;
2035 if (strlen(gi->textarea.value) == MAX_GADGET_TEXTSIZE) /* no space left */
2039 printf("::: '%s' + '%c'", gi->textarea.value, c);
2042 strcpy(text, gi->textarea.value);
2043 strcpy(&gi->textarea.value[cursor_position + 1], &text[cursor_position]);
2044 gi->textarea.value[cursor_position] = c;
2047 printf(" => '%s'\n", gi->textarea.value);
2050 setTextAreaCursorPosition(gi, gi->textarea.cursor_position + 1);
2053 void HandleGadgetsKeyInput(Key key)
2055 struct GadgetInfo *gi = last_gi;
2057 if (gi == NULL || !gi->mapped ||
2058 !(gi->type & GD_TYPE_TEXT_INPUT ||
2059 gi->type & GD_TYPE_TEXT_AREA ||
2060 gi->type & GD_TYPE_SELECTBOX))
2063 if (key == KSYM_Return) /* valid for both text input and selectbox */
2065 if (gi->type & GD_TYPE_TEXT_INPUT)
2066 CheckRangeOfNumericInputGadget(gi);
2067 else if (gi->type & GD_TYPE_SELECTBOX)
2068 gi->selectbox.index = gi->selectbox.current_index;
2070 if (gi->type & GD_TYPE_TEXT_AREA)
2072 insertCharIntoTextArea(gi, '\n');
2074 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2078 DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
2080 gi->event.type = GD_EVENT_TEXT_RETURN;
2085 if (gi->event_mask & GD_EVENT_TEXT_RETURN)
2086 gi->callback_action(gi);
2088 else if (gi->type & GD_TYPE_TEXT_INPUT) /* only valid for text input */
2090 char text[MAX_GADGET_TEXTSIZE];
2091 int text_length = strlen(gi->textinput.value);
2092 int cursor_pos = gi->textinput.cursor_position;
2093 char letter = getCharFromKey(key);
2094 boolean legal_letter = (gi->type == GD_TYPE_TEXT_INPUT_NUMERIC ?
2095 letter >= '0' && letter <= '9' :
2098 if (legal_letter && text_length < gi->textinput.size)
2100 strcpy(text, gi->textinput.value);
2101 strcpy(&gi->textinput.value[cursor_pos + 1], &text[cursor_pos]);
2102 gi->textinput.value[cursor_pos] = letter;
2103 gi->textinput.cursor_position++;
2105 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2107 else if (key == KSYM_Left && cursor_pos > 0)
2109 gi->textinput.cursor_position--;
2111 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2113 else if (key == KSYM_Right && cursor_pos < text_length)
2115 gi->textinput.cursor_position++;
2117 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2119 else if (key == KSYM_BackSpace && cursor_pos > 0)
2121 strcpy(text, gi->textinput.value);
2122 strcpy(&gi->textinput.value[cursor_pos - 1], &text[cursor_pos]);
2123 gi->textinput.cursor_position--;
2125 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2127 else if (key == KSYM_Delete && cursor_pos < text_length)
2129 strcpy(text, gi->textinput.value);
2130 strcpy(&gi->textinput.value[cursor_pos], &text[cursor_pos + 1]);
2132 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2135 else if (gi->type & GD_TYPE_TEXT_AREA) /* only valid for text area */
2137 char text[MAX_GADGET_TEXTSIZE];
2138 int text_length = strlen(gi->textarea.value);
2139 int area_ysize = gi->textarea.ysize;
2140 int cursor_x_pref = gi->textarea.cursor_x_preferred;
2141 int cursor_y = gi->textarea.cursor_y;
2142 int cursor_pos = gi->textarea.cursor_position;
2143 char letter = getCharFromKey(key);
2144 boolean legal_letter = (letter != 0);
2147 printf("::: KEY: %x!\n", key);
2152 insertCharIntoTextArea(gi, letter);
2154 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2156 else if (key == KSYM_Left && cursor_pos > 0)
2158 setTextAreaCursorPosition(gi, gi->textarea.cursor_position - 1);
2160 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2162 else if (key == KSYM_Right && cursor_pos < text_length)
2164 setTextAreaCursorPosition(gi, gi->textarea.cursor_position + 1);
2166 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2168 else if (key == KSYM_Up && cursor_y > 0)
2170 setTextAreaCursorXY(gi, cursor_x_pref, cursor_y - 1);
2171 gi->textarea.cursor_x_preferred = cursor_x_pref;
2173 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2175 else if (key == KSYM_Down && cursor_y < area_ysize - 1)
2177 setTextAreaCursorXY(gi, cursor_x_pref, cursor_y + 1);
2178 gi->textarea.cursor_x_preferred = cursor_x_pref;
2180 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2182 else if (key == KSYM_BackSpace && cursor_pos > 0)
2184 strcpy(text, gi->textarea.value);
2185 strcpy(&gi->textarea.value[cursor_pos - 1], &text[cursor_pos]);
2187 setTextAreaCursorPosition(gi, gi->textarea.cursor_position - 1);
2189 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2191 else if (key == KSYM_Delete && cursor_pos < text_length)
2193 strcpy(text, gi->textarea.value);
2194 strcpy(&gi->textarea.value[cursor_pos], &text[cursor_pos + 1]);
2196 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2199 else if (gi->type & GD_TYPE_SELECTBOX) /* only valid for selectbox */
2201 int index = gi->selectbox.current_index;
2202 int num_values = gi->selectbox.num_values;
2204 if (key == KSYM_Up && index > 0)
2206 gi->selectbox.current_index--;
2208 DrawGadget(gi, DG_PRESSED, gi->direct_draw);
2210 else if (key == KSYM_Down && index < num_values - 1)
2212 gi->selectbox.current_index++;
2214 DrawGadget(gi, DG_PRESSED, gi->direct_draw);