rnd-20050202-1-src
[rocksndiamonds.git] / src / libgame / gadgets.c
index 55174eef8b749c09812f870cd7d2059d33f11205..50dc853129dd107128b5a6f103520758cb46c763 100644 (file)
@@ -105,8 +105,6 @@ static struct GadgetInfo *getGadgetInfoFromMousePosition(int mx, int my)
   return NULL;
 }
 
-#if 1
-
 static void setTextAreaCursorExt(struct GadgetInfo *gi, boolean set_cursor_pos)
 {
   char *text = gi->textarea.value;
@@ -147,15 +145,6 @@ static void setTextAreaCursorExt(struct GadgetInfo *gi, boolean set_cursor_pos)
     pos++;
   }
 
-  if (y >= area_ysize)
-  {
-    x = area_xsize - 1;
-  }
-
-#if 0
-  printf("::: %d, %d [%d]\n", cursor_x, cursor_y, cursor_position);
-#endif
-
   gi->textarea.cursor_x = x;
   gi->textarea.cursor_y = y;
   gi->textarea.cursor_x_preferred = x;
@@ -177,98 +166,6 @@ static void setTextAreaCursorPosition(struct GadgetInfo *gi, int pos)
   setTextAreaCursorExt(gi, FALSE);
 }
 
-#else
-
-static void setTextAreaCursorPosition(struct GadgetInfo *gi, int x, int y)
-{
-  char *text = gi->textarea.value;
-  int area_xsize = gi->textarea.xsize;
-  int area_ysize = gi->textarea.ysize;
-  int cursor_x = 0;
-  int cursor_y = 0;
-  int cursor_position = 0;
-
-  while (*text && cursor_y < area_ysize)
-  {
-    char buffer[MAX_OUTPUT_LINESIZE + 1];
-    int i;
-
-    for (i=0; i < area_xsize && *text && *text != '\n'; i++)
-      buffer[i] = *text++;
-    buffer[i] = '\0';
-
-#if 1
-    if (i == 0 && *text == '\n')
-    {
-      text++;
-      cursor_position++;
-    }
-#endif
-
-    if (x == -1 && y == -1)            /* get x/y from cursor position */
-    {
-      if (cursor_position + i >= gi->textarea.cursor_position)
-      {
-#if 0
-       printf("::: cursor: %d + %d >= %d\n", cursor_position, i,
-              gi->textarea.cursor_position);
-#endif
-
-       cursor_x = gi->textarea.cursor_position - cursor_position;
-       cursor_position = gi->textarea.cursor_position;
-
-       break;
-      }
-    }
-
-    if (cursor_y == y || !*text)       /* correct y position found */
-    {
-      cursor_x = MIN(i, x);
-      cursor_position += cursor_x;
-
-      break;
-    }
-    else
-      cursor_position += i;
-
-#if 0
-    if (*text == '\n')
-    {
-      text++;
-      cursor_position++;
-
-      if (i == area_xsize)
-       cursor_y++;
-    }
-#endif
-
-    cursor_y++;
-  }
-
-  if (cursor_x >= area_xsize)
-  {
-    cursor_x = 0;
-    cursor_y++;
-  }
-
-  if (cursor_y >= area_ysize)
-  {
-    cursor_x = area_xsize - 1;
-    cursor_y = area_ysize - 1;
-  }
-
-#if 0
-  printf("::: %d, %d [%d]\n", cursor_x, cursor_y, cursor_position);
-#endif
-
-  gi->textarea.cursor_x = cursor_x;
-  gi->textarea.cursor_y = cursor_y;
-  gi->textarea.cursor_x_preferred = cursor_x;
-  gi->textarea.cursor_position = cursor_position;
-}
-
-#endif
-
 static void default_callback_info(void *ptr)
 {
   return;
@@ -378,11 +275,6 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
        cursor_string[0] = (cursor_letter != '\0' ? cursor_letter : ' ');
        cursor_string[1] = '\0';
 
-#if 0
-       if (pressed)
-         printf("::: PRESSED!\n");
-#endif
-
        /* draw cursor, if active */
        if (pressed)
          DrawTextExt(drawto,
@@ -466,21 +358,14 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
 
        /* gadget text value */
        DrawTextToTextArea(gi->x + border_x, gi->y + border_y,
-                          gi->textarea.value, font_nr,
-                          gi->textarea.xsize, gi->textarea.ysize);
+                          gi->textarea.value, font_nr, gi->textarea.xsize,
+                          gi->textarea.xsize, gi->textarea.ysize,
+                          BLIT_ON_BACKGROUND);
 
        cursor_letter = gi->textarea.value[gi->textarea.cursor_position];
        cursor_string[0] = (cursor_letter != '\0' ? cursor_letter : ' ');
        cursor_string[1] = '\0';
 
-#if 0
-       printf("::: '%s' [%d, %d] [%d] [%d -> '%s']\n",
-              gi->textarea.value,
-              gi->textarea.cursor_x, gi->textarea.cursor_y,
-              gi->textarea.cursor_position,
-              pressed, cursor_string);
-#endif
-
        /* draw cursor, if active */
        if (pressed)
          DrawTextExt(drawto,
@@ -635,7 +520,10 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
          /* selectbox text values */
          for (i=0; i < gi->selectbox.num_values; i++)
          {
-           int mask_mode;
+           int mask_mode = BLIT_MASKED;
+
+           strncpy(text, gi->selectbox.options[i].text, gi->selectbox.size);
+           text[gi->selectbox.size] = '\0';
 
            if (i == gi->selectbox.current_index)
            {
@@ -645,17 +533,11 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
                            gi->selectbox.width - 2 * border_x, font_height,
                            gi->selectbox.inverse_color);
 
-             strncpy(text, gi->selectbox.options[i].text, gi->selectbox.size);
-             text[1 + gi->selectbox.size] = '\0';
-
-             mask_mode = BLIT_INVERSE;
-           }
-           else
-           {
-             strncpy(text, gi->selectbox.options[i].text, gi->selectbox.size);
+             /* prevent use of cursor graphic by drawing at least two chars */
+             strcat(text, "  ");
              text[gi->selectbox.size] = '\0';
 
-             mask_mode = BLIT_MASKED;
+             mask_mode = BLIT_INVERSE;
            }
 
            DrawTextExt(drawto,
@@ -670,15 +552,15 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
        {
          gi->selectbox.open = FALSE;
 
-         /* redraw closed selectbox */
-         DrawGadget(gi, FALSE, FALSE);
-
          /* restore background under selectbox */
          BlitBitmap(gfx.field_save_buffer, drawto,
                     gi->selectbox.x,     gi->selectbox.y,
                     gi->selectbox.width, gi->selectbox.height,
                     gi->selectbox.x,     gi->selectbox.y);
 
+         /* redraw closed selectbox */
+         DrawGadget(gi, FALSE, FALSE);
+
          redraw_selectbox = TRUE;
        }
       }
@@ -707,7 +589,7 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
                               xpos, ypos);
 
        /* middle part of gadget */
-       for (i=0; i<num_steps; i++)
+       for (i=0; i < num_steps; i++)
          BlitBitmapOnBackground(gd->bitmap, drawto,
                                 gd->x, gd->y + gi->border.ysize,
                                 gi->width, design_body,
@@ -754,7 +636,7 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
                               xpos, ypos);
 
        /* middle part of gadget */
-       for (i=0; i<num_steps; i++)
+       for (i=0; i < num_steps; i++)
          BlitBitmapOnBackground(gd->bitmap, drawto,
                                 gd->x + gi->border.xsize, gd->y,
                                 design_body, gi->height,
@@ -799,6 +681,22 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
                    gi->y > gfx.vy ? REDRAW_DOOR_2 : REDRAW_DOOR_3);
 }
 
