#include <string.h>
#include "gadgets.h"
+#include "image.h"
#include "text.h"
#include "misc.h"
#define DG_BUFFERED 0
#define DG_DIRECT 1
-#define GADGET_DEACTIVATED(g) ((g)->x < 0 || (g)->y < 0)
-
#define OPTION_TEXT_SELECTABLE(g, t) \
(t[0] != g->selectbox.char_unselectable && \
t[0] != '\0' && \
return gi;
}
-static int getNewGadgetID()
+static int getNewGadgetID(void)
{
int id = next_free_gadget_id++;
boolean check_horizontal = (IS_WHEEL_BUTTON_HORIZONTAL(button) ||
GetKeyModState() & KMOD_Shift);
+ /* check for the first active scrollbar directly under the mouse pointer */
+ for (gi = gadget_list_first_entry; gi != NULL; gi = gi->next)
+ {
+ if (gi->mapped && gi->active &&
+ (gi->type & GD_TYPE_SCROLLBAR) &&
+ mx >= gi->x && mx < gi->x + gi->width &&
+ my >= gi->y && my < gi->y + gi->height)
+ return gi;
+ }
+
/* check for the first active scrollbar with matching mouse wheel area */
for (gi = gadget_list_first_entry; gi != NULL; gi = gi->next)
{
return;
}
+static void DoGadgetCallbackAction(struct GadgetInfo *gi, boolean changed)
+{
+ if (changed || gi->callback_action_always)
+ gi->callback_action(gi);
+}
+
static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
{
struct GadgetDesign *gd;
int state = (pressed ? GD_BUTTON_PRESSED : GD_BUTTON_UNPRESSED);
boolean redraw_selectbox = FALSE;
- if (gi == NULL)
+ if (gi == NULL || gi->deactivated)
return;
gd = (!gi->active ? &gi->alt_design[state] :
case GD_TYPE_NORMAL_BUTTON:
case GD_TYPE_CHECK_BUTTON:
case GD_TYPE_RADIO_BUTTON:
+
BlitBitmapOnBackground(gd->bitmap, drawto,
gd->x, gd->y, gi->width, gi->height,
gi->x, gi->y);
+
if (gi->deco.design.bitmap)
- BlitBitmap(gi->deco.design.bitmap, drawto,
- gi->deco.design.x, gi->deco.design.y,
- gi->deco.width, gi->deco.height,
- gi->x + gi->deco.x + (pressed ? gi->deco.xshift : 0),
- gi->y + gi->deco.y + (pressed ? gi->deco.yshift : 0));
+ {
+ // make sure that decoration does not overlap gadget border
+ int deco_x = gi->deco.x + (pressed ? gi->deco.xshift : 0);
+ int deco_y = gi->deco.y + (pressed ? gi->deco.yshift : 0);
+ int deco_width = MIN(gi->deco.width, gi->width - deco_x);
+ int deco_height = MIN(gi->deco.height, gi->height - deco_y);
+
+ if (gi->deco.masked)
+ BlitBitmapMasked(gi->deco.design.bitmap, drawto,
+ gi->deco.design.x, gi->deco.design.y,
+ deco_width, deco_height,
+ gi->x + deco_x, gi->y + deco_y);
+ else
+ BlitBitmap(gi->deco.design.bitmap, drawto,
+ gi->deco.design.x, gi->deco.design.y,
+ deco_width, deco_height,
+ gi->x + deco_x, gi->y + deco_y);
+ }
+
break;
case GD_TYPE_TEXT_BUTTON:
{
int tag = first_tag;
- if (gi == NULL)
+ if (gi == NULL || gi->deactivated)
return;
while (tag != GDI_END)
{
switch(tag)
{
+ case GDI_IMAGE_ID:
+ gi->image_id = va_arg(ap, int);
+ break;
+
case GDI_CUSTOM_ID:
gi->custom_id = va_arg(ap, int);
break;
gi->direct_draw = (boolean)va_arg(ap, int);
break;
+ case GDI_CALLBACK_ACTION_ALWAYS:
+ gi->callback_action_always = (boolean)va_arg(ap, int);
+ break;
+
case GDI_CHECKED:
gi->checked = (boolean)va_arg(ap, int);
break;
gi->deco.yshift = va_arg(ap, int);
break;
+ case GDI_DECORATION_MASKED:
+ gi->deco.masked = (boolean)va_arg(ap, int);
+ break;
+
case GDI_EVENT_MASK:
gi->event_mask = va_arg(ap, unsigned int);
break;
tag = va_arg(ap, int); /* read next tag */
}
- /* check if gadget is complete */
+ gi->deactivated = FALSE;
+
+ /* check if gadget has undefined bitmaps */
if (gi->type != GD_TYPE_DRAWING_AREA &&
- (!gi->design[GD_BUTTON_UNPRESSED].bitmap ||
- !gi->design[GD_BUTTON_PRESSED].bitmap))
- Error(ERR_EXIT, "gadget incomplete (missing Bitmap)");
+ (gi->design[GD_BUTTON_UNPRESSED].bitmap == NULL ||
+ gi->design[GD_BUTTON_PRESSED].bitmap == NULL))
+ gi->deactivated = TRUE;
+
+ /* check if gadget is placed off-screen */
+ if (gi->x < 0 || gi->y < 0)
+ gi->deactivated = TRUE;
/* adjust gadget values in relation to other gadget values */
void RedrawGadget(struct GadgetInfo *gi)
{
- if (gi == NULL)
+ if (gi == NULL || gi->deactivated)
return;
if (gi->mapped)
/* always start with reliable default values */
new_gadget->id = getNewGadgetID();
+ new_gadget->image_id = -1;
new_gadget->callback_info = default_callback_info;
new_gadget->callback_action = default_callback_action;
new_gadget->active = TRUE;
static void MapGadgetExt(struct GadgetInfo *gi, boolean redraw)
{
- if (gi == NULL || gi->mapped || GADGET_DEACTIVATED(gi))
+ if (gi == NULL || gi->deactivated || gi->mapped)
return;
gi->mapped = TRUE;
void UnmapGadget(struct GadgetInfo *gi)
{
- if (gi == NULL || !gi->mapped)
+ if (gi == NULL || gi->deactivated || !gi->mapped)
return;
gi->mapped = FALSE;
}
}
-void UnmapAllGadgets()
+void UnmapAllGadgets(void)
{
MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_UNMAP);
}
-void RemapAllGadgets()
+void RemapAllGadgets(void)
{
MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_REMAP);
}
-boolean anyTextInputGadgetActive()
+boolean anyTextInputGadgetActive(void)
{
return (last_gi && (last_gi->type & GD_TYPE_TEXT_INPUT) && last_gi->mapped);
}
-boolean anyTextAreaGadgetActive()
+boolean anyTextAreaGadgetActive(void)
{
return (last_gi && (last_gi->type & GD_TYPE_TEXT_AREA) && last_gi->mapped);
}
-boolean anySelectboxGadgetActive()
+boolean anySelectboxGadgetActive(void)
{
return (last_gi && (last_gi->type & GD_TYPE_SELECTBOX) && last_gi->mapped);
}
-boolean anyScrollbarGadgetActive()
+boolean anyScrollbarGadgetActive(void)
{
return (last_gi && (last_gi->type & GD_TYPE_SCROLLBAR) && last_gi->mapped);
}
-boolean anyTextGadgetActive()
+boolean anyTextGadgetActive(void)
{
return (anyTextInputGadgetActive() ||
anyTextAreaGadgetActive() ||
void ClickOnGadget(struct GadgetInfo *gi, int button)
{
- if (!gi->mapped)
+ if (gi == NULL || gi->deactivated || !gi->mapped)
return;
/* simulate releasing mouse button over last gadget, if still pressed */
if (button_status)
HandleGadgets(-1, -1, 0);
+ int x = gi->x;
+ int y = gi->y;
+
+ /* set cursor position to the end of the text for text input gadgets */
+ if (gi->type & GD_TYPE_TEXT_INPUT)
+ x = gi->x + gi->width - 1;
+
/* simulate pressing mouse button over specified gadget */
- HandleGadgets(gi->x, gi->y, button);
+ HandleGadgets(x, y, button);
/* simulate releasing mouse button over specified gadget */
- HandleGadgets(gi->x, gi->y, 0);
+ HandleGadgets(x, y, 0);
}
boolean HandleGadgets(int mx, int my, int button)
gi->event.type = GD_EVENT_TEXT_LEAVING;
- if (gadget_changed && !(gi->type & GD_TYPE_SELECTBOX))
- gi->callback_action(gi);
+ if (!(gi->type & GD_TYPE_SELECTBOX))
+ DoGadgetCallbackAction(gi, gadget_changed);
last_gi = NULL;
if (gadget_pressed_inside_select_line)
new_gi = NULL;
+
+ StopTextInput();
}
gadget_pressed =
{
int last_x = gi->event.x;
int last_y = gi->event.y;
+ int last_mx = gi->event.mx;
+ int last_my = gi->event.my;
- gi->event.x = mx - gi->x;
- gi->event.y = my - gi->y;
+ gi->event.x = gi->event.mx = mx - gi->x;
+ gi->event.y = gi->event.my = my - gi->y;
if (gi->type == GD_TYPE_DRAWING_AREA)
{
gi->event.x /= gi->drawing.item_xsize;
gi->event.y /= gi->drawing.item_ysize;
- if (last_x != gi->event.x || last_y != gi->event.y)
+ if (last_x != gi->event.x || last_y != gi->event.y ||
+ ((last_mx != gi->event.mx || last_my != gi->event.my) &&
+ gi->event_mask & GD_EVENT_PIXEL_PRECISE))
changed_position = TRUE;
}
else if (gi->type & GD_TYPE_TEXT_INPUT && button != 0 && !motion_status)
if (gi->textinput.cursor_position != old_cursor_position)
DrawGadget(gi, DG_PRESSED, gi->direct_draw);
+
+ if (press_event)
+ StartTextInput(gi->x, gi->y, gi->width, gi->height);
}
else if (gi->type & GD_TYPE_TEXT_AREA && button != 0 && !motion_status)
{
if (gi->textarea.cursor_position != old_cursor_position)
DrawGadget(gi, DG_PRESSED, gi->direct_draw);
+
+ if (press_event)
+ StartTextInput(gi->x, gi->y, gi->width, gi->height);
}
else if (gi->type & GD_TYPE_SELECTBOX && gi->selectbox.open &&
!keep_selectbox_open)
new_gi->event.type = GD_EVENT_INFO_ENTERING;
new_gi->callback_info(new_gi);
}
- else if (last_info_gi != NULL)
+ else if (last_info_gi != NULL && last_info_gi->mapped)
{
last_info_gi->event.type = GD_EVENT_INFO_LEAVING;
last_info_gi->callback_info(last_info_gi);
{
boolean scroll_single_step = ((GetKeyModState() & KMOD_Alt) != 0);
- item_steps = (scroll_single_step ? 1 : DEFAULT_WHEEL_STEPS);
+ item_steps = (scroll_single_step ? 1 : wheel_steps);
item_direction = (button == MB_WHEEL_UP ||
button == MB_WHEEL_LEFT ? -1 : +1);
}
gi->event.type = GD_EVENT_MOVING;
gi->event.off_borders = FALSE;
- if (gi->event_mask & GD_EVENT_MOVING && changed_position)
- gi->callback_action(gi);
+ if (gi->event_mask & GD_EVENT_MOVING)
+ DoGadgetCallbackAction(gi, changed_position);
return TRUE;
}
gi->event.type = GD_EVENT_MOVING;
gi->event.off_borders = gadget_moving_off_borders;
- if (gi->event_mask & GD_EVENT_MOVING && changed_position &&
+ if (gi->event_mask & GD_EVENT_MOVING &&
(gadget_moving_inside || gi->event_mask & GD_EVENT_OFF_BORDERS))
- gi->callback_action(gi);
+ DoGadgetCallbackAction(gi, changed_position);
}
if (gadget_released_inside)
gi->state = GD_BUTTON_UNPRESSED;
gi->event.type = GD_EVENT_RELEASED;
- if ((gi->event_mask & GD_EVENT_RELEASED) && gadget_changed)
- {
- gi->callback_action(gi);
- }
+ if ((gi->event_mask & GD_EVENT_RELEASED))
+ DoGadgetCallbackAction(gi, gadget_changed);
}
if (gadget_released_off_borders)
{
struct GadgetInfo *gi = last_gi;
- if (gi == NULL || !gi->mapped ||
+ if (gi == NULL || gi->deactivated || !gi->mapped ||
!(gi->type & GD_TYPE_TEXT_INPUT ||
gi->type & GD_TYPE_TEXT_AREA ||
gi->type & GD_TYPE_SELECTBOX))
return FALSE;
- if (key == KSYM_Return) /* valid for both text input and selectbox */
+ if (key == KSYM_Escape)
+ {
+ StopTextInput();
+ }
+ else if (key == KSYM_Return) /* valid for both text input and selectbox */
{
boolean gadget_changed = ((gi->event_mask & GD_EVENT_TEXT_RETURN) != 0);
strcpy(gi->textinput.last_value, gi->textinput.value);
else
gadget_changed = FALSE;
+
+ StopTextInput();
}
else if (gi->type & GD_TYPE_SELECTBOX)
{
last_gi = NULL;
}
- if (gadget_changed)
- gi->callback_action(gi);
+ DoGadgetCallbackAction(gi, gadget_changed);
}
else if (gi->type & GD_TYPE_TEXT_INPUT) /* only valid for text input */
{
return TRUE;
}
+
+void DumpGadgetIdentifiers(void)
+{
+ struct GadgetInfo *gi;
+
+ Print("Gadgets on current screen:\n");
+
+ for (gi = gadget_list_first_entry; gi != NULL; gi = gi->next)
+ {
+ if (gi->mapped && gi->image_id != -1)
+ {
+ char *token = getTokenFromImageID(gi->image_id);
+ char *prefix = "gfx.";
+
+ if (strPrefix(token, prefix))
+ token = &token[strlen(prefix)];
+
+ Print("- '%s'\n", token);
+ }
+ }
+
+ Print("Done.\n");
+}
+
+boolean DoGadgetAction(int image_id)
+{
+ struct GadgetInfo *gi;
+
+ for (gi = gadget_list_first_entry; gi != NULL; gi = gi->next)
+ {
+ if (gi->mapped && gi->image_id == image_id)
+ {
+ gi->callback_action(gi);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}