#include "project.h"
#ifdef PROJECT_VOPTS
   #include "vopts.h"
#endif
#ifdef PROJECT_VMAKE
   #include "vmake.h"
#endif
#include <proto/gadtools.h>

Prototype void enable_menu(void);
Prototype struct Menu *create_menus(struct NewMenu *menulist);
Prototype int layout_menus(struct Menu *menus);
Prototype void free_menus(struct Menu *menus);
Prototype void ghost_menus(void);
Prototype void unghost_menus(void);

/***********************************************************************************
 * Procedure: enable_menu
 * Synopsis:  enable_menu();
 * Purpose:   Attach any system defined menus to the current window.
 ***********************************************************************************/
void enable_menu()
{
   int i, level, newlev, count[4];
   struct NewMenu *newmenu;

   if (global.window == NULL) return;

   /* Let us go through and validate the menu they have constructed */
   level = 0;
   newmenu = global.menuitem;
   for (i = 0; i < MAX_MENU; i++)
   {
      newlev = newmenu[i].nm_Type;
      if (newlev > level)
      {
         level++;
         /* Make sure we are going down only one level */
         if (newmenu[i].nm_Type != level || level > MENU_SUB) return;
         count[level] = 0; /* Initialize our count */
      }
      else if (newlev == level)
      {
         /* Make sure we don't have two TITLES in a row */
         if (level == MENU_MENU) return;
         count[level]++;
      }
      else
      {
         /* We are going up a level.  Validate the counts on the way up */
         if (newlev < MENU_END) return;
         while (level > newlev)
         {
            if (count[level] >= "!\x20\x40\x20"[level])
               return;
            else
               level--;
         }
         /* When we hit the end of the menus, break */
         if (level == 0) break;
         count[level]++;
      }

   }
   /* Lastly ensure that they had an END marker */
   if (level) return;

   if (!(global.menu = create_menus(newmenu)))
      return;

   if (!layout_menus(global.menu))
      return;

   SetMenuStrip(global.window, global.menu);
}

/***********************************************************************************
 * Procedure: create_menus
 * Synopsis:  Menu = create_menus(NewMenu);
 * Purpose:   Create the appropriate menu structures for a given NewMenu list
 ***********************************************************************************/
struct Menu *create_menus(struct NewMenu *menulist
                         )
{
   int i;
   struct Menu   *retmenu;
   struct XMenu  *thismenu;
   struct XItem  *thisitem;
   static struct Image sep_image = {5, 1, 1000, 2,0, NULL, 0,0, NULL};


   if (GadToolsBase != NULL)
   {
      struct TagItem taglist[2];

      taglist[0].ti_Tag = GTMN_FrontPen;
      taglist[0].ti_Data = 0;
      taglist[1].ti_Tag = TAG_DONE;

      return(CreateMenusA(menulist, taglist));
   }

   /* We don't have GadTools around to do the work for us, so just create the */
   /* Menu structures directly.                                               */

   retmenu = NULL;  /* Default to giving them NOTHING */

   for (i = 0; i < MAX_MENU; i++)
   {
      switch (menulist[i].nm_Type)
      {
         case NM_TITLE:
            {
               struct XMenu  *newmenu;

               newmenu = (struct XMenu *)get_mem(sizeof(struct XMenu));
               if (newmenu == NULL)
               {
                  free_menus(retmenu);
                  return(NULL);
               }

               /* Link the menu structure onto the list of other menus */
               if (retmenu == NULL)
                  retmenu = &newmenu->menu;
               else
                  thismenu->menu.NextMenu = &newmenu->menu;

               thismenu = newmenu;
            }

            thismenu->menu.Flags    = MENUENABLED;
            thismenu->menu.MenuName = (BYTE *)menulist[i].nm_Label;
            thismenu->userdata      = menulist[i].nm_UserData;
            thismenu->image         = sep_image;
            break;

        case NM_ITEM:
            if (thismenu == NULL) return(NULL);

            {
               struct XItem  *newitem;

               newitem = (struct XItem *)get_mem(sizeof(struct XItem));
               if (newitem == NULL)
               {
                  free_menus(retmenu);
                  return(NULL);
               }
               if (thismenu->menu.FirstItem == NULL)
                  thismenu->menu.FirstItem = &newitem->item;
               else
                  thisitem->item.NextItem  = &newitem->item;
               thisitem = newitem;
            }

            if (menulist[i].nm_Label == NM_BARLABEL)
            {
               thisitem->item.Flags        = 0;
               thisitem->item.ItemFill     = &thismenu->image;
            }
            else
            {
               /* fill in the intuitext with some reasonable defaults */

               thisitem->itext.FrontPen    = 0;
               thisitem->itext.DrawMode    = JAM1;
               thisitem->itext.LeftEdge    = 5;
               thisitem->itext.TopEdge     = 1;
               thisitem->itext.ITextFont   = &global.ri.TextAttr;
               thisitem->itext.IText       = menulist[i].nm_Label;

               thisitem->item.Flags        = ITEMTEXT|ITEMENABLED|HIGHCOMP;
               thisitem->item.ItemFill     = (APTR)&thisitem->itext;

               if (menulist[i].nm_CommKey)
               {
                  thisitem->item.Command   = *menulist[i].nm_CommKey;
                  thisitem->item.Flags    |= COMMSEQ;
               }
            }
            thisitem->userdata = menulist[i].nm_UserData;
            break;

         case NM_END:
             return(retmenu);
      }
   }
   return(retmenu);
}

