/* Copyright ) Darin Johnson, 1989 */

#include <exec/memory.h>
#include <graphics/text.h>
#include <graphics/gfxbase.h>
#include <intuition/intuition.h>
#include <clib/macros.h>
#include <functions.h>
#include "MyMenu.h"

BYTE *copystr();

unsigned long mnum, inum, snum, menu_num;
struct ext_MenuItem *tail_list;
UWORD font_width, font_height;
UBYTE menu_pen;		/* pen number for newly created items */

extern struct GfxBase *GfxBase;

struct Menu *mptr;
struct MenuItem *iptr, *sptr;

/* clean things up and initialize */
start_menu() {
  MM->my_menu = NULL;
  menu_pen = 0;	/* default */
  MM->item_list = tail_list = NULL;
  font_width = GfxBase->DefaultFont->tf_XSize;
  font_height = GfxBase->DefaultFont->tf_YSize;
}

/* clean up widths and other info now that menu is all built */
end_menu() {
  UWORD maxw, smaxw, txtw, top, stop, left;
  
  ULONG ilock;
  if (MM->prev_menu) {
    ilock = LockIBase(0L);
    left = MM->prev_menu->LeftEdge + MM->prev_menu->Width + SEPARATE;
    UnlockIBase(ilock);
  }
  for (mptr = MM->my_menu; mptr; mptr=mptr->NextMenu) {
    mptr->LeftEdge = left;
    maxw = mptr->Width = (strlen(mptr->MenuName)+1) * font_width;
    maxw += SEPARATE;
    left += maxw;
    top = 0;
      /* determine max width */
    for (iptr = mptr->FirstItem; iptr; iptr=iptr->NextItem) {
      iptr->TopEdge = top;
      top += iptr->Height;
      txtw = IntuiTextLength(iptr->ItemFill);
      if (iptr->Flags & COMMSEQ)
        txtw += COMMWIDTH+font_width+4;
      if (txtw > maxw)
        maxw = txtw;
    }
    for (iptr = mptr->FirstItem; iptr; iptr=iptr->NextItem) {
      iptr->Width = maxw;
      stop = smaxw = 0;
      for (sptr=iptr->SubItem; sptr; sptr=sptr->NextItem) {
        sptr->LeftEdge = maxw;
        sptr->TopEdge = stop;
        stop += sptr->Height;
        txtw = IntuiTextLength(sptr->ItemFill);
        if (sptr->Flags & COMMSEQ)
          txtw += COMMWIDTH+font_width+4;
        if (txtw > smaxw)
          smaxw = txtw;
      }
      for (sptr=iptr->SubItem; sptr; sptr=sptr->NextItem)
        sptr->Width = smaxw;
    }
  }
}

/* allocate and initialize a new menu */
struct Menu *new_menu(menustr)
  char *menustr;
{
  struct Menu *menu;
  menu = (struct Menu *)
	AllocMem(sizeof(struct Menu), MEMF_PUBLIC|MEMF_CLEAR);
  menu->MenuName = copystr(menustr);
  menu->Flags = MENUENABLED;
  menu->NextMenu = NULL;
  menu->FirstItem = NULL;
  menu->TopEdge = menu->Height = 0;
  return menu;
}

/* Find a menu.  If not found, create a new one */
struct Menu *get_menu(menustr)
  char *menustr;
{
  struct Menu *menu, *prev;
  mnum = menu_num + 1;
  if (MM->my_menu == NULL)		/* our menu strip */
    menu = MM->my_menu = new_menu(menustr);
  else {
    for (menu=MM->my_menu; menu; menu=menu->NextMenu) {
      if (!strcmp(menustr, (char *)menu->MenuName))
        break;	/* already here */
      prev = menu;
      mnum++;
    }
    if (menu == NULL)
      menu = prev->NextMenu = new_menu(menustr);
  }
  return menu;
}