+static int get_minimal_size_for_numeric_input(int minmax_value)
+{
+  int min_size = 1;    /* value needs at least one digit */
+  int i;
+
+  /* add number of digits needed for absolute value */
+  for (i = 10; i <= ABS(minmax_value); i *= 10)
+    min_size++;
+
+  /* if min/max value is negative, add one digit for minus sign */
+  if (minmax_value < 0)
+    min_size++;
+
+  return min_size;
+}
+
 static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
 {
   int tag = first_tag;
@@ -878,6 +776,7 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
       case GDI_NUMBER_VALUE:
        gi->textinput.number_value = va_arg(ap, long);
        sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
+       strcpy(gi->textinput.last_value, gi->textinput.value);
        gi->textinput.cursor_position = strlen(gi->textinput.value);
        break;
 
@@ -887,6 +786,7 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
        {
          gi->textinput.number_value = gi->textinput.number_min;
          sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
+         strcpy(gi->textinput.last_value, gi->textinput.value);
        }
        break;
 
@@ -896,6 +796,7 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
        {
          gi->textinput.number_value = gi->textinput.number_max;
          sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
+         strcpy(gi->textinput.last_value, gi->textinput.value);
        }
        break;
 
@@ -907,12 +808,15 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
            max_textsize = MIN(gi->textinput.size, MAX_GADGET_TEXTSIZE - 1);
 
          strncpy(gi->textinput.value, va_arg(ap, char *), max_textsize);
+         strcpy(gi->textinput.last_value, gi->textinput.value);
+
          gi->textinput.value[max_textsize] = '\0';
          gi->textinput.cursor_position = strlen(gi->textinput.value);
 
          /* same tag also used for other gadget definitions */
          strcpy(gi->textbutton.value, gi->textinput.value);
          strcpy(gi->textarea.value, gi->textinput.value);
+         strcpy(gi->textarea.last_value, gi->textinput.value);
        }
        break;
 
@@ -923,10 +827,17 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
 
          gi->textinput.size = max_textsize;
          gi->textinput.value[max_textsize] = '\0';
+         strcpy(gi->textinput.last_value, gi->textinput.value);
 
          /* same tag also used for other gadget definitions */
-         strcpy(gi->textbutton.value, gi->textinput.value);
-         gi->textbutton.size = gi->textinput.size;
+
+         gi->textarea.size = max_textsize;
+         gi->textarea.value[max_textsize] = '\0';
+         strcpy(gi->textarea.last_value, gi->textinput.value);
+
+         gi->textbutton.size = max_textsize;
+         gi->textbutton.value[max_textsize] = '\0';
+
          gi->selectbox.size = gi->textinput.size;
        }
        break;
@@ -1103,6 +1014,19 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
     int border_xsize = gi->border.xsize;
     int border_ysize = gi->border.ysize;
 
+    if (gi->type == GD_TYPE_TEXT_INPUT_NUMERIC)
+    {
+      int number_min = gi->textinput.number_min;
+      int number_max = gi->textinput.number_max;
+      int min_size_min = get_minimal_size_for_numeric_input(number_min);
+      int min_size_max = get_minimal_size_for_numeric_input(number_max);
+      int min_size = MAX(min_size_min, min_size_max);
+
+      /* expand gadget text input size, if maximal value is too large */
+      if (gi->textinput.size < min_size)
+       gi->textinput.size = min_size;
+    }
+
     gi->width  = 2 * border_xsize + (gi->textinput.size + 1) * font_width;
     gi->height = 2 * border_ysize + font_height;
   }
@@ -1177,16 +1101,23 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
   if (gi->type & GD_TYPE_SCROLLBAR)
   {
     struct GadgetScrollbar *gs = &gi->scrollbar;
+    int scrollbar_size_cmp;
 
     if (gi->width == 0 || gi->height == 0 ||
        gs->items_max == 0 || gs->items_visible == 0)
       Error(ERR_EXIT, "scrollbar gadget incomplete (missing tags)");
 
     /* calculate internal scrollbar values */
+    gs->size_min = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ?
+                   gi->width : gi->height);
     gs->size_max = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ?
                    gi->height : gi->width);
