
/* "UNTIL", a graphics editor,
   Copyright (C) 1985, 1990 California Institute of Technology.
   Original authors: Glenn Gribble, port by Steve DeWeerth
   Unix Port Maintainer: John Lazzaro
   Maintainers's address: lazzaro@hobiecat.cs.caltech.edu;
                          CB 425 CU Boulder/Boulder CO 91125. 

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation (Version 1, 1989).

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
USA. */


/*******************************************************************************/
/*                                                                             */ 
/*  menu handling procedures                                                   */
/*  cleaned up by steve - 9 May 1990                                           */
/*                                                                             */ 
/*******************************************************************************/

#include <p2c/p2c.h>

#include "menu.h"

#ifndef NEWASM_H
#include <p2c/newasm.h>
#endif

#ifndef SYSDEVS_H
#include <p2c/sysdevs.h>
#endif


/* exported global variables */

boolean quit;
long botOfMenuBar, leftOfMenu, rightOfMenu;

menux *head, *curr;
Pixmap saveArea;
fixedColorRec fixedColor;
void *fixedMenuHandler;
fixedMenuRec fixedMenu[maxFixed];



Static void setPlotArea(void)
{
  if (leftOfMenu > m_across / 2) {
    plotArea.xh = leftOfMenu - 2;
    plotArea.xl = 0;
  } else {
    plotArea.xh = m_across;
    plotArea.xl = rightOfMenu + 2;
  }
  plotArea.yl = 0;
  plotArea.yh = botOfMenuBar - 2;
}


void menu_clip_on(void)
{
  setPlotArea();
  m_clip(plotArea.xl, plotArea.yl, plotArea.xh, plotArea.yh);
}


void menu_clip_off(void)
{
  m_noclip();
}


void menu_show(void)
{
  gr_bright();
  m_nocursor();
  m_linestyle(0);
  m_choosefont(1);
  m_linewidth(0);
  m_colormode(m_normal);
  menu_clip_off();

  drawMenuBar();
  drawFixedMenu();

  menu_clip_on();
}


void initPull(void)
{
  head = NULL;
  curr = NULL;
  botOfMenuBar = m_down - chHi;
  leftOfMenu = 0;
  rightOfMenu = leftOfMenu + 64;
}


void startMenu(Char *name)
{
  menux *mp;

  curr = Malloc(sizeof(menux));
  if (head == NULL)
    head = curr;
  else {
    mp = head;
    while (mp->next != NULL)
      mp = mp->next;
    mp->next = curr;
  }
  curr->next = NULL;
  sprintf(curr->title, "  %s  ", name);
  curr->elems = NULL;
  curr->selected = false;
}


void addElem(Char *name, Char *key, void *proc)
{
  menuElem *ep, *newElem;

  newElem = Malloc(sizeof(menuElem));
  if (curr == NULL)
    printf("AddElem while curr=NIL!\n");
  if (curr->elems == NULL)
    curr->elems = newElem;
  else {
    ep = curr->elems;
    while (ep->next != NULL)
      ep = ep->next;
    ep->next = newElem;
  }
  newElem->next = NULL;
  strcpy(newElem->name, name);
  newElem->active = true;
  newElem->color = menuForeColor;
  strcpy(newElem->keq, key);
  newElem->proc = proc;
}


void outString(long x, long y, long field, long foreColor, long backColor, Char *s)
{
  m_nocursor();
  if (field <= 0)
    field = m_strwidth(NULL, s);

  m_colormode(m_normal);
  m_color(backColor);
  m_fillrect(x, y, x + field - 1, y + chHi);
  m_clip(x, y, x + field - 1, y + chHi);   /**/
  m_color(foreColor);
  m_drawstr(x, y + chOff, NULL, s);
  m_noclip();
}


void outCString(long x, long y, long field, long foreColor, long backColor, Char *s)
{
  long f2;

  m_nocursor();
  if (field <= 0)
    field = m_strwidth(NULL, s);
  f2 = field / 2;
  field -= f2;

  m_colormode(m_normal);
  m_color(backColor);
  m_fillrect(x - f2, y, x + field - 1, y + chHi);
  m_clip(x - f2, y, x + field - 1, y + chHi);
  m_color(foreColor);
  m_centerstr(x, y + chOff, NULL, s);
  m_noclip();
}


