/* PopMenu code by Helene (Lee) Taran, from the Splines program 
** from Fish disk 97 (which replace disk 57).
*/

#include <functions.h>
#include "popmenu.h"

/* For some reason, someone forgot to include NewLayerInfo() in functions.h...
*/
struct Layer_Info *NewLayerInfo();
struct Layer_Info *PopUp_LayerInfo;
struct Font *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.
 */
int Init_MenuPackage()
{ 
  static struct TextAttr MenuFont = {(UBYTE *)"topaz.font",8,0,0};

  if (!(menufont = OpenFont(&MenuFont))) return (0);
  if (!(PopUp_LayerInfo = (struct Layer_Info *)NewLayerInfo())) return (0);
  return(1);
}

/* 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. 
 */
Close_MenuPackage()
{
  CloseFont(menufont);
  if (PopUp_LayerInfo != NULL) DisposeLayerInfo(PopUp_LayerInfo);
}

int Init_PopUp_Menu (menu)
struct PopUp_Menu *menu;
{
  struct PopUp_Item *item;
  int longest = 0;
  int total_height = 0;
  int i;

  if (menu == NULL) return(0);
  item = menu->first_item;
  while (item != NULL) {
    /* note: bad things may happen if you allocate an empty string for
     * your item text.
     */
     item->width = MAX(item->width, FONT_WIDTH * strlen(item->text));
     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),
             (long)menu->depth,(long)menu->width,(long)menu->height);
  InitRastPort(&(menu->rp));  
  SetFont(&(menu->rp),menufont);
  for (i = 0; i < menu->depth; i++) 
    if (!(menu->bitmap.Planes[i] = (PLANEPTR) AllocRaster((long)menu->width, (long)menu->height))) {
      Dispose_PopUp(menu); return(0);
    }
  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),(long)menu->area_color);
  SetOPen(&(menu->rp),(long)menu->outline_color);

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

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

/* 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.
 */
SelectItem(menu,Window)
struct PopUp_Menu *menu;
struct Window *Window;
{  struct 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.
 */
ComplementItem(menu,item,Window)
struct PopUp_Menu *menu;
struct PopUp_Item *item;
struct Window *Window;
{  BYTE old_mode = Window->WScreen->RastPort.DrawMode;

   if (item == NULL) return;  /* do nothing */
   SetDrMd(&(Window->WScreen->RastPort), COMPLEMENT);
   RectFill(&(Window->WScreen->RastPort), (long)menu->left,
                           menu->top +  item->top - 1L,
                           menu->left + menu->width -1L,
                           menu->top +  item->top + item->height - 2L);
   SetDrMd(&(Window->WScreen->RastPort), (long)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_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)
struct PopUp_Menu *menu;
struct 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);
*/

  ReportMouse (TRUE, Window);  /* Start telling us about mouse loc */    

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

       Wait(1L << Window->UserPort->mp_SigBit);
       while (message = (struct IntuiMessage *)GetMsg(Window->UserPort)) { 
            msg = *message;         /* make a backup copy of message */
            ReplyMsg(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 */
                    ReportMouse (FALSE, Window);  
                    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);
      } 
}

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

     
     
