		/*************************************
		 *                                   *
		 *             Menus v2.0            *
		 *   by Torsten Jürgeleit in 08/91   *
		 *                                   *
		 *              Routines             *
		 *                                   *
		 *************************************/

	/* Includes */

#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <functions.h>
#include <string.h>
#include "/render/render.h"
#include "menus.h"

	/* Static prototypes */

ULONG get_buffer_size(struct MenuList  *ml);
VOID  get_item_text_pens(struct MenuList  *ml);
BOOL  init_menu(struct MenuList  *ml);
VOID  equal_select_box_width(struct MenuItem  *item);

	/* Static pragmas */

#pragma regcall(get_buffer_size(a0))
#pragma regcall(get_item_text_pens(a0))
#pragma regcall(init_menu(a0))
#pragma regcall(equal_select_box_width(a0))

	/* Create menu list */

   struct MenuList *
create_menu(struct RenderInfo  *ri, struct Window  *win,
				 struct MenuData  *md, struct TextAttr  *ta)
{
   struct MenuList  *ml;

   if (ri && md) {
      if (ml = AllocMem((LONG)sizeof(struct MenuList), (LONG)MEMF_PUBLIC |
							      MEMF_CLEAR)) {
	 /* init menu list struct */
	 ml->ml_RenderInfo = ri;
	 ml->ml_Window     = win;   /* needed for colors !!! */
	 ml->ml_Data       = md;
	 ml->ml_TextAttr   = ta;
	 ml->ml_Flags      = 0;
	 if (ta) {
	    ml->ml_TextFont = OpenFont(ta);
	 } else {
	    ml->ml_TextFont = ri->ri_TextFont;
	 }
	 if (ml->ml_TextFont) {
	    if (ml->ml_BufferSize = get_buffer_size(ml)) {
	       if (ml->ml_Buffer = AllocMem(ml->ml_BufferSize, (LONG)
						MEMF_PUBLIC | MEMF_CLEAR)) {
		  get_item_text_pens(ml);
		  if (init_menu(ml) == TRUE) {
		     return(ml);
		  }
		  FreeMem(ml->ml_Buffer, ml->ml_BufferSize);
	       }
	    }
	    if (ta) {
	       CloseFont(ml->ml_TextFont);
	    }
	 }
	 FreeMem(ml, (LONG)sizeof(struct MenuList));
      }
   }
   return(NULL);
}
	/* Attach menu list to given window */

   VOID
attach_menu(struct Window  *win, struct MenuList  *ml)
{
   if (win && ml) {
      /* remove menu strip from old window if any and attach it to new one */
      if (ml->ml_Flags & MENU_LIST_FLAG_ATTACHED) {
	 ClearMenuStrip(ml->ml_Window);
      }
      SetMenuStrip(win, (struct Menu *)ml->ml_Buffer);
      ml->ml_Window = win;
      ml->ml_Flags |= MENU_LIST_FLAG_ATTACHED;
   }
}
	/* Return menu item address */

   struct MenuItem *
menu_item_address(struct MenuList  *ml, USHORT menu_num)
{
   return(ItemAddress((struct Menu *)ml->ml_Buffer, (LONG)menu_num));
}
	/* Remove menu list from window currently attached to */

   VOID
remove_menu(struct MenuList  *ml)
{
   if (ml) {
      if (ml->ml_Flags & MENU_LIST_FLAG_ATTACHED) {
	 ClearMenuStrip(ml->ml_Window);
	 ml->ml_Flags &= ~MENU_LIST_FLAG_ATTACHED;
      }
   }
}
	/* Free menu */

   VOID
free_menu(struct MenuList  *ml)
{
   if (ml) {
      if (ml->ml_Flags & MENU_LIST_FLAG_ATTACHED) {
	 ClearMenuStrip(ml->ml_Window);
      }
      if (ml->ml_TextAttr) {
	 CloseFont(ml->ml_TextFont);
      }
      FreeMem(ml->ml_Buffer, ml->ml_BufferSize);
      FreeMem(ml, (LONG)sizeof(struct MenuList));
   }
}
	/* Get buffer size for menus, items and intui texts */

   STATIC ULONG