-    gs->size = gs->size_max * gs->items_visible / gs->items_max;
-    gs->position = gs->size_max * gs->item_position / gs->items_max;
+
+    scrollbar_size_cmp = gs->size_max * gs->items_visible / gs->items_max;
+    gs->size = MAX(scrollbar_size_cmp, gs->size_min);
+    gs->size_max_cmp = (gs->size_max - (gs->size - scrollbar_size_cmp));
+
+    gs->position = gs->size_max_cmp * gs->item_position / gs->items_max;
     gs->position_max = gs->size_max - gs->size;
     gs->correction = gs->size_max / gs->items_max / 2;
 
@@ -1385,21 +1316,26 @@ void RemapAllGadgets()
   MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_REMAP);
 }
 
-static boolean anyTextInputGadgetActive()
+boolean anyTextInputGadgetActive()
 {
   return (last_gi && (last_gi->type & GD_TYPE_TEXT_INPUT) && last_gi->mapped);
 }
 
-static boolean anyTextAreaGadgetActive()
+boolean anyTextAreaGadgetActive()
 {
   return (last_gi && (last_gi->type & GD_TYPE_TEXT_AREA) && last_gi->mapped);
 }
 
-static boolean anySelectboxGadgetActive()
+boolean anySelectboxGadgetActive()
 {
   return (last_gi && (last_gi->type & GD_TYPE_SELECTBOX) && last_gi->mapped);
 }
 
+boolean anyScrollbarGadgetActive()
+{
+  return (last_gi && (last_gi->type & GD_TYPE_SCROLLBAR) && last_gi->mapped);
+}
+
 boolean anyTextGadgetActive()
 {
   return (anyTextInputGadgetActive() ||
@@ -1407,8 +1343,27 @@ boolean anyTextGadgetActive()
          anySelectboxGadgetActive());
 }
 
+static boolean insideSelectboxLine(struct GadgetInfo *gi, int mx, int my)
+{
+  return(gi != NULL &&
+        gi->type & GD_TYPE_SELECTBOX &&
+        mx >= gi->x && mx < gi->x + gi->width &&
+        my >= gi->y && my < gi->y + gi->height);
+}
+
+static boolean insideSelectboxArea(struct GadgetInfo *gi, int mx, int my)
+{
+  return(gi != NULL &&
+        gi->type & GD_TYPE_SELECTBOX &&
+        mx >= gi->selectbox.x && mx < gi->selectbox.x + gi->selectbox.width &&
+        my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height);
+}
+
 void ClickOnGadget(struct GadgetInfo *gi, int button)
 {
+  if (!gi->mapped)
+    return;
+
   /* simulate releasing mouse button over last gadget, if still pressed */
   if (button_status)
     HandleGadgets(-1, -1, 0);
@@ -1420,19 +1375,25 @@ void ClickOnGadget(struct GadgetInfo *gi, int button)
   HandleGadgets(gi->x, gi->y, 0);
 }
 
