/*

   This is the C-language source for the s_menu routine.  We don't expect
you'll want to tinker with it, but it might be interesting for you to see
how things were done.

*/

#include <tb/tb.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>

static struct TextAttr MF = /* menu font */
  {"topaz.font",TOPAZ_SIXTY,FS_NORMAL,FPF_ROMFONT};

static struct IntuiText MT = /* template for menu item text */
  {0,1,JAM2,0,1,&MF,NULL,NULL};

static struct Menu *OldMenu = NULL; /* True BASIC's menu */
static struct Menu *FirstMenu = NULL; /* head of our menu list */
static struct Menu *CurrentMenu = NULL; /* tail of our menu list */
static struct MenuItem *CurrentMenuItem = NULL; /* tail of our item list */

#define MQSIZE 10 /* ten entries in the menu queue */

static UWORD menuq[MQSIZE]; /* the circular menu queue */
static UWORD *qinsert; /* insert pointer */
static UWORD *qdelete; /* delete pointer */
static UWORD *qend = menuq+MQSIZE; /* pointer past the end */

LONG SysBase;
LONG IntuitionBase;

void s_menu(arg,uv)
   LONG **arg;
   struct TBUserVector *uv;

  {struct TBInt *tbxip, *tbyip, *tbip;

   struct Window *wp;
   WORD result,code,x,y,z;
   void (*GiveandTake) ();
   extern void setselect(),getselect(),able(),freemenu();

   GiveandTake = (void (*)()) (*(((LONG *) uv)-1)); /* good old C */

   tbip = (struct TBInt *) *arg--; /* first arg: function code */
   code = tbip->integer;
   tbxip = (struct TBInt *) *arg--; /* second arg */
   x = tbxip->integer;
   tbyip = (struct TBInt *) *arg--; /* third arg */
   y = tbyip->integer;
   tbip = (struct TBInt *) *arg--; /* fourth arg */
   z = tbip->integer;
   tbip = (struct TBInt *) *arg--; /* fifth arg: result code */
   tbip->exponent = -1; /* result will be an integer */

   wp = uv->window;
   SysBase = uv->SysBase;
   IntuitionBase = uv->IntuitionBase;
   result = 0; /* assume success */

   switch(code)
     {case 0: /* initialize everything */
         if (OldMenu == NULL) /* if we need to take over from TB */
           {OldMenu = wp->MenuStrip; /* record old menu */
            (*GiveandTake) (4,setselect);} /* tell TB we're taking over */
         ClearMenuStrip(wp); /* clear strip, initialize queue */
         for (code=1,qinsert=menuq; code<=10; code++) *qinsert++ = MENUNULL;
         qinsert = qdelete = menuq;
         freemenu(); /* free previous private menu if any */
         break;
      case 1: /* add menu */
         result = addmenu(x,y,**arg);
         break;
      case 2: /* add item */
         result = additem(x,y,z,**arg);
         break;
      case 3: /* install menu */
         SetMenuStrip(wp,FirstMenu);
         break;
      case 4: /* report selection */
         getselect(tbxip,tbyip);
         break;
      case 5: /* enable */
         able(x,y,1,wp);
         break;
      case 6: /* disable */
         able(x,y,0,wp);
         break;
      case 7: /* return menu to TB */
         if (OldMenu) /* if we've taken over */
           {ClearMenuStrip(wp); /* clear our menu stuff */
            (*GiveandTake) (3,NULL); /* tell TB it has the menus now */
            SetMenuStrip(wp,OldMenu); /* restore TB menu */
            OldMenu = NULL;} /* remember TB is in charge */
         freemenu(); /* free private menu if any */
         break;}

   tbip->integer = result;} /* non-zero result means out of memory */


void freemenu()

  {register struct Menu *mp;
   register struct MenuItem *ip;
   register struct IntuiText *tp;
   register char *cp;

   mp = FirstMenu;
   while (mp) /* for each menu */
     {ip = mp->FirstItem;
      while (ip) /* for each item */
        {tp = (struct IntuiText *) ip->ItemFill;
         if (tp) /* if IntuiText structure was allocated */
           {cp = tp->IText;
            if (cp) FreeMem(cp,strlen(cp)+1); /* free text if allocated */
            FreeMem(tp,sizeof(struct IntuiText));} /* free IntuiText */
         cp = (char *) ip;
         ip = ip->NextItem;
         FreeMem(cp,sizeof(struct MenuItem));} /* free item */
      cp = mp->MenuName;
      if (cp) FreeMem(cp,strlen(cp)+1); /* free menu text if allocated */
      cp = (char *) mp;
      mp = mp->NextMenu;
      FreeMem(cp,sizeof(struct Menu));} /* free menu */

   FirstMenu = NULL; /* sppml */
   CurrentMenu = NULL;
   CurrentMenuItem = NULL;}


void setselect(im)
   struct IntuiMessage *im;

  {if (*qinsert != MENUNULL) return; /* queue full */
   if (im->Code == MENUNULL) return; /* null selection */
   
   *qinsert++ = im->Code; /* add to queue */
   if (qinsert == qend) qinsert = menuq;} /* circularize */
   

void getselect(tbxip,tbyip)
   struct TBInt *tbxip,*tbyip;

  {register WORD x;

   tbyip->exponent = tbxip->exponent = -1; /* these will be integers */
   tbyip->integer = tbxip->integer = 0; /* assume zero */

   if (*qdelete != MENUNULL) /* if queue not empty */
     {x = *qdelete; /* get entry */
      *qdelete++ = MENUNULL; /* this position empty now */
      if (qdelete == qend) qdelete = menuq; /* circularize */
      tbxip->integer = MENUNUM(x)+1; /* assign menu# and item# */
      tbyip->integer = ITEMNUM(x)+1;}}
   

void able(x,y,flag,wp)
   WORD x,y,flag;
   struct Window *wp;

  {if (y==0) y=NOITEM; /* 0 is NOITEM */
   else y-=1; /* Intuition menu and item #'s start at 0 */
   x-=1; 

   x = (SHIFTMENU(x) | SHIFTITEM(y));
   if (flag) OnMenu(wp,x); /* enable */
   else OffMenu(wp,x);} /* or disable */


addmenu(x,y,tbsp)
   WORD x,y;
   struct TBString *tbsp;

  {register struct Menu *Menu;
   extern char *copystring();

   Menu = (struct Menu *) AllocMem(sizeof(struct Menu),0); /* new menu */
   if (Menu == NULL) return (-1); /* no menu */

   if (CurrentMenu == NULL) FirstMenu = Menu; /* head of the list */
   else CurrentMenu->NextMenu = Menu; /* next in the list */
   CurrentMenu = Menu; /* in any case, tail of the list */

   Menu->NextMenu = NULL;
   Menu->LeftEdge = x; /* BASIC sent desired left edge */
   Menu->TopEdge = 0;
   Menu->Width = y; /* and width */
   Menu->Height = 0;
   Menu->Flags = MENUENABLED;
   Menu->MenuName = copystring(tbsp);
   if (Menu->MenuName == NULL) return (-1); /* couldn't copy name */

   CurrentMenuItem = NULL; /* start new item list */
   return (0);}


additem(x,y,z,tbsp)
   WORD x,y,z;
   struct TBString *tbsp;
 
  {register struct MenuItem *MenuItem;
   struct IntuiText *IntuiText;
   extern char *copystring();

   MenuItem = (struct MenuItem *) AllocMem(sizeof(struct MenuItem),MEMF_CLEAR);
   if (MenuItem == NULL) return (-1); /* new item, done if none */

   if (CurrentMenuItem == NULL) CurrentMenu->FirstItem = MenuItem; /* head of the list */
   else CurrentMenuItem->NextItem = MenuItem; /* next in the list */
   CurrentMenuItem = MenuItem; /* in any case, tail of the list */

   IntuiText = (struct IntuiText *) AllocMem(sizeof(struct IntuiText),0);
   if (IntuiText == NULL) return (-1); /* new IntuiText, done if none */
   movmem(&MT,IntuiText,sizeof(struct IntuiText)); /* copy from template */
   MenuItem->ItemFill = (APTR) IntuiText; /* link in now */

   IntuiText->IText = copystring(tbsp); /* copy name string */
   if (IntuiText->IText == NULL) return (-1); /* done if none */

   MenuItem->TopEdge = x; /* BASIC sent top edge */
   MenuItem->Width = y; /* and width */
   MenuItem->Height = 10; /* height is 10 for every item */
   if (z) MenuItem->Flags = COMMSEQ|ITEMTEXT|ITEMENABLED|HIGHCOMP;
   else MenuItem->Flags = ITEMTEXT|ITEMENABLED|HIGHCOMP;
   MenuItem->Command = z; /* BASIC sent command-key shortcut */
   return (0);}


char *copystring(tbsp)
   struct TBString *tbsp;

  {register LONG x;
   register char *cp;

   x = tbsp->length;
   cp = (char *) AllocMem(x+1,0); /* allocate space for the string */
   if (cp)
     {movmem(tbsp->text,cp,x); /* copy it */
      *(cp+x) = 0;} /* null-terminate */
   return(cp);}