void rightStr(long x, long y, Char *s)
{
  m_rightstr(x, y + chOff, NULL, s);
}


Void getcpicture(int x1, int y1, int x2, int y2, Pixmap *saveArea)
{
  GC gc;
  int x, y, width, height, depth;

  gc = XCreateGC(m_display, m_window, 0, NULL);

  x = (x1 <= x2) ? x1 : x2;
  y = (y1 <= y2) ? m_down - y2 : m_down - y1;
  width = (x1 <= x2) ? x2 - x1 : x1 - x2;
  height = (y1 <= y2) ? y2 - y1 : y1 - y2;
  depth = DefaultDepth(m_display, DefaultScreen(m_display));

  *saveArea = XCreatePixmap(m_display, m_window, width, height, depth);
  XCopyArea(m_display, m_window, *saveArea, gc, x, y, width, height, 0, 0);
}


Void putcpicture(int x1, int y1, int x2, int y2, Pixmap saveArea)
{
  GC gc;
  int x, y, width, height;

  gc = XCreateGC(m_display, m_window, 0, NULL);

  x = (x1 <= x2) ? x1 : x2;
  y = (y1 <= y2) ? m_down - y2 : m_down - y1;
  width = (x1 <= x2) ? x2 - x1 : x1 - x2;
  height = (y1 <= y2) ? y2 - y1 : y1 - y2;

  XCopyArea(m_display, saveArea, m_window, gc, 0, 0, width, height, x, y);
}


Void disposepicture(Pixmap *saveArea)
{
  XFreePixmap(m_display, *saveArea);
}


void saveRegion(long x1, long y1, long x2, long y2, region *r)
{
  long t;

  if (x1 > x2) {
    t = x2;
    x2 = x1;
    x1 = t;
  }
  if (y1 < y2) {
    t = y2;
    y2 = y1;
    y1 = t;
  }
  getcpicture(x1, y1, x2, y2, &r->saveArea);
  r->x = x1;
  r->y = y1;
}


void restoreRegion(region *r)
{
  putcpicture(r->x, r->y, r->x + r->width, r->y + r->height, r->saveArea);
  disposepicture(&r->saveArea);
}


void startDialog(dialogRec *d)
{
  m_nocursor();
  saveRegion(d->baseX - 2, d->baseY - 2, d->baseX + d->width + 2,
	     d->baseY + d->height + 2, &d->savedRegion);
  m_colormode(m_normal);
  m_color(d->backColor);
  m_fillrect(d->baseX - 2, d->baseY - 2, d->baseX + d->width + 2,
	     d->baseY + d->height + 2);
  m_color(menuForeColor);
  m_drawrect(d->baseX - 1, d->baseY - 1, d->baseX + d->width + 1,
	     d->baseY + d->height + 1);
}


void finishDialog(dialogRec *d)
{
  restoreRegion(&d->savedRegion);
}


void drawMenuBar(void)
{
  menux *mp;
  short w, x, y;

  m_nocursor();
  y = botOfMenuBar;
  m_choosefont(1);
  m_colormode(m_normal);
  x = 0;
  mp = head;
  while (mp != NULL) {
    w = m_strwidth(NULL, mp->title);
    if (mp->selected) {
      outString(x, y, w, menuSelectForeColor, menuSelectColor, mp->title);
    }
    else {
      outString(x, y, w, menuForeColor, menuBackColor, mp->title);
    }
    if (x == 0)
      mp->lx = 1;
    else
      mp->lx = x;
    x += w;
    mp->hx = x - 1;
    mp = mp->next;
  }
  m_color(menuBackColor);
  m_fillrect(x, botOfMenuBar, 1024, 1024);
}