-void HandleGadgets(int mx, int my, int button)
+boolean HandleGadgets(int mx, int my, int button)
 {
   static struct GadgetInfo *last_info_gi = NULL;
   static unsigned long pressed_delay = 0;
   static int last_button = 0;
   static int last_mx = 0, last_my = 0;
+  static int pressed_mx = 0, pressed_my = 0;
   int scrollbar_mouse_pos = 0;
   struct GadgetInfo *new_gi, *gi;
   boolean press_event;
   boolean release_event;
   boolean mouse_moving;
+  boolean mouse_inside_select_line;
+  boolean mouse_inside_select_area;
+  boolean mouse_released_where_pressed;
   boolean gadget_pressed;
   boolean gadget_pressed_repeated;
+  boolean gadget_pressed_off_borders;
+  boolean gadget_pressed_inside_select_line;
   boolean gadget_moving;
   boolean gadget_moving_inside;
   boolean gadget_moving_off_borders;
@@ -1445,7 +1406,7 @@ void HandleGadgets(int mx, int my, int button)
 
   /* check if there are any gadgets defined */
   if (gadget_list_first_entry == NULL)
-    return;
+    return FALSE;
 
   /* simulated release of mouse button over last gadget */
   if (mx == -1 && my == -1 && button == 0)
@@ -1467,191 +1428,71 @@ void HandleGadgets(int mx, int my, int button)
   last_mx = mx;
   last_my = my;
 
-#if 1
-
-#if 1
-
-  /* if mouse button pressed outside text or selectbox gadget, deactivate it */
-  if (anyTextGadgetActive() &&
-      button != 0 && !motion_status && new_gi != last_gi)
+  if (press_event && new_gi != last_gi)
   {
-    CheckRangeOfNumericInputGadget(last_gi);   /* in case of numeric gadget */
-
-    DrawGadget(last_gi, DG_UNPRESSED, last_gi->direct_draw);
-
-    last_gi->event.type = GD_EVENT_TEXT_LEAVING;
-
-    if (last_gi->event_mask & GD_EVENT_TEXT_LEAVING)
-      last_gi->callback_action(last_gi);
-
-    last_gi = NULL;
+    pressed_mx = mx;
+    pressed_my = my;
   }
 
-#else
+  mouse_released_where_pressed =
+    (release_event && mx == pressed_mx && my == pressed_my);
 
-  /* special treatment for leaving text and number input gadgets */
-  if (anyTextInputGadgetActive() &&
-      button != 0 && !motion_status && new_gi != last_gi)
-  {
-    /* if mouse button pressed outside text input gadget, deactivate it */
-    CheckRangeOfNumericInputGadget(last_gi);
-    DrawGadget(last_gi, DG_UNPRESSED, last_gi->direct_draw);
+  mouse_inside_select_line = insideSelectboxLine(new_gi, mx, my);
+  mouse_inside_select_area = insideSelectboxArea(new_gi, mx, my);
 
-    last_gi->event.type = GD_EVENT_TEXT_LEAVING;
+  gadget_pressed_off_borders = (press_event && new_gi != last_gi);
 
-    if (last_gi->event_mask & GD_EVENT_TEXT_LEAVING)
-      last_gi->callback_action(last_gi);
+  gadget_pressed_inside_select_line =
+    (press_event && new_gi != NULL &&
+     new_gi->type & GD_TYPE_SELECTBOX && new_gi->selectbox.open &&
+     insideSelectboxLine(new_gi, mx, my));
 
-    last_gi = NULL;
-  }
-
-  /* special treatment for leaving text area gadgets */
-  if (anyTextAreaGadgetActive() &&
-      button != 0 && !motion_status && new_gi != last_gi)
-  {
-    /* if mouse button pressed outside text input gadget, deactivate it */
-    DrawGadget(last_gi, DG_UNPRESSED, last_gi->direct_draw);
-
-    last_gi->event.type = GD_EVENT_TEXT_LEAVING;
-
-    if (last_gi->event_mask & GD_EVENT_TEXT_LEAVING)
-      last_gi->callback_action(last_gi);
-
-    last_gi = NULL;
-  }
-
-  /* special treatment for leaving selectbox gadgets */
-  if (anySelectboxGadgetActive() &&
+  /* 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)
-  {
-    /* if mouse button pressed outside selectbox gadget, deactivate it */
-    DrawGadget(last_gi, DG_UNPRESSED, last_gi->direct_draw);
-
-    last_gi->event.type = GD_EVENT_TEXT_LEAVING;
-
-    if (last_gi->event_mask & GD_EVENT_TEXT_LEAVING)
-      last_gi->callback_action(last_gi);
-
-    last_gi = NULL;
-  }
-
 #endif
-
-#else
-
-  /* special treatment for text and number input gadgets */
-  if (anyTextInputGadgetActive() && button != 0 && !motion_status)
   {
     struct GadgetInfo *gi = last_gi;
+    boolean gadget_changed = (gi->event_mask & GD_EVENT_TEXT_LEAVING);
 
-    if (new_gi == last_gi)
-    {
-      int old_cursor_position = gi->textinput.cursor_position;
-
-      /* if mouse button pressed inside activated text gadget, set cursor */
-      gi->textinput.cursor_position =
-       (mx - gi->x - gi->border.xsize) / getFontWidth(gi->font);
-
-      if (gi->textinput.cursor_position < 0)
-       gi->textinput.cursor_position = 0;
-      else if (gi->textinput.cursor_position > strlen(gi->textinput.value))
-       gi->textinput.cursor_position = strlen(gi->textinput.value);
-
-      if (gi->textinput.cursor_position != old_cursor_position)
-       DrawGadget(gi, DG_PRESSED, gi->direct_draw);
-    }
-    else
+    /* check if text gadget has changed its value */
+    if (gi->type & GD_TYPE_TEXT_INPUT)
     {
-      /* if mouse button pressed outside text input gadget, deactivate it */
       CheckRangeOfNumericInputGadget(gi);
-      DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
 
-      gi->event.type = GD_EVENT_TEXT_LEAVING;
-
-      if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
-       gi->callback_action(gi);
-
-      last_gi = NULL;
+      if (strcmp(gi->textinput.value, gi->textinput.last_value) != 0)
+       strcpy(gi->textinput.last_value, gi->textinput.value);
+      else
+       gadget_changed = FALSE;
     }
-  }
 
-  /* special treatment for text area gadgets */
-  if (anyTextAreaGadgetActive() && button != 0 && !motion_status)
-  {
-    struct GadgetInfo *gi = last_gi;
-
-    if (new_gi == last_gi)
-    {
-      int old_cursor_position = gi->textarea.cursor_position;
-      int x = (mx - gi->x - gi->border.xsize) / getFontWidth(gi->font);
-      int y = (my - gi->y - gi->border.ysize) / getFontHeight(gi->font);
+    /* selectbox does not change its value when closed by clicking outside */
+    if (gi->type & GD_TYPE_SELECTBOX)
+      gadget_changed = FALSE;
 
-      x = (x < 0 ? 0 : x >= gi->textarea.xsize ? gi->textarea.xsize - 1 : x);
-      y = (y < 0 ? 0 : y >= gi->textarea.ysize ? gi->textarea.ysize - 1 : y);
+    DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
 
-      setTextAreaCursorXY(gi, x, y);
+    gi->event.type = GD_EVENT_TEXT_LEAVING;
 
-#if 0
-      printf("::: %d -----> %d\n",
-            old_cursor_position,
-            gi->textarea.cursor_position);
+#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
 
-      if (gi->textarea.cursor_position != old_cursor_position)
-       DrawGadget(gi, DG_PRESSED, gi->direct_draw);
-    }
-    else
-    {
-      /* if mouse button pressed outside text input gadget, deactivate it */
-      DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
-
-      gi->event.type = GD_EVENT_TEXT_LEAVING;
-
-      if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
-       gi->callback_action(gi);
-
-      last_gi = NULL;
-    }
-  }
-
-  /* special treatment for selectbox gadgets */
-  if (anySelectboxGadgetActive() && button != 0 && !motion_status)
-  {
-    struct GadgetInfo *gi = last_gi;
-
-    if (new_gi == last_gi)
-    {
-      int old_index = gi->selectbox.current_index;
-
-      /* if mouse button pressed inside activated selectbox, select value */
-      if (my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height)
-       gi->selectbox.current_index =
-         (my - gi->selectbox.y - gi->border.ysize) / getFontHeight(gi->font);
-
-      if (gi->selectbox.current_index < 0)
-       gi->selectbox.current_index = 0;
-      else if (gi->selectbox.current_index > gi->selectbox.num_values - 1)
-       gi->selectbox.current_index = gi->selectbox.num_values - 1;
-
-      if (gi->selectbox.current_index != old_index)
-       DrawGadget(gi, DG_PRESSED, gi->direct_draw);
-    }
-    else
-    {
-      /* if mouse button pressed outside selectbox gadget, deactivate it */
-      DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
-
-      gi->event.type = GD_EVENT_TEXT_LEAVING;
-
-      if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
-       gi->callback_action(gi);
+    last_gi = NULL;
 
-      last_gi = NULL;
-    }
+    if (gadget_pressed_inside_select_line)
+      new_gi = NULL;
   }
 
-#endif
-
   gadget_pressed =
     (button != 0 && last_gi == NULL && new_gi != NULL && press_event);
   gadget_pressed_repeated =
@@ -1670,12 +1511,17 @@ void HandleGadgets(int mx, int my, int button)
   {
     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
   }
   else
   {
@@ -1698,12 +1544,23 @@ void HandleGadgets(int mx, int my, int button)
   /* if mouse button released, no gadget needs to be handled anymore */
   if (gadget_released)
   {
-    if (last_gi->type & GD_TYPE_SELECTBOX &&
+#if 1
+    if (gi->type & GD_TYPE_SELECTBOX &&
+       (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;
-    else if (!(last_gi->type & GD_TYPE_TEXT_INPUT ||
-              last_gi->type & GD_TYPE_TEXT_AREA))  /* text input stays open */
+#endif
+    else if (!(gi->type & GD_TYPE_TEXT_INPUT ||
+              gi->type & GD_TYPE_TEXT_AREA))       /* text input stays open */
       last_gi = NULL;
   }
 
@@ -1757,7 +1614,7 @@ void HandleGadgets(int mx, int my, int button)
       if (gi->textarea.cursor_position != old_cursor_position)
        DrawGadget(gi, DG_PRESSED, gi->direct_draw);
     }
-    else if (gi->type & GD_TYPE_SELECTBOX)
+    else if (gi->type & GD_TYPE_SELECTBOX && gi->selectbox.open)
     {
       int old_index = gi->selectbox.current_index;
 
@@ -1789,15 +1646,6 @@ void HandleGadgets(int mx, int my, int button)
     {
       last_info_gi->event.type = GD_EVENT_INFO_LEAVING;
       last_info_gi->callback_info(last_info_gi);
-
-#if 0
-      default_callback_info(NULL);
-
-      printf("It seems that we are leaving gadget [%s]!\n",
-            (last_info_gi != NULL &&
-             last_info_gi->info_text != NULL ?
-             last_info_gi->info_text : ""));
-#endif
     }
 
     last_info_gi = new_gi;
@@ -1887,11 +1735,10 @@ void HandleGadgets(int mx, int my, int button)
        /* don't handle this scrollbar anymore while mouse button pressed */
        last_gi = NULL;
 
-       return;
+       return TRUE;
       }
     }
 
-    /* !!! bad for TEXT_INPUT ... !!! */
     DrawGadget(gi, DG_PRESSED, gi->direct_draw);
 
     gi->state = GD_BUTTON_PRESSED;
@@ -1906,11 +1753,6 @@ void HandleGadgets(int mx, int my, int button)
       gi->callback_action(gi);
   }
 
-#if 0
-  if (!mouse_moving)
-    printf("::: PRESSED...?\n");
-#endif
-
   if (gadget_pressed_repeated)
   {
     gi->event.type = GD_EVENT_PRESSED;
@@ -1959,7 +1801,7 @@ void HandleGadgets(int mx, int my, int button)
        gs->position = gs->position_max;
 
       gs->item_position =
-       gs->items_max * (gs->position + gs->correction) / gs->size_max;
+       gs->items_max * (gs->position + gs->correction) / gs->size_max_cmp;
 
       if (gs->item_position < 0)
        gs->item_position = 0;
@@ -1988,14 +1830,29 @@ void HandleGadgets(int mx, int my, int button)
   if (gadget_released_inside)
   {
     boolean deactivate_gadget = TRUE;
+    boolean gadget_changed = TRUE;
 
     if (gi->type & GD_TYPE_SELECTBOX)
     {
+#if 1
+      if (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 */
+         gadget_released_off_borders)               /* selectbox stays open */
+      {
        deactivate_gadget = FALSE;
-      else
+       gadget_changed = FALSE;
+      }
+#endif
+      else if (gi->selectbox.index != gi->selectbox.current_index)
        gi->selectbox.index = gi->selectbox.current_index;
+      else
+       gadget_changed = FALSE;
     }
 
     if (deactivate_gadget &&
@@ -2006,8 +1863,15 @@ void HandleGadgets(int mx, int my, int button)
     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)
@@ -2025,6 +1889,9 @@ void HandleGadgets(int mx, int my, int button)
   /* handle gadgets unmapped/mapped between pressing and releasing */
   if (release_event && !gadget_released && new_gi)
     new_gi->state = GD_BUTTON_UNPRESSED;
+
+  return (gadget_pressed || gadget_pressed_repeated ||
+         gadget_released || gadget_moving);
 }
 
 static void insertCharIntoTextArea(struct GadgetInfo *gi, char c)
@@ -2035,22 +1902,14 @@ static void insertCharIntoTextArea(struct GadgetInfo *gi, char c)
   if (strlen(gi->textarea.value) == MAX_GADGET_TEXTSIZE) /* no space left */
     return;
 
-#if 0
-  printf("::: '%s' + '%c'", gi->textarea.value, c);
-#endif
-
   strcpy(text, gi->textarea.value);
   strcpy(&gi->textarea.value[cursor_position + 1], &text[cursor_position]);
   gi->textarea.value[cursor_position] = c;
 
-#if 0
-  printf(" => '%s'\n", gi->textarea.value);
-#endif
-
   setTextAreaCursorPosition(gi, gi->textarea.cursor_position + 1);
 }
 
-void HandleGadgetsKeyInput(Key key)
+boolean HandleGadgetsKeyInput(Key key)
 {
   struct GadgetInfo *gi = last_gi;
 
@@ -2058,14 +1917,28 @@ void HandleGadgetsKeyInput(Key key)
       !(gi->type & GD_TYPE_TEXT_INPUT ||
        gi->type & GD_TYPE_TEXT_AREA ||
        gi->type & GD_TYPE_SELECTBOX))
-    return;
+    return FALSE;
 
   if (key == KSYM_Return)      /* valid for both text input and selectbox */
   {
+    boolean gadget_changed = (gi->event_mask & GD_EVENT_TEXT_RETURN);
+
     if (gi->type & GD_TYPE_TEXT_INPUT)
+    {
       CheckRangeOfNumericInputGadget(gi);
+
+      if (strcmp(gi->textinput.value, gi->textinput.last_value) != 0)
+       strcpy(gi->textinput.last_value, gi->textinput.value);
+      else
+       gadget_changed = FALSE;
+    }
     else if (gi->type & GD_TYPE_SELECTBOX)
-      gi->selectbox.index = gi->selectbox.current_index;
+    {
+      if (gi->selectbox.index != gi->selectbox.current_index)
+       gi->selectbox.index = gi->selectbox.current_index;
+      else
+       gadget_changed = FALSE;
+    }
 
     if (gi->type & GD_TYPE_TEXT_AREA)
     {
@@ -2082,8 +1955,13 @@ void HandleGadgetsKeyInput(Key key)
       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 */
   {
@@ -2143,10 +2021,6 @@ void HandleGadgetsKeyInput(Key key)
     char letter = getCharFromKey(key);
     boolean legal_letter = (letter != 0);
 
-#if 0
-    printf("::: KEY: %x!\n", key);
-#endif
-
     if (legal_letter)
     {
       insertCharIntoTextArea(gi, letter);
@@ -2214,4 +2088,6 @@ void HandleGadgetsKeyInput(Key key)
       DrawGadget(gi, DG_PRESSED, gi->direct_draw);
     }
   }
+
+  return TRUE;
 }