added gadget tag to always execute the callback action when leaving gadget
[rocksndiamonds.git] / src / libgame / gadgets.c
index 736f1465fb2d381b5a07419f4f28759ff55de656..937f7a3a22bd5e8c9b00bbe058d065ed8b0c5b1c 100644 (file)
@@ -13,6 +13,7 @@
 #include <string.h>
 
 #include "gadgets.h"
+#include "image.h"
 #include "text.h"
 #include "misc.h"
 
@@ -59,7 +60,7 @@ static struct GadgetInfo *getGadgetInfoFromGadgetID(int id)
   return gi;
 }
 
-static int getNewGadgetID()
+static int getNewGadgetID(void)
 {
   int id = next_free_gadget_id++;
 
@@ -212,6 +213,12 @@ static void default_callback_action(void *ptr)
   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;
@@ -782,6 +789,10 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
   {
     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;
@@ -836,6 +847,10 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
        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;
@@ -1281,6 +1296,7 @@ struct GadgetInfo *CreateGadget(int first_tag, ...)
 
   /* 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;
@@ -1426,37 +1442,37 @@ static void MultiMapGadgets(int mode)
   }
 }
 
-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() ||
@@ -1481,18 +1497,25 @@ static boolean insideSelectboxArea(struct GadgetInfo *gi, int mx, int my)
 
 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)
@@ -1598,8 +1621,8 @@ 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;
 
@@ -1681,16 +1704,20 @@ boolean HandleGadgets(int mx, int my, int button)
   {
     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)
@@ -1709,7 +1736,8 @@ boolean HandleGadgets(int mx, int my, int button)
       if (gi->textinput.cursor_position != old_cursor_position)
        DrawGadget(gi, DG_PRESSED, gi->direct_draw);
 
-      StartTextInput(gi->x, gi->y);
+      if (press_event)
+       StartTextInput(gi->x, gi->y, gi->width, gi->height);
     }
     else if (gi->type & GD_TYPE_TEXT_AREA && button != 0 && !motion_status)
     {
@@ -1725,7 +1753,8 @@ boolean HandleGadgets(int mx, int my, int button)
       if (gi->textarea.cursor_position != old_cursor_position)
        DrawGadget(gi, DG_PRESSED, gi->direct_draw);
 
-      StartTextInput(gi->x, gi->y);
+      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)
@@ -1756,7 +1785,7 @@ boolean HandleGadgets(int mx, int my, int button)
       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);
@@ -1846,8 +1875,8 @@ boolean HandleGadgets(int mx, int my, int button)
        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;
       }
@@ -2002,9 +2031,9 @@ boolean HandleGadgets(int mx, int my, int button)
     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)
@@ -2036,10 +2065,8 @@ boolean HandleGadgets(int mx, int my, int button)
     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)
@@ -2130,8 +2157,7 @@ boolean HandleGadgetsKeyInput(Key key)
       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 */
   {
@@ -2261,3 +2287,43 @@ boolean HandleGadgetsKeyInput(Key key)
 
   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;
+}