/***********************************************************************************
 * Procedure: layout_menus
 * Synopsis:  rc = layout_menus(Menu);
 * Purpose:   Lays out a menu given the current global information
 ***********************************************************************************/
int layout_menus(struct Menu *menus)
{
   struct XMenu     *menu;
   struct XItem     *item;
   int              xpos;

   if (GadToolsBase != NULL)
   {
      struct VisualInfo *vi;
      struct TagItem taglist;
      int rc;

      taglist.ti_Tag = TAG_DONE;

      rc = 0;
      if ((vi = GetVisualInfoA(global.window->WScreen, &taglist)))
      {
         rc = LayoutMenusA(global.menu, vi, &taglist);
         FreeVisualInfo(vi);
      }
      return(rc);
   }

   xpos = VBAR;

   for (menu = (struct XMenu *)menus; menu;
        menu = (struct XMenu *)menu->menu.NextMenu)
   {
      int width, ypos;

      /* set the x position of the menu to the next available position.
       * Set the menu width based on the size of the text.
       */

      menu->menu.LeftEdge = xpos;
      menu->menu.Width    = text_width(menu->menu.MenuName) + DVBAR;

      /* set the initial item y-position to just below the title bar */

      ypos = 0;

      /* walk through the list of items for the first pass. The object
       * is to find the width of the largest item. We will also set
       * the y-position of each item on this pass.
       */

      width = menu->menu.Width;    /* make items at least as big as menu */

      for (item = (struct XItem *)menu->menu.FirstItem;
           item;
           item = (struct XItem *)item->item.NextItem)
      {
         int  iwidth;

         /* set the y-position of the item to the next available. Also,
          * put the item just to the left of the menu. You can make
          * this number zero if you prefer.
          */

         item->item.TopEdge = ypos;
         item->item.LeftEdge = -VBAR;

         /* if it's a text item, see how big it is. */

         if (item->item.Flags & ITEMTEXT)
         {
            struct IntuiText *itext;

            itext = (struct IntuiText *)item->item.ItemFill;

            /* the height is just the font height + 2. The width is
             * based on the string pixel width.
             */

            item->item.Height = global.ri.FontSize + 2;
            iwidth = text_width(itext->IText) + 10;

            /* if it has a command-key sequence, then add in the space needed
             * for the command key and the command symbol. Note that in
             * lo-res, we should actually use LOWCOMMWIDTH, but I'm
             * feeling lazy today.
             */

            if (item->item.Flags & COMMSEQ)
            {
               char buf[2];

               buf[0] = item->item.Command;
               buf[1] = 0;
               iwidth += text_width(buf) + COMMWIDTH + 8;
            }
         }
         else
         {
            /* separator bars are always the same height */
            item->item.Height = 4;
            iwidth = 0; /* nasty things happen in Dos 1.3 if not initialized */
         }

         /* update the y-position variable. Make sure the items don't
          * run off the bottom.
          */

         ypos  += item->item.Height;
         if (ypos >= (global.ri.ScreenHeight - DHBAR))
            return(0);

          /* now see if this item is the largest one. */
          if (iwidth > width) width = iwidth;
      }

      /* on our second pass, we set the widths of all the items to the
       * width of the largest item so that it looks good. In addition,
       * we are going to adjust menus that might run off the right edge.
       */

      for (item = (struct XItem *)menu->menu.FirstItem;
          item;
          item = (struct XItem *)item->item.NextItem)
      {
         item->item.Width = width;
         if ((xpos + width) >= (global.ri.ScreenWidth - 2))
            item->item.LeftEdge =
                   global.ri.ScreenWidth - 2 - xpos - width;
      }

      menu->image.Width = width - 7;

      /* now update the x position variable, make sure that the menu
       * header is not off the edge of the screen, and add a little space
       * between the headers. Feel free to make this number whatever
       * you want. If you are feeling really ambitious, you could
       * make this number adaptable.
       */
      xpos += menu->menu.Width;
      if (xpos > global.ri.ScreenWidth) return(NULL);
      xpos += 8;
   }
   return(TRUE);
}

