/* The routines in this file are copyright (c) 1987 by Helene (Lee) Taran.
 * Permission is granted for use and free distribution as long as the
 * original author's name is included with the code.
 */

/*
 * Reformatted, and modified to compile without warnings and errors
 * under SAS/C -6.5x by gduncan@philips.oz.au (GMD). This included
 * proto generation, renaming of some literals to avoid name collisions,
 * and Amiga Version string (also displayed in Title bar).
 * No original version number, this version arbitrarily named 1.1. 
 * - otherwise no functional changes.
 * GMD - Sep 94
 */

#include "all.h"

LAYER_INFO *PopUp_LayerInfo;
TEXTFONT *menufont;

/* User should call Init_MenuPackage() before trying to pop up any
 * menus. Will return TRUE if the package has been initialized correctly.
 * Do not attempt to use this package if this initialization routine 
 * returns FALSE.
 */

BOOL
Init_MenuPackage ()
{
  static TEXTATTR MenuFont =
  {(UBYTE *) "topaz.font", 8, 0, 0};

  if (!(menufont = OpenFont (&MenuFont)))
    {
      fprintf (stderr, "sorry couldn't find topaz eight for menu font]\n");
      return (FALSE);
    }
  if (!(PopUp_LayerInfo = (LAYER_INFO *) NewLayerInfo ()))
    {
      fprintf (stderr, "sorry, couldn't allocate a layer info structure\n");
      return (FALSE);
    }
  return (TRUE);
}

/* The user should call Close_MenuPackage before exiting their program
 * to ensure that the menu package releases any memory that it
 * has allocated for its own use. 
 */

void
Close_MenuPackage ()
{
  CloseFont (menufont);
  if (PopUp_LayerInfo != NULL)
    DisposeLayerInfo (PopUp_LayerInfo);

}

BOOL
Init_PopUp_Menu (menu)
     SPL_POPUP_MENU *menu;
{

  SPL_POPUP_ITEM *item;
  int longest = 0;
  int total_height = 0;
  int i;

  if (menu == NULL)
    return (FALSE);
  item = menu->first_item;
  while (item != NULL)
    {
      /* note: bad things may happen if you allocate an empty string for
       * your item text.
       */

      i = strlen (item->text);	/* GMD ; extract to deconfuse compiler */

      item->width = MAX (item->width, FONT_WIDTH * i);
      item->left = MAX (0, item->left);
      longest = MAX (longest, (item->width + item->left));
      item->height = MAX (item->height, FONT_HEIGHT);
      item->top = MAX (0, item->top);
      total_height += (item->height + item->top);
      /* re-initialize item-top to be the top relative to the menu top */
      item->top = (total_height - item->height);
      item = item->next;
    }
  menu->height = total_height + 1;
  menu->width = MAX (menu->width, longest);

  /* initialize a bitmap the same size as the required menu image  */
  /* initialize a raster port to facillitate drawing into the bitmap */
  InitBitMap (&(menu->bitmap), menu->depth, menu->width, menu->height);
  InitRastPort (&(menu->rp));

#if 1
  SetFont (&menu->rp, menufont);	/*GMD */
#else
  SetFont (menu->rp, menufont);
#endif

  for (i = 0; i < menu->depth; i++)
    if (!(menu->bitmap.Planes[i] = (PLANEPTR)
	  AllocRaster (menu->width, menu->height)))
      {
	Dispose_PopUp (menu);
	return (FALSE);
      }
  menu->rp.BitMap = &(menu->bitmap);
  menu->cr.BitMap = &(menu->bitmap);

  /* draw the menu outline and background color into its own bitmap */
  SetDrMd (&(menu->rp), JAM1);
  SetAPen (&(menu->rp), menu->area_color);
  SetOPen (&(menu->rp), menu->outline_color);

  RectFill (&(menu->rp), 0, 0, menu->width - 1, menu->height - 1);
  BNDRYOFF (&(menu->rp));	/* turn off the raster port outlining */

  item = menu->first_item;

  /* Now, draw each menu item */
  while (item != NULL)
    {
      SetAPen (&(menu->rp), item->color);
      Move (&(menu->rp), item->left, item->top + FONT_BASELINE);
      Text (&(menu->rp), item->text, strlen (item->text));
      item = item->next;
    }
  return (TRUE);
}

/* SelectItem : activates the item located at <x,y>. Assumes the <menu>
 * is currently active and thus, menu->left and menu->top should represent
 * the menu's current top,left corner.  If there is an item located
 * at <x,y> within the menu, then it is made 'active' ...this means
 * that the item becomes menu->active_item and is highlighted. The
 * old menu->active_item, if any, is deselected. Note: if you're using
 * a GIMMEZEROZERO window you'll have to fiddle with the x,y coordinates.
 */

void
SelectItem (menu, Window)
     SPL_POPUP_MENU *menu;
     WINDOW *Window;
{
  SPL_POPUP_ITEM *item;

  SHORT x = Window->MouseX + Window->LeftEdge;
  SHORT y = Window->MouseY + Window->TopEdge;

  item = menu->first_item;
  x -= menu->left;
  y -= menu->top;
  if ((x >= 0) && (x < menu->width) && (y >= 0) && (y < menu->height))
    while (item != NULL)
      {
	if ((y >= item->top - 1) && (y < item->top - 1 + item->height))
	  break;		/* we found an active item {yeah yeah...bad style, i know...} */
	else
	  item = item->next;
      }
  else
    item = NULL;		/* mouse isn't on menu */

  if (menu->active_item != item)
    {				/* deselect previously active item */
      ComplementItem (menu, menu->active_item, Window);
      ComplementItem (menu, item, Window);
    }
  menu->active_item = item;
}