get_buffer_size(struct MenuList  *ml)
{
   struct MenuData   *md = ml->ml_Data;
   USHORT menus = 0, items = 0;
   ULONG  buffer_size = 0;

   if (md->md_Type == MENU_DATA_TYPE_TITLE) {
      do {
	 switch (md++->md_Type) {
	    case MENU_DATA_TYPE_TITLE :
	       menus++;
	       break;
	    case MENU_DATA_TYPE_ITEM :
	    case MENU_DATA_TYPE_SUBITEM :
	       items++;
	       break;
	 }
      } while (md->md_Type != INTUISUP_DATA_END &&
					 md->md_Type <= MAX_MENU_DATA_TYPE);
      if (menus && items) {
	 buffer_size = menus * sizeof(struct Menu) + items *
		       (sizeof(struct MenuItem) + sizeof(struct IntuiText));
      }
   }
   return(buffer_size);
}
	/* Get two different text pens for items */

   STATIC VOID
get_item_text_pens(struct MenuList  *ml)
{
   struct RenderInfo  *ri = ml->ml_RenderInfo;
   struct ColorMap    *cmap;
   SHORT i, j, temp, num_colors, back_color, colors[MAX_RENDER_COLORS],
	 pens[MAX_RENDER_COLORS];
   
   if ((num_colors = 1 << ri->ri_ScreenDepth) > MAX_RENDER_COLORS) {
      num_colors = MAX_RENDER_COLORS;
   }
   if (num_colors < 3) {
      /* set render pens for monochrome screen */
      if (ml->ml_Window->BlockPen) {
	 ml->ml_TextPen1 = 0;
	 ml->ml_TextPen2 = 0;
      } else {
	 ml->ml_TextPen1 = 1;
	 ml->ml_TextPen2 = 1;
      }
   } else {
      /* get current screen colors */
      Forbid();
      cmap = ml->ml_Window->WScreen->ViewPort.ColorMap;
      for (i = 0; i < num_colors; i++) {
	 colors[i] = GetRGB4(cmap, (LONG)i);
	 pens[i]   = i;
      }
      Permit();
      /* save normal background color */
      back_color = colors[ml->ml_Window->BlockPen];
      /* set pen 1 */
      for (i = 0, j = 0; i < num_colors; i++) {
	 if ((temp = calc_color_difference(colors[i], back_color)) > j) {
	    j               = temp;
	    ml->ml_TextPen1 = pens[i];
	 }
      }
      /* set text pen 2 */
      for (i = 0, j = 0; i < num_colors; i++) {
	 if ((temp = calc_color_difference(colors[i], back_color)) > j &&
					       pens[i] != ri->ri_TextPen1) {
	    j               = temp;
	    ml->ml_TextPen2 = pens[i];
	 }
      }
   }
}
	/* Initialize menu from menu list */

   STATIC BOOL
