added check to ensure that decoration does not overlap gadget border
[rocksndiamonds.git] / src / libgame / gadgets.c
index 9c974b872ed91f485820fe4378e17478f19b9fd0..b21ecdca09607e9afb25eb4dec7f1fb8ad302e19 100644 (file)
 
 #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' &&                                                     \
+   !strEqual(t, " "))
+#define CURRENT_OPTION_SELECTABLE(g)                                   \
+  OPTION_TEXT_SELECTABLE(g, g->selectbox.options[g->selectbox.current_index].text)
+
 
 static struct GadgetInfo *gadget_list_first_entry = NULL;
 static struct GadgetInfo *gadget_list_last_entry = NULL;
@@ -89,6 +96,16 @@ static struct GadgetInfo *getGadgetInfoFromMousePosition(int mx, int my,
     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)
     {
@@ -214,15 +231,24 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
     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)
+      {
+       // 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);
+
        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));
+                  deco_width, deco_height, gi->x + deco_x, gi->y + deco_y);
+      }
+
       break;
 
     case GD_TYPE_TEXT_BUTTON:
@@ -404,7 +430,8 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
       {
        int i;
        char text[MAX_GADGET_TEXTSIZE + 1];
-       int font_nr = (pressed ? gi->font_active : gi->font);
+       int font_nr_default = (pressed ? gi->font_active : gi->font);
+       int font_nr = font_nr_default;
        int font_width = getFontWidth(font_nr);
        int font_height = getFontHeight(font_nr);
        int border_x = gi->border.xsize;
@@ -441,6 +468,10 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
                gi->selectbox.size);
        text[gi->selectbox.size] = '\0';
 
+       /* set font value */
+       font_nr = (OPTION_TEXT_SELECTABLE(gi, text) ? font_nr_default :
+                  gi->font_unselectable);
+
        /* gadget text value */
        DrawTextExt(drawto, gi->x + border_x, gi->y + border_y, text,
                    font_nr, BLIT_MASKED);
@@ -549,7 +580,11 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
            strncpy(text, gi->selectbox.options[i].text, gi->selectbox.size);
            text[gi->selectbox.size] = '\0';
 
-           if (i == gi->selectbox.current_index)
+           font_nr = (OPTION_TEXT_SELECTABLE(gi, text) ? font_nr_default :
+                      gi->font_unselectable);
+
+           if (i == gi->selectbox.current_index &&
+               OPTION_TEXT_SELECTABLE(gi, text))
            {
              FillRectangle(drawto,
                            gi->selectbox.x + border_x,
@@ -688,6 +723,10 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
       return;
   }
 
+  // do not use direct gadget drawing anymore; this worked as a speed-up once,
+  // but would slow things down a lot now the screen is always fully redrawn
+  direct = FALSE;
+
   if (direct)
   {
     BlitBitmap(drawto, window,
@@ -873,12 +912,18 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
        gi->font = va_arg(ap, int);
        if (gi->font_active == 0)
          gi->font_active = gi->font;
+       if (gi->font_unselectable == 0)
+         gi->font_unselectable = gi->font;
        break;
 
       case GDI_TEXT_FONT_ACTIVE:
        gi->font_active = va_arg(ap, int);
        break;
 
+      case GDI_TEXT_FONT_UNSELECTABLE:
+       gi->font_unselectable = va_arg(ap, int);
+       break;
+
       case GDI_SELECTBOX_OPTIONS:
        gi->selectbox.options = va_arg(ap, struct ValueTextInfo *);
        break;
@@ -887,6 +932,10 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
        gi->selectbox.index = va_arg(ap, int);
        break;
 
+      case GDI_SELECTBOX_CHAR_UNSELECTABLE:
+       gi->selectbox.char_unselectable = (char)va_arg(ap, int);
+       break;
+
       case GDI_DESIGN_UNPRESSED:
        gi->design[GD_BUTTON_UNPRESSED].bitmap = va_arg(ap, Bitmap *);
        gi->design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
@@ -954,14 +1003,12 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
        gi->drawing.area_ysize = va_arg(ap, int);
 
        /* determine dependent values for drawing area gadget, if needed */
-       if (gi->width == 0 && gi->height == 0 &&
-           gi->drawing.item_xsize !=0 && gi->drawing.item_ysize !=0)
+       if (gi->drawing.item_xsize != 0 && gi->drawing.item_ysize != 0)
        {
          gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
          gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
        }
-       else if (gi->drawing.item_xsize == 0 && gi->drawing.item_ysize == 0 &&
-                gi->width != 0 && gi->height != 0)
+       else if (gi->width != 0 && gi->height != 0)
        {
          gi->drawing.item_xsize = gi->width / gi->drawing.area_xsize;
          gi->drawing.item_ysize = gi->height / gi->drawing.area_ysize;
@@ -984,14 +1031,12 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
        gi->drawing.item_ysize = va_arg(ap, int);
 
        /* determine dependent values for drawing area gadget, if needed */
-       if (gi->width == 0 && gi->height == 0 &&
-           gi->drawing.area_xsize !=0 && gi->drawing.area_ysize !=0)
+       if (gi->drawing.area_xsize != 0 && gi->drawing.area_ysize != 0)
        {
          gi->width = gi->drawing.area_xsize * gi->drawing.item_xsize;
          gi->height = gi->drawing.area_ysize * gi->drawing.item_ysize;
        }
-       else if (gi->drawing.area_xsize == 0 && gi->drawing.area_ysize == 0 &&
-                gi->width != 0 && gi->height != 0)
+       else if (gi->width != 0 && gi->height != 0)
        {
          gi->drawing.area_xsize = gi->width / gi->drawing.item_xsize;
          gi->drawing.area_ysize = gi->height / gi->drawing.item_ysize;
@@ -1591,7 +1636,8 @@ boolean HandleGadgets(int mx, int my, int button)
     if (gi->type & GD_TYPE_SELECTBOX &&
        (keep_selectbox_open ||
         mouse_released_where_pressed ||
-        !gadget_released_inside_select_area))       /* selectbox stays open */
+        !gadget_released_inside_select_area ||
+        !CURRENT_OPTION_SELECTABLE(gi)))           /* selectbox stays open */
     {
       gi->selectbox.stay_open = TRUE;
       pressed_mx = 0;
@@ -1752,7 +1798,7 @@ boolean HandleGadgets(int mx, int my, int button)
        {
          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);
        }
@@ -1949,7 +1995,8 @@ boolean HandleGadgets(int mx, int my, int button)
     {
       if (keep_selectbox_open ||
          mouse_released_where_pressed ||
-         !gadget_released_inside_select_area)       /* selectbox stays open */
+         !gadget_released_inside_select_area ||
+         !CURRENT_OPTION_SELECTABLE(gi))           /* selectbox stays open */
       {
        deactivate_gadget = FALSE;
        gadget_changed = FALSE;