/* create and initialize a new menu item */
struct MenuItem *new_item(itemstr)
  char *itemstr;
{
  struct MenuItem *item;
  struct IntuiText *it;
    /* notice that we allocate extra space */
  item = (struct MenuItem *)
	AllocMem(sizeof(struct ext_MenuItem), MEMF_PUBLIC|MEMF_CLEAR);
  it = (struct IntuiText *)
	AllocMem(sizeof(struct IntuiText), MEMF_PUBLIC|MEMF_CLEAR);
  item->NextItem = item->SubItem = NULL;
  item->SelectFill = NULL;
  item->NextSelect = 0;
  item->Height = font_height + 1;
  item->LeftEdge = item->TopEdge = item->MutualExclude = 0;
  item->Flags = ITEMTEXT+HIGHCOMP+ITEMENABLED;
  it->FrontPen = menu_pen;
  it->BackPen = AUTOBACKPEN;
  it->LeftEdge = it->TopEdge = 1;
  it->ITextFont = NULL;
  it->NextText = NULL;
  it->DrawMode = JAM2;
  it->IText = (UBYTE*)copystr(itemstr);
  item->ItemFill = (APTR)it;
  return item;
}

/* find a menu item, if not found, create a new one */
struct MenuItem *get_item(menustr, itemstr)
  char *menustr, *itemstr;
{
  struct Menu *menu;
  struct MenuItem *item, *prev;
  menu = get_menu(menustr);
  inum = 0;
  if (menu->FirstItem == NULL)
    item = menu->FirstItem = new_item(itemstr);
  else {
    for (item=menu->FirstItem; item; item=item->NextItem) {
      if (!strcmp(itemstr, ((struct IntuiText *)item->ItemFill)->IText))
        break;	/* already here */
      prev = item;
      inum++;
    }
    if (item == NULL)
      item = prev->NextItem = new_item(itemstr);
  }
  return item;
}

#define EITEM ((struct ext_MenuItem *)item)

/* create a new menu item (or sub item) */
struct ext_MenuItem *add_menu(menustr, itemstr, substr, comm)
  char *menustr, *itemstr, *substr, comm;
{
  struct MenuItem *item, *sub, *prev;
  if (!itemstr)
    return NULL;	/* oops */
  item = get_item(menustr, itemstr);	/* get (or create) the item */
  if (substr && *substr) {
    snum = 0;
    if (item->SubItem == NULL)
      sub = item->SubItem = new_item(substr);	/* get the sub-item */
    else {
      for (sub=item->SubItem; sub; sub=sub->NextItem) {
        if (!strcmp(substr, (char *)sub->ItemFill))
          break;	/* duplicate */
        prev = sub;
	snum++;
      }
      if (sub == NULL)
        sub = prev->NextItem = new_item(substr);
    }
    item = sub;
  } else
    snum = NOSUB;
  if (comm) {
    item->Command = comm;
    item->Flags |= COMMSEQ;
  }
    /* fill in our private fields */
  EITEM->id = SHIFTMENU(mnum)|SHIFTITEM(inum)|SHIFTSUB(snum);
  EITEM->cmd = EITEM->args = NULL;
  EITEM->type = NULL;
    /* stick onto item_list */
  EITEM->next_item = NULL;
  if (MM->item_list==NULL)
    MM->item_list = EITEM;
  else
    tail_list->next_item = EITEM;
  tail_list = EITEM;
  return EITEM;
}

/* free up memory used by a menu item */
free_item(item)
  struct ext_MenuItem *item;
{
  struct IntuiText *it;
  it = (struct IntuiText *)item->MenuItem.ItemFill;
  FreeMem(it->IText, strlen(it->IText)+1);
  FreeMem(it, sizeof(struct IntuiText));
  if (item->cmd)
    FreeMem(item->cmd, strlen(item->cmd)+1);
  if (item->args)
    FreeMem(item->args, strlen(item->args)+1);
  FreeMem(item, sizeof(struct ext_MenuItem));
}

/* free up all space taken up by our menus */
free_menus() {
  struct Menu *mtmp;
  struct MenuItem *itmp;

  mptr = MM->my_menu;
  MM->my_menu = NULL;
  while (mptr) {
    iptr = mptr->FirstItem;
    while (iptr) {
      sptr = iptr->SubItem;
      while (sptr) {
        itmp = sptr;
	sptr = sptr->NextItem;
	free_item(itmp);
      }
      itmp = iptr;
      iptr = iptr->NextItem;
      free_item(itmp);
    }
    mtmp = mptr;
    mptr = mptr->NextMenu;
    FreeMem(mtmp->MenuName, strlen(mtmp->MenuName)+1);
    FreeMem(mtmp, sizeof(struct Menu));
  }
}
