/* $Revision Header *** Header built automatically - do not edit! ***********
 *
 *	(C) Copyright 1991 by Torsten Jürgeleit
 *
 *	Name .....: menus.c
 *	Created ..: Thursday 19-Dec-91 18:02:58
 *	Revision .: 2
 *
 *	Date        Author                 Comment
 *	=========   ====================   ====================
 *	03-Jun-92   Torsten Jürgeleit      subitems now indicated by `»',
 *                                         alternate color for item texts,
 *                                         now correct item width calculated
 *                                         in init_menu()
 *	20-Apr-92   Torsten Jürgeleit      Enforcer hit in init_menu -> *key
 *	19-Dec-91   Torsten Jürgeleit      Created this file!
 *
 ****************************************************************************
 *
 *	Menu support routines
 *
 * $Revision Header ********************************************************/

	/* Includes */

#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#ifdef AZTEC_C
#include <functions.h>   /* needed for Aztec C - prototypes and pragmas for all Amiga system functions */
#endif
#include <libraries/memwatch.h>   /* header file for memory debug link library (Fish 240) - AFTER functions.h */
#include <string.h>
#include "/render/render.h"
#include "/language/language.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);
USHORT intui_text_length(BYTE *text, struct TextAttr  *ta);
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(intui_text_length(a0,a1))
#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, BYTE **language_text_array)
{
   if (ri && ri->ri_ID == ISUP_ID && win && md) {
      struct MenuList  *ml;

      if (ml = AllocMem((LONG)sizeof(struct MenuList),
					  (LONG)MEMF_PUBLIC | MEMF_CLEAR)) {
	 struct TextFont  *tf;

	 /* 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_LanguageTextArray = language_text_array;
	 ml->ml_Flags             = 0;
	 ml->ml_ID                = ISUP_ID;

	 /* Get font for menu texts */
	 if (ta) {
	    tf = OpenFont(ta);
	 } else {
	    tf = ri->ri_TextFont;
	 }
	 if (ml->ml_TextFont = tf) {
	    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(tf);
	    }
	 }
	 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 && ml->ml_ID == ISUP_ID) {

      /* 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)
{
   struct MenuItem  *mi = NULL;

   if (ml && ml->ml_ID == ISUP_ID) {
      mi = ItemAddress((struct Menu *)ml->ml_Buffer, (LONG)menu_num);
   }
   return(mi);
}
	/* Remove menu list from window currently attached to */

   struct Window *
remove_menu(struct MenuList  *ml)
{
   struct Window  *win = NULL;

   if (ml && ml->ml_ID == ISUP_ID) {
      if (ml->ml_Flags & MENU_LIST_FLAG_ATTACHED) {
	 win = ml->ml_Window;
	 ClearMenuStrip(win);
	 ml->ml_Flags &= ~MENU_LIST_FLAG_ATTACHED;
      }
   }
   return(win);
}
	/* Free menu */

   VOID
free_menu(struct MenuList  *ml)
{
   if (ml && ml->ml_ID == ISUP_ID) {
      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;
   ULONG buffer_size = 0;

   if (md->md_Type != INTUISUP_DATA_END &&
				      md->md_Type == MENU_DATA_TYPE_TITLE) {
      USHORT menus = 0, items = 0, subitem_texts = 0;
      BOOL   error = FALSE, subitem = FALSE;

      do {
	 switch (md++->md_Type) {
	    case MENU_DATA_TYPE_TITLE :
	       menus++;
	       subitem = FALSE;
	       break;

	    case MENU_DATA_TYPE_ITEM :
	       items++;
	       subitem = FALSE;
	       break;

	    case MENU_DATA_TYPE_SUBITEM :
	       items++;

	       /* If first subitem then add text with `»' to previous item */
	       if (subitem == FALSE) {
		  subitem_texts++;
		  subitem = TRUE;
	       }
	       break;

	    default :
	       error = TRUE;
	       break;
	 }
      } while (error == FALSE && md->md_Type != INTUISUP_DATA_END &&
					 md->md_Type <= MAX_MENU_DATA_TYPE);
      if (error == FALSE && menus && items) {
	 buffer_size = menus * sizeof(struct Menu) + items *
		      (sizeof(struct MenuItem) + sizeof(struct IntuiText)) +
				   subitem_texts * 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;
   SHORT num_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 {
      struct ColorMap  *cmap;
      SHORT i, max_diff, back_color, colors[MAX_RENDER_COLORS],
	    pens[MAX_RENDER_COLORS];

      /* 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 text pen 1 - search color with greatest `difference' to backgound color */
      for (i = 0, max_diff = 0; i < num_colors; i++) {
	 SHORT diff;

	 if ((diff = calc_color_difference(colors[i], back_color)) >
								 max_diff) {
	    max_diff        = diff;
	    ml->ml_TextPen1 = pens[i];
	 }
      }

      /* Set text pen 2 - search color with 2nd greatest `difference' to backgound color */
      for (i = 0, max_diff = 0; i < num_colors; i++) {
	 SHORT diff;

	 if ((diff = calc_color_difference(colors[i], back_color)) >
				   max_diff && pens[i] != ri->ri_TextPen1) {
	    max_diff        = diff;
	    ml->ml_TextPen2 = pens[i];
	 }
      }
   }
}
	/* Initialize menu from menu list */

   STATIC BOOL
init_menu(struct MenuList  *ml)
{
   struct RenderInfo  *ri = ml->ml_RenderInfo;
   struct TextFont    *menu_tf = ri->ri_TextFont, *item_tf = ml->ml_TextFont;
   struct TextAttr    *menu_ta = &ri->ri_TextAttr, *item_ta = ml->ml_TextAttr;
   struct MenuData    *md;
   struct Menu        *last_menu, *menu = NULL;		/* MUST be defined OUT of for loop */
   struct MenuItem    *last_item, *last_subitem, *item = NULL;	/* MUST be defined OUT of for loop */
   BYTE  *buffer = ml->ml_Buffer;
   SHORT item_ypos, subitem_xpos, subitem_ypos;		/* MUST be defined OUT of for loop */
   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++) {
      struct IntuiText  *itext;
      USHORT type, flags;
      BYTE   *name, *key;

      /* Get data from menu data structure */
      type  = md->md_Type;
      flags = md->md_Flags;
      name  = get_language_text(md->md_Text, ml->ml_LanguageTextArray);
      key   = (!md->md_CommandKey ? NULL :
	    get_language_text(md->md_CommandKey, ml->ml_LanguageTextArray));

      /* Init structures according to data type */
      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) {

	       /* If this isn't the first menu then connect it to last menu */
	       last_menu->NextMenu = menu;
	       menu->LeftEdge      = last_menu->LeftEdge + last_menu->Width +
							  menu_tf->tf_XSize;
	    } else {
	       menu->LeftEdge = 5;
	    }
	    menu->Width     = intui_text_length(name, menu_ta) +
							  menu_tf->tf_XSize;
	    menu->Height    = menu_tf->tf_YSize;
	    menu->Flags     = MIDRAWN | (flags & MENU_DATA_FLAG_DISABLED ?
							   0 : MENUENABLED);
	    menu->MenuName  = 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  = (flags & MENU_DATA_FLAG_TEXT_COLOR2 ?
					 ml->ml_TextPen2 : ml->ml_TextPen1);
	    itext->DrawMode  = JAM1;
	    itext->ITextFont = item_ta;
	    itext->IText     = (UBYTE *)name;

	    /* Init menu item */
	    item->Width         = intui_text_length(name, item_ta) +
			     (key ? COMMWIDTH + 2 * item_tf->tf_XSize : 0) +
			(flags & MENU_DATA_FLAG_ATTRIBUTE ? CHECKWIDTH : 0);
	    item->Height        = item_tf->tf_YSize + 1;
	    item->Flags         = ITEMTEXT | (key ? 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       = (key ? *key : 0);
	    item->NextSelect    = MENUNULL;

	    /* 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 {
		     struct IntuiText  *new_itext,
				       *last_itext = (struct IntuiText *)
							last_item->ItemFill;
		     /* Create intui text for sub item indicator `»' */
		     new_itext            = (struct IntuiText *)buffer;
		     buffer              += sizeof(struct IntuiText);
		     new_itext->TopEdge   = last_itext->TopEdge;
		     new_itext->FrontPen  = last_itext->FrontPen;
		     new_itext->DrawMode  = JAM1;
		     new_itext->ITextFont = item_ta;
		     new_itext->IText     = (UBYTE *)"»";
		     last_itext->NextText = new_itext;

		     /* Connect sub item to menu item */
		     last_item->Width  += intui_text_length((BYTE *)
			     new_itext->IText, item_ta) + item_tf->tf_XSize;
		     last_item->SubItem = item;
		     subitem_xpos       = intui_text_length((BYTE *)
					       last_itext->IText, item_ta) +
			      (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);
}
	/* Calc text length in pixels */

   STATIC USHORT
intui_text_length(BYTE *text, struct TextAttr  *ta)
{
   struct IntuiText  itext;

   /* Init dummy intui text and return text length */
   itext.IText     = (UBYTE *)text;
   itext.ITextFont = ta;
   return(IntuiTextLength(&itext));
}
	/* 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;

      /* If subitem then calc left edge of subitem indicator `»' */
      if (item->SubItem) {
	 struct IntuiText  *itext = ((struct IntuiText *)
						  item->ItemFill)->NextText;
	 itext->LeftEdge = max_width - intui_text_length((BYTE *)
					    itext->IText, itext->ITextFont);
      }
   } while (item = item->NextItem);
}