init_menu(struct MenuList  *ml)
{
   struct RenderInfo  *ri = ml->ml_RenderInfo;
   struct MenuData    *md;
   struct TextFont    *menu_tf = ri->ri_TextFont, *item_tf = ml->ml_TextFont;
   struct Menu        *last_menu, *menu = NULL;
   struct MenuItem    *last_item, *last_subitem, *item = NULL;
   struct IntuiText   *itext, *last_itext;
   SHORT  item_ypos, subitem_xpos, subitem_ypos;
   BYTE   *buffer = ml->ml_Buffer;
   USHORT type, flags;
   BOOL   success = FALSE, error = FALSE, subitem_flag = FALSE;

   /* init menus and items */
   for (md = ml->ml_Data; md->md_Type != INTUISUP_DATA_END &&
				  md->md_Type <= MAX_MENU_DATA_TYPE; md++) {
      type  = md->md_Type;
      flags = md->md_Flags;
      switch (type) {
	 case MENU_DATA_TYPE_TITLE :
	    /* prepare last menu and subitem list */
	    if (menu) {
	       equal_select_box_width(menu->FirstItem);
	    }
	    if (subitem_flag == TRUE) {
	       equal_select_box_width(last_item->SubItem);
	    }
	    /* init data for new menu */
	    item         = NULL;
	    last_item    = NULL;
	    last_subitem = NULL;
	    item_ypos    = 0;
	    subitem_flag = FALSE;
	    last_menu    = menu;
	    menu         = (struct Menu *)buffer;
	    buffer      += sizeof(struct Menu);
	    /* init new menu */
	    if (last_menu) {
	       last_menu->NextMenu = menu;
	       menu->LeftEdge      = last_menu->LeftEdge + last_menu->Width
							+ menu_tf->tf_XSize;
	    } else {
	       menu->LeftEdge = 5;
	    }
	    menu->Width     = (strlen(md->md_Name) + 1) * menu_tf->tf_XSize;
	    menu->Height    = menu_tf->tf_YSize;
	    menu->Flags     = MIDRAWN | (flags & MENU_DATA_FLAG_DISABLED ?
							   0 : MENUENABLED);
	    menu->MenuName  = md->md_Name;
	    menu->FirstItem = (struct MenuItem *)buffer;
	    break;
	 case MENU_DATA_TYPE_ITEM :
	 case MENU_DATA_TYPE_SUBITEM :
	    /* init item data */
	    if (subitem_flag == TRUE) {
	       last_subitem = item;
	    } else {
	       last_item = item;
	    }
	    item    = (struct MenuItem *)buffer;
	    itext   = (struct IntuiText *)(item + 1);
	    buffer += sizeof(struct MenuItem) + sizeof(struct IntuiText);
	    /* init intui text */
	    itext->LeftEdge  = (flags & MENU_DATA_FLAG_ATTRIBUTE ?
							    CHECKWIDTH : 0);
	    itext->TopEdge   = 1;
	    itext->FrontPen  = ml->ml_TextPen1;
	    itext->DrawMode  = JAM1;
	    itext->ITextFont = ml->ml_TextAttr;
	    itext->IText     = (UBYTE *)md->md_Name;
	    /* init menu item */
	    item->Width         = (strlen(md->md_Name) +
			  (md->md_CommandKey ? 2 : 0)) * item_tf->tf_XSize +
				       (md->md_CommandKey ? COMMWIDTH : 0) +
			(flags & MENU_DATA_FLAG_ATTRIBUTE ? CHECKWIDTH : 0);
	    item->Height        = item_tf->tf_YSize + 1;
	    item->Flags         = ITEMTEXT |
					 (md->md_CommandKey ? COMMSEQ : 0) |
		       (flags & MENU_DATA_FLAG_DISABLED ? 0 : ITEMENABLED) |
			      (flags & MENU_DATA_FLAG_HIGH_NONE ? HIGHNONE :
		   (flags & MENU_DATA_FLAG_HIGH_BOX ? HIGHBOX : HIGHCOMP)) |
			  (flags & MENU_DATA_FLAG_ATTRIBUTE ? CHECKIT : 0) |
			    (flags & MENU_DATA_FLAG_SELECTED ? CHECKED : 0);
	    item->MutualExclude = md->md_MutualExclude;
	    item->ItemFill      = (APTR)itext;
	    item->SelectFill    = (APTR)itext;
	    item->Command       = *md->md_CommandKey;
	    /* insert empty line before item ? */
	    if (flags & MENU_DATA_FLAG_EMPTY_LINE) {
	       if (type == MENU_DATA_TYPE_SUBITEM) {
		  subitem_ypos += item->Height;
	       } else {
		  item_ypos += item->Height;
	       }
	    }
	    if (type == MENU_DATA_TYPE_SUBITEM) {
	       if (subitem_flag == TRUE) {
		  last_subitem->NextItem = item;
	       } else {
		  if (!last_item) {
		     error = TRUE;   /* no menu item to attach subitem */
		     break;
		  } else {
		     last_itext           = (struct IntuiText *)
							last_item->ItemFill;
		     last_itext->FrontPen = ml->ml_TextPen2;
		     last_item->SubItem   = item;
		     subitem_xpos         = strlen((BYTE *)last_itext->IText)
			 * item_tf->tf_XSize + (last_item->Flags & CHECKIT ?
							    CHECKWIDTH : 0);
		     subitem_ypos         = 0;
		     subitem_flag         = TRUE;
		  }
	       }
	       item->LeftEdge = subitem_xpos;
	       item->TopEdge  = subitem_ypos;
	       subitem_ypos  += item->Height;
	    } else {
	       if (last_item) {
		  last_item->NextItem = item;
	       }
	       item->TopEdge = item_ypos;
	       item_ypos    += item->Height;
	       if (subitem_flag == TRUE) {
		  /* prepare last subitem list */
		  equal_select_box_width(last_item->SubItem);
		  subitem_flag = FALSE;
	       }
	    }
	    break;
      }
      if (error == FALSE && item) {
	 /* prepare current menu and subitem list */
	 equal_select_box_width(menu->FirstItem);
	 if (subitem_flag == TRUE) {
	    equal_select_box_width(last_item->SubItem);
	 }
	 success = TRUE;
      }
   }
   return(success);
}
	/* Set equal select box width for given menu item list */

   STATIC VOID
equal_select_box_width(struct MenuItem  *item)
{
   struct MenuItem  *save_item = item;
   ULONG max_width = 0;

   /* search max width */
   do {
      if (item->Width > max_width) {
	 max_width = item->Width;
      }
   } while (item = item->NextItem);
   /* set max width */
   item = save_item;
   do {
      item->Width = max_width;
   } while (item = item->NextItem);
}
