rnd-19981217-2
[rocksndiamonds.git] / src / buttons.c
index 62c5a1c1332a2f794f146cba92d05f5def4a5114..7ac61e36a7a10e147e60b500302f1993f6ad40f2 100644 (file)
@@ -11,6 +11,8 @@
 *  buttons.c                                               *
 ***********************************************************/
 
+#include <stdarg.h>
+
 #include "buttons.h"
 #include "tools.h"
 #include "misc.h"
@@ -838,7 +840,7 @@ void DrawElemButton(int button_nr, int button_state)
     DrawMiniGraphicExt(drawto,gc,
                       DX+ED_BUTTON_ELEM_XPOS+3+shift + 
                       (elem_pos % MAX_ELEM_X)*ED_BUTTON_ELEM_XSIZE,
-                      DY+ED_BUTTON_ELEM_YPOS+3-shift +
+                      DY+ED_BUTTON_ELEM_YPOS+3+shift +
                       (elem_pos / MAX_ELEM_X)*ED_BUTTON_ELEM_YSIZE,
                       graphic);
   }
@@ -1461,3 +1463,511 @@ int CheckCountButtons(int mx, int my, int button)
   BackToFront();
   return(return_code);
 }
+
+
+/* NEW GADGET STUFF -------------------------------------------------------- */
+
+
+static struct GadgetInfo *gadget_list_first_entry = NULL;
+static struct GadgetInfo *gadget_list_last_entry = NULL;
+static int next_free_gadget_id = 1;
+static boolean gadget_id_wrapped = FALSE;
+
+static struct GadgetInfo *getGadgetInfoFromGadgetID(int id)
+{
+  struct GadgetInfo *gi = gadget_list_first_entry;
+
+  while (gi && gi->id != id)
+    gi = gi->next;
+
+  return gi;
+}
+
+static int getNewGadgetID()
+{
+  int id = next_free_gadget_id++;
+
+  if (next_free_gadget_id <= 0)                /* counter overrun */
+  {
+    gadget_id_wrapped = TRUE;          /* now we must check each ID */
+    next_free_gadget_id = 0;
+  }
+
+  if (gadget_id_wrapped)
+  {
+    next_free_gadget_id++;
+    while (getGadgetInfoFromGadgetID(next_free_gadget_id) != NULL)
+      next_free_gadget_id++;
+  }
+
+  if (next_free_gadget_id <= 0)                /* cannot get new gadget id */
+    Error(ERR_EXIT, "too much gadgets -- this should not happen");
+
+  return id;
+}
+
+static struct GadgetInfo *getGadgetInfoFromMousePosition(int mx, int my)
+{
+  struct GadgetInfo *gi = gadget_list_first_entry;
+
+  while (gi)
+  {
+    if (gi->mapped &&
+       mx >= gi->x && mx < gi->x + gi->width &&
+       my >= gi->y && my < gi->y + gi->height)
+      break;
+
+    gi = gi->next;
+  }
+
+  return gi;
+}
+
+static void default_callback_function(void *ptr)
+{
+  return;
+}
+
+struct GadgetInfo *CreateGadget(int first_tag, ...)
+{
+  struct GadgetInfo *new_gadget = checked_malloc(sizeof(struct GadgetInfo));
+  int tag = first_tag;
+  va_list ap;
+
+  va_start(ap, first_tag);
+
+  /* always start with reliable default values */
+  memset(new_gadget, 0, sizeof(struct GadgetInfo));    /* zero all fields */
+  new_gadget->id = getNewGadgetID();                   /* new gadget id */
+  new_gadget->callback = default_callback_function;    /* dummy function */
+
+  while (tag != GDI_END)
+  {
+    switch(tag)
+    {
+      case GDI_CUSTOM_ID:
+       new_gadget->custom_id = va_arg(ap, int);
+       break;
+
+      case GDI_X:
+       new_gadget->x = va_arg(ap, int);
+       break;
+
+      case GDI_Y:
+       new_gadget->y = va_arg(ap, int);
+       break;
+
+      case GDI_WIDTH:
+       new_gadget->width = va_arg(ap, int);
+       break;
+
+      case GDI_HEIGHT:
+       new_gadget->height = va_arg(ap, int);
+       break;
+
+      case GDI_TYPE:
+       new_gadget->type = va_arg(ap, unsigned long);
+       break;
+
+      case GDI_STATE:
+       new_gadget->state = va_arg(ap, unsigned long);
+       break;
+
+      case GDI_ALT_STATE:
+       new_gadget->state = va_arg(ap, boolean);
+       break;
+
+      case GDI_NUMBER_VALUE:
+       new_gadget->number_value = va_arg(ap, long);
+       break;
+
+      case GDI_TEXT_VALUE:
+       strcpy(new_gadget->text_value, va_arg(ap, char *));
+       break;
+
+      case GDI_DESIGN_UNPRESSED:
+       new_gadget->design[GD_BUTTON_UNPRESSED].pixmap = va_arg(ap, Pixmap);
+       new_gadget->design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
+       new_gadget->design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
+       break;
+
+      case GDI_DESIGN_PRESSED:
+       new_gadget->design[GD_BUTTON_PRESSED].pixmap = va_arg(ap, Pixmap);
+       new_gadget->design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
+       new_gadget->design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
+       break;
+
+      case GDI_ALT_DESIGN_UNPRESSED:
+       new_gadget->alt_design[GD_BUTTON_UNPRESSED].pixmap= va_arg(ap, Pixmap);
+       new_gadget->alt_design[GD_BUTTON_UNPRESSED].x = va_arg(ap, int);
+       new_gadget->alt_design[GD_BUTTON_UNPRESSED].y = va_arg(ap, int);
+       break;
+
+      case GDI_ALT_DESIGN_PRESSED:
+       new_gadget->alt_design[GD_BUTTON_PRESSED].pixmap = va_arg(ap, Pixmap);
+       new_gadget->alt_design[GD_BUTTON_PRESSED].x = va_arg(ap, int);
+       new_gadget->alt_design[GD_BUTTON_PRESSED].y = va_arg(ap, int);
+       break;
+
+      case GDI_EVENT_MASK:
+       new_gadget->event_mask = va_arg(ap, unsigned long);
+       break;
+
+      case GDI_AREA_SIZE:
+       new_gadget->drawing.area_xsize = va_arg(ap, int);
+       new_gadget->drawing.area_ysize = va_arg(ap, int);
+
+       /* determine dependent values for drawing area gadget, if needed */
+       if (new_gadget->width == 0 &&
+           new_gadget->height == 0 &&
+           new_gadget->drawing.item_xsize !=0 &&
+           new_gadget->drawing.item_ysize !=0)
+       {
+         new_gadget->width =
+           new_gadget->drawing.area_xsize * new_gadget->drawing.item_xsize;
+         new_gadget->height =
+           new_gadget->drawing.area_ysize * new_gadget->drawing.item_ysize;
+       }
+       else if (new_gadget->drawing.item_xsize == 0 &&
+                new_gadget->drawing.item_ysize == 0 &&
+                new_gadget->width != 0 &&
+                new_gadget->height != 0)
+       {
+         new_gadget->drawing.item_xsize =
+           new_gadget->width / new_gadget->drawing.area_xsize;
+         new_gadget->drawing.item_ysize =
+           new_gadget->height / new_gadget->drawing.area_ysize;
+       }
+       break;
+
+      case GDI_ITEM_SIZE:
+       new_gadget->drawing.item_xsize = va_arg(ap, int);
+       new_gadget->drawing.item_ysize = va_arg(ap, int);
+
+       /* determine dependent values for drawing area gadget, if needed */
+       if (new_gadget->width == 0 &&
+           new_gadget->height == 0 &&
+           new_gadget->drawing.area_xsize !=0 &&
+           new_gadget->drawing.area_ysize !=0)
+       {
+         new_gadget->width =
+           new_gadget->drawing.area_xsize * new_gadget->drawing.item_xsize;
+         new_gadget->height =
+           new_gadget->drawing.area_ysize * new_gadget->drawing.item_ysize;
+       }
+       else if (new_gadget->drawing.area_xsize == 0 &&
+                new_gadget->drawing.area_ysize == 0 &&
+                new_gadget->width != 0 &&
+                new_gadget->height != 0)
+       {
+         new_gadget->drawing.area_xsize =
+           new_gadget->width / new_gadget->drawing.item_xsize;
+         new_gadget->drawing.area_ysize =
+           new_gadget->height / new_gadget->drawing.item_ysize;
+       }
+       break;
+
+      case GDI_CALLBACK:
+       new_gadget->callback = va_arg(ap, gadget_callback_function);
+       break;
+
+      default:
+       Error(ERR_EXIT, "CreateGadget(): unknown tag %d", tag);
+    }
+
+    tag = va_arg(ap, int);     /* read next tag */
+  }
+
+  va_end(ap);
+
+  /* check if gadget complete */
+  if (new_gadget->type != GD_TYPE_DRAWING_AREA &&
+      (!new_gadget->design[GD_BUTTON_UNPRESSED].pixmap ||
+       !new_gadget->design[GD_BUTTON_PRESSED].pixmap))
+    Error(ERR_EXIT, "gadget incomplete (missing Pixmap)");
+
+  /* insert new gadget into gloabl gadget list */
+
+  if (gadget_list_last_entry)
+  {
+    gadget_list_last_entry->next = new_gadget;
+    gadget_list_last_entry = gadget_list_last_entry->next;
+  }
+  else
+  {
+    gadget_list_first_entry = gadget_list_last_entry = new_gadget;
+  }
+
+  return new_gadget;
+}
+
+void FreeGadget(struct GadgetInfo *gi)
+{
+  struct GadgetInfo *gi_previous = gadget_list_first_entry;
+
+  while (gi_previous && gi_previous->next != gi)
+    gi_previous = gi_previous->next;
+
+  if (gi == gadget_list_first_entry)
+    gadget_list_first_entry = gi->next;
+
+  if (gi == gadget_list_last_entry)
+    gadget_list_last_entry = gi_previous;
+
+  gi_previous->next = gi->next;
+  free(gi);
+}
+
+static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
+{
+  int state = (pressed ? 1 : 0);
+  struct GadgetDesign *gd = (gi->alt_state ?
+                            &gi->alt_design[state] :
+                            &gi->design[state]);
+
+  if (gi->type != GD_TYPE_NORMAL_BUTTON)
+    return;
+
+  XCopyArea(display, gd->pixmap, drawto, gc,
+           gd->x, gd->y, gi->width, gi->height, gi->x, gi->y);
+
+  if (direct)
+    XCopyArea(display, gd->pixmap, window, gc,
+             gd->x, gd->y, gi->width, gi->height, gi->x, gi->y);
+  else
+    redraw_mask |= REDRAW_ALL;
+}
+
+void MapGadget(struct GadgetInfo *gi)
+{
+  if (gi == NULL)
+    return;
+
+  gi->mapped = TRUE;
+
+  DrawGadget(gi, (gi->state == GD_BUTTON_PRESSED), FALSE);
+}
+
+void UnmapGadget(struct GadgetInfo *gi)
+{
+  if (gi == NULL)
+    return;
+
+  gi->mapped = FALSE;
+}
+
+void HandleGadgets(int mx, int my, int button)
+{
+  static struct GadgetInfo *gi = NULL;
+  static unsigned long pressed_delay = 0;
+
+#if 0
+  static boolean pressed = FALSE;
+#endif
+
+  struct GadgetInfo *new_gi;
+  boolean gadget_pressed;
+  boolean gadget_pressed_repeated;
+  boolean gadget_moving_inside;
+  boolean gadget_moving_outside;
+  boolean gadget_released;
+
+  if (gadget_list_first_entry == NULL)
+    return;
+
+  new_gi = getGadgetInfoFromMousePosition(mx,my);
+
+  gadget_pressed =
+    (button != 0 && gi == NULL && new_gi != NULL);
+  gadget_pressed_repeated =
+    (button != 0 && gi != NULL && new_gi == gi);
+  gadget_moving_inside =
+    (button != 0 && gi != NULL && new_gi == gi && motion_status);
+  gadget_moving_outside =
+    (button != 0 && gi != NULL && new_gi != gi && motion_status);
+  gadget_released =
+    (button == 0 && gi != NULL && new_gi == gi);
+
+  if (gadget_pressed)
+    gi = new_gi;
+
+  if (gi)
+  {
+    gi->event.x = mx - gi->x;
+    gi->event.y = 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 (gadget_pressed)
+  {
+    DrawGadget(gi, TRUE, TRUE);
+
+    gi->state = GD_BUTTON_PRESSED;
+    gi->event.type = GD_EVENT_PRESSED;
+    gi->event.button = button;
+    gi->event.off_borders = FALSE;
+
+    /* initialize delay counter */
+    pressed_delay = 0;
+    DelayReached(&pressed_delay, GADGET_FRAME_DELAY);
+
+    if (gi->event_mask & GD_EVENT_PRESSED)
+      gi->callback(gi);
+  }
+
+  if (gadget_pressed_repeated)
+  {
+    if (gi->event_mask & GD_EVENT_REPEATED &&
+       DelayReached(&pressed_delay, GADGET_FRAME_DELAY))
+      gi->callback(gi);
+  }
+
+  if (gadget_moving_inside)
+  {
+    if (gi->state == GD_BUTTON_UNPRESSED)
+      DrawGadget(gi, TRUE, TRUE);
+
+    gi->state = GD_BUTTON_PRESSED;
+    gi->event.type = GD_EVENT_MOVING;
+    gi->event.off_borders = FALSE;
+    if (gi->event_mask & GD_EVENT_MOVING)
+      gi->callback(gi);
+  }
+
+  if (gadget_moving_outside)
+  {
+    if (gi->state == GD_BUTTON_PRESSED)
+      DrawGadget(gi, FALSE, TRUE);
+
+    gi->state = GD_BUTTON_UNPRESSED;
+    gi->event.type = GD_EVENT_MOVING;
+    gi->event.off_borders = TRUE;
+
+    if (gi->event_mask & GD_EVENT_MOVING)
+      gi->callback(gi);
+  }
+
+  if (gadget_released)
+  {
+    DrawGadget(gi, FALSE, TRUE);
+
+    gi->state = GD_BUTTON_UNPRESSED;
+    gi->event.type = GD_EVENT_RELEASED;
+
+    if (gi->event_mask & GD_EVENT_RELEASED)
+      gi->callback(gi);
+  }
+
+  if (button == 0)
+    gi = NULL;
+
+
+
+#if 0
+  if (button)
+  {
+    if (!motion_status)                /* mouse button just pressed */
+    {
+      if (new_gi != NULL)
+      {
+       gi = new_gi;
+       gi->state = GD_BUTTON_PRESSED;
+       gi->event.type = GD_EVENT_PRESSED;
+       gi->event.button = button;
+       DrawGadget(gi, TRUE, TRUE);
+
+       /* initialize delay counter */
+       pressed_delay = 0;
+       DelayReached(&pressed_delay, GADGET_FRAME_DELAY);
+
+
+       printf("new gadget pressed\n");
+
+
+       if (gi->event_mask & GD_EVENT_PRESSED)
+         gi->callback(gi);
+
+       pressed = TRUE;
+      }
+    }
+    else                       /* mouse movement with pressed mouse button */
+    {
+      if (new_gi != gi && gi != NULL)
+      {
+       if (pressed)
+         DrawGadget(gi, FALSE, TRUE);
+       gi->state = GD_BUTTON_UNPRESSED;
+       gi->event.type = GD_EVENT_MOVING;
+
+
+       printf("outside gadget\n");
+
+
+
+
+       if (gi->event_mask & GD_EVENT_MOVING)
+         gi->callback(gi);
+
+       pressed = FALSE;
+      }
+      else if (new_gi == gi && gi != NULL)
+      {
+       if (!pressed)
+         DrawGadget(gi, TRUE, TRUE);
+       gi->state = GD_BUTTON_PRESSED;
+       gi->event.type = GD_EVENT_MOVING;
+
+
+       printf("inside gadget\n");
+
+
+
+
+       if (gi->event_mask & GD_EVENT_MOVING)
+         gi->callback(gi);
+
+       pressed = TRUE;
+      }
+    }
+
+    if (gi != NULL &&
+       gi->event_mask & GD_EVENT_PRESSED_REPEATED &&
+       gi->state == GD_BUTTON_PRESSED &&
+       DelayReached(&pressed_delay, GADGET_FRAME_DELAY))
+    {
+       printf("gadget pressed (repeated)\n");
+
+
+       gi->callback(gi);
+    }
+  }
+  else                         /* mouse button just released */
+  {
+    if (new_gi == gi && gi != NULL && pressed)
+    {
+      gi->state = GD_BUTTON_UNPRESSED;
+      gi->event.type = GD_EVENT_RELEASED;
+      DrawGadget(gi, FALSE, TRUE);
+
+
+      printf("gadget released\n");
+
+
+
+      if (gi->event_mask & GD_EVENT_RELEASED)
+       gi->callback(gi);
+    }
+
+    gi = NULL;
+    pressed = FALSE;
+  }
+#endif
+
+
+
+}