#include <stdio.h>
/*
 *  MENU.C
 *
 *  Menu routines... made to take up as little space as possible, and
 *  thus uses many tricks which you should watch out for.
 *  ------------------------------------------------------------------
 *  Changes added 06/94 by Karl Lukas
 *
 *  Menus can now have keyboard shortcuts: menuadd <men> <item|c> <cmd>
 *  The vertical bar '|' and the command character c must immediatly
 *  follow the item name 
 *
 *  Menu items can now be separated by NM_BARLABELs:
 *    menuadd <men> "NM_BARLABEL|n" null
 *  The vertical bar '|' and width of the separator n must immediatly
 *  follow the NM_BARLABEL keyword. If you dont't supply a width
 *  value a fixed default value will be used
 *
 *  These changes are semi-font-adaptive, i.e. they rely on the '~'
 *  tilde character. Very large fonts might cause a visual mess.
 */

#include "defs.h"
#include "graphics/gfxbase.h"
#include "graphics/rastport.h"
#include <string.h>

extern struct GfxBase *GfxBase;
extern BYTE GetShortcut(char *itemname);    /* Added 06/94 by Karl Lukas */
extern int GetBarlabLength(char *itemname); /* Added 06/94 by Karl Lukas */

#define BARLAB_DEFAULT 22                /* Default length of separator,  KL */
#define BARLAB_MAXLENGTH 50              /* Don't increase this value,    KL */

typedef struct {
    ITEM item;
    char *com;
} XITEM;

char menucmd_buffer[256];

short Menuoff;
short DoMenuoff;

MENU *Menu;

void
menu_strip(win)
WIN *win;
{
    if (!Menuoff && Menu) {
	SetMenuStrip(win,Menu);
	Forbid();
	win->Flags &= ~RMBTRAP;
	Permit();
    }
}

void menu_off(void)
{
    ED *ed;
    if (Menuoff == 0) {
	for (ed = (ED *)DBase.mlh_Head; ed->Node.mln_Succ; ed = (ED *)ed->Node.mln_Succ) {
	    ClearMenuStrip(ed->Win);
	    Forbid();
	    ed->Win->Flags |= RMBTRAP;
	    Permit();
	}
    }
    ++Menuoff;
}

void menu_on(void)
{
    ED *ed;
    if (Menu && Menuoff == 1) {
	fixmenu();
	for (ed = (ED *)DBase.mlh_Head; ed->Node.mln_Succ; ed = (ED *)ed->Node.mln_Succ) {
	    SetMenuStrip(ed->Win,Menu);
	    Forbid();
	    ed->Win->Flags &= ~RMBTRAP;
	    Permit();
	}
    }
    --Menuoff;
}

void do_menuoff(void)
{
    menu_off();
    ++DoMenuoff;
}

void do_menuon(void)
{
    if (DoMenuoff) {
	--DoMenuoff;
	menu_on();
    }
}

char *menutomacro(char *str)
{
    char header[64];
    char itembuf[64];
    short i;
    char *ptr;
    MENU *menu;
    ITEM *item;

    for (i = 0; str[i] && str[i] != '-'; ++i);
    if (str[i] == '-') {
	strncpy(header, str, i);
	header[i] = 0;
	strcpy(itembuf, str + i + 1);
	for (menu = Menu; menu; menu = menu->NextMenu) {
	    if (ncstrcmp(header, menu->MenuName) == 0) {
		for (item = menu->FirstItem; item; item = item->NextItem) {
		    ptr = (char *)((ITEXT *)item->ItemFill)->IText;
		    if (ncstrcmp(itembuf, ptr) == 0) {
			ptr = ((XITEM *)item)->com;
			goto done;
		    }
		}
	    }
	}
    }
    ptr = NULL;
done:
    return(ptr);
}


/* Changed 06/94 by Karl Lukas. Now Handles CHECKIT menus */