void pull(menux *mp)
{
  menuElem *ep;
  short w, h, nw;
  boolean keqPresent;

  m_nocursor();
  m_choosefont(1);

  w = 0;
  h = 0;
  keqPresent = false;
  ep = mp->elems;
  while (ep != NULL) {
    h += chHi;
    nw = m_strwidth(NULL, ep->name) + m_strwidth(NULL, ep->keq);
    if (*ep->keq != '\0')
      keqPresent = true;
    ep->changed = true;
    ep->selected = false;
    if (nw > w)
      w = nw;
    ep = ep->next;
  }
  if (keqPresent)   /* min separation */
    w += 6;

  mp->width = w + 1;   /* allow a border pixel */
  mp->height = h + 2;   /* one pixel above and below */

  getcpicture(mp->lx - 1, botOfMenuBar - mp->height - 1, 
              mp->lx + mp->width, botOfMenuBar - 1, &saveArea); 

  m_colormode(m_normal);
  m_color(menuForeColor);
  m_drawrect(mp->lx - 1, botOfMenuBar - mp->height - 1, mp->lx + mp->width, botOfMenuBar - 1);

  m_color(menuBackColor);
  m_fillrect(mp->lx, botOfMenuBar - mp->height, mp->lx + mp->width - 1, botOfMenuBar - 2);
}


void unpull(menux *mp)
{
  m_nocursor();
  /* m_ removed by steve 3 May 1990 */
  putcpicture(mp->lx - 1, botOfMenuBar - mp->height - 1, 
              mp->lx + mp->width, botOfMenuBar - 1, saveArea); 
  disposepicture(&saveArea);
}


void pullUpdate(menux *mp)
{
  menuElem *ep;
  short x, y;

  m_nocursor();
  m_choosefont(1);

  y = botOfMenuBar - chHi - 2;
  x = mp->lx;
  ep = mp->elems;
  while (ep != NULL) {
    if (ep->changed) {
      if (*ep->name != '\0') {
	if (ep->selected)
	  outString(x, y, mp->width, menuSelectForeColor, menuSelectColor,
		    ep->name);
	else
	  outString(x, y, mp->width, menuForeColor, menuBackColor, ep->name);
	/* Note: foreground color will be left ok for next operation */
	rightStr(x + mp->width - 2, y, ep->keq);
      } else {
	m_color(menuForeColor);
	m_linestyle(7);
	m_drawline(x, y + chHi / 2, x + mp->width - 1, y + chHi / 2);
	m_linestyle(0);
      }
    }
    ep->changed = false;
    ep->hy = y + chHi - 1;
    ep->ly = y;
    y -= chHi;
    ep = ep->next;
  }
}


void about(void)
{
  dialogRec d;
  m_tablet_info p;

  d.baseX = 100;
  d.baseY = 100;
  d.width = 312;
  d.height = 200;
  d.backColor = 7;

  startDialog(&d);
  outString(d.baseX + 80, d.baseY + 180, 0, 1, 3, "Until by Glenn");
  outString(d.baseX + 4, d.baseY + 120, 0, 0, 7,
	    "Lots and lots of new things have been added");
  outString(d.baseX + 4, d.baseY + 100, 0, 4, 7,
	    "New features: Hex grid, TeX-Labels, Drag-select boxes");
  outString(d.baseX + 4, d.baseY + 80, 0, 2, 7,
	    "(and a new user-interface, not all of which is complete)");
  outString(d.baseX + 60, d.baseY + 5, 0, 0, 7, "[press pen down to return]");
  do {
    m_trackpen(&p);
  } while (!p.dn);
  finishDialog(&d);
}


Local void findMenu(m_tablet_info p, menux **mp)
{
  *mp = head;
  while (*mp != NULL && p.x > (*mp)->hx)
    *mp = (*mp)->next;
}

Local void findElem(m_tablet_info p, menux *mp, menuElem **ep)
{
  /* Check X-range */
  if (p.x < mp->lx || p.x >= mp->lx + mp->width) {
    *ep = NULL;
    return;
  }

  *ep = mp->elems;

  while (*ep != NULL && p.y < (*ep)->ly)
    *ep = (*ep)->next;
}

void pullDownMenus(m_tablet_info p)
{
  menux *mp;
  menuElem *ep;

  menux *omp, *smp;
  menuElem *oep, *sep;
  short i, j;

  smp = NULL;
  sep = NULL;
  omp = NULL;
  do {
    if (p.y >= botOfMenuBar) {
      findMenu(p, &mp);
      if (mp != omp) {
	if (omp != NULL) {
	  omp->selected = false;
	  unpull(omp);
	}
	if (mp != NULL) {
	  oep = NULL;
	  ep = NULL;
	  mp->selected = true;
	  drawMenuBar();  /* added by steve - 4 May 1990 */
	  pull(mp);
	  pullUpdate(mp);
	}
	drawMenuBar();  /* should be removed, but screws up cursor - steve */
      } else {  /* Same menu, nuke ep */
	ep = NULL;
	if (oep != NULL) {
	  oep->selected = false;
	  oep->changed = true;
	  pullUpdate(mp);
	}
	oep = NULL;
      }
    } else if (mp != NULL) {
      findElem(p, mp, &ep);
      if (ep != oep) {
	if (oep != NULL) {
	  oep->selected = false;
	  oep->changed = true;
	}
	if (ep != NULL) {
	  ep->selected = true;
	  ep->changed = true;
	}
	pullUpdate(mp);
	oep = ep;
      }
    }
    do {
      m_trackpen(&p);
    } while (!p.moving);
    omp = mp;
  } while (!p.up);
  if (omp != NULL) {
    if (ep != NULL) {   /* Flash EP */
      for (i = 1; i <= 4; i++) {
	for (j = 1; j <= 5000; j++) ;
	ep->selected = false;
	ep->changed = true;
	pullUpdate(omp);
	for (j = 1; j <= 5000; j++) ;
	ep->selected = true;
	ep->changed = true;
	pullUpdate(omp);
      }

      smp = omp;
      sep = ep;
    }

    omp->selected = false;
    unpull(omp);
  }
  drawMenuBar();

  if (sep != NULL)
    ((void(*)(void))sep->proc)();
}


void drawFixedColors(void)
{
  short i, j, ii, jj, c;

  m_nocursor();
  for (j = 1; j <= 4; j++) {
    for (i = 0; i <= 3; i++) {
      c = i + j * 4 - 4;
      ii = fixedColor.x + i * fixedColor.wid;
      jj = fixedColor.y + (4 - j) * fixedColor.wid;

      if (c == 0) {
	m_color(menuSelectColor);
	m_drawrect(ii - 1, jj - 1, ii + fixedColor.wid - 3,
		   jj + fixedColor.wid - 3);
	m_color(curColor);
	m_fillrect(ii + 1, jj + 1, ii + fixedColor.wid - 5,
		   jj + fixedColor.wid - 5);
      } else {
	if (c == curColor)
	  m_color(menuSelectColor);
	else
	  m_color(menuForeColor);
	m_drawrect(ii - 1, jj - 1, ii + fixedColor.wid - 3,
		   jj + fixedColor.wid - 3);
	m_color(c);
	m_fillrect(ii + 1, jj + 1, ii + fixedColor.wid - 5,
		   jj + fixedColor.wid - 5);
	if (c == 15) {
	  m_color(0);
	  m_drawline(ii + 1, jj + 1, ii + fixedColor.wid - 5,
		     jj + fixedColor.wid - 5);
	  m_drawline(ii + 1, jj + fixedColor.wid - 5, ii + fixedColor.wid - 5,
		     jj + 1);
	}
      }
    }
  }
}


Local void hLine(short *y)
{
  *y -= 2;
  m_color(menuForeColor);
  m_drawline(rightOfMenu, *y, leftOfMenu, *y);
  *y--;
}

Local void fixedEntry(Char *s, short w, short *f, short *y)
{
  fixedMenu[*f].hy = *y - 1;
  *y += -chHi - 1;
  fixedMenu[*f].ly = *y;

  if (fixedMenu[*f].changed) {
    if (fixedMenu[*f].sel)
      outCString((leftOfMenu + rightOfMenu) / 2, *y, w - 4, menuSelectForeColor, menuSelectColor, s);
    else
      outCString((leftOfMenu + rightOfMenu) / 2, *y, w - 4, menuForeColor, menuBackColor, s);

    fixedMenu[*f].changed = false;
  }

  (*f)++;
}

