
/* "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. */


/*******************************************************************************/
/*                                                                             */ 
/*  until main program file                                                    */
/*  cleaned up by steve - 10 May 1990                                          */
/*                                                                             */ 
/*******************************************************************************/

#include <p2c/p2c.h>


#ifndef MYLIB_H
#include <p2c/mylib.h>
#endif

#ifndef SYSGLOBALS_H
#include <p2c/sysglobals.h>
#endif

#ifndef ASM_H
#include <p2c/asm.h>
#endif

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

#ifndef NEWCI_H
#include <p2c/newci.h>
#endif

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

#ifndef NEWKBD_H
#include <p2c/newkbd.h>
#endif

#ifndef CITINFOMOD_H
#include <p2c/citinfomod.h>
#endif

#ifndef LUNIX_PAS_H
#include <p2c/lunix_pas.h>
#endif

#ifndef FILEPACK_H
#include <p2c/filepack.h>
#endif

#ifndef TABLET_STUFF_H
#include "tablet_stuff.h"
#endif

#ifndef GR_STUFF_H
#include "gr_stuff.h"
#endif

#ifndef MAT_STUFF_H
#include "mat_stuff.h"
#endif

#ifndef CRT_STUFF_H
#include "crt_stuff.h"
#endif

#ifndef BB_STUFF_H
#include "bb_stuff.h"
#endif

#ifndef DATA_TYPES_H
#include "data_types.h"
#endif

#ifndef FFMAN_H
#include "ffman.h"
#endif

#ifndef PRIM_STUFF_H
#include "prim_stuff.h"
#endif

#ifndef MENU_STUFF_H
#include "menu_stuff.h"
#endif

#ifndef MENU_H
#include "menu.h"
#endif

#ifndef CNF_STUFF_H
#include "cnf_stuff.h"
#endif

#ifndef NAME_STUFF_H
#include "name_stuff.h"
#endif

#ifndef DATAWALK_H
#include "datawalk.h"
#endif


  
/* added by MAJ */

#ifdef ultrix
#include <unistd.h>
#else
#ifdef linux
#include <unistd.h>
#else
#include <sys/unistd.h>
#endif
#endif

#define minflashontime  30   /* 100ths of a second */
#define minflashofftime  15


Static object *flashobj;   /* keep track of the last thing flashed */
Static long flashpart, flashtime;
Static boolean flashOn;

Static boolean doneNews;   /* Have we shown the news yet (if P-loaded) */
/* Call this when something happens */
Static void *modeProc;



#define len_            6
#define wng             3

/* Draw a single arrow head at (sx,sy).  Big deal. */
Static void arrowHead(long sx, long sy, long ex, long ey)
{
  long dx, dy, ax, ay, bx, by, r;

  dx = ex - sx;
  dy = ey - sy;
  r = (long)floor(sqrt((double)(dx * dx + dy * dy)) + 0.5);
  if (r == 0) {
    r = 1;
    dx = 1;
    dy = 0;
  }
  ax = len_ * dx;
  ay = len_ * dy;
  bx = -wng * dy;
  by = wng * dx;
  m_move(sx + (ax + bx) / r, sy + (ay + by) / r);
  m_draw(sx, sy);
  m_draw(sx + (ax - bx) / r, sy + (ay - by) / r);
}

#undef len_
#undef wng


/* Draw an entire yardstick from (a,b) to (c,d). */
Static void yardStick(long a, long b, long c, long d)
{
  long dx, dy, t;
  double delta;
  Char s[21];

  m_choosefont(1);
  m_color(7);
  dx = c - a;
  dy = d - b;
  if (dx < 0) {   /* swap */
    t = c;
    c = a;
    a = t;
    t = d;
    d = b;
    b = t;
    dx = -dx;
    dy = -dy;
  }
  mat_transform(a, b, &a, &b);
  mat_transform(c, d, &c, &d);
  m_move(a, b);
  m_draw(c, d);
  arrowHead(a, b, c, d);
  arrowHead(c, d, a, b);

  delta = sqrt((double)(dx * dx + dy * dy));
  s[0] = '\0';
  if (fabs((long)floor(delta + 0.5) - delta) < 0.1) {
    sprintf(s, "%ld", (long)floor(delta + 0.5));
    t = strlen(s) + 1;
  } else {
    sprintf(s, "%0.1f", delta);
    t = strlen(s) + 1;
  }
  if (dy > 0)
    m_drawstr((a + c) / 2, (b + d) / 2 - 3, NULL, s);
  else
    m_drawstr((a + c) / 2, (b + d) / 2 + 10, NULL, s);
  nk_gotoxy(50, 0);
  printf("\tdx=%ld dy=%ld delta=%0.4f angle=%ld",
	 labs(dx), labs(dy), delta, aTan2I(dx, dy));
}


Static void cursor(long x, long y)
{
  mat_transform(x, y, &x, &y);
  m_cursor(x, y);
}


Static void mypage(void)
{
  if (CURRENTCRT == ALPHATYPE)
    printf("\f");
  else
    printf("\001\t");
}


Static void waitForNear(void)
{
  do {
    update_tablet();
  } while (!curPen.near_);
}


Static void yardStickMode(void)
{
  boolean good;
  long a, b, c, d;
  _PROCEDURE TEMP;

  mypage();
  boop();
  printf("Press pen to start ruler.\n");
  printf("Lift pen to abort Yardstick mode.\n");

  waitForNear();
  do {
    do {
      update_tablet();
      cursor(curPen.x, curPen.y);
    } while (!curPen.dn && curPen.near_);
    if (curPen.dn) {
      a = curPen.x;
      b = curPen.y;
      m_color(7);
      TEMP.proc = yardStick;
      TEMP.link = NULL;
      rubberBand(TEMP, a, b, &c, &d, &good);
      if (good)
	yardStick(a, b, c, d);
    }
  } while (curPen.near_);
}


Static void zoom_to_window(void)
{
  boolean good;
  long xl, yl, xh, yh;

  long dx, dy;
  _PROCEDURE TEMP;

  mypage();
  boop();
  printf("           Enter window for zoom.");
  update_root_mbb();
  waitForNear();
  do {
    update_tablet();
    mat_transform(curPen.x, curPen.y, &xl, &yl);
    m_cursor(xl, yl);
  } while (!curPen.dn && curPen.near_);
  xl = curPen.x;
  yl = curPen.y;

  if (curPen.near_) {
    m_colormode(m_xor);
    m_color(15);
    TEMP.proc = drawbox;
    TEMP.link = NULL;
    rubberBand(TEMP, xl, yl, &xh, &yh, &good);
    if (good) {
      off_x = (xh + xl) / 2;
      off_y = (yh + yl) / 2;
      dx = labs(xh - xl);
      dy = labs(yh - yl);
      if (dx != 0 || dy != 0) {   /* ignore entirely */
	if (dx > dy)
	  set_zoom((m_across - 100.0) / dx);
	else
	  set_zoom((m_down - 40.0) / dy);
      }
    }
  }
  mypage();
}


Static void showhelp(void)
{
  mypage();
  printf("UNTIL keyboard commands\n\n");
  printf("arrows, 2,4,6,8 - pan around\n");
  printf("5   - center display\n");
  printf("\" \" - refresh\n");
  printf("C,c - clear alpha screen\n");
  printf("-,+ - double/half zoom\n");
  printf("Z,z - zoom to window\n");
  printf("e   - enter extended edit mode on current figure.\n");
  printf("E   - enter extended edit mode on all files.\n");
  printf("G   - enter extended edit mode on selected globals.\n");
  printf("g   - toggle grid on/off.\n");
  printf("q,Q - quit (asks first).\n");
  printf("y,r - enter Yardstick or Ruler mode.\n");
  printf("s,l - save/load files.\n");
  printf("p   - plot/ps current figure\n");
  printf("f   - select figure\n");
  printf(":   - enter configuration commands\n");
  printf("<,> - cycle through grid sizes\n");
}


Static boolean okOver(Char *fn)
{
  FILE *f;
  char ch;

  if (f = fopen(fn, "rb")) {
    fclose(f);

    boop();
    printf("%s exists, overwrite? ", fn);
    ch = nk_getkey();
    return (ch == 'y' || ch == 'Y');
  }
  else
    return true;
}


Static void a_save(void)
{
  Char s[201], t[201];
  figure *fp;
  Char *TEMP;

  mypage();

  if (!strcmp(curFile->fileName, "UNTITLED.FF")) {
    printf("New file name [UNTITLED.FF]? ");

    fgets(s, 201, stdin);
    TEMP = (char *) strchr(s, '\n');
    if (TEMP != NULL)
      *TEMP = '\0';

    newci_fixfname(s, "ff", "");

    if (*s != '\0') {
      strcpy(curFile->fileName, s);
      fp = curFile->figures;

      while (fp != NULL && strcmp(fp->name, "UNTITLED"))
	fp = fp->next;

      if (fp != NULL && !strcmp(fp->name, "UNTITLED")) {
	newci_forcefname(s, "", "");
	strlower(s, s);
	printf("New figure name [%s] (was UNTITLED)? ", s);
	fgets(t, 201, stdin);

	TEMP = (char *) strchr(t, '\n');
	if (TEMP != NULL)
	  *TEMP = 0;

	if (*t == '\0')
	  strcpy(fp->name, s);
	else
	  strcpy(fp->name, t);
      }
    }
  }

  if (okOver(curFile->fileName)) {
    write_out_the_File(curFile);
    printf("DONE.\n");
  }
}


Static void disp_globals(void)
{
  gr_dim();
  m_nocursor();
  startWalk(d_gbl, NULL);
  gr_bright();
}


Static void myRefresh(void)
{
  refresh();
  flashOn = false;
  m_noclip();
}


Static void do_kbdPlot(void);

Static void a_selFig(void);


Local void showpan(long panx, long pany)
{
  long left, right;

  if (leftOfMenu == 0) {
    left = rightOfMenu;
    right = m_across;
  }
  else {
    left = 0;
    right = leftOfMenu;
  }

  m_colormode(m_xor);
  m_color(m_white);
  if (panx < 0)
    m_drawline(right + panx, 0, right + panx, m_down - 1);
  else if (panx > 0)
    m_drawline(left + panx, 0, left + panx, m_down - 1);
  if (pany < 0)
    m_drawline(left, m_down + pany, right - 1, m_down + pany);
  else if (pany > 0)
    m_drawline(left, pany, right - 1, pany);
}

/* Pan dx,dy pixels */
Local void dopan(long dx, long dy, boolean *ref, long *panx, long *pany)
{
  *ref = true;
  showpan(*panx, *pany);
  *panx += dx;
  *pany += dy;
  showpan(*panx, *pany);
}

/* Handle the : command */
Local void stringCommand(boolean *ref, boolean *continue_)
{
  Char inLine[256];

  mypage();
  gr_dim();
  *continue_ = false;
  boop();
  printf("          \215:\210");
  gets(inLine);
  *ref = (*inLine != '\0');
  if (*ref)
    readCnfLine(inLine);
}


Static void my_update_tablet(boolean all)
{
  boolean ref, continue_;   /* Accept more key strokes? */
  long panx, pany;
  Char cmd;
  boolean first;   /* first keystroke? */
  long j;

  first = true;
  ref = false;
  continue_ = true;
  panx = 0;
  pany = 0;

  while (nk_keybufsize() > 0 && continue_) {
    a_cleara();  /* steve */
    cmd = nk_getkey();
    switch (cmd) {

    case '\034':
      dopan(10, 0, &ref, &panx, &pany);
      break;

    case '\b':
      dopan(-10, 0, &ref, &panx, &pany);
      break;

    case '\n':
      dopan(0, -10, &ref, &panx, &pany);
      break;

    case '\037':
      dopan(0, 10, &ref, &panx, &pany);
      break;

    case '6':
      dopan(100, 0, &ref, &panx, &pany);
      break;

    case '4':
      dopan(-100, 0, &ref, &panx, &pany);
      break;

    case '2':
      dopan(0, -100, &ref, &panx, &pany);
      break;

    case '8':
      dopan(0, 100, &ref, &panx, &pany);
      break;

    case '-':
      set_zoom(zoom / 2);
      ref = true;
      break;

    case '+':
      set_zoom(zoom * 2);
      ref = true;
      break;

    case ':':
      stringCommand(&ref, &continue_);
      break;

    case '5':
      center_display();
      center_display();
      ref = true;
      break;

    case 'c':
    case 'C':
      mypage();
      break;

    case 's':
      if (all)
	a_save();
      else
	BEEP();
      break;

    case 'l':
      if (all)
	a_load();
      else
	BEEP();
      break;

    case 'z':
    case 'Z':
      if (all) {
	zoom_to_window();
	ref = true;
      } else
	BEEP();
      break;

    case ' ':
    case '\372':  /* steve */
      ref = true;
      mypage();
      break;

    case '\373':  /* steve */
      botOfMenuBar = m_down - chHi;
      break;

    case '?':
      showhelp();
      break;

    case 'x':
    case 'X':
      printf("All is %s\n", all ? " TRUE" : "FALSE");
      break;

    case 'g':
      showGrid = !showGrid;
      if (showGrid)   /* adjust for grid */
	set_zoom(zoom);
      continue_ = false;
      ref = true;
      break;

    case 'G':
      disp_globals();
      ref = true;
      continue_ = false;
      break;

    case 'e':
      gr_dim();
      m_nocursor();
      startWalk(d_fig, curFigure);
      gr_bright();
      ref = true;
      continue_ = false;
      break;

    case 'E':
      gr_dim();
      m_nocursor();
      startWalk(d_file, files);
      gr_bright();
      ref = true;
      continue_ = false;
      break;

    case 'y':
    case 'r':
      yardStickMode();
      break;

    case 'q':
    case 'Q':
      do_exit();
      continue_ = false;
      break;

    case 'f':
      if (all) {
	/* Refresh is done in a_selFig; */
	continue_ = false;
	a_selFig();
      } else
	BEEP();
      break;

    case 'p':
      do_kbdPlot();
      break;

    case '<':  /* added by steve (02 April 1990) */
      printf("old grid_mul = %hd   ", grid_mul);
      {
	short i = gridRec.num - 1;
	while ((i > -1) && (gridRec.arr[i] >= grid_mul))
	  i--;
	if (i == -1) 
	  i = gridRec.num - 1;
	grid_mul = gridRec.arr[i];
      }
      printf("new grid_mul = %hd \n", grid_mul);
      break;


    case '>':  /* added by steve (02 April 1990) */
      printf("old grid_mul = %hd   ", grid_mul);
      {
        short i = 0;
        while ((i < gridRec.num) && (gridRec.arr[i] <= grid_mul))
	  i++;
	if (i == gridRec.num) 
	  i = 0;
	grid_mul = gridRec.arr[i];
      }
      printf("new grid_mul = %hd \n", grid_mul);
      break;


    default:
      printf("\007          Unknown keyboard command %o, ? for help.\n", (unsigned char)cmd);
      continue_ = false;
      break;
    }
  
    /* Wait >1/10th second for more keys */
    if (nk_keybufsize() == 0 && ref && continue_)
    {
      j = sysclock();
      while (j + 15 > sysclock() && nk_keybufsize() == 0) ;
      if (first) {
	while (j + 40 > sysclock() && nk_keybufsize() == 0) ;
      }
    }

    first = false;
  }

  curPen.didRef = ref;
  if (ref) {
    off_x += panx * zoom_den / zoom_num;
    off_y += pany * zoom_den / zoom_num;
    myRefresh();
  }

  update_tablet0();
}


Static void unflash(void)
{
  if (!flashOn)
    return;
  objColor(flashobj);
  flash(flashobj, flashpart);
  restore_color();
  flashOn = false;
}


Static void newflash(object *o, long p)
{
  objColor(o);
  if (o != flashobj || p != flashpart) {   /* new object to flash */
    unflash();
    flashtime = sysclock() + minflashontime;
    flashpart = p;
    flashobj = o;
    flash(o, p);
    flashOn = true;
  }
  else {  /* same old flashy object */
    if (sysclock() > flashtime) {
      flash(o, p);
      flashOn = !flashOn;
      if (flashOn)
	flashtime = sysclock() + minflashontime;
      else
	flashtime = sysclock() + minflashofftime;
    }
  }
  restore_color();
}


Static void delOne(long x, long y, boolean down)
{
  object *op;
  long part, area;

  part = 0;
  op = NULL;
  selectOne(&op, x, y, &part, &area);
  if (part < 1) {
    unflash();
    return;
  }
  if (!down)
    newflash(op, 1);
  else {
    unflash();
    delete__(op);
  }
}


Static void delSel(long x, long y, long ex, long ey)
{
  selListElem *l, *n;

  selectLots(&l, x, y, ex, ey);
  while (l != NULL) {
    n = l->next;
    delete__(l->obj);
    Free(l);
    l = n;
  }
}


Static void changeOne(long x, long y, boolean down)
{
  object *op;
  long part, area;

  part = 0;
  op = NULL;
  selectOne(&op, x, y, &part, &area);
  if (part < 1) {
    unflash();
    return;
  }
  if (!down) {
    newflash(op, 1);
    return;
  }
  unflash();
  op->color.value = curColor;
  drawObj(op);
}


Static void changeSel(long x, long y, long ex, long ey)
{
  selListElem *l, *n;

  selectLots(&l, x, y, ex, ey);
  while (l != NULL) {
    n = l->next;
    l->obj->color.value = curColor;
    drawObj(l->obj);
    Free(l);
    l = n;
  }
}


Static void editMode(long x, long y, boolean down, boolean moveonly, boolean xtnd)
{
  object *op;
  long part, area;

  selectOne(&op, x, y, &part, &area);

  if (part < 1) {
    unflash();
    return;
  }

  if (moveonly || xtnd)
    part = 1;

  if (!down) {
    newflash(op, part);
    return;
  }

  if (!xtnd) {
    unflash();
    edit(op, x, y, part);
    return;
  }

  m_nocursor();
  startWalk(d_obj, op);
  changed();
  myRefresh();
}


Static void editModeN(long x, long y, boolean down)
{
  add_point = false;
  del_point = false;
  editMode(x, y, down, false, false);
}


Static void editModeA(long x, long y, boolean down)
{
  add_point = true;
  del_point = false;
  editMode(x, y, down, false, false);
}


Static void editModeD(long x, long y, boolean down)
{
  add_point = false;
  del_point = true;
  editMode(x, y, down, false, false);
}


Static void editModeX(long x, long y, boolean down)
{
  add_point = false;
  del_point = false;
  editMode(x, y, down, false, true);
}


/* Local variables for doCopyMoveSel: */
struct LOC_doCopyMoveSel {
  long x, y, ex, ey;
} ;

Local void mybox(long x1, long y1, long x2, long y2,
		 struct LOC_doCopyMoveSel *LINK)
{
  long dx, dy;

  dx = x2 - x1;
  dy = y2 - y1;
  drawbox(LINK->x + dx, LINK->y + dy, LINK->ex + dx, LINK->ey + dy);
}


Static void doCopyMoveSel(long x_, long y_, long ex_, long ey_, boolean copy)
{
  struct LOC_doCopyMoveSel V;
  selListElem *l, *nl;
  long fx, fy;
  boolean good;
  _PROCEDURE TEMP;

  V.x = x_;
  V.y = y_;
  V.ex = ex_;
  V.ey = ey_;
  selectLots(&l, V.x, V.y, V.ex, V.ey);
  if (l == NULL)
    return;
  m_color(7);
  TEMP.proc = mybox;
  TEMP.link = &V;
  rubberBand(TEMP, V.ex, V.ey, &fx, &fy, &good);
  V.x = fx - V.ex;
  V.y = fy - V.ey;

  if (good) {
    while (l != NULL) {
      changed();
      if (copy)
	l->obj = copyObj(l->obj);
      moveObj(l->obj, V.x, V.y, !copy);
      l = l->next;
    }
  } else {
    if (copy)
      printf("Copy");
    else
      printf("Move");
    printf(" aborted.\n");
  }
  while (l != NULL) {
    nl = l->next;
    Free(l);
    l = nl;
  }
}


Static void doCopyOne(long x, long y, boolean down)
{
  object *op, *op1;
  long part, area;

  part = 0;
  op = NULL;
  selectOne(&op, x, y, &part, &area);
  if (part < 1) {
    unflash();
    return;
  }
  part = 1;

  if (!down) {
    newflash(op, part);
    return;
  }
  unflash();
  changed();
  op1 = copyObj(op);
  edit(op1, x, y, part);
  m_colormode(m_normal);
  drawObj(op);   /* Show the one from the old spot */
}


Static void useFigure(fileSpec *filP, figure *figP)
{
  curFigure = figP;
  if (filP != NULL)
    curFile = filP;
  center_display();
  myRefresh();
}


Static void addMode(long x, long y, boolean down, objkinds kind)
{
  unflash();
  if (!down)
    return;
  /* While waiting for pen to come up, look for selected objects */
  /* if pen comes backup with in 1/10th??? second, then do the normal thing */
  m_color(curColor);
  setup(x, y, kind);
  changed();
}


Static void r_line(long x, long y, boolean down)
{
  newLine = l_line;
  addMode(x, y, down, pr_line);
}


Static void r_curve(long x, long y, boolean down)
{
  newLine = l_curve;
  addMode(x, y, down, pr_line);
}


Static void r_arrow(long x, long y, boolean down)
{
  if (wideArrows)
    newLine = l_wArrow;
  else
    newLine = l_nArrow;
  addMode(x, y, down, pr_line);
}


Static void r_polygon(long x, long y, boolean down)
{
  newLine = l_polygon;
  addMode(x, y, down, pr_line);
}


Static void r_box(long x, long y, boolean down)
{
  addMode(x, y, down, pr_box);
}


Static void r_dot(long x, long y, boolean down)
{
  addMode(x, y, down, pr_dot);
}


Static void r_ellipse(long x, long y, boolean down)
{
  addMode(x, y, down, pr_ellipse);
}


Static void r_circle(long x, long y, boolean down)
{
  addMode(x, y, down, pr_circle);
}


Static void r_text(long x, long y, boolean down)
{
  addMode(x, y, down, pr_text);
}