char *menu_cmd(UWORD Code)
{
    XITEM *item;
    if (item = (XITEM *)ItemAddress(Menu, Code))
    {
       if (item->item.Flags & CHECKIT)
       {
          char *command = item->com;
          int i = 0;
          while (command[i] && (command[i] != '|'))
             i++;
          if (command[i] != '|')
          {
             title("Command error in CHECKIT menu");
             return NULL;
          }
          if (item->item.Flags & CHECKED)
          {
             strncpy(menucmd_buffer, command, i);
             menucmd_buffer[i] = 0;
          }
          else
             strcpy(menucmd_buffer, command + i + 1); 
          return menucmd_buffer;
          
       }
       else
          return(item->com);
    
    }
    return(NULL);
}

/*
 *  new fixmenu by BIX:dhack handles 2.0 fonts
 */

void fixmenu(void)
{
    MENU *menu;
    ITEM *item;
    ITEXT *it;
    int row, col, maxc, scr;
    struct IntuiText itxt;  /* To find width in case of proportional fonts */

    /* Initialize the few elements needed */
    itxt.ITextFont = Ep->Win->WScreen->Font;
    itxt.NextText = NULL;

    col = 0;
    for (menu = Menu; menu; menu = menu->NextMenu) {
	short offset;  /* Offset for width; used for position of item
			  relative to it's menu header at end of loop */

	/* Put this menu header into the IntuiText and figure out how
	   wide it is in pixels */
	itxt.IText = menu->MenuName;
	maxc = IntuiTextLength(&itxt);    /* This is now # of pixels */

	/* Find 'average' character size */
	offset = (maxc/strlen(menu->MenuName))*2;
	menu->Width = maxc + offset;

	row = 0;
	for (item = menu->FirstItem; item; item = item->NextItem) {
	    it = (ITEXT *)item->ItemFill;
	    item->TopEdge = row;
	    if(it != NULL) {
		it->TopEdge = 1;	 /* Center in select box */
		it->LeftEdge = offset/4;                               /* Changed /4 KL*/
		it->DrawMode = JAM1;	 /* How workbench's menus are... */
	    }

	    /* Figure out how wide this item is */
	    itxt.IText = ((ITEXT *)item->ItemFill)->IText;
	    scr = IntuiTextLength(&itxt);  /* This is now # of pixels */

            if (item->Flags & COMMSEQ)      /* KL */
               scr += COMMWIDTH;            /* KL COMMSEQ's need additional space */

            if (item->Flags & CHECKIT)      /* KL */
            {                               /* KL */
               scr += CHECKWIDTH;           /* KL CHECKIT's need additional space */
               it->LeftEdge += CHECKWIDTH;  /* KL */
            }                               /* KL */

	    if (scr > maxc)
		maxc = scr;

	    /* Figure out how tall the text is */
	    if(Ep->Win->WScreen->Font != NULL)
		item->Height = Ep->Win->WScreen->Font->ta_YSize + 2;   /* Changed from 2 to 1, KL */

	    else /* This is something to default to, just in case */
		item->Height = GfxBase->DefaultFont->tf_YSize + 2;     /* Changed from 2 to 1, KL */


            if (*it->IText == '~')                                     /* This is a NM_BARLAB, KL */
            {                                                          /* KL */
                item->TopEdge++;                                       /* KL */
                item->Height = item->Height / 2 + 3;                   /* KL */
            }                                                          /* KL */

	    row += item->Height;
	}
	maxc += offset / 4;                                            /* Changed KL */

	/* Fit the menu onto the screen */
	if( (maxc+10) > Ep->Win->WScreen->Width ) {   /* Wider than screen! */
	    offset = -col;
	    /* Unfortunately, this won't reduce the menu's width */
	    maxc = Ep->Win->WScreen->Width - 12;
	} else if( (offset = Ep->Win->WScreen->Width - (col+maxc+10)) > 0 )
	    offset = 0;

	for (item = menu->FirstItem; item; item = item->NextItem) {
	    item->Width = maxc;
	    item->LeftEdge = offset / 4;                               /* Changed KL */
	}

	menu->LeftEdge = col;
	menu->Height = row;
	col += menu->Width;
    }
}


