static struct GadgetInfo *gadget_list_first_entry = NULL;
static struct GadgetInfo *gadget_list_last_entry = NULL;
+static struct GadgetInfo *last_info_gi = NULL;
static int next_free_gadget_id = 1;
static boolean gadget_id_wrapped = FALSE;
return id;
}
-#if 0
-void DUMP_GADGET_MAP_STATE()
+static struct GadgetInfo *getGadgetInfoFromMousePosition(int mx, int my,
+ int button)
{
- struct GadgetInfo *gi = gadget_list_first_entry;
+ struct GadgetInfo *gi;
- while (gi != NULL)
+ /* first check for scrollbars in case of mouse scroll wheel button events */
+ if (IS_WHEEL_BUTTON(button))
{
- printf("-XXX-1-> '%s': %s\n",
- gi->info_text, (gi->mapped ? "mapped" : "not mapped"));
+ /* real horizontal wheel or vertical wheel with modifier key pressed */
+ boolean check_horizontal = (IS_WHEEL_BUTTON_HORIZONTAL(button) ||
+ GetKeyModState() & KMOD_Shift);
- gi = gi->next;
- }
-}
-#endif
+ /* check for the first active scrollbar with matching mouse wheel area */
+ for (gi = gadget_list_first_entry; gi != NULL; gi = gi->next)
+ {
+ if (gi->mapped && gi->active &&
+ ((gi->type & GD_TYPE_SCROLLBAR_HORIZONTAL && check_horizontal) ||
+ (gi->type & GD_TYPE_SCROLLBAR_VERTICAL && !check_horizontal)) &&
+ mx >= gi->wheelarea.x && mx < gi->wheelarea.x + gi->wheelarea.width &&
+ my >= gi->wheelarea.y && my < gi->wheelarea.y + gi->wheelarea.height)
+ return gi;
+ }
-static struct GadgetInfo *getGadgetInfoFromMousePosition(int mx, int my)
-{
- struct GadgetInfo *gi;
+ /* no active scrollbar found -- ignore this scroll wheel button event */
+ return NULL;
+ }
/* open selectboxes may overlap other active gadgets, so check them first */
for (gi = gadget_list_first_entry; gi != NULL; gi = gi->next)
static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
{
+ struct GadgetDesign *gd;
int state = (pressed ? GD_BUTTON_PRESSED : GD_BUTTON_UNPRESSED);
- struct GadgetDesign *gd = (!gi->active ? &gi->alt_design[state] :
- gi->checked ? &gi->alt_design[state] :
- &gi->design[state]);
boolean redraw_selectbox = FALSE;
+ if (gi == NULL)
+ return;
+
+ gd = (!gi->active ? &gi->alt_design[state] :
+ gi->checked ? &gi->alt_design[state] : &gi->design[state]);
+
switch (gi->type)
{
case GD_TYPE_NORMAL_BUTTON:
{
int tag = first_tag;
+ if (gi == NULL)
+ return;
+
while (tag != GDI_END)
{
switch(tag)
gi->scrollbar.item_position = va_arg(ap, int);
break;
+ case GDI_WHEEL_AREA_X:
+ gi->wheelarea.x = va_arg(ap, int);
+ break;
+
+ case GDI_WHEEL_AREA_Y:
+ gi->wheelarea.y = va_arg(ap, int);
+ break;
+
+ case GDI_WHEEL_AREA_WIDTH:
+ gi->wheelarea.width = va_arg(ap, int);
+ break;
+
+ case GDI_WHEEL_AREA_HEIGHT:
+ gi->wheelarea.height = va_arg(ap, int);
+ break;
+
case GDI_CALLBACK_INFO:
gi->callback_info = va_arg(ap, gadget_function);
break;
void RedrawGadget(struct GadgetInfo *gi)
{
+ if (gi == NULL)
+ return;
+
if (gi->mapped)
DrawGadget(gi, gi->state, gi->direct_draw);
}
{
struct GadgetInfo *gi_previous = gadget_list_first_entry;
+ /* prevent "last_info_gi" from pointing to memory that will be freed */
+ if (last_info_gi == gi)
+ last_info_gi = NULL;
+
while (gi_previous != NULL && gi_previous->next != gi)
gi_previous = gi_previous->next;
boolean HandleGadgets(int mx, int my, int button)
{
- static struct GadgetInfo *last_info_gi = NULL;
static unsigned long pressed_delay = 0;
+ static unsigned long pressed_delay_value = GADGET_FRAME_DELAY;
static int last_button = 0;
static int last_mx = 0, last_my = 0;
static int pressed_mx = 0, pressed_my = 0;
+ static boolean keep_selectbox_open = FALSE;
+ static boolean gadget_stopped = FALSE;
int scrollbar_mouse_pos = 0;
struct GadgetInfo *new_gi, *gi;
boolean press_event;
boolean gadget_pressed_repeated;
boolean gadget_pressed_off_borders;
boolean gadget_pressed_inside_select_line;
+ boolean gadget_pressed_delay_reached;
boolean gadget_moving;
boolean gadget_moving_inside;
boolean gadget_moving_off_borders;
+ boolean gadget_draggable;
+ boolean gadget_dragging;
boolean gadget_released;
boolean gadget_released_inside;
boolean gadget_released_inside_select_line;
}
/* check which gadget is under the mouse pointer */
- new_gi = getGadgetInfoFromMousePosition(mx, my);
+ new_gi = getGadgetInfoFromMousePosition(mx, my, button);
/* check if button state has changed since last invocation */
- press_event = (button != 0 && last_button == 0);
+ press_event = (button != 0 && last_button == 0);
release_event = (button == 0 && last_button != 0);
last_button = button;
insideSelectboxLine(new_gi, mx, my));
/* if mouse button pressed outside text or selectbox gadget, deactivate it */
-#if 1
if (anyTextGadgetActive() &&
(gadget_pressed_off_borders ||
(gadget_pressed_inside_select_line && !mouse_inside_select_area)))
-#else
- if (anyTextGadgetActive() &&
- button != 0 && !motion_status && new_gi != last_gi)
-#endif
{
struct GadgetInfo *gi = last_gi;
boolean gadget_changed = (gi->event_mask & GD_EVENT_TEXT_LEAVING);
{
CheckRangeOfNumericInputGadget(gi);
- if (strcmp(gi->textinput.value, gi->textinput.last_value) != 0)
+ if (!strEqual(gi->textinput.last_value, gi->textinput.value))
strcpy(gi->textinput.last_value, gi->textinput.value);
else
gadget_changed = FALSE;
gi->event.type = GD_EVENT_TEXT_LEAVING;
-#if 1
if (gadget_changed && !(gi->type & GD_TYPE_SELECTBOX))
gi->callback_action(gi);
-#else
- if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
- gi->callback_action(gi);
-#endif
last_gi = NULL;
gadget_pressed_repeated =
(button != 0 && last_gi != NULL && new_gi == last_gi);
+ gadget_pressed_delay_reached =
+ DelayReached(&pressed_delay, pressed_delay_value);
+
gadget_released = (release_event && last_gi != NULL);
gadget_released_inside = (gadget_released && new_gi == last_gi);
gadget_released_off_borders = (gadget_released && new_gi != last_gi);
/* when handling selectbox, set additional state values */
if (gadget_released_inside && (last_gi->type & GD_TYPE_SELECTBOX))
{
- struct GadgetInfo *gi = last_gi;
-
-#if 1
- gadget_released_inside_select_line = insideSelectboxLine(gi, mx, my);
- gadget_released_inside_select_area = insideSelectboxArea(gi, mx, my);
-#else
- gadget_released_inside_select_line =
- (mx >= gi->x && mx < gi->x + gi->width &&
- my >= gi->y && my < gi->y + gi->height);
- gadget_released_inside_select_area =
- (mx >= gi->selectbox.x && mx < gi->selectbox.x + gi->selectbox.width &&
- my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height);
-#endif
+ gadget_released_inside_select_line = insideSelectboxLine(last_gi, mx, my);
+ gadget_released_inside_select_area = insideSelectboxArea(last_gi, mx, my);
}
else
{
gadget_released_inside_select_area = FALSE;
}
+ /* setting state for handling over-large selectbox */
+ if (keep_selectbox_open && (press_event || !mouse_inside_select_line))
+ keep_selectbox_open = FALSE;
+
/* if new gadget pressed, store this gadget */
if (gadget_pressed)
last_gi = new_gi;
/* if mouse button released, no gadget needs to be handled anymore */
if (gadget_released)
{
-#if 1
if (gi->type & GD_TYPE_SELECTBOX &&
- (mouse_released_where_pressed ||
- !gadget_released_inside_select_area)) /* selectbox stays open */
+ (keep_selectbox_open ||
+ mouse_released_where_pressed ||
+ !gadget_released_inside_select_area)) /* selectbox stays open */
{
gi->selectbox.stay_open = TRUE;
pressed_mx = 0;
pressed_my = 0;
}
-#else
- if (gi->type & GD_TYPE_SELECTBOX &&
- (gadget_released_inside_select_line ||
- gadget_released_off_borders)) /* selectbox stays open */
- gi->selectbox.stay_open = TRUE;
-#endif
else if (!(gi->type & GD_TYPE_TEXT_INPUT ||
gi->type & GD_TYPE_TEXT_AREA)) /* text input stays open */
last_gi = NULL;
if (button == 0 && !release_event)
gi = new_gi;
+ /* if new gadget or if no gadget was pressed, release stopped processing */
+ if (gadget_pressed || new_gi == NULL)
+ gadget_stopped = FALSE;
+
+ /* if gadget was stopped while being handled, stop gadget processing here */
+ if (gadget_stopped)
+ return TRUE;
+
if (gi != NULL)
{
int last_x = gi->event.x;
if (gi->textarea.cursor_position != old_cursor_position)
DrawGadget(gi, DG_PRESSED, gi->direct_draw);
}
- else if (gi->type & GD_TYPE_SELECTBOX && gi->selectbox.open)
+ else if (gi->type & GD_TYPE_SELECTBOX && gi->selectbox.open &&
+ !keep_selectbox_open)
{
int old_index = gi->selectbox.current_index;
last_info_gi = new_gi;
}
- if (gadget_pressed)
- {
- if (gi->type == GD_TYPE_CHECK_BUTTON)
- {
- gi->checked = !gi->checked;
- }
- else if (gi->type == GD_TYPE_RADIO_BUTTON)
- {
- struct GadgetInfo *rgi = gadget_list_first_entry;
+#if 1
- while (rgi)
- {
- if (rgi->mapped &&
- rgi->type == GD_TYPE_RADIO_BUTTON &&
- rgi->radio_nr == gi->radio_nr &&
- rgi != gi)
- {
- rgi->checked = FALSE;
- DrawGadget(rgi, DG_UNPRESSED, rgi->direct_draw);
- }
+ gadget_draggable = (gi && gi->type & GD_TYPE_SCROLLBAR);
- rgi = rgi->next;
- }
+ /* reset drag position for newly pressed scrollbar to "not dragging" */
+ if (gadget_pressed && gadget_draggable)
+ gi->scrollbar.drag_position = -1;
- gi->checked = TRUE;
+ gadget_dragging = (gadget_draggable && gi->scrollbar.drag_position != -1);
+
+ /* clicking next to a scrollbar to move it is not considered "moving" */
+ if (gadget_draggable && !gadget_dragging)
+ gadget_moving = FALSE;
+
+ /* when leaving scrollbar area when jump-scrolling, stop gadget processing */
+ if (gadget_draggable && !gadget_dragging && gadget_moving_off_borders)
+ gadget_stopped = TRUE;
+
+ if ((gadget_pressed) ||
+ (gadget_pressed_repeated && gadget_pressed_delay_reached))
+ {
+ if (gadget_pressed) /* gadget pressed the first time */
+ {
+ /* initialize delay counter */
+ DelayReached(&pressed_delay, 0);
+
+ /* start gadget delay with longer delay after first click on gadget */
+ pressed_delay_value = GADGET_FRAME_DELAY_FIRST;
}
- else if (gi->type & GD_TYPE_SCROLLBAR)
+ else /* gadget hold pressed for some time */
{
- int mpos, gpos;
+ /* after first repeated gadget click, continue with shorter delay value */
+ pressed_delay_value = GADGET_FRAME_DELAY;
+ }
- if (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL)
- {
- mpos = mx;
- gpos = gi->x;
- }
- else
- {
- mpos = my;
- gpos = gi->y;
- }
+ if (gi->type & GD_TYPE_SCROLLBAR && !gadget_dragging)
+ {
+ int mpos = (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? mx : my);
+ int gpos = (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? gi->x : gi->y);
+ int slider_start = gpos + gi->scrollbar.position;
+ int slider_end = gpos + gi->scrollbar.position + gi->scrollbar.size - 1;
+ boolean inside_slider = (mpos >= slider_start && mpos <= slider_end);
- if (mpos >= gpos + gi->scrollbar.position &&
- mpos < gpos + gi->scrollbar.position + gi->scrollbar.size)
- {
- /* drag scrollbar */
- gi->scrollbar.drag_position =
- scrollbar_mouse_pos - gi->scrollbar.position;
- }
- else
+ if (IS_WHEEL_BUTTON(button) || !inside_slider)
{
/* click scrollbar one scrollbar length up/left or down/right */
struct GadgetScrollbar *gs = &gi->scrollbar;
int old_item_position = gs->item_position;
+ int item_steps = gs->items_visible - 1;
+ int item_direction = (mpos < gpos + gi->scrollbar.position ? -1 : +1);
+
+ if (IS_WHEEL_BUTTON(button))
+ {
+ boolean scroll_single_step = (GetKeyModState() & KMOD_Alt);
+
+ item_steps = (scroll_single_step ? 1 : DEFAULT_WHEEL_STEPS);
+ item_direction = (button == MB_WHEEL_UP ||
+ button == MB_WHEEL_LEFT ? -1 : +1);
+ }
changed_position = FALSE;
- gs->item_position +=
- gs->items_visible * (mpos < gpos + gi->scrollbar.position ? -1 : +1);
+ gs->item_position += item_steps * item_direction;
if (gs->item_position < 0)
gs->item_position = 0;
if (gi->event_mask & GD_EVENT_MOVING && changed_position)
gi->callback_action(gi);
- /* don't handle this scrollbar anymore while mouse button pressed */
- last_gi = NULL;
-
return TRUE;
}
+ else
+ {
+ /* don't handle this scrollbar anymore when mouse position reached */
+ if (gadget_pressed_repeated)
+ {
+ gadget_stopped = TRUE;
+
+ return TRUE;
+ }
+ }
+ }
+ }
+
+#endif
+
+ if (gadget_pressed)
+ {
+ if (gi->type == GD_TYPE_CHECK_BUTTON)
+ {
+ gi->checked = !gi->checked;
+ }
+ else if (gi->type == GD_TYPE_RADIO_BUTTON)
+ {
+ struct GadgetInfo *rgi = gadget_list_first_entry;
+
+ while (rgi)
+ {
+ if (rgi->mapped &&
+ rgi->type == GD_TYPE_RADIO_BUTTON &&
+ rgi->radio_nr == gi->radio_nr &&
+ rgi != gi)
+ {
+ rgi->checked = FALSE;
+ DrawGadget(rgi, DG_UNPRESSED, rgi->direct_draw);
+ }
+
+ rgi = rgi->next;
+ }
+
+ gi->checked = TRUE;
+ }
+ else if (gi->type & GD_TYPE_SCROLLBAR)
+ {
+ int mpos = (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? mx : my);
+ int gpos = (gi->type == GD_TYPE_SCROLLBAR_HORIZONTAL ? gi->x : gi->y);
+ int slider_start = gpos + gi->scrollbar.position;
+ int slider_end = gpos + gi->scrollbar.position + gi->scrollbar.size - 1;
+ boolean inside_slider = (mpos >= slider_start && mpos <= slider_end);
+
+ if (!IS_WHEEL_BUTTON(button) && inside_slider)
+ {
+ /* start dragging scrollbar */
+ gi->scrollbar.drag_position =
+ scrollbar_mouse_pos - gi->scrollbar.position;
+ }
+ }
+ else if (gi->type & GD_TYPE_SELECTBOX)
+ {
+ /* keep selectbox open in case of over-large selectbox */
+ keep_selectbox_open = (mouse_inside_select_line &&
+ mouse_inside_select_area);
}
DrawGadget(gi, DG_PRESSED, gi->direct_draw);
gi->event.button = button;
gi->event.off_borders = FALSE;
- /* initialize delay counter */
- DelayReached(&pressed_delay, 0);
-
if (gi->event_mask & GD_EVENT_PRESSED)
gi->callback_action(gi);
}
{
gi->event.type = GD_EVENT_PRESSED;
- if (gi->event_mask & GD_EVENT_REPEATED &&
- DelayReached(&pressed_delay, GADGET_FRAME_DELAY))
+ if (gi->event_mask & GD_EVENT_REPEATED && gadget_pressed_delay_reached)
gi->callback_action(gi);
}
else if (gadget_moving_off_borders && gi->state == GD_BUTTON_PRESSED)
DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
}
- else if (gi->type & GD_TYPE_SELECTBOX)
+ else if (gi->type & GD_TYPE_SELECTBOX && !keep_selectbox_open)
{
int old_index = gi->selectbox.current_index;
DrawGadget(gi, DG_PRESSED, gi->direct_draw);
}
- gi->state = (gadget_moving_inside || gi->type & GD_TYPE_SCROLLBAR ?
+ gi->state = (gadget_moving_inside || gadget_draggable ?
GD_BUTTON_PRESSED : GD_BUTTON_UNPRESSED);
gi->event.type = GD_EVENT_MOVING;
gi->event.off_borders = gadget_moving_off_borders;
if (gi->type & GD_TYPE_SELECTBOX)
{
-#if 1
- if (mouse_released_where_pressed ||
+ if (keep_selectbox_open ||
+ mouse_released_where_pressed ||
!gadget_released_inside_select_area) /* selectbox stays open */
{
deactivate_gadget = FALSE;
gadget_changed = FALSE;
}
-#else
- if (gadget_released_inside_select_line ||
- gadget_released_off_borders) /* selectbox stays open */
- {
- deactivate_gadget = FALSE;
- gadget_changed = FALSE;
- }
-#endif
else if (gi->selectbox.index != gi->selectbox.current_index)
gi->selectbox.index = gi->selectbox.current_index;
else
gi->state = GD_BUTTON_UNPRESSED;
gi->event.type = GD_EVENT_RELEASED;
-#if 1
if ((gi->event_mask & GD_EVENT_RELEASED) && gadget_changed)
{
gi->callback_action(gi);
}
-#else
- if ((gi->event_mask & GD_EVENT_RELEASED) && deactivate_gadget)
- gi->callback_action(gi);
-#endif
}
if (gadget_released_off_borders)
if (gi->type & GD_TYPE_SCROLLBAR)
DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
+#if 1
+ gi->state = GD_BUTTON_UNPRESSED;
+#endif
gi->event.type = GD_EVENT_RELEASED;
if (gi->event_mask & GD_EVENT_RELEASED &&
{
CheckRangeOfNumericInputGadget(gi);
- if (strcmp(gi->textinput.value, gi->textinput.last_value) != 0)
+ if (!strEqual(gi->textinput.last_value, gi->textinput.value))
strcpy(gi->textinput.last_value, gi->textinput.value);
else
gadget_changed = FALSE;
last_gi = NULL;
}
-#if 1
if (gadget_changed)
gi->callback_action(gi);
-#else
- if (gi->event_mask & GD_EVENT_TEXT_RETURN)
- gi->callback_action(gi);
-#endif
}
else if (gi->type & GD_TYPE_TEXT_INPUT) /* only valid for text input */
{