/*
  MENUBAR
    Macro to implement a simple menubar system for interacting with
    ME. You can change this so that the menu items invoke any editor
    primitive or any user-defined macro. This macro package relies
    on ME's feature of calling internal C functions in order to
    display the menubar and submenus.

    The menubar definiton should be in a file with an .MNU extension.
    In this macro, the CTRL-B key is used to bring up the menubar; you
    can change this key by altering the assign_key() call in the init()
    function here.
    
    The menubar definition must be in the following format :

    [optional comment lines]
    .....
    MENUBAR menu-name [COLOR color]
    {
      text-of-menubar-item-1
     [MENU
      {
        subpopup1-text     [->function-to-call]
      ......
      }]
    }
    
    Please note that items enclosed by square brackets are optional. In this
    case, the subpopup definition after a menubar item is optional. Also,
    the function to invoke after an item is chosed is optional. Look at the
    accompanying file MENUBAR.MNU for an actual menubar definition. The
    menubar definition starts with first line with the word MENUBAR starting 
    at column 1. 
    
    (C) Copyright 1988   Marc Adler/Magma Systems    All Rights Reserved
*/

#define HORIZ 1
#define VERT  2
#define TOP_LEFT  3
#define TOP_RIGHT 4
#define BOT_LEFT  5
#define BOT_RIGHT 6

#define DOWN    208
#define UP      200
#define LEFT    203
#define RIGHT   205

#define MAXMENUBARITEMS 16

string BoxChars[10];

int    MenuRow1, MenuCol1, MenuRow2, MenuCol2;
int    MenuColor;
string MenuName;
int    MenubarBuf;
int    MenubarItemBufID;

int debug;
int MenubarWindow;
int    MenuBuf[MAXMENUBARITEMS];
string MenuNames[MAXMENUBARITEMS];
int    PopupWindow[MAXMENUBARITEMS];
int NumMenus;
string MenubarText;
int CurrMenuItem;

int    OriginalFileBufID;


/*
  Init() - initialization function - done once when the macro is loaded
*/
init()
{
  /* Set up the values for box-drawing characters */
  BoxChars[HORIZ]     = "Í";
  BoxChars[VERT]      = "º";
  BoxChars[TOP_LEFT]  = "É";
  BoxChars[TOP_RIGHT] = "»";
  BoxChars[BOT_LEFT]  = "È";
  BoxChars[BOT_RIGHT] = "¼";
  
  /* Set the dimensions of the menubar */
  MenuRow1 = 1;
  MenuCol1 = 0;
  MenuRow2 = 3;
  MenuCol2 = 79;

  /* CTRL-B will invoke the default menubar macro */
  assign_key("do_default_menu", 2);
}

do_default_menu()
{
  do_menu("menubar");
}


/*
  Do_Menu() - driver for reading, parsing, and displaying a menubar. The
              passed argument is the root part of the menubar file name.
*/
do_menu(menuname)
  string menuname;
{
  debug = 0;
  menubar_already_here = 0;

  OriginalFileBufID = old_buf = currbuf();

  /* Open the Menubar definition file if it doesn't exist already. */
  if (!index(menuname, ".mnu"))
    menuname = strcat(menuname, ".mnu");

  if ((MenubarBuf = find_buffer(menuname)) <= 0)
    MenubarBuf = create_buffer(menuname);
  else
    menubar_already_here = 1;
  new_buf = setcurrbuf(MenubarBuf);
  setcol(1);
  vidsetcursor(32, 0);

  /* Parse the defn. If there's an error, abort. */
  if (!menubar_already_here)
  {
    if (MenuParse() < 0)
    {
      delete_buffer(new_buf);
      show_buffer(old_buf);
      return;
    }
  }
  
  /* Create a window for the menubar and attach the menubar buffer to it. */
  /* This is the ME paradigm for creating popup windows.                  */
  MenubarWindow = create_window(MenuRow1+1,MenuCol1+1,MenuRow2-1,MenuCol2-1,MenuColor);
  attach_window(MenubarWindow, new_buf);
  gobof();
  DrawBox(0, 0, 2, 79, MenuColor);

  /* Everything is displayed --- get the user input */
  MenubarProcess();

  /* After we're done, get rid of the menubar's window, */
  /* and go back to the file we were editing.           */
  remove_window(MenubarWindow);
  show_buffer(old_buf);
  refresh();
}


/*
  MenuParse() - parse the menubar defn which is in the menubar buffer.
                Returns 1 if everything went OK, -1 if there was an error.
*/
MenuParse()
{
  int old_buf, new_buf;

  /* Span all initial comment lines */
  while (index(currline(), "MENUBAR") != 1)
    if (!down())
      return -1;

  /* The first token should be the word "MENUBAR" */
  if (get_token() != "MENUBAR")
  {
    get_tty_str("The file is not a menubar definition file");
    return -1;
  }

  /* The next token is the name of the menubar */
  MenuName = get_token();
  
  /*
    The cursor is either at the COLOR or the name of the first item
  */
  if ((s = get_token()) == "COLOR")
    MenuColor = get_int_token();
  else
    MenuColor = 07;

  old_buf = currbuf();
  NumMenus = 1;

  while (1)
  {
    /* The token should be the name of the popup */
    go_past_func = index(currline(), "->");

    if ((mname = get_token()) == "" || mname == "}")
      break;
    /* After this, the token ptr should be at the first item of the popup */
    MenuNames[NumMenus] = mname;

    if (go_past_func)
    {
      down();
      gobol();
    }

    if (index(currline(), "MENU") > 0)
    {
      MenuBuf[NumMenus] = new_buf = create_buffer(strcat(mname, ".men"));
      down();
      if (index(ltrim(currline()), "{"))
      {
        down();
        gobol();
      }
    }
    else
      MenuBuf[NumMenus] = new_buf = 0;
    NumMenus++;

    if (debug)
      get_tty_str("The name of the popup is [%s]", mname);

    itemcnt = 1;

    if (new_buf)        /* We have a submenu */
    {
      /* Get each line until we encounter a line with a right curly brace. */
      /* Insert the entire line into the buffer which holds the submenu.   */
      while (index(cl = ltrim(currline()), "}") == 0)
      {
        if (debug)
          get_tty_str("Inserting item [%s]", cl);
        oldcol = currcol();
        setcurrbuf(new_buf);
        if (itemcnt > 1)
        {
          goeol();
          insert("\n");
        }
        gobol();
        insert(ltrim(cl));
        itemcnt++;

        setcurrbuf(old_buf);
        setcol(oldcol);
        if (!down())
          break;
        gobol();
      } /* end while */
      
      /* Move to the next line after the right curly brace */
      if (!down() || currline() == "")
        break;
      gobol();
    } /* end if submenu */
  } /* end while */
  
  /* We form the menubar's text line by concatinating the menubar items. */
  /* We then stick this line on top of the menubar buffer. Since the     */
  /* menubar window only shows 1 line, this is the line that we'll see.  */
  MenubarText = MenuNames[1];
  for (i = 2;  i <= NumMenus;  i++)
    MenubarText = sprintf("%s   %s", MenubarText, MenuNames[i]);
  gobof();
  insert(MenubarText);  insert("\n");
  gobol();
    
  /* Success!!! */
  return 1;
}


get_token()
{
  string s;

  if (is_eol() || currchar() == ' ')
    if (!nextword())
      return "";

  if (is_eof())
    return "";

  start = currcol();
  while (!is_eol() && currchar() != ' ')
    right();
  s = substr(currline(), start, currcol() - start);
  nextword();
  if (debug)
    get_tty_str(sprintf("GET_TOKEN returns [%s]", s));
  return s;
}

get_int_token()
{
  n = atoi(get_token());
  if (debug)
    get_tty_str(sprintf("GET_INT_TOKEN - n is %d", n));
  return n;
}

/* ------------------------- MENU PROCESSING ------------------------- */

#define ESC   27
#define UP    200
#define DOWN  208

MenubarProcess()
{
  CurrMenuItem = 1;

do_it:
  highlite_word(1);
  if (MenuBuf[CurrMenuItem])
  {
    DrawPopup(currcol());
    c = MenuProcess();
    clear_mark();
    if (PopupWindow[CurrMenuItem])
      remove_window(PopupWindow[CurrMenuItem]);
    if (c == '\r' || c == ESC)
      return;
    show_buffer(setcurrbuf(MenubarBuf));
    DrawBox(0, 0, 2, 79, MenuColor);
  }
  else
    c = get_tty_char();

  if (c != '\r' && c != ESC)
  {
    switch (c)
    {
      case LEFT :
        MenuLeft();
        break;
      case RIGHT :
        MenuRight();
        break;
      default   :
        bell();
        break;
    }
  }
  goto do_it;
}


MenuProcess()
{
  markline();

do_it:
  while ((c = get_tty_char()) != '\r' && c != ESC)
  {
    switch (c)
    {
      case UP   :
        MenuUp();
        break;
      case DOWN :
        MenuDown();
        break;
      case LEFT :
        return(LEFT);
      case RIGHT :
        return(RIGHT);
      default   :
        bell();
        break;
    }
  }
  
  if (c == '\r')
  {
    cln = currlinenum();

    if ((i = index(currline(), "->")) > 0)
    {
      macname = substr(currline(), i+2, 100);
      /*
        If we want to perform the macro on the original file we
        were editing, then we must switch back to the old buffer.
      */
      menubuf = currbuf();
      show_buffer(OriginalFileBufID);
      setprompt(2);
      domacro(macname);
      setprompt(0);
      setcurrbuf(menubuf);
    }

    if (debug)
      get_tty_str("After indirect call - before DrawBox");
    DrawBox(MenuRow1-1, MenuCol1, MenuRow2-1, MenuCol2, MenuColor);
/*
    goto do_it;
*/
  }
  
  return c;
}



/*
  DrawPopup() - displays a submenu
*/
DrawPopup(startcol)
{
/*
 BUGS - figure out how wide the submenu should be
*/
  setcurrbuf(MenuBuf[CurrMenuItem]);
  x1 = startcol - 1;
  x2 = x1 + 16;
  wid = create_window(4, x1+1, lastlinenum()+3, x2-1, 7);
  attach_window(wid, MenuBuf[CurrMenuItem]);
  PopupWindow[CurrMenuItem] = wid;
  gobof();
  DrawBox(2, x1, lastlinenum()+3, x2, MenuColor);
}

/*
  HighliteWord() - draws the current word with highliting (if rev is 1)
*/
highlite_word(rev)
{
  /* Determine the proper attribute to draw the word with */
  if (rev)
    rev = (MenuColor % 16) * 16 + (MenuColor / 16);
  else
    rev = MenuColor;

  /* Get the starting & ending columns of the word */
  col = currcol();
  endpos = index(substr(currline(), col, 100), " ") + col - 1;
  word = substr(currline(), col, endpos - col);

  /* Redraw the word with the specified attribute */
  display(1, col, endpos-1, rev, word);
}


/*
  MenuLeft() - move left one item in the menubar
*/
MenuLeft()
{
  highlite_word(0);
  if (!is_bol())
    CurrMenuItem--;
  prevword();
  highlite_word(1);
}

/*
  MenuRight() - move right one item in the menubar
*/
MenuRight()
{
  if (index(substr(currline(), currcol(), 100), " ") > 0)
  {
    highlite_word(0);           /* remove highliting */
    if (!is_eol())
      CurrMenuItem++;
    nextword();                 /* move to the next item */
    highlite_word(1);           /* highlite the new item */
  }
}

/*
  MenuUp() - move up one line in the submenu
*/
MenuUp()
{
  if (currlinenum() > 1)
  {
    clear_mark();       /* remove highlite from the old line */
    up();
    markline();         /* highlight the new line */
  }
}

/*
  MenuDown() - move down one line in the submenu
*/
MenuDown()
{
  if (currlinenum() < lastlinenum())
  {
    clear_mark();       /* remove highlite from the old line */
    down();
    markline();         /* highlight the new line */
  }
}


/*
  DrawBox() - draws a box using the passed coordinates and attribute.
*/
DrawBox(row1, col1, row2, col2, color)
{
  width = col2 - col1 + 1;
  horiz_line = repstr(BoxChars[HORIZ], width - 2);
  blank_line = repstr(" ", width - 2);

  display(row1, col1 + 1, col1 + width - 1,  color, horiz_line);
  display(row2, col1 + 1, col1 + width - 1,  color, horiz_line);
  for (r = row1+1;  r < row2;  r++)
  {
    display(r, col1, col1, color, BoxChars[VERT]);
    display(r, col2, col2, color, BoxChars[VERT]);
  }
  display(row1, col1, col1,  color, BoxChars[TOP_LEFT]);
  display(row1, col2, col2,  color, BoxChars[TOP_RIGHT]);
  display(row2, col1, col1,  color, BoxChars[BOT_LEFT]);
  display(row2, col2, col2,  color, BoxChars[BOT_RIGHT]);
}