/* ComplementItem : complements the given menu item area.  Assumes that
 * the <menu> is currently being displayed and thus,the current menu->left
 * and menu->top represent the current coordinates of the menu's top,left
 * corner.  Assumes the <item> is  an popup_item that belongs to <menu>
 * and that its left/top offsets have been initialized.
 */

void
ComplementItem (menu, item, Window)
     SPL_POPUP_MENU *menu;
     SPL_POPUP_ITEM *item;
     WINDOW *Window;
{
  BYTE old_mode = Window->WScreen->RastPort.DrawMode;

  if (item == NULL)
    return;			/* do nothing */
  SetDrMd (&(Window->WScreen->RastPort), COMPLEMENT);
  RectFill (&(Window->WScreen->RastPort), menu->left,
	    menu->top + item->top - 1,
	    menu->left + menu->width - 1,
	    menu->top + item->top + item->height - 2);
  SetDrMd (&(Window->WScreen->RastPort), (int) old_mode);
}

/* PopUp : this is one of those 'do everything' type of functions. It
 * displays the <menu> in the window  given its upper left hand 
 * coordinates <left,top>.  It tracks the user's mouse movements,
 * highlighting the currently active item until the user deselects
 * the menu by releasing the mouse's select button, at which time,
 * this function will remove the menu from the display. Returns the
 * selection_id of the item that was active when the user releases
 * the select button. Returns 0 if nothing was selected.
 * Assumes that the <menu> has been initialized by a call to 
 * Init_SPL_POPUP_MENU and that the user has requested ReportMouse for the
 * Window in question (ReportMouse makes sure that the Window's MouseX and
 * MouseY coordinates are kept up to date).
 */
int
PopUp (menu, Window)
     SPL_POPUP_MENU *menu;
     WINDOW *Window;
{
  ULONG oldflags;
  SHORT left, top;

  left = Window->MouseX;
  top = Window->MouseY;
  if (!Inside_Window (left, top, Window))
    return (OUTSIDE_WINDOW);

  if ((menu->width <= Window->Width) && (left + menu->width > Window->Width))
    left += Window->Width - (left + menu->width);

  if ((menu->height <= Window->Height) && (top + menu->height > Window->Height))
    top += Window->Height - (top + menu->height);

  LockLayers (PopUp_LayerInfo);

  /* SwapBits...() requires that the clipping rectangle's bounds are given
   * with respect to the screen's 0,0 coordinate but mouse movements
   * are reported to the Window and are given in terms of the window's
   * coordinate system so...sLeft and sTop represent the adjustment of
   * the Window's coordinates to match the screen's coordinates.
   * Note: if you're using a GIMMEZEROZERO window, you'll have to muck
   * with the left,top coordinates to get the complementing to work.
   */
  left = (left + Window->LeftEdge) & 0xfff0;
  top = top + Window->TopEdge;

  menu->cr.bounds.MinX = left;
  menu->cr.bounds.MinY = top;
  menu->cr.bounds.MaxX = left + menu->width - 1;
  menu->cr.bounds.MaxY = top + menu->height - 1;
  SwapBitsRastPortClipRect (Window->RPort, &(menu->cr));

  menu->left = left;
  menu->top = top;
  SelectItem (menu, Window);
  oldflags = Window->IDCMPFlags;
  ModifyIDCMP (Window, oldflags | MOUSEMOVE);

  while (1)			/* wait for the user to select an item and deselect the menu */
    {
      INTUIMESSAGE msg, *message;
      int MouseMoved = FALSE;

      Wait (1 << Window->UserPort->mp_SigBit);
      while (message = (INTUIMESSAGE *) GetMsg (Window->UserPort))
	{
	  msg = *message;	/* make a backup copy of message */
	  ReplyMsg ((MESSAGE *) message);	/* reply immediately to prevent deadlock */
	  if (msg.Class == MOUSEMOVE)
	    /* just keep track of the fact that the mouse moved */
	    MouseMoved = TRUE;
	  else if ((msg.Class == MOUSEBUTTONS) && (msg.Code == menu->deactivate))
	    {
	      /* user has deselected the menu */
	      ModifyIDCMP (Window, oldflags);	/* restore idcmp flags */
	      SelectItem (menu, Window);
	      SwapBitsRastPortClipRect (Window->RPort, &(menu->cr));
	      UnlockLayers (PopUp_LayerInfo);
	      if (menu->active_item != NULL)
		return (menu->active_item->selection_id);
	      else
		return (NOITEM_SELECTED);
	    }			/* end of deselection routine */
	}
      /* 
       * at this point the message queue should be empty so the mouse
       * has stopped for the moment.
       */
      if (MouseMoved)
	SelectItem (menu, Window);
    }
}

void
Dispose_PopUp (menu)
     SPL_POPUP_MENU *menu;
{
  int i;
  for (i = 0; i < menu->depth; i++)
    if (menu->bitmap.Planes[i] != NULL)
      FreeRaster (menu->bitmap.Planes[i], menu->width, menu->height);
}
/*--*/

/*--*/