void updateFixedMenu(void)
{
  short f, y, w;

  f = 0;

  m_nocursor();
  m_colormode(m_normal);

  w = rightOfMenu - leftOfMenu + 1;
  y = botOfMenuBar - 2;

  fixedEntry(curFile->fileName, w, &f, &y);
  fixedEntry(curFigure->name, w, &f, &y);
  hLine(&y);

  fixedColor.x = leftOfMenu + 3;
  fixedColor.y = y - 60;
  fixedColor.wid = 15;

  y = fixedColor.y - 2;

  hLine(&y);
  fixedEntry("Edit", w, &f, &y);
  fixedEntry("Ed-AddPnt", w, &f, &y);
  fixedEntry("Ed-DelPnt", w, &f, &y);
  fixedEntry("Ed-Extend", w, &f, &y);
  fixedEntry("Copy", w, &f, &y);
  fixedEntry("Move", w, &f, &y);
  fixedEntry("Delete", w, &f, &y);
  fixedEntry("Chng Color", w, &f, &y);
  fixedEntry("Flatten", w, &f, &y);

  hLine(&y);
  fixedEntry("Add Inst", w, &f, &y);
  fixedEntry("Undelete", w, &f, &y);
  fixedEntry("Zoom", w, &f, &y);

  hLine(&y);
  fixedEntry("Arrow", w, &f, &y);
  fixedEntry("Line", w, &f, &y);
  fixedEntry("Curve", w, &f, &y);
  fixedEntry("Box", w, &f, &y);
  fixedEntry("Dot", w, &f, &y);
  fixedEntry("Circle", w, &f, &y);
  fixedEntry("Ellipse", w, &f, &y);
  fixedEntry("Polygon", w, &f, &y);
  fixedEntry("Text", w, &f, &y);
}


void drawFixedMenu(void)
{
  short f;

  for (f = 0; f < maxFixed; f++)
    fixedMenu[f].changed = true;

  m_nocursor();
  m_colormode(m_normal);
  m_color(menuBackColor);
  m_fillrect(leftOfMenu, botOfMenuBar - 2, rightOfMenu, 0);

  updateFixedMenu();
  drawFixedColors();
}


void doFixedMenu(m_tablet_info p)
{
  short newColor, f;

  /* Check for colors */
  if (p.x >= fixedColor.x && p.x <= fixedColor.x + fixedColor.wid * 4 &&
      p.y >= fixedColor.y && p.y <= fixedColor.y + fixedColor.wid * 4) {
    newColor = (p.x - fixedColor.x) / fixedColor.wid +
          (3 - (p.y - fixedColor.y) / fixedColor.wid) * 4;
    if (newColor >= 1 && newColor <= 15) {
      curColor = newColor;
      drawFixedColors();
    }
    return;
  }
  f = 1;
  while (f <= maxFixed && fixedMenu[f - 1].ly > p.y)
    f++;
  if (f > maxFixed || fixedMenu[f - 1].hy < p.y)
    f = 0;

  if (f != 0)
    ((void(*)(long f))fixedMenuHandler)(f);
}


#define tThresh         25   /* 1/4 second */
#define pThresh         5   /* max 5 pixels */


void clickOrDrag(m_tablet_info p)
{
  long start;
  m_tablet_info np, op;

  start = sysclock();
  m_colormode(m_xor);
  m_color(15);

  np = p;
  do {
    op = np;
    m_drawrect(p.x, p.y, op.x, op.y);
    do {
      m_trackpen(&np);
    } while (!np.moving);
    m_drawrect(p.x, p.y, op.x, op.y);
  } while (!np.up);

  m_colormode(m_normal);
  if (sysclock() - start < tThresh && labs(p.x - np.x) < pThresh &&
      labs(p.y - np.y) < pThresh) {
    m_color(p.x & 15);
    m_drawline(p.x, p.y - 10, p.x, p.y + 10);
    m_drawline(p.x - 10, p.y, p.x + 10, p.y);
    return;
  }
  m_color(p.y & 15);
  m_fillrect(p.x, p.y, np.x, np.y);
  m_color(15 - (p.y & 15));
  m_drawrect(p.x, p.y, np.x, np.y);
}

#undef tThresh
#undef pThresh
