/* easy menus: Copyright 1987 Peter da Silva, all rights reserved.
 *
 *     Permission is granted to use this in any application, so long as
 *     this notice is retained in the source. Permission is granted to
 *     modify the code as you like, so long as this notice (between the
 *     first line beginning "easy menus" and the end of this paragraph,
 *     inclusive) is retained intact.
 *
 * Usage:
 *
 *     #include "menu.h"
 *
 *     struct MenuPtr menudata;
 *     struct MenuPtr *menuptr = &menudata;
 *
 *     init_menus(menuptr);    / * Just zero menu pointer out * /
 *
 *     for(each menu item) {
 *             add_menu(menuptr, menu, item, subitem, flags);
 *     }
 *
 *  Flags:
 *    SUBITEM_NOCHECK  -- subitem does not require a checkmark.
 *    SUBITEM_SELECTOR -- subitem is a 1 of n selector, use mutual-exclude.
 *    SUBITEM_TOGGLE   -- subitem is a toggled flag.
 *    SUBITEM_SELECTED -- defaults to checked.
 *
 *
 *     SetMenuStrip(yourwindow, menuptr->MenuBar);
 *
 *     ...
 *
 *     ClearMenuStrip(yourwindow);
 *
 *     trash_menus(menuptr);
 *
 * Notes:
 *
 *     if you don't want any subitems, use zero for the subitem value.
 *
 *     subitem is always initialised as a CHECKIT item with all the other
 *     subitems mutually excluded.
 *
 *     it is intended that the menu be set up with all action items in
 *     the first level of the menu, and all toggles in the second level...
 *     this is a piece of blatant authoritarianism on my part. I've seen
 *     too many menus with no rhyme or reason. Look at AmigaTerm (the term
 *     program that comes with the Amiga modem) some time. Baud rate has
 *     an item all by itself, but word size is hidden off in a menu with
 *     things like bell sound.
 *
 *     the appearance of the menus produced by this is (in my humble
 *     opinion) good. I took some care making text centered in menu boxes,
 *     for example.
 */
#include <exec/memory.h>
#include <intuition/intuition.h>
#include "menu.h"
#include "fonts.h"

/*
struct MenuPtr {
       struct Menu *MenuBar;
       struct Remember *MenuMemory;
};
*/

char *AllocRemember();

static struct Menu *new_menu();
static struct MenuItem *new_item(), *new_subitem();

#define TOMENUNUM(i,j,k) (SHIFTMENU(i)|SHIFTITEM(j)|SHIFTSUB(k))
#define TextLen(s) (strlen(s)*FONTWIDTH)

trash_menus(menuptr)
struct MenuPtr *menuptr;
{
       FreeRemember(&menuptr->MenuMemory, 1);
       menuptr->MenuMemory = 0;
       menuptr->MenuBar = 0;
}

init_menus(menuptr)
struct MenuPtr *menuptr;
{
       menuptr->MenuMemory = 0;
       menuptr->MenuBar = 0;
}

int add_menu(menuptr, menuname, itemname, subitemname, flags)
struct MenuPtr *menuptr;
char *menuname, *itemname, *subitemname;
long flags;
{
       int i, j, k;
       struct Menu *menu;
       struct MenuItem *item;
       struct MenuItem *subitem;

       if(menuptr->MenuBar) {
               for(i = 0, menu = menuptr->MenuBar;
                   menu;
                       menu = menu->NextMenu, i++
                  )
                       if(strcmp(menuname, menu->MenuName)==0)
                               break;
               if(!menu)
                       menu = new_menu(menuptr, menuname);
               if(!menu)
                       return MENUNULL;
       } else {
               i = 0;
               menu = new_menu(menuptr, menuname);
               if(!menu)
                       return MENUNULL;
       }
       for(j = 0, item = menu->FirstItem;
               item;
               item = item->NextItem, j++
          ) {
               struct IntuiText *text;
               text = (struct IntuiText *)item->ItemFill;
               if(strcmp(itemname, text->IText) == 0)
                       break;
       }
       if(subitemname) {
               if(!item)
                       item = new_item(menuptr, menu, itemname);
               if(!item)
                       return MENUNULL;
               for(k = 0, subitem = item->SubItem;
                       subitem;
                       subitem = subitem->NextItem, k++
                  ) {
                       struct IntuiText *text;
                       text = (struct IntuiText *)subitem->ItemFill;
                       if(strcmp(subitemname, text->IText) == 0)
                               break;
               }
               if(!subitem)
                       subitem = new_subitem(menuptr, item, subitemname, flags);
               if(!subitem)
                       return MENUNULL;
               return TOMENUNUM(i, j, k);
       } else {
               if(!item)
                       item = new_item(menuptr, menu, itemname);
               if(!item)
                       return MENUNULL;
               return TOMENUNUM(i, j, NOSUB);
       }
}

static struct Menu *
new_menu(menuptr, name)
struct MenuPtr *menuptr;
char *name;
{
       struct Menu *menu;

       menu = (struct Menu *)AllocRemember(
               &menuptr->MenuMemory,
               sizeof(struct Menu),
               MEMF_PUBLIC);
       if(!menu)
               return 0;
       menu->NextMenu = NULL;
       menu->LeftEdge = 0;
       menu->TopEdge = 0;
       menu->Width = TextLen(name)+FONTWIDTH;
       menu->Height = 0;
       menu->Flags = MENUENABLED;
       menu->MenuName = name;
       menu->FirstItem = 0;
       if(menuptr->MenuBar) {
               struct Menu *ptr, *prev;
               for(ptr = menuptr->MenuBar; ptr; ptr=ptr->NextMenu) {
                       menu->LeftEdge += ptr->Width;
                       prev = ptr;
               }
               prev->NextMenu = menu;
       } else {
               menuptr->MenuBar = menu;
       }

       return menu;
}