#ifdef NOTDEF
void fixmenu(void)
{
    MENU *menu;
    ITEM *item;
    ITEXT *it;
    int row, col, maxc, scr;

    col = 0;
    for (menu = Menu; menu; menu = menu->NextMenu) {
	short hdr_width;
	maxc = strlen(menu->MenuName);
	hdr_width = maxc * 8 + 16;
	menu->Width = hdr_width;
	row = 0;
	for (item = menu->FirstItem; item; item = item->NextItem) {
	    it = (ITEXT *)item->ItemFill;
	    item->TopEdge = row;
	    scr = strlen(((ITEXT *)item->ItemFill)->IText);
	    if (scr > maxc)
		maxc = scr;
	    item->Height = 10;
	    row += item->Height;
	}
	maxc = (maxc * 8) + 16;
	for (item = menu->FirstItem; item; item = item->NextItem)
	    item->Width = maxc;
	menu->LeftEdge = col;
	menu->Height = row;
	col += hdr_width;
    }
}
#endif

/*
 *  menuclear
 *  menuadd	header	item	command
 *  menudel	header	item
 *  menudelhdr	header
 */

void do_menuclear(void)
{
    menu_off();
    while (Menu) {
	av[1] = (ubyte *)Menu->MenuName;
	do_menudelhdr();
    }
    menu_on();
}

/* Keyboard shortcuts and separators added by Karl Lukas 06/94 */

void do_menuadd(void)
{
    int checkit = 0;
    MENU *menu, **mpr;
    ITEM *item, **ipr;
    ITEXT *it;
    BYTE shortcut = 0;                                                     /* KL */
    char *barlabel = "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"; /* KL */
    int barlength = 0;                                                     /* KL */

    menu_off();
    mpr = &Menu;
    for (menu = *mpr; menu; menu = *mpr) {
	if (strcmp(av[1], menu->MenuName) == 0) {
	    ipr = &menu->FirstItem;
	    for (item = *ipr; item; item = *ipr) {
		if (strcmp(av[2], ((ITEXT *)item->ItemFill)->IText) == 0)
		    goto newname;
		ipr = &item->NextItem;
	    }
	    goto newitem;
	}
	mpr = &menu->NextMenu;
    }
newmenu:    /*	create new menu */
    menu = malloc(sizeof(MENU));
    clrmem(menu, sizeof(MENU));
    menu->NextMenu = *mpr;
    *mpr = menu;
    menu->Flags = MENUENABLED;
    menu->MenuName = malloc(strlen(av[1])+1);
    strcpy(menu->MenuName, av[1]);
    ipr = &menu->FirstItem;
    *ipr = NULL;
newitem:    /*	create new item */
    it = malloc(sizeof(ITEXT));
    clrmem(it, sizeof(ITEXT));
    it->BackPen = 1;
    it->DrawMode = JAM2;

    if (strncmp(av[2], "NM_BARLAB", 9) == 0)                   /* KL */
    {                                                          /* KL */
       barlength = GetBarlabLength(av[2]);                     /* KL */
       av[2] = malloc(barlength + 1);                          /* KL */
       strncpy(av[2], barlabel, barlength);                    /* KL */
       av[2][barlength] = 0;                                   /* KL */
    }                                                          /* KL */
    else                                                       /* KL */
       shortcut = GetShortcut(av[2]);                          /* KL */

    if (av[2][0] == '_')                                       /* KL */
    {                                                          /* KL */
       if (av[2][1] == '_') /* CHECKIT, initial state unchecked,  KL */
       {                                                       /* KL */
          checkit = 2;                                         /* KL */
          memmove(av[2], av[2] + 2, strlen(av[2] + 1));        /* KL */
       }                                                       /* KL */
       else  /* Initial state CHECKED                             KL */
       {                                                       /* KL */
          checkit = 1;                                         /* KL */
          memmove(av[2], av[2] + 1, strlen(av[2]));            /* KL */
       }                                                       /* KL */
    }                                                          /* KL */

    it->IText = malloc(strlen(av[2])+1);
    strcpy(it->IText, av[2]);
    item = malloc(sizeof(XITEM));
    clrmem(item, sizeof(XITEM));
    item->NextItem = *ipr;
    *ipr = item;
    item->ItemFill = (APTR)it;

    item->Flags = ITEMTEXT|ITEMENABLED|HIGHCOMP;

    if (barlength)                                             /* KL */
       item->Flags = ITEMENABLED|ITEMTEXT|HIGHNONE;            /* KL */
    if (shortcut)                                              /* KL */
    {                                                          /* KL */
       item->Flags |= COMMSEQ;                                 /* KL */
       item->Command = shortcut;                               /* KL */
    }                                                          /* KL */
    if (checkit == 1)                                          /* KL */
       item->Flags |= CHECKIT|CHECKED|MENUTOGGLE;              /* KL */
    if (checkit == 2)                                          /* KL */
       item->Flags |= CHECKIT|MENUTOGGLE;                      /* KL */

newname:    /*	create new name */
    if (((XITEM *)item)->com)
	free(((XITEM *)item)->com);
    ((XITEM *)item)->com = malloc(strlen(av[3])+1);
    strcpy(((XITEM *)item)->com, av[3]);
    menu_on();
}

