/* $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 .: 0
 *
 *	Date        Author                 Comment
 *	=========   ====================   ====================
 *	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>
#include <functions.h>
#include <string.h>
#include <libraries/memwatch.h>		/* AFTER functions.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);
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, 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)) {
	 /* 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;
	 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 && 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 == MENU_DATA_TYPE_TITLE) {
      USHORT menus = 0, items = 0;

      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;
   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 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   *name, *key, *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;
      name  = get_language_text(md->md_Text, ml->ml_LanguageTextArray);
      key   = (!md->md_CommandKey ? NULL :
	    get_language_text(md->md_CommandKey, ml->ml_LanguageTextArray));
      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(name) + 1) * 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  = ml->ml_TextPen1;
	    itext->DrawMode  = JAM1;
	    itext->ITextFont = ml->ml_TextAttr;
	    itext->IText     = (UBYTE *)name;

	    /* Init menu item */
	    item->Width         = (strlen(name) + (key ? 2 : 0)) *
				 item_tf->tf_XSize + (key ? COMMWIDTH : 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;
	    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 {
		     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);
}