static struct item *
new_item(menuptr, menu, name)
struct MenuPtr *menuptr;
char *name;
struct Menu *menu;
{
       struct MenuItem *item;
       struct IntuiText *text;

       item = (struct MenuItem *)AllocRemember(
               &menuptr->MenuMemory,
               sizeof(struct MenuItem),
               MEMF_PUBLIC);
       if(!item)
               return 0;
       text = (struct IntuiText *)AllocRemember(
               &menuptr->MenuMemory,
               sizeof(struct IntuiText),
               MEMF_PUBLIC);
       if(!text)
               return 0;

       text->FrontPen = AUTOFRONTPEN;
       text->BackPen = AUTOBACKPEN;
       text->DrawMode = JAM2;
       text->LeftEdge = 1;
       text->TopEdge = 1;
       text->ITextFont = NULL;
       text->IText = name;
       text->NextText = NULL;

       item->NextItem = NULL;
       item->LeftEdge = 0;
       item->TopEdge = 0;
       item->Width = IntuiTextLength(text)+2;
       if(item->Width <= menu->Width)
               item->Width = menu->Width+1;
       item->Height = FONTHEIGHT+1;
       item->Flags = ITEMTEXT|HIGHCOMP|ITEMENABLED;
       item->MutualExclude = 0;
       item->ItemFill = text;
       item->SelectFill = NULL;
       item->Command = 0;
       item->SubItem = NULL;
       item->NextSelect = NULL;

       if(menu->FirstItem) {
               struct MenuItem *ptr, *prev;
               for(ptr = menu->FirstItem; ptr; ptr=ptr->NextItem) {
                       if(item->Width > ptr->Width) {
                               if(ptr->SubItem)
                                       nudge(ptr->SubItem, item->Width-ptr->Width);
                               ptr->Width = item->Width;
                       } else if(ptr->Width>item->Width)
                               item->Width = ptr->Width;
                       prev = ptr;
               }
               item->TopEdge = prev->TopEdge + prev->Height;
               prev->NextItem = item;
       } else {
               menu->FirstItem = item;
       }i}

       return item;
}

static nudge(item, delta)
struct MenuItem *item;
int delta;
{
       while(item) {
               item->LeftEdge += delta;
               item = item->NextItem;
       }
}

static struct item *
new_subitem(menuptr, item, name, flags)
struct MenuPtr *menuptr;
char *name;
struct MenuItem *item;
long flags;
{
       struct MenuItem *subitem;
       struct IntuiText *text;

       subitem = (struct MenuItem *)AllocRemember(
               &menuptr->MenuMemory,
               sizeof(struct MenuItem),
               MEMF_PUBLIC);
       if(!subitem)
               return 0;
       text = (struct IntuiText *)AllocRemember(
               &menuptr->MenuMemory,
               sizeof(struct IntuiText),
               MEMF_PUBLIC);
       if(!text)
               return 0;

       text->FrontPen = AUTOFRONTPEN;
       text->BackPen = AUTOBACKPEN;
       text->DrawMode = JAM2;
       text->LeftEdge = CHECKWIDTH+1;
       text->TopEdge = 1;
       text->ITextFont = NULL;
       text->IText = name;
       text->NextText = NULL;

       subitem->NextItem = NULL;
       subitem->LeftEdge = item->Width;
       subitem->TopEdge = 0;
       subitem->Width = IntuiTextLength(text)+2;
       if(flags != SUBITEM_NOCHECK) subitem->Width += CHECKWIDTH;
       subitem->Height = FONTHEIGHT+1;
       subitem->Flags = ITEMTEXT|ITEMENABLED|HIGHCOMP;
       subitem->MutualExclude = 0;
       if(flags != SUBITEM_NOCHECK) {
               subitem->Flags |= CHECKIT;
               if(flags & SUBITEM_TOGGLE) subitem->Flags |= MENUTOGGLE;
               if(flags & SUBITEM_SELECTED) subitem->Flags |= CHECKED;
       }
       subitem->ItemFill = text;
       subitem->SelectFill = NULL;
       subitem->Command = 0;
       subitem->SubItem = NULL;
       subitem->NextSelect = NULL;

       if(item->SubItem) {
               struct MenuItem *ptr, *prev;
               int i;
               for(i=0, ptr = item->SubItem; ptr; i++, ptr=ptr->NextItem) {
                       if(subitem->Width > ptr->Width)
                               ptr->Width = subitem->Width;
                       else if(ptr->Width>subitem->Width)
                               subitem->Width = ptr->Width;
                       prev = ptr;
               }
               subitem->TopEdge = prev->TopEdge + prev->Height;
               if(flags & SUBITEM_SELECTOR)
                       subitem->MutualExclude = ~(1<<i);
               prev->NextItem = subitem;
       } else {
               item->SubItem = subitem;
               if(flags & SUBITEM_SELECTOR)
                       subitem->MutualExclude = ~1;
       }

       return subitem;
}