Static void a_figures(void)
{
  fileSpec *filp;
  figure *figp;

  mypage();
  gr_dim();
  filp = files;
  while (filp != NULL) {
    printf("File: %s\n", filp->fileName);
    figp = filp->figures;
    while (figp != NULL) {
      printf("     fig: %s", figp->name);
      if (figp->changes == 0)
	printf(" No changes");
      else
	printf("%d changes.", figp->changes);
      putchar('\n');
      figp = figp->next;
    }
    filp = filp->next;
  }
}


Static Char *primkinds_NAMES[] = {
  "PR_NONE", "PR_DOT", "PR_LINE", "PR_BOX", "PR_CIRCLE", "PR_ELLIPSE",
  "PR_NGON", "PR_TEXT", "PR_INST", "PR_LAST"
} ;


/* This procedure flattens the instance flatten into the current main object. */
/* Flatten must point to an instance.  Duh! */
Static void flatten_instance(object *flatten)
{
  object *o, *flatObj;
  mat_state m, m1;
  object *saveNext, *savePrev;
  pnt_elem *p, *p1;
  long t, rotation;
  pr_dot_rec *WITH;
  pr_circle_rec *WITH1;
  pr_text_rec *WITH2;
  pr_ellipse_rec *WITH3;
  pr_box_rec *WITH4;

  changed();
  mat_push(&m);

  if (flatten == NULL)
    goto _L1;

  if (flatten->kind != pr_inst) {
    printf("flatten_instance: WRONG KIND.\007\n");
    goto _L1;
  }

  printf("flatten_instance: at %s\n",
	 flatten->okind.i.contents->okind.base.parent->name);

  /* Set the current transformation matrix by flatten^.i.m */
  ctm = flatten->okind.i.m;
  rotation = mat_findRot();

  o = flatten->okind.i.contents;
  if (o == NULL)   /* abort!!! */
    goto _L1;
  /* this will scan the horizontal list */
  do {
    if (o->kind != o_base) {
      if (o->kind != pr_inst) {
	flatObj = getprim(o->kind, 0, 0, 0, 0);
	saveNext = flatObj->next;
	savePrev = flatObj->prev;
	memmove((long *)flatObj, (long *)o, primBytes(o->kind));
	flatObj->next = saveNext;
	flatObj->prev = savePrev;
      }

      switch (o->kind) {

      case pr_dot:
	WITH = &flatObj->okind.d;
	mat_transform(WITH->x, WITH->y, &WITH->x, &WITH->y);
	newbb(&flatObj->bb, WITH->x - 1, WITH->y - 1, WITH->x + 1,
	      WITH->y + 1);
	break;

      case pr_circle:
	WITH1 = &flatObj->okind.c;
	mat_transform(WITH1->cx, WITH1->cy, &WITH1->cx, &WITH1->cy);
	WITH1->r = mat_newLength(WITH1->r);
	newbb(&flatObj->bb, WITH1->cx - WITH1->r, WITH1->cy - WITH1->r,
	      WITH1->cx + WITH1->r, WITH1->cy + WITH1->r);
	break;

      case pr_text:
	WITH2 = &flatObj->okind.t;
	mat_transform(WITH2->x, WITH2->y, &WITH2->x, &WITH2->y);
	WITH2->rot = (WITH2->rot + rotation) % 360;
	/* But, what about rotation??   PUKE! */
	newbb(&flatObj->bb, WITH2->x - 20, WITH2->y - 20, WITH2->x + 20, WITH2->y + 20);
	break;

      case pr_ellipse:
	WITH3 = &flatObj->okind.e;
	mat_transform(WITH3->cx, WITH3->cy, &WITH3->cx, &WITH3->cy);
	WITH3->a = mat_newLength(WITH3->a);
	WITH3->b = mat_newLength(WITH3->b);
	WITH3->r = (WITH3->r + rotation) % 360;
	set_ellipse_mbb(flatObj);
	break;

      case pr_box:
	WITH4 = &flatObj->okind.b;
	mat_transform(WITH4->x1, WITH4->y1, &WITH4->x1, &WITH4->y1);
	mat_transform(WITH4->x2, WITH4->y2, &WITH4->x2, &WITH4->y2);
	if (WITH4->x1 > WITH4->x2) {
	  t = WITH4->x1;
	  WITH4->x1 = WITH4->x2;
	  WITH4->x2 = t;
	}
	if (WITH4->y1 > WITH4->y2) {
	  t = WITH4->y1;
	  WITH4->y1 = WITH4->y2;
	  WITH4->y2 = t;
	}
	newbb(&flatObj->bb, WITH4->x1, WITH4->y1, WITH4->x2, WITH4->y2);
	break;

      case pr_line:
	p = o->okind.l.pnts;
	p1 = Malloc(sizeof(pnt_elem));
	flatObj->okind.l.pnts = p1;
	mat_transform(p->x, p->y, &p1->x, &p1->y);
	p = p->next;
	while (p != NULL) {
	  p1->next = Malloc(sizeof(pnt_elem));
	  p1 = p1->next;
	  mat_transform(p->x, p->y, &p1->x, &p1->y);
	  p = p->next;
	}
	p1->next = NULL;
	set_line_mbb(&flatObj->bb, flatObj->okind.l.pnts);
	break;

      case pr_inst:
	mat_push(&m1);
	/* Copy current instance, leaving it as a Mat-only instance */
	mat_premult(&o->okind.i.m);
	flatObj = make_instance(o->okind.i.contents, ctm, true);
	mat_pop(&m1);
	break;

      default:
	printf("flatten: %s not supported.\n",
	       primkinds_NAMES[(long)o->kind]);
	break;
      }
    }
    o = o->next;
  } while (o->kind != o_base);

_L1:
  mat_pop(&m);
  myRefresh();
}


Static void p_flatten(long x, long y, boolean down)
{
  object *op;
  long part, area;

  part = 0;
  op = NULL;
  selectOne(&op, x, y, &part, &area);

  if (part < 1 || op->kind != pr_inst) {
    unflash();
    return;
  }
  part = 1;

  if (!down) {
    newflash(op, part);
    return;
  }
  unflash();
  changed();
  delete__(op);
  flatten_instance(op);
}


Static void a_flatten(void)
{
  object *p;
  object *insts[40];
  long numinst;

  printf("Begin flattening.\n");
  p = curFigure->guts->okind.i.contents->next;
  numinst = 0;
  while (p->kind != o_base) {
    if (p->kind == pr_inst) {
      numinst++;
      insts[numinst - 1] = p;
    }
    p = p->next;
  }
  printf("Found %ld instances.\n", numinst);
  while (numinst > 0) {
    flatten_instance(insts[numinst - 1]);
    delete__(insts[numinst - 1]);
    numinst--;
  }
}


/* procedures do_plot, do_cmPlot, etc. were removed from here */


Static void comment(Char *s_)
{
  Char s[256];
  long i;
  Char STR1[256], STR2[256];

  strcpy(s, s_);
  i = 1;
  while (i <= strlen(s)) {
    if (s[i - 1] == '\\' || s[i - 1] == ')' || s[i - 1] == '(') {
      sprintf(STR1, "\\%s", s + i - 1);
      strcpy(s + i - 1, STR1);
      i++;
    }
    i++;
  }
  if (ps_out.link != NULL) {
    sprintf(STR2, "(%s) Cmt", s);
    ((void(*)(Char *s, void *_link))ps_out.proc)(STR2, ps_out.link); 
  } else {
    sprintf(STR2, "(%s) Cmt", s);
    ((void(*)(Char *s))ps_out.proc)(STR2);
  }
}




/* Expand a file name - this assumes r is big enough */
Static Char *ex(Char *r, Char *s)
{
  while (isspace(*s))
    s++;

  if ((*s == '~') && (*(s+1) == '/')) {
     strcpy(r, getenv("HOME"));
     strcat(r, s+1);
  }
  else {
     strcpy(r, s);
  }

  return r;
}


#define spew            false


/* Local variables for do_psx: */
struct LOC_do_psx {
  FILE *ps, *lbl;
  long lp, ox, oy;   /* For label mode */
} ;

Local void my_ps_out(Char *s, struct LOC_do_psx *LINK)
{
  if (*s == '\0')
    return;
  if (s[0] == '%') {
    if (LINK->lp > 0) {
      if (spew)
	putchar('\n');
      putc('\n', LINK->ps);
    }
    if (strlen(s) > 1) {
      printf("\213%s\210\n", s);
      fprintf(LINK->ps, "%s\n", s);
    }
    LINK->lp = 0;
    return;
  }
  if (LINK->lp + strlen(s) > 75) {
    if (spew)
      putchar('\n');
    putc('\n', LINK->ps);
    LINK->lp = 0;
  }
  if (LINK->lp > 0) {
    if (spew)
      putchar(' ');
    putc(' ', LINK->ps);
    LINK->lp++;
  }
  if (spew)
    printf("\215%s\210", s);
  fputs(s, LINK->ps);
  LINK->lp += strlen(s);
}

#undef spew

Local void my_lbl_out(long x, long y, long rot, Char *mode, Char *s, struct LOC_do_psx *LINK)
{
  fprintf(LINK->lbl, "\\%s{%ld}{%ld}{%s}{%ld}%%\n", mode, x - LINK->ox, y - LINK->oy, s, rot);
}


Static void do_psx(boolean useLabels, boolean doSpool)
{
  struct LOC_do_psx V;
  mat_state m;
  long i, map;
  boolean found;
  Char s[81];   /*3/20/88 Cal [80];*/
  Char mapFile[91];
  Char psname[fidleng + 1], lblname[fidleng + 1];
  textState oTextState;
  Char STR1[140];
  Char STR2[81];
  Char STR4[256];
  Char STR5[256];
  Char cmdline[1024];
  bbrec *WITH;
  FILE *preamble;
  int predata;

  V.lbl = NULL;
  V.ps = NULL;
  oTextState = showTextState;
  gr_dim();

  if (*curFigure->mapFile != '\0') {
    found = true;
    strcpy(mapFile, curFigure->mapFile);

    printf("Reading %s...", mapFile);

    readPSColors(mapFile);
  
    if (found)
      printf("done.\n");
    else
      printf("\007Could not find %s use [e] to change map file.\n", mapFile);
  }
  else {
    map = 1;
    found = false;
    do {
      found = (access(pensPsSearch[map - 1], F_OK) == 0);
      if (found) {
	printf("Reading %s...", pensPsSearch[map - 1]);
	
	readPSColors(pensPsSearch[map - 1]);
	strcpy(mapFile, pensPsSearch[map - 1]);
	
	if (found)
	  printf("done.\n");
      }
      map++;
    } while (!(map > 5 || found));
  }

  if (!found) {
    printf("\007Could not find a PENSPS.MAP file.  You loose.\n");
    goto _L1;   /* Return from this junk */
   }

  strcpy(psname, curFigure->name);
  if (doSpool)
    sprintf(psname, "/tmp/until%ld.ps",
	    newci_fullseconds() % 1000);
  newci_forcefname(psname, "ps", "");

  printf("Output to %s\n", psname);
  if (V.ps != NULL)
    V.ps = freopen(psname, "w", V.ps);
  else
    V.ps = fopen(psname, "w");
  if (V.ps == NULL)
    _EscIO(FileNotFound);

  if (!useLabels)
    {
      preamble=fopen(UNTILHEADER,"r");
      while((predata = fgetc(preamble)) != EOF)
	{
	  putc(predata,V.ps);
	}
      fclose(preamble);
    }
  ps_out.proc = my_ps_out;
  ps_out.link = &V;
  V.lp = 0;

  showTextState = sts_words;
  if (useLabels) {
    showTextState = sts_TeXwords;
    strcpy(lblname, curFigure->name);
    newci_forcefname(lblname, "lbl", "");
    printf("Label output to %s\n", lblname);
    if (V.lbl != NULL)
      V.lbl = freopen(lblname, "w", V.lbl);
    else
      V.lbl = fopen(lblname, "w");
    if (V.lbl == NULL)
      _EscIO(FileNotFound);
    lbl_out.proc = my_lbl_out;
    lbl_out.link = &V;
  }

  mat_push(&m);

  today(headinfo.whowhn);
  ex(headinfo.fnm, curFile->fileName);
  strcpy(headinfo.fignm, curFigure->name);

  headinfo.bbxl = curFigure->guts->bb.xl;
  headinfo.bbyl = curFigure->guts->bb.yl;
  headinfo.bbxh = curFigure->guts->bb.xh;
  headinfo.bbyh = curFigure->guts->bb.yh;

  gr_reInit(gr_ps);

  update_root_mbb();
  ctm = mat_ident_mat;

  /* Output any comments */
  if (draftMode) {
    if (ps_out.link != NULL)
      ((void(*)(Char *s, void *_link))ps_out.proc)("{", ps_out.link);
    else
      ((void(*)(Char *s))ps_out.proc)("{");

    sprintf(STR5, "Mapfile: %s        Printed: %s", ex(STR4, mapFile), today(STR2));
    comment(STR5);
    sprintf(STR5, "File: %s        Figure: %s        Last changed: %s",
	    ex(STR4, curFile->fileName), curFigure->name,
	    curFigure->edit_date);
    comment(STR5);

    if (ps_out.link != NULL)
      ((void(*)(Char *s, void *_link))ps_out.proc)("} Not-In-TeX", ps_out.link);
    else
      ((void(*)(Char *s))ps_out.proc)("} Not-In-TeX");
  }

  /* Output my MBB */
  s[0] = '\0';
  sprintf(s, "[ %ld %ld %ld %ld ] TeX-do-square-scale",
	  curFigure->guts->bb.xl, curFigure->guts->bb.yl,
          curFigure->guts->bb.xh, curFigure->guts->bb.yh);
  i = strlen(s) + 1;

  if (ps_out.link != NULL)
    ((void(*)(Char *s, void *_link))ps_out.proc)(s, ps_out.link);
  else
    ((void(*)(Char *s))ps_out.proc)(s);

  if (useLabels) {
    WITH = &curFigure->guts->bb;
    V.ox = WITH->xl;
    V.oy = WITH->yl;
    fprintf(V.lbl, "\\FIGSIZE{%ld}{%ld}%%\n",
	    WITH->xh - WITH->xl, WITH->yh - WITH->yl);
  }

  for (i = 0; i <= maxColors; i++) {
    if (colors[i].cval >= 0) {
      plotColor = colors[i].cval;
      setPSColor(i);
      refObj(curFigure->guts);
    }
  }

  plotColor = -2;

  gr_finish();

  if (V.ps != NULL)
    {
      fclose(V.ps);
      if (doSpool)
	{
	  sprintf(cmdline,"lpr %s\n", psname);
	  system(cmdline);
	  sprintf(cmdline,"rm %s\n", psname);
	  system(cmdline);
	}
    }
  V.ps = NULL;
  if (useLabels) {
    if (V.lbl != NULL)
      fclose(V.lbl);
    V.lbl = NULL;
  }

  mat_pop(&m);

_L1:
  showTextState = oTextState;
  gr_reInit(gr_m);
  myRefresh();
  if (V.ps != NULL)
    fclose(V.ps);
  if (V.lbl != NULL)
    fclose(V.lbl);
}


Static void do_kbdPlot(void)
{
  Char c;

  mypage();
  boop();
  printf("\201P\200s, ps-\201L\200bl, ps-\201S\200pool, plo\201T\200? ");
  c = nk_getkey();
  if (c > ' ')
    putchar(c);
  switch (c) {

  case 'p':
    do_psx(false, false);
    break;

  case 'l':
    do_psx(true, false);
    break;

/*
  case 's':
    do_psx(false, true);
    break;

  case 't':
    do_plot();
    break;

  case 'c':
    do_cmPlot();
    break;
*/

  default:
    BEEP();
    myRefresh();
    break;
  }
}


/* Return true if we find the given figure. */
Static boolean find_fig(Char *name_, fileSpec **figFile, figure **figure_)
{
  Char name[256];
  fileSpec *filp;
  figure *figp;
  boolean found;
  Char STR1[256];

  strcpy(name, name_);
  found = false;
  *figFile = NULL;
  *figure_ = NULL;
  strupper(name, name);
  if (*name == '\0')
    return found;
  filp = files;
  while (filp != NULL && !found) {
    figp = filp->figures;
    while (figp != NULL && !found) {
      found = (strcmp(strupper(STR1, figp->name), name) == 0);
      if (!found)
	figp = figp->next;
    }
    if (!found)
      filp = filp->next;
  }
  *figFile = filp;
  *figure_ = figp;
  return found;
}


Static void a_selFig(void)
{
  fileSpec *filp;
  figure *figp;
  Char name[81];   /*3/20/88 Cal [80];*/

  update_root_mbb();   /* Set the current mbb before we leave this figure. */

  getFigure(name);
  if (*name == '\0')
    return;
  if (!find_fig(name, &filp, &figp)) {
    printf("\007Could not find figure named: %s\n", name);
  } else
    useFigure(filp, figp);
}


Static void a_makeFig(void)
{
  Char s[81];   /*3/20/88 Cal [80];*/
  fileSpec *filp;
  figure *figp;
  Char *TEMP;

  update_root_mbb();   /* Set the current mbb before we leave! */
  mypage();
  gr_dim();
  printf("         Making a new figure.\n");
  printf("         What do you want to call it? ");
  fgets(s, 81, stdin);
  TEMP = (char *) strchr(s, '\n');
  if (TEMP != NULL)
    *TEMP = 0;
  if (find_fig(s, &filp, &figp)) {
    printf("\007        That figure name is already used.\n");
    return;
  }
  if (*s == '\0')
    return;
  make_figure(s);
  if (!find_fig(s, &filp, &figp))
    printf("\007SPAZ MAXIMALLY, could not find figure I just made.\n");
  else {
    useFigure(filp, figp);
    mypage();
  }


}


/* mark all figures inactive and mark which file they belong to */
Static void unMark(void)
{
  figure *figp;
  fileSpec *filp;

  filp = files;
  while (filp != NULL) {
    figp = filp->figures;
    while (figp != NULL) {
      figp->active = false;
      figp->written = false;
      figp->thisfile = (filp == curFile);
      figp = figp->next;
    }
    filp = filp->next;
  }
}


/* Mark all figures called by this one. Assume that structure is not recursive yet. */
Static void markCalls(figure *fp)
{
  object *op;

  if (fp->active)
    return;
  fp->active = true;
  op = fp->guts->okind.i.contents->next;
  while (op->kind != o_base) {
    if (op->kind == pr_inst)
      markCalls(op->okind.i.contents->okind.base.parent);
    op = op->next;
  }
}


Static void a_instance(void)
{
  Char s[81];   /*3/20/88 Cal string[80];*/
  fileSpec *filp;
  figure *figp;
  object *op, *WITH;
  pr_inst_rec *WITH1;

  mypage();
  boop();
  getFigure(s);
  if (find_fig(s, &filp, &figp)) {
    WITH = figp->guts;
    unMark();
    markCalls(figp);
    if (curFigure->active)
      printf("\001\t\007          ERROR: \"%s\" calls current figure:\"%s\"",
	     s, curFigure->name);
    else {
      if (WITH->kind != pr_inst)
	printf("\007\007OOOOOPS!, pk=\007%s\n",
	       primkinds_NAMES[(long)WITH->kind]);
      op = make_instance(WITH->okind.i.contents, WITH->okind.i.m, true);
      WITH1 = &op->okind.i;
      WITH1->rot = 0;
      WITH1->tx = 0;
      WITH1->ty = 0;
      WITH1->sx = 1;
      WITH1->sy = 1;
      WITH1->den = 1;
      WITH1->matOnly = false;
      set_instance_mat(op);
      /*set_instance_mbb(op^,false);*/
      changed();
      myRefresh();
    }
  } else if (*s != '\0')
    printf("\001\t\007          Could not find \"%s\".", s);
}


Static void a_delFig(void)  /* steve */
{
  Char s[81];   /*3/20/88 Cal string[80];*/
  fileSpec *filp;
  figure *figp, *temp;

  getFigure(s);
  if (find_fig(s, &filp, &figp)) {
    printf("program cannot delete figures yet");
  } else if (*s != '\0')
    printf("\001\t\007          Could not find \"%s\".", s);
}


Static void zoom_it(void)
{
  zoom_to_window();
  myRefresh();
}


Static void nothing(void)
{
}


Static void pd_ps(void)
{
  do_psx(false, false);
}


Static void pd_psLbl(void)
{
  do_psx(true, false);
}


Static void pd_spoolPS(void)
{
  do_psx(false, true);
}


Static void extendGlobals(void)
{
  disp_globals();
  myRefresh();
}


Static void extendCurFig(void)
{
  gr_dim();
  m_nocursor();
  startWalk(d_fig, curFigure);
  gr_bright();
  myRefresh();
}


Static void extendFiles(void)
{
  gr_dim();
  m_nocursor();
  startWalk(d_file, files);
  gr_bright();
  myRefresh();
}


#define tThresh         40   /* in centiseconds */
#define pThresh         20   /* max pixels movement */

Static boolean isSingleClick(long x, long y, long *ex, long *ey)
{
  boolean Result;

  long start, nx, ny, ox, oy, x0, y0, sx0, sy0;

  mat_transform(curPen.x, curPen.y, &sx0, &sy0);
      /* Starting pixel position */
  unflash();

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

  nx = x;
  ny = y;
  do {
    ox = nx;
    oy = ny;
    drawbox(x, y, ox, oy);

    do {
      update_tablet();
      mat_transform(curPen.x, curPen.y, &x0, &y0);
      m_cursor(x0, y0);
    } while (!(curPen.moving || curPen.up));
    nx = curPen.x;
    ny = curPen.y;

    drawbox(x, y, ox, oy);
  } while (!curPen.up);

  m_colormode(m_normal);
  if (sysclock() - start < tThresh && labs(sx0 - curPen.sx) < pThresh &&
      labs(sy0 - curPen.sy) < pThresh) {
    /* Single Click Point */
    return true;
  }
  /* Drag Select Box */
  Result = false;
  *ex = nx;
  *ey = ny;
  return Result;
}

#undef tThresh
#undef pThresh


Static void delModeX(long x, long y, boolean down)
{
  long ex, ey;

  if (!down) {
    delOne(x, y, down);
    return;
  }
  if (isSingleClick(x, y, &ex, &ey))
    delOne(x, y, down);
  else
    delSel(x, y, ex, ey);
}


Static void changeModeX(long x, long y, boolean down)
{
  long ex, ey;

  if (!down) {
    changeOne(x, y, down);
    return;
  }
  if (isSingleClick(x, y, &ex, &ey))
    changeOne(x, y, down);
  else
    changeSel(x, y, ex, ey);
}


Static void moveModeX(long x, long y, boolean down)
{
  long ex, ey;

  add_point = false;
  del_point = false;
  if (!down) {
    editMode(x, y, down, true, false);
    return;
  }
  if (isSingleClick(x, y, &ex, &ey))
    editMode(x, y, down, true, false);
  else
    doCopyMoveSel(x, y, ex, ey, false);
}


Static void copyModeX(long x, long y, boolean down)
{
  long ex, ey;

  if (!down) {
    doCopyOne(x, y, down);
    return;
  }
  if (isSingleClick(x, y, &ex, &ey))
    doCopyOne(x, y, down);
  else
    doCopyMoveSel(x, y, ex, ey, true);
}


Local void activate(long f)
{
  short i;

  for (i = 0; i < maxFixed; i++) {
    if (fixedMenu[i].sel) {
      fixedMenu[i].sel = false;
      fixedMenu[i].changed = true;
    }
  }
  fixedMenu[f - 1].sel = true;
  fixedMenu[f - 1].changed = true;
  updateFixedMenu();
}


Static void myFixedMenuHandler(long f)
{
  if ((unsigned long)f >= 32 || ((1L << f) & 0x7006) == 0)
    activate(f);
  switch (f) {

  case 1:   /*fileName*/
    break;

  case 2:   /*figname*/
    break;

  case 3:   /*Edit*/
    modeProc = editModeN;
    break;

  case 4:   /*Edit-AddPnt*/
    modeProc = editModeA;
    break;

  case 5:   /*Edit-DelPnt*/
    modeProc = editModeD;
    break;

  case 6:   /*Edit-Extend*/
    modeProc = editModeX;
    break;

  case 7:   /*Copy*/
    modeProc = copyModeX;
    break;

  case 8:   /*Move*/
    modeProc = moveModeX;
    break;

  case 9:   /*Delete*/
    modeProc = delModeX;
    break;

  case 10:   /*Change Color*/
    modeProc = changeModeX;
    break;

  case 11:   /*Flatten*/
    modeProc = p_flatten;
    break;

  case 12:   /*Add Instance*/
    a_instance();
    break;

  case 13:   /*Undelete*/
    until_undelete();
    break;

  case 14:   /*Zoom*/
    zoom_it();
    break;

  case 15:   /*Arrow*/
    modeProc = r_arrow;
    break;

  case 16:   /*Line*/
    modeProc = r_line;
    break;

  case 17:   /*Curve*/
    modeProc = r_curve;
    break;

  case 18:   /*Box*/
    modeProc = r_box;
    break;

  case 19:   /*Dot*/
    modeProc = r_dot;
    break;

  case 20:   /*Circle*/
    modeProc = r_circle;
    break;

  case 21:   /*Ellipse*/
    modeProc = r_ellipse;
    break;

  case 22:   /*Polygon*/
    modeProc = r_polygon;
    break;

  case 23:   /*Text*/
    modeProc = r_text;
    break;
  }
}


Static void newtrackit(void)
{
  long x0, y0;
  m_tablet_info p;
  XEvent ev;

  quit = false;
  while (!quit) {
    update_tablet_all();

    if (curPen.sx < 0)
      curPen.sx = 0;
    if (curPen.sy < 0)
      curPen.sy = 0;
    if (curPen.sy >= botOfMenuBar) {
      unflash();
      p.x = curPen.sx;
      p.y = curPen.sy;
      m_cursor(curPen.sx, curPen.sy);
      if (curPen.dn)
        pullDownMenus(p);
    }
    else if (curPen.sx >= leftOfMenu && curPen.sx <= rightOfMenu) {
      unflash();
      p.x = curPen.sx;
      p.y = curPen.sy;
      m_cursor(curPen.sx, curPen.sy);
      if (curPen.dn)
        doFixedMenu(p);
    }
    else {
      if (curPen.near_) {
        mat_transform(curPen.x, curPen.y, &x0, &y0);
        m_cursor(x0, y0);
	((void(*)(long x, long y, boolean down))modeProc)(curPen.x, curPen.y, curPen.dn);
      } 
      else {
        m_nocursor();
        unflash();
      }
    }
    /* added to reduce load average - Steve/Mass/Bassen - 18 June 1990 */
    XNextEvent(m_display, &ev);
    XPutBackEvent(m_display, &ev);
  }
}


Static void myTrackAndTest(void)
{
  build_matrix();
  quit = false;
  gr_reInit(gr_m);

  ((void(*)(long f))fixedMenuHandler)(16);

  newtrackit();

  mypage();
}


Static void changeMenuName(void *handle, Char *newname)
{
  menux *mp;
  menuElem *ep;

  mp = head;
  while (mp != NULL) {
    ep = mp->elems;
    while (ep != NULL) {
      if (ep->proc == handle)
	strcpy(ep->name, newname);

      ep = ep->next;
    }
    mp = mp->next;
  }
}


Static void toggleGrid(void)
{
  showGrid = !showGrid;

  if (showGrid)
    changeMenuName(toggleGrid, "Hide Grid");
  else
    changeMenuName(toggleGrid, "Show Grid");

  if (showGrid)   /* adjust for grid */
    set_zoom(zoom);

  myRefresh();
}


Static void toggleMenuSide(void)
{
  if (leftOfMenu == 0) {
    leftOfMenu = m_across - 64;
    changeMenuName(toggleMenuSide, "Left Menu");
  }
  else {
    leftOfMenu = 0;
    changeMenuName(toggleMenuSide, "Right Menu");
  }

  rightOfMenu = leftOfMenu + 64;
  myRefresh();
}


Static void toggleTextState(void);

Local void doit(textState n, Char *s)
{
  showTextState = n;
  changeMenuName(toggleTextState, s);
  myRefresh();
}


Static void toggleTextState(void)
{
  switch (showTextState) {

  case sts_words:
    doit(sts_TeXwords, "Hide Labels");
    break;

  case sts_TeXwords:
    doit(sts_none, "Show Labels");
    break;

  case sts_none:
    doit(sts_words, "Show TeX Labels");
    break;
  }
}


Static void togglePSComments(void)
{
  draftMode = !draftMode;

  if (draftMode)
    changeMenuName(togglePSComments, "Hide PS Comments");
  else
    changeMenuName(togglePSComments, "Show PS Comments");
}


Static void toggleInstBB(void)
{
  showInstBB = !showInstBB;

  if (showInstBB)
    changeMenuName(toggleInstBB, "Hide Inst BB");
  else
    changeMenuName(toggleInstBB, "Show Inst BB");

  myRefresh();
}


Static void toggleControl(void)
{
  gr_show_control = !gr_show_control;

  if (gr_show_control)
    changeMenuName(toggleControl, "Hide Control Points");
  else
    changeMenuName(toggleControl, "Show Control Points");

  myRefresh();
}