BYTE GetShortcut(char *itemname) /* Added 06/94 by Karl Lukas */
{
   int i = 0;

   while (itemname[i] && (itemname[i] != '|'))
      i++;
   if (itemname[i])
      itemname[i++] = 0;
   return (BYTE)itemname[i];  
}

int GetBarlabLength(char *itemname)
{
   int ret = BARLAB_DEFAULT;

   if (itemname[9] == '|' && itemname[10] >= '0' && itemname[10] <= '9')
      ret = atoi(itemname + 10);
   if (ret > BARLAB_MAXLENGTH)
      ret = BARLAB_DEFAULT;
   return ret;
}


void do_menudelhdr(void)
{
    MENU *menu;
    MENU **mpr;

    menu_off();
    mpr = &Menu;
    for (menu = *mpr; menu; menu = *mpr) {
	if (strcmp(av[1], menu->MenuName) == 0) {
	    if (menu->FirstItem) {
		while (menu->FirstItem) {
		    av[2] = ((ITEXT *)menu->FirstItem->ItemFill)->IText;
		    if (do_menudel())
			break;
		}
		break;
	    }
	    *mpr = menu->NextMenu;
	    free(menu->MenuName);
	    free(menu);
	    break;
	}
	mpr = &menu->NextMenu;
    }
    menu_on();
}

int do_menudel(void)
{
    MENU *menu;
    ITEM *item, **ipr;
    ITEXT *it;
    short ret = 0;

    menu_off();
    for (menu = Menu; menu; menu = menu->NextMenu) {
	if (strcmp(av[1], menu->MenuName) == 0) {
	    ipr = &menu->FirstItem;
	    for (item = *ipr; item; item = *ipr) {
		it = (ITEXT *)item->ItemFill;
		if (strcmp(av[2], it->IText) == 0) {
		    *ipr = item->NextItem;
		    free(it->IText);
		    free(it);
		    free(((XITEM *)item)->com);
		    free(item);
		    if (!menu->FirstItem) {
			do_menudelhdr();
			ret = 1;
		    }
		    menu_on();
		    return((int)ret);
		}
		ipr = &item->NextItem;
	    }
	}
    }
    menu_on();
    return((int)ret);
}