/***********************************************************************************
 * Procedure: free_menus
 * Synopsis:  free_menus(Menu);
 * Purpose:   Return any storage allocated for a given Menu structure.
 ***********************************************************************************/
void free_menus(struct Menu *menus)
{
   struct XMenu *menu, *nextmenu;
   struct XItem *item, *nextitem;

   if (global.window)
      ClearMenuStrip(global.window);

   if (!menus) return;

   if (GadToolsBase != NULL)
   {
      FreeMenus(menus);
   }

   else
   {
      for (menu = (struct XMenu *)menus; menu; menu = nextmenu)
      {
         nextmenu = (struct XMenu *)menu->menu.NextMenu;
         for (item = (struct XItem *)menu->menu.FirstItem;
              item;
              item = nextitem)
         {
            nextitem = (struct XItem *)item->item.NextItem;
            free_mem(item, sizeof(struct XItem));
         }
         free_mem(menu, sizeof(struct XMenu));
      }
   }
   global.menu = NULL;
}


/***********************************************************************************
 * Procedure: ghost_menus
 * Synopsis:  ghost_menus();
 * Purpose:   disable all menu entries except Open and New  
 ***********************************************************************************/
void ghost_menus(void)
{
   int i;
   char *udp;

   free_menus(global.menu);

   for (i = 0; i < MAX_MENU; i++)
   {

      if (global.menuitem[i].nm_Type == NM_ITEM) /* only ghost entries */
      {
         /* to allow flexible menu configuration, base our checks on   */
         /* what the menu entry is programmed to do.  We allow READ,   */
         /* NEW or QUIT when no project is loaded, ghost anything else.*/
         udp = global.menuitem[i].nm_UserData;
         if (udp) /* Menu bars don't have user data */
            if ((strnicmp(udp, "read", 4) != 0)
               && (strnicmp(udp, "new",  3) != 0)
               && (strnicmp(udp, "quit", 4) != 0))
               global.menuitem[i].nm_Flags |= NM_ITEMDISABLED;
      }
   }

   enable_menu();
}
 

/***********************************************************************************
 * Procedure: unghost_menus
 * Synopsis:  unghost_menus();
 * Purpose:   reenable all menu entries (undo effect of ghost_menus())  
 ***********************************************************************************/
void unghost_menus(void)
{
   int i;

   free_menus(global.menu);

   for (i = 0; i < MAX_MENU; i++)
   {
      global.menuitem[i].nm_Flags &= ~NM_ITEMDISABLED;
   }

   enable_menu();
}