Static void toggleForceGrid(void)
{
  forceToGrid = !forceToGrid;

  if (forceToGrid)
    changeMenuName(toggleForceGrid, "Don't Jump To Grid");
  else
    changeMenuName(toggleForceGrid, "Jump To Grid");
}


Static void toggleHexGrid(void)
{
  grid_hex = !grid_hex;

  if (grid_hex)
    changeMenuName(toggleHexGrid, "Rectangular Grid");
  else
    changeMenuName(toggleHexGrid, "Hexagonal Grid");

  myRefresh();
}


/* Handle the : command */
Static void colonCommand(void)
{
  Char inLine[256];

  mypage();
  boop();
  printf("          \215:\210");
  gets(inLine);
  if (*inLine != '\0') {
    readCnfLine(inLine);
    myRefresh();
  }
}


Static void buildMenus(void)
{
  initPull();
  fixedMenuHandler = myFixedMenuHandler;

  startMenu("Until");
  addElem("About Until...", "", about);
  addElem("Status", "", nothing);
  addElem("Right Menu", "", toggleMenuSide);

  startMenu("File");
  addElem("New", "^N", nothing);
  addElem("Open", "^O", a_load);
  addElem("Save", "^S", a_save);
  addElem("Save as...", "", nothing);
  addElem("Close", "", nothing);
  addElem("", "", nothing);
  addElem("Show PS Comments", "", togglePSComments);
  addElem("PS", "", pd_ps);
  addElem("PS-LBL", "", pd_psLbl);
  addElem("Spooled PS", "", pd_spoolPS);
/*
  addElem("Plot", "", do_plot);
  addElem("Color Master", "", do_cmPlot);
*/
  addElem("", "", nothing);
  addElem("Quit", "^Q", do_exit);

  startMenu("Edit");
  addElem("Undelete", "", until_undelete);
  addElem("", "", nothing);
  addElem("Extend-curFig", "", extendCurFig);
  addElem("Extend-files", "", extendFiles);
  addElem("Extend-globals", "", extendGlobals);

  startMenu("View");
  addElem("Show Grid", "G", toggleGrid);
  addElem("Hexagonal Grid", "", toggleHexGrid);
  addElem("Show TeX Labels", "", toggleTextState);
  addElem("", "", nothing);
  addElem("Home", "", a_home);
  addElem("Center", "5", center_display);
  addElem("Zoom", "", zoom_it);
  addElem("Zoom In", "+", a_zoomin);
  addElem("Zoom Out", "-", a_zoomout);
  addElem("Pan Left", "4", nothing);
  addElem("Pan Right", "6", nothing);
  addElem("Pan Up", "8", nothing);
  addElem("Pan Down", "2", nothing);
  addElem("", "", nothing);
  addElem("Yardstick", "Y", yardStickMode);
  addElem("", "", nothing);
  addElem("Clear Alpha", "C", a_cleara);
  addElem("Refresh", "\" \"", myRefresh);

  startMenu("Figure");
  addElem("New", "N", a_makeFig);
  addElem("Open", "O", a_selFig);
  addElem("Copy", "", nothing);
  addElem("Instance", "I", a_instance);
  addElem("Rename", "", nothing);
  addElem("Delete", "", a_delFig);  /* steve */

  startMenu("Transform");
  addElem("Rotate 90", "R", nothing);
  addElem("Mirror X", "X", nothing);
  addElem("Mirror Y", "Y", nothing);
  addElem("Scale", "S", nothing);
  addElem("Rotate by...", "", nothing);

  startMenu("Setup");
  addElem("Arrows...", "", extendGlobals);
  addElem("Lines...", "", extendGlobals);
  addElem("Cursor...", "", extendGlobals);
  addElem("Grid...", "", extendGlobals);
  addElem("Text...", "", extendGlobals);
  addElem("Colors...", "", extendGlobals);
  addElem("", "", nothing);
  addElem("Show Control", "", toggleControl);
  addElem("Show Inst BB", "", toggleInstBB);
  addElem("Don't Jump To Grid", "", toggleForceGrid);
  addElem("", "", nothing);
  addElem("Read Config", "", nothing);
  addElem("Config Command", ":", colonCommand);
}


Static void loadfiles(int argc, Char *argv[])
{
  long i;
  fileSpec *bogus;

  for (i = 1; i < argc; i++) {
    bogus = read_in_a_file(argv[i]);
    if (bogus == NULL)
      maybeMakeFile(argv[i]);
  }
}


Static boolean tryCnf(Char *tmp)
{
  boolean ok;
  char cnf[256];

  ex(cnf, tmp);

  ok = (access(cnf, F_OK) == 0);
  if (ok)
    readCnf(cnf);
  return ok;
}


main(int argc, Char *argv[])
{
  long i;
  extern char *GetChipmunkPath();


  PASCAL_MAIN(argc, argv);


  updateProc = my_update_tablet;

  boopTime = 10;
  boopFreq = 10;
  curFigure = NULL;
  curFile = NULL;
  files = NULL;

  curLWidW = 32;   /* Wide Arr-Line Wid   */
  curWWidW = 32;   /* Wide Arr-Wing Wid   */
  curWLenW = 96;   /* Wide Arr-Wing Len   */
  curWWidN = 32;   /* Narrow Arr-Wing Wid */
  curWLenN = 64;   /* Narrow Arr-Wing Len */

  curTextFont = 13;
  curTextScale = 12;
  curTextRot = 0;
  curTextSlant = 0;
  curTextOrig = TCC;
  nc_text_in_window = true;

  mypage();
  flashOn = false;
  mat_init();
  gr_init();
  m_init_pen(0);
  init_prim_stuff();
  for (i = 0; i < maxFonts; i++)
    *fonts[i].dispName = '\0';
  for (i = 0; i < maxFonts; i++)
    *fonts[i].psName = '\0';
  buildMenus();

  m_colormode(m_normal);
  off_x = 0;
  off_y = 0;
  set_zoom(default_zoom);
  build_matrix();
  curRots = 4;
  m_choosecursor(0);

  loadfiles(argc, argv);

  if (!doneNews) {
/*    newci_runprogram("news -w until");  procedure not available */
    doneNews = true;
  }
 
  printf("\f");   /* Always clear at this point */

  while (curFigure == NULL) {
    gr_dim();
    printf("\f\nNo files currently loaded.\001");
    a_load();
  }

/* tryCnf lines modified by steve */
  if (!tryCnf(".untilrc")) {
    if (!tryCnf("until.cnf")) {
      if (!tryCnf("~/until/.untilrc")) {
	if (!tryCnf("~/until/until.cnf")) {
	  if (!tryCnf("~/.untilrc")) {
	    if (!tryCnf("~/until.cnf")) {
	      if (!tryCnf(GetChipmunkPath("UNTILRC", UNTILRC))) {
		gr_dim();
		printf("\007Could not find any configuration files!!\n");
		printf("\213    (what happened to /lib/until/until.cnf?)\200\n");
		printf("\007You are on your own (things may not be setup right).\n");
		printf("Press [enter] to continue at your own risk:");
		scanf("%*[^\n]");
		getchar();
	      }
	    }
	  }
	}
      }
    }
  }

  m_color(curColor);
  set_grid(grid_mul);

  center_display();
  center_display();
  printf("\f");
  myRefresh();

  nk_gotoxy(0, 100);
  printf("             %s", announce);   /* Put this on the bottom line */

  myTrackAndTest();
}  /* main */


/*
main(int argc, Char *argv[])
{
  PASCAL_MAIN(argc, argv);
  main_();
  exit(0);
}
*/


/* End. */


