
/* "WOL", an integrated circuit layout tool,
   Copyright (C) 1983, 1990 California Institute of Technology.
   Author: Massimo Sivilotti
   Thanks to: Glenn Gribble, Marty Sirkin, Sylvie Rychebusch
	      Maryann Mayer, Carver Mead, Rick Koshi, Torre Lande
   Maintainer: John Lazzaro
   Maintainers's address: lazzaro@hobiecat.cs.caltech.edu;
                          CB 425/ CU Boulder/Boulder CO 91125. 
			  Send questions, bug fixes, to this address.

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

/* Output from p2c, the Pascal-to-C translator */
/* p2c: wolopt.text, line 9: Note: Range checking is OFF [216] */
/* p2c: wolopt.text, line 10: Note: Stack checking is OFF [217] */
/* p2c: wolopt.text, line 16: Note: Range checking is ON [216] */
/* From input file "wol_gr_2.text" */


/* Change these for testing */


#include "global.h"


#define WOL_GRAPHICS_2_G
#include "wol_graphics_2.h"


#define dbug            false   /* set to true to print debugging info */
#define display_test    false


Static short redisp_last_layer;


extern void menu_show(void);


void do_refresh(void)
{
  if (!strcmp(m_machine, "320")) {
    big_graphics();
    clear_screen();
  }
  if (display_buffers[current_buffer].refresh.link != NULL)
    ((void(*)(void *_link))display_buffers[current_buffer].refresh.proc)(
      display_buffers[current_buffer].refresh.link);
  else
    ((void(*)(void))display_buffers[current_buffer].refresh.proc)();
  if (!strcmp(m_machine, "320")) {
    menu_show();
    small_graphics();
  }
}


void set_buffer(Char *n)
{
  short i;
  Char STR2[32];
  buffer_rec *WITH;

  if (!strcmp(n, current_buffer_name))
    return;
  i = 0;
  while (i <= max_buffer && strcmp(n, display_buffers[i].name))
    i++;
  if (i > max_buffer) {
    sprintf(STR2, "\007set_buffer: \"%s\" not found.", n);
    show_message(STR2, false);
    return;
  }
  /* Save old zoom stuff */
  if (current_buffer >= 0) {
    WITH = &display_buffers[current_buffer];
    WITH->b_off_x = off_x;
    WITH->b_off_y = off_y;
    WITH->b_zoom = zoom;
    WITH->b_izoom = izoom;
    WITH->b_intzoom = intzoom;
  }
  /* Now setup the current parameters */
  current_buffer = i;
  WITH = &display_buffers[current_buffer];
  off_x = WITH->b_off_x;
  off_y = WITH->b_off_y;
  zoom = WITH->b_zoom;
  izoom = WITH->b_izoom;
  intzoom = WITH->b_intzoom;
  strcpy(current_buffer_name, WITH->name);
}


void make_buffer(Char *n, _PROCEDURE p)
{
  short i;
  Char STR2[42];
  buffer_rec *WITH;

  i = 0;
  while (i <= max_buffer && strcmp("None", display_buffers[i].name))
    i++;
  if (i > max_buffer) {
    sprintf(STR2, "\007make_buffer: Out of buffers for \"%s\".", n);
    show_message(STR2, false);
    return;
  }
  WITH = &display_buffers[i];
  WITH->b_off_x = 0;
  WITH->b_off_y = 0;
  WITH->b_zoom = 8.0;
  WITH->b_izoom = 8;
  WITH->b_intzoom = true;
  strcpy(WITH->name, n);
  WITH->refresh = p;
  WITH->windows = NULL;
}


#define arrowlength     5   /* pixels */

#define fuzz            0.006   /* comparison tolerance */


void draw_arrow(point start, point finish, boolean erase)
{
  long startx, starty, endx, endy, tmp_coord;
  double vectorx, vectory, normalx, normaly, leng;
  Char outstr[81];
  point tp_d1, tp_d2;
  long j, TEMP, TEMP1;

  gsave();

  startx = start.x;
  starty = start.y;
  endx = finish.x;
  endy = finish.y;

  /* turn on xor */
  xor_on();
  m_color(WHITE);
  m_linestyle(0);
  /* sort the coords, so vector goes up */
  if (starty > endy) {
    tmp_coord = starty;
    starty = endy;
    endy = tmp_coord;
    tmp_coord = startx;
    startx = endx;
    endx = tmp_coord;
  }
  m_nocursor();
  /* now calculate direction vector, and its normal,  both normalized */
  if (endx == startx)
    vectorx = 0.0;
  else {
    TEMP = endx - startx;
    TEMP1 = endy - starty;
    vectorx = (endx - startx) / sqrt((double)(TEMP * TEMP + TEMP1 * TEMP1));
  }
  if (endy == starty)
    vectory = 0.0;
  else {
    TEMP = endx - startx;
    TEMP1 = endy - starty;
    vectory = (endy - starty) / sqrt((double)(TEMP * TEMP + TEMP1 * TEMP1));
  }
  normalx = vectory;
  normaly = -vectorx;
  /* now draw vector, with arrowheads */
  m_move(startx, starty);
  m_draw(endx, endy);

  m_move(startx, starty);
  m_draw((long)(startx + arrowlength * (vectorx + normalx)),
	 (long)(starty + arrowlength * (vectory + normaly)));

  m_move(startx, starty);
  m_draw((long)(startx + arrowlength * (vectorx - normalx)),
	 (long)(starty + arrowlength * (vectory - normaly)));

  m_move(endx, endy);
  m_draw((long)(endx + arrowlength * (normalx - vectorx)),
	 (long)(endy + arrowlength * (normaly - vectory)));

  m_move(endx, endy);
  m_draw((long)(endx + arrowlength * (-vectorx - normalx)),
	 (long)(endy + arrowlength * (-vectory - normaly)));

  /* now calculate and print vector length */
  m_move((long)((startx + endx) / 2.0 + 6 * normalx),
	 (long)((starty + endy) / 2.0 + 10 * normaly - 4));
  tp_d1 = scale_down(start);   /* convert to lambda units */
  tp_d2 = scale_down(finish);
  startx = tp_d1.x;
  starty = tp_d1.y;
  endx = tp_d2.x;
  endy = tp_d2.y;

  TEMP = endx - startx;
  TEMP1 = endy - starty;
  leng = sqrt((double)(TEMP * TEMP + TEMP1 * TEMP1));
  outstr[0] = '\0';
  if (endx - startx == 0 || endy - starty == 0 ||
      fabs(leng - 1.0 * (long)leng) <= fuzz)
  {   /* its an integer */
    sprintf(outstr, "%0.0f", leng);
    j = strlen(outstr) + 1;
  } else {
    sprintf(outstr, "%0.1f", leng);
    j = strlen(outstr) + 1;
  }
  display_text(outstr);

  grestore();
}

#undef arrowlength
#undef fuzz


/*******************************************************************************/

void do_ruler(point start)
{
  /* waits for the pen to be depressed twice on geometry, then draws vector
     between the points, labelled with the length of the vector */
  point old_pt, new_pt;
  tablet_info new_pen;
  boolean first_time;

  /* first press was on grid area */
  debounce();   /* we now have pen position for first click */
  old_pt = start;
  first_time = true;
  do {
    new_pen = check_pen();
    new_pt.x = new_pen.x;
    new_pt.y = new_pen.y;
    new_pt = scale_up(scale_down(new_pt));   /* put it on lambda grid */
    if (new_pt.x != old_pt.x || new_pt.y != old_pt.y) {
      if (!first_time)
	draw_arrow(start, old_pt, true);
      first_time = false;
      draw_arrow(start, new_pt, false);
      old_pt = new_pt;
    }
  } while (!new_pen.depressed);
  debounce();
}



void highlight_g(node *nd)
{
  point a, b;
  long old_layer;

  /*$if dbug$ writeln ('Highlighting box in layer: ',nd^.layer:0); $end$*/
  gsave();
  small_graphics();

  a = transform_point(nd->ll);   /* XFORM */
  b = transform_point(nd->ur);

  m_colormode(m_normal);
  m_color(WHITE);
  m_linestyle(0);
  smartbox(a.x, a.y, b.x, b.y);

  old_layer = last_layer;
  set_layer(nd->layer);
  m_linestyle(1);
  m_color(curr_color);
  smartbox(a.x, a.y, b.x, b.y);

  set_layer(old_layer);
  grestore();
  /* highlight object */
}


void unhighlight_g(node *nd)
{
  point a, b;
  long old_layer;

  /*$if dbug$ writeln ('unHighlighting box in layer: ',nd^.layer:0); $end$*/
  gsave();
  small_graphics();

  a = transform_point(nd->ll);   /* XFORM */
  b = transform_point(nd->ur);

  m_linestyle(0);
  m_color(BLACK);
  smartbox(a.x, a.y, b.x, b.y);
  old_layer = last_layer;
  set_layer(nd->layer);
  smartbox(a.x, a.y, b.x, b.y);
  set_layer(old_layer);
  grestore();
  /* Un-highlight object */
}


void highlight_c(c_cell *nd)
{
  point pt1, pt2;

  gsave();
  xor_on();
  small_graphics();
  gstate.dotted = true;
  set_layer(comp_box);
  xform_node(nd, &pt1, &pt2);
  pt1 = scale_up(pt1);
  pt2 = scale_up(pt2);
  draw_rbb(pt1, pt2);
  /*$if dbug$ writeln ('Highlighting comp cell.'); $end$*/
  grestore();
  /* highlight object */
}


void unhighlight_c(c_cell *nd)
{
  point pt1, pt2;

  /*$if dbug$ writeln ('Un-Highlighting comp cell.'); $end$*/
  gsave();
  small_graphics();
  xor_off();
  gstate.dotted = false;
  set_layer(comp_box);
  xform_node(nd, &pt1, &pt2);
  pt1 = scale_up(pt1);
  pt2 = scale_up(pt2);
  draw_rbb(pt1, pt2);
  grestore();
  /* un-highlight object */
}



void select_object(point pt)
{
  /* accept a point in lambda coordinates;  traverse the data structure,
     building a tree along the way;  returns after EVERY possible object
     has been selected;  returns that object in last_selected_object_g
  */
  if (!strcmp(current_buffer_name, "GEOM")) {
    if (last_selected_object_g != NULL) {
      unhighlight_g(last_selected_object_g);
      last_selected_object_g = last_selected_object_g->next;
    }
    if (last_selected_object_g == NULL)   /* wrap around */
      last_selected_object_g = select_list_head_g;
    if (last_selected_object_g != NULL)   /* no objects found */
      highlight_g(last_selected_object_g);
    else
      select_in_progress_g = false;
    return;
  }
  if (strcmp(current_buffer_name, "COMP"))  /* current buffer = COMP */
    return;
  if (last_selected_object_c != NULL) {
    unhighlight_c(last_selected_object_c);
    last_selected_object_c = last_selected_object_c->next;
  }
  if (last_selected_object_c == NULL)   /* wrap around */
    last_selected_object_c = select_list_head_c;
  if (last_selected_object_c != NULL)   /* no objects found */
    highlight_c(last_selected_object_c);
  else
    select_in_progress_c = false;
}


Local void get_next_g(node *nd, point pt)
{
  /* traverses the geometry tree depth first */
  node *newnode;

  if (asm_memavail() < min_mem_recurse) {
    show_message("\007Low on memory in GET_NEXT_G", false);
    return;
  }
  while (nd != NULL) {
    get_next_g(nd->child, pt);
    if ((edges_only &&
	 (nd->ll.x == pt.x && pt.y >= nd->ll.y && pt.y <= nd->ur.y ||
	  nd->ll.y == pt.y && pt.x >= nd->ll.x && pt.x <= nd->ur.x ||
	  nd->ur.x == pt.x && pt.y >= nd->ll.y && pt.y <= nd->ur.y ||
	  nd->ur.y == pt.y && pt.x >= nd->ll.x && pt.x <= nd->ur.x)) ||
	(!edges_only && pt.x >= nd->ll.x && pt.x <= nd->ur.x &&
	 pt.y >= nd->ll.y && pt.y <= nd->ur.y)) {
      /*  get_next_g (nd^.child, pt);  -- necessary for edges_only */
      newnode = get_a_node();
      if (select_list_head_g == NULL)
	select_list_head_g = newnode;
      else
	select_list_tail_g->next = newnode;
      *newnode = *nd;
      newnode->next = NULL;
      newnode->child = NULL;
      newnode->parent = nd;   /* put a pointer to the real box here */
      select_list_tail_g = newnode;
      /*$if dbug$ writeln ('Found box in layer: ',newnode^.layer:0); $end$*/
    }
    nd = nd->next;
  }
}

Local void get_next_c(c_cell *nd, point pt)
{
  /* traverses the composition linked list */
  c_cell *newnode;
  point xll, xur;   /* transformed extent of BB */

  while (nd != NULL) {
    xll = nd->ll;
    xur = nd->ur;
    if ((edges_only && (xll.x == pt.x && pt.y >= xll.y && pt.y <= xur.y ||
			xll.y == pt.y && pt.x >= xll.x && pt.x <= xur.x ||
			xur.x == pt.x && pt.y >= xll.y && pt.y <= xur.y ||
			xur.y == pt.y && pt.x >= xll.x && pt.x <= xur.x)) ||
	(!edges_only && pt.x >= xll.x && pt.x <= xur.x && pt.y >= xll.y &&
	 pt.y <= xur.y)) {
      newnode = get_c_c_cell();
      if (select_list_head_c == NULL)
	select_list_head_c = newnode;
      else
	select_list_tail_c->next = newnode;
      *newnode = *nd;
      newnode->next = NULL;
      newnode->select_list = nd;   /* put a pointer to the real box here */
      select_list_tail_c = newnode;
      /*$if dbug$ writeln ('Found a composition cell.'); $end$*/
    }
    nd = nd->next;
  }
}


void find_all_possible_objects(point pt)
{
  /* searches the geometry buffer for suitable objects, builds
     a linked list, which can then be traversed by select_object
  */
  if (!strcmp(current_buffer_name, "GEOM")) {
    recl_nodes(&select_list_head_g);
    select_list_head_g = NULL;
    get_next_g(main_, pt);
    last_selected_object_g = NULL;
    return;
  }
  if (strcmp(current_buffer_name, "COMP"))
	/* next line is WRONG -- only reclaims one node */
	  return;
  if (select_list_head_c != NULL) {
    printf("WARNING:  memory being wasted here -- recl_c_c_cell\n");
    recl_c_c_cell(&select_list_head_c);
  }
  select_list_head_c = NULL;
  get_next_c(comp_cells->data, pt);
  last_selected_object_c = NULL;
}


/**************************************************************************/

tablet_info scale(tablet_info in_val, short arg)
{

  /* SCALE     -  Takes two arguments.  The first is the point to scale.  */
  /*              The second has values of 1,2,3.  1 = scale only x. 2 =  */
  /*              scale only y.  3 = scale both.                          */
  if (arg == 1 || arg == 3)
    in_val.x = (long)((long)(in_val.x / zoom) * zoom);
  if (arg == 2 || arg == 3)
    in_val.y = (long)((long)(in_val.y / zoom) * zoom);
  return in_val;
}


/*******************************************************************************/

/* Convert a point in LAMBDA coordinates and output SCREEN coordinates */
point scale_up(point tlp)
{
  point Result;

  Result.x = (long)((tlp.x - off_x) * zoom);
  Result.y = (long)((tlp.y - off_y) * zoom);
  return Result;
}


/*******************************************************************************/

/* Convert a point in SCREEN coordinates to LAMBDA coordinates */
point scale_down(point tlp)
{
  point Result;

  Result.x = (long)(tlp.x / zoom) + off_x;
  Result.y = (long)(tlp.y / zoom) + off_y;
  return Result;
}


/*******************************************************************************/

point change_to_point(long a, long b)
{
  point Result;

  Result.x = a;
  Result.y = b;
  return Result;
}


#define error           16   /* 1/16 */


/*******************************************************************************/

/* Re-make the transformation matrix, using the current offset and zoom */
/* Always call this after changing OFFSET or ZOOM */
void make_matrix(void)
{
  long scale, num;

  /* Find a nice rational zoom */
  num = 1;
  scale = (long)floor(zoom + 0.5);

  while (num < 256 && (double)error / num * fabs(scale - num * zoom) >= zoom) {
    /*writeln(#139'num='#136,num:2,'  scale=',scale:3,'=',zoom:0:5,*/
    /*'  error=',error/num*abs(scale-num*zoom):0:5);*/
    num *= 2;
    scale = (long)floor(num * zoom + 0.5);
  }
  /*writeln('num=',num:2,'  scale=',scale:3,'=',zoom:0:5,*/
  /*'  error=',error/num*abs(scale-num*zoom):0:5);*/
  pl_ident();
  /* Always change ZOOM to reflect the actual value */
  zoom = (double)scale / num;
  pl_scale(scale, scale, num);
  pl_tran(-off_x, -off_y);
  tr_set_matrix();
}

#undef error


/*******************************************************************************/

void set_bb(cell *c)
{
  node *scan;

  c->ll.x = INFIN;
  c->ll.y = INFIN;
  c->ur.x = INFIN;
  c->ur.y = INFIN;
  scan = c->data;
  while (scan != NULL) {  /* Keep up the B.B */
    if (c->ll.x == INFIN || scan->ll.x < c->ll.x)
      c->ll.x = scan->ll.x;
    if (c->ll.y == INFIN || scan->ll.y < c->ll.y)
      c->ll.y = scan->ll.y;
    if (c->ur.x == INFIN || scan->ur.x > c->ur.x)
      c->ur.x = scan->ur.x;
    if (c->ur.y == INFIN || scan->ur.y > c->ur.y)
      c->ur.y = scan->ur.y;
    scan = scan->next;
  }
}


/*******************************************************************************/

void set_c_bb(comp_list *c)
{
  c_cell *scan;
  point ll, ur;

  c->ll.x = INFIN;
  c->ll.y = INFIN;
  c->ur.x = INFIN;
  c->ur.y = INFIN;
  scan = c->data;
  while (scan != NULL) {
    xform_node(scan, &ll, &ur);
    if (c->ll.x == INFIN || ll.x < c->ll.x)   /* Keep up the B.B */
      c->ll.x = ll.x;
    if (c->ll.y == INFIN || ll.y < c->ll.y)
      c->ll.y = ll.y;
    if (c->ur.x == INFIN || ur.x > c->ur.x)
      c->ur.x = ur.x;
    if (c->ur.y == INFIN || ur.y > c->ur.y)
      c->ur.y = ur.y;
    scan = scan->next;
  }
}


/*******************************************************************************/

void xform_node(c_cell *c, point *vll, point *vur)
{
  point t1, t2;

  switch (c->xform) {

  case 0:
    t1 = c->ll;
    t2 = c->ur;
    break;

  case 1:  /* 5*/
    t1 = change_to_point(c->ll.x - c->ur.y + c->ll.y, c->ll.y);
    t2 = change_to_point(c->ll.x, c->ll.y + c->ur.x - c->ll.x);
    break;

  case 2:  /* 6 */
    t1 = change_to_point(c->ll.x * 2 - c->ur.x, c->ll.y * 2 - c->ur.y);
    t2 = c->ll;
    break;

  case 3:  /* 7 */
    t1 = change_to_point(c->ll.x, c->ll.y - c->ur.x + c->ll.x);
    t2 = change_to_point(c->ll.x + c->ur.y - c->ll.y, c->ll.y);
    break;

  case 4:
    t1 = change_to_point(c->ll.x * 2 - c->ur.x, c->ll.y);
    t2 = change_to_point(c->ll.x, c->ur.y);
    break;

  case 5:
    t1 = change_to_point(c->ll.x - c->ur.y + c->ll.y,
			 c->ll.y - c->ur.x + c->ll.x);
    t2 = c->ll;
    break;

  case 6:
    t1 = change_to_point(c->ll.x, c->ll.y * 2 - c->ur.y);
    t2 = change_to_point(c->ur.x, c->ll.y);
    break;

  case 7:
    t1 = c->ll;
    t2 = change_to_point(c->ll.x + c->ur.y - c->ll.y,
			 c->ll.y + c->ur.x - c->ll.x);
    break;
  }
  *vll = t1;
  *vur = t2;
}


/*******************************************************************************/

void scroll_window(boolean immediate, Char firstchar)
{
  long step_size;
  boolean done;   /* done with loop? */
  Char ch;   /* input character */
  long page_width;   /* how wide is a page? */
  long page_height;   /* how high is a page? */
  long movex, movey;   /* how far to move in lambda */
  long savemode, savecolor, oldx, oldy;

  /* make the scrolling rate proportional to the zoom factor */
  step_size = (long)(20 / zoom);
  if (step_size == 0)
    step_size = 1;
  /* only move 80% at a time via page commands */
  page_width = (long)(0.8 * RIGHT_OF_SCREEN / zoom);
  page_height = (long)(0.8 * TOP_OF_SCREEN / zoom);

  done = false;

  do {
    savemode = m_curcolormode();
    m_colormode(m_xor);
    savecolor = m_curcolor();
    m_color(m_white);

    movex = 0;
    movey = 0;
    switch (firstchar) {

    case '\b':
      movex = -step_size;
      break;

    case '\034':
      movex = step_size;
      break;

    case '\n':
      movey = -step_size;
      break;

    case '\037':
      movey = step_size;
      break;

    case '4':
      movex = -page_width;
      break;

    case '6':
      movex = page_width;
      break;

    case '2':
      movey = -page_height;
      break;

    case '8':
      movey = page_height;
      break;
    }

    if (movex < 0)
      m_drawline((long)floor(m_across + movex * zoom + 0.5), 0,
		 (long)floor(m_across + movex * zoom + 0.5), m_down);
    else if (movex > 0)
      m_drawline((long)floor(movex * zoom + 0.5), 0,
		 (long)floor(movex * zoom + 0.5), m_down);
    if (movey < 0)
      m_drawline(0, (long)floor(m_down + movey * zoom + 0.5), m_across,
		 (long)floor(m_down + movey * zoom + 0.5));
    else if (movey > 0)
      m_drawline(0, (long)floor(movey * zoom + 0.5), m_across,
		 (long)floor(movey * zoom + 0.5));
    oldx = movex;
    oldy = movey;

    while (nk_keybufsize() > 0 && !done) {
      ch = nk_getkey();
      switch (ch) {

      case '\b':
	movex -= step_size;
	break;

      case '\034':
	movex += step_size;
	break;

      case '\n':
	movey -= step_size;
	break;

      case '\037':
	movey += step_size;
	break;

      case '4':
	movex -= page_width;
	break;

      case '6':
	movex += page_width;
	break;

      case '2':
	movey -= page_height;
	break;

      case '8':
	movey += page_height;
	break;

      default:
	done = true;
	break;
      }

      if (oldx < 0)
	m_drawline((long)floor(m_across + oldx * zoom + 0.5), 0,
		   (long)floor(m_across + oldx * zoom + 0.5), m_down);
      else if (oldx > 0)
	m_drawline((long)floor(oldx * zoom + 0.5), 0,
		   (long)floor(oldx * zoom + 0.5), m_down);
      if (oldy < 0)
	m_drawline(0, (long)floor(m_down + oldy * zoom + 0.5), m_across,
		   (long)floor(m_down + oldy * zoom + 0.5));
      else if (oldy > 0)
	m_drawline(0, (long)floor(oldy * zoom + 0.5), m_across,
		   (long)floor(oldy * zoom + 0.5));

      if (movex < 0)
	m_drawline((long)floor(m_across + movex * zoom + 0.5), 0,
		   (long)floor(m_across + movex * zoom + 0.5), m_down);
      else if (movex > 0)
	m_drawline((long)floor(movex * zoom + 0.5), 0,
		   (long)floor(movex * zoom + 0.5), m_down);
      if (movey < 0)
	m_drawline(0, (long)floor(m_down + movey * zoom + 0.5), m_across,
		   (long)floor(m_down + movey * zoom + 0.5));
      else if (movey > 0)
	m_drawline(0, (long)floor(movey * zoom + 0.5), m_across,
		   (long)floor(movey * zoom + 0.5));

      oldx = movex;
      oldy = movey;

      /* Wait for more keys if we feel like it. */
      if (!done && nk_keybufsize() == 0)
	waitfor(20);
    }  /* while loop */

    if (oldx < 0)
      m_drawline((long)floor(m_across + oldx * zoom + 0.5), 0,
		 (long)floor(m_across + oldx * zoom + 0.5), m_down);
    else if (oldx > 0)
      m_drawline((long)floor(oldx * zoom + 0.5), 0,
		 (long)floor(oldx * zoom + 0.5), m_down);
    if (oldy < 0)
      m_drawline(0, (long)floor(m_down + oldy * zoom + 0.5), m_across,
		 (long)floor(m_down + oldy * zoom + 0.5));
    else if (oldy > 0)
      m_drawline(0, (long)floor(oldy * zoom + 0.5), m_across,
		 (long)floor(oldy * zoom + 0.5));

    m_color(savecolor);
    m_colormode(savemode);

    if (movex != 0 || movey != 0) {
      off_x += movex;
      off_y += movey;
      do_refresh();
    }
  } while (!(done || immediate));
  m_alpha_off();
}


#define max_zoom        32.0
#define min_zoom        0.01


/*******************************************************************************/

void zoom_it(double new_zoom)
{
  point t_calc;

  t_calc.x = X_CENTER;
  t_calc.y = Y_CENTER;
  t_calc = scale_down(t_calc);

  if (new_zoom > max_zoom)
    zoom = max_zoom;
  else if (new_zoom < min_zoom)
    zoom = min_zoom;
  else
    zoom = new_zoom;
  if (zoom > 1.0)
    zoom = (long)zoom;
  izoom = (long)zoom;
  intzoom = (zoom == izoom);

  off_x = t_calc.x - (long)(X_CENTER / zoom);
  off_y = t_calc.y - (long)(Y_CENTER / zoom);
  m_alpha_off();
  do_refresh();
}

#undef max_zoom
#undef min_zoom


/* FUNCTION get_rbb (start : point; var finish : point) : boolean; forward; */
/* FUNCTION tr_pen : tablet_info; forward;   */
void window_zoom(boolean switch_windows)
{
  /* if switch_windows = TRUE then actually switch, else just push */
  tablet_info pen;
  point start, finish;
  double zoom1, zoom2;
  short old_color;
  double old_zoom;   /* temp holder for calculaated zoom */
  long old_off_x, old_off_y;   /* temp holders for new offsets */

  TRY(try1);
    pen = tr_pen();
    start = change_to_point(pen.x, pen.y);
    old_color = curr_color;
    set_layer(sel_box);
    if (get_rbb(&start, &finish)) {
      old_zoom = zoom;
      old_off_x = off_x;
      old_off_y = off_y;
      if (switch_windows)   /* save current window */
	push_window();
      /*$if dbug$ writeln ('start  = ', start.x:0, ' ', start.y:0); $end$*/
      /*$if dbug$ writeln ('finish = ', finish.x:0, ' ', finish.y:0); $end$*/
      zoom1 = (double)RIGHT_OF_SCREEN / (finish.x - start.x);
      zoom2 = (double)TOP_OF_SCREEN / (finish.y - start.y);
      if (zoom1 >= zoom2)
	zoom = zoom2;
      else
	zoom = zoom1;
      if (zoom > 32)   /* clip somewhere */
	zoom = 32.0;
      if (zoom > 1.0)
	zoom = (long)zoom;
      izoom = (long)zoom;
      intzoom = (zoom == izoom);

      off_x = (start.x + finish.x - (long)(RIGHT_OF_SCREEN / zoom)) / 2;
      off_y = (start.y + finish.y - (long)(TOP_OF_SCREEN / zoom)) / 2;
      /*$if dbug$ writeln ('zoom = ',zoom:0:2, '  off_x = ',off_x:0, '  off_y = ',off_y:0); $end$*/

      if (switch_windows)
	do_refresh();
      else {  /* push the new window onto the stack, restore the old */
	push_window();
	zoom = old_zoom;
	izoom = (long)zoom;
	intzoom = (zoom == izoom);
	off_x = old_off_x;
	off_y = old_off_y;
      }
    }
  RECOVER(try1);
    if (P_escapecode == 10)
      _Escape(10);
    else {
      show_error("ERROR in Window_zoom:", false);
      _Escape(P_escapecode);
    }
  ENDTRY(try1);
  set_layer(old_color);
}


void push_window(void)
{
  /* store current window on window_stack */
  window_rec *tmp;
  buffer_rec *WITH;

  if (garbage_window_stack != NULL) {   /* pop one off */
    tmp = garbage_window_stack;
    garbage_window_stack = garbage_window_stack->next;
  } else
    tmp = Malloc(sizeof(window_rec));
  tmp->zoom = zoom;
  tmp->izoom = izoom;
  tmp->intzoom = intzoom;
  tmp->off_x = off_x;
  tmp->off_y = off_y;
  WITH = &display_buffers[current_buffer];
  tmp->next = WITH->windows;
  WITH->windows = tmp;
}


void pop_window(void)
{
  /* pop a window off the stack, redraw the screen if necessary */
  window_rec *tmp;
  boolean redraw_it;
  buffer_rec *WITH;

  WITH = &display_buffers[current_buffer];
  if (WITH->windows == NULL)
    return;
  tmp = WITH->windows;
  redraw_it = (zoom != tmp->zoom || off_x != tmp->off_x ||
	       off_y != tmp->off_y);
  zoom = tmp->zoom;
  izoom = tmp->izoom;
  intzoom = tmp->intzoom;
  off_x = tmp->off_x;
  off_y = tmp->off_y;
  WITH->windows = tmp->next;
  tmp->next = garbage_window_stack;
  garbage_window_stack = tmp;
  if (redraw_it)
    do_refresh();
}


void exchange_window(void)
{
  /* exchange the top two elements on the stack, and go to the top element,
     WITHOUT popping the stack
  */
  window_rec *tmp1, *tmp2, *tmp3;
  buffer_rec *WITH;

  WITH = &display_buffers[current_buffer];
  if (WITH->windows == NULL)
    return;
  tmp1 = WITH->windows;
  WITH->windows = tmp1->next;
  if (WITH->windows == NULL) {
    WITH->windows = tmp1;
    return;
  }
  tmp2 = WITH->windows;
  /* now exchange tmp1 and tmp2, making a copy of tmp2 */
  tmp1->next = WITH->windows->next;
  tmp2->next = tmp1;
  if (garbage_window_stack != NULL) {   /* pop one off */
    tmp3 = garbage_window_stack;
    garbage_window_stack = garbage_window_stack->next;
  } else
    tmp3 = Malloc(sizeof(window_rec));
  *tmp3 = *tmp2;
  tmp3->next = tmp2;
  WITH->windows = tmp3;
  pop_window();

  /* no tmp2, so put tmp1 back on stack */
  /* no windows to exchange */
}


tablet_info tr_pen(void)
{
  tablet_info t_i;

  big_graphics();   /* Force this */
  do {
    t_i = check_pen();
    if (t_i.y > TOP_OF_SCREEN && t_i.x > END_OF_MENU)
      t_i.y = TOP_OF_SCREEN;
    if (t_i.y <= TOP_OF_SCREEN)
      t_i = scale(t_i, 3);
    if (cursor_on)
      m_cursor(t_i.x, t_i.y);
  } while (!t_i.depressed);
  t_i.menu = 0;
  return t_i;
}


/*******************************************************************************/

tablet_info track(void)
{
  tablet_info n_ti;

  debounce();   /* Make sure pen is up  */
  n_ti = tr_pen();   /* Track the pen */
  debounce();   /* Get the pen up again */
  if (n_ti.y <= TOP_OF_SCREEN + 3)
    n_ti.menu = 0;
  else
    n_ti.menu = 11;
  m_nocursor();
      /* GEG if the calling routine wants it, it will put it back */
  return n_ti;   /* Set the value of the  */
}


/*******************************************************************************/

point transform_point(point pt)
{
  /* XFORM */
  point Result;

  tr_2(pt.x, pt.y);
  Result.x = tr_ax;
  Result.y = tr_ay;
  /*transform_point.x := (pt.x*tr_ctm.a+pt.y*tr_ctm.c+tr_ctm.e) div tr_ctm.g;*/
  /*transform_point.y := (pt.x*tr_ctm.b+pt.y*tr_ctm.d+tr_ctm.f) div tr_ctm.g;*/
  return Result;
}


#define l               0   /* left of screen */
#define b_              0   /* bottom of screen */


/*******************************************************************************/

/* This returns TRUE if any part of the current box would fit on the screen */
/* As a usefull side-effect, it sets the value of the global BB_STATE */
bb_states bb_check(point bll, point bur)
{
  long t, u, r;

  r = RIGHT_OF_SCREEN;
  u = TOP_OF_SCREEN;
  if (!debug_clip) {
    bb_state = bb_clip;
    return bb_state;
  }
  bll = transform_point(bll);
  bur = transform_point(bur);

  if (bur.x < bll.x) {
    t = bur.x;
    bur.x = bll.x;
    bll.x = t;
  }
  if (bur.y < bll.y) {
    t = bur.y;
    bur.y = bll.y;
    bll.y = t;
  }

  if (bll.x > r || bll.y > u || bur.x < l || bur.y < b_) {
    bb_state = bb_off;
    return bb_state;
  }
  if (bur.x <= r && bur.y <= u && bll.x >= l && bll.y >= b_)
    bb_state = bb_on;
  else
    bb_state = bb_clip;
  return bb_state;
}

#undef l
#undef b_


/****************************************************************************/

/* Draw a box without retracing a single pixel--works in XOR mode */
void smartbox(long x1, long y1, long x2, long y2)
{
  long t;

  if (x1 == x2 || y1 == y2) {
    m_move(x1, y1);
    m_draw(x2, y2);
    return;
  }
  /* points must be sorted */
  if (x1 > x2) {
    t = x2;
    x2 = x1;
    x1 = t;
  }
  if (y1 > y2) {
    t = y2;
    y2 = y1;
    y1 = t;
  }
  m_move(x1, y1 + 1);
  m_draw(x1, y2);
  m_move(x1 + 1, y2);
  m_draw(x2, y2);
  m_move(x2, y2 - 1);
  m_draw(x2, y1);
  m_move(x2 - 1, y1);
  m_draw(x1, y1);
}


/*******************************************************************************/

void draw_rbb(point pta, point ptb)
{
  m_nocursor();
  m_linestyle(curr_style);   /* Restart linestyle generator */
  small_graphics();
  smartbox(pta.x, pta.y, ptb.x, ptb.y);
  big_graphics();
  /* show cursor when in menu area */
  if (making_wire && ptb.y > TOP_OF_SCREEN)
    m_cursor(ptb.x, ptb.y);
}


/*******************************************************************************/

boolean within(node *t, point d1, point d2, short fl)
{
  if (t == NULL)
    return false;
  else {
    if (fl == 1)
      return (t->ll.x <= d1.x && t->ll.y <= d1.y && t->ur.x >= d1.x &&
	      t->ur.y >= d1.y);
    else  /* fl = 2 */
      return (t->ll.x <= d1.x && t->ll.y <= d1.y && t->ur.x >= d1.x &&
	      t->ur.y >= d1.y && t->ll.x <= d2.x && t->ll.y <= d2.y &&
	      t->ur.x >= d2.x && t->ur.y >= d2.y);
  }
}


/*******************************************************************************/

node *parent_of(node *st, point c1, point c2, short flg)
{
  node *Result;
  /* Parent  - Returns the smallest shape that contains C1 (and sometimes C2). */
  /*         - Returns the largest shape that contains C1 if C1 is on an edge. */
  /*           ST - Is the node to start searching with.                        */
  /*           C1,C2 - Are points (Either pen point, or the ll,ur corners of a wire).*/
  /*           FLG   - = 1 Only look at C1.  = 2 Look at both C1, C2            */
  /*                   = 3 look at both C1 and C2, but return the LARGEST
                          shape containing both                                */
  node *t1;
  boolean check;
  node *parent1, *parent2;

  if (asm_memavail() < min_mem_recurse) {
    Result = NULL;
    show_message("\007Low on memory in PARENT_OF", false);
    goto _L1;
  }

  if (edges_only && !select_in_progress_g && flg == 1) {
    find_all_possible_objects(c1);
    if (select_list_head_g != NULL)
      Result = select_list_head_g->parent;
    else {
      edges_only = false;
      t1 = parent_of(st, c1, c2, flg);
      edges_only = true;
      Result = t1;
    }
  } else {
    if (select_in_progress_g && last_selected_object_g != NULL && flg == 1) {
      Result = last_selected_object_g->parent;
      unhighlight_g(last_selected_object_g);
      recl_nodes(&select_list_head_g);
	  /* reclaim list of selected objects */
      select_list_head_g = NULL;
      last_selected_object_g = NULL;
      select_in_progress_g = false;
    } else {  /* not select_in_progress_g and not edges_only */
      check = within(st, c1, c2, flg);
      while (st != NULL && check == false) {
	st = st->next;
	check = within(st, c1, c2, flg);
      }
      if (st == NULL)
	Result = NULL;
      else {
	if (flg == 3) {
	  parent1 = st;
	  parent2 = parent_of(st->child, c1, c2, 2);
	  if (parent1 == NULL || parent2 == NULL)
	    Result = parent1;
	  else if (parent1->ll.x == c1.x && parent1->ll.y == c1.y &&
		   parent1->ur.x == c2.x && parent1->ur.y == c2.y &&
		   parent1->ll.x == parent2->ll.x &&
		   parent1->ur.x == parent2->ur.x &&
		   parent1->ll.y == parent2->ll.y &&
		   parent1->ur.y == parent2->ur.y)
	    Result = parent1;
	  else
	    Result = parent2;
	} else {
	  if (flg == 1 &&
	      (st->ll.x == c1.x && c1.y >= st->ll.y && c1.y <= st->ur.y ||
	       st->ll.y == c1.y && c1.x >= st->ll.x && c1.x <= st->ur.x ||
	       st->ur.x == c1.x && c1.y >= st->ll.y && c1.y <= st->ur.y ||
	       st->ur.y == c1.y && c1.x >= st->ll.x && c1.x <= st->ur.x))
	    Result = st;
	  else {
	    t1 = parent_of(st->child, c1, c2, flg);
	    if (t1 == NULL)
	      Result = st;
	    else
	      Result = t1;
	  }
	}
      }
    }
  }
_L1:

  return Result;
}


/*$if false$
FUNCTION parent_of(st : node_ptr; c1,c2 : point; flg : shortint) : node_ptr;

{ Parent  - Returns the smallest shape that contains C1 (and sometimes C2). }
{         - Returns the largest shape that contains C1 if C1 is on an edge. }
{           ST - Is the node to start searching with.                        }
{           C1,C2 - Are points (Either pen point, or the ll,ur corners of a wire).}
{           FLG   - = 1 Only look at C1.  = 2 Look at both C1, C2            }

 VAR
   t1,t2          :     node_ptr;
   check          :     boolean;

BEGIN
TRY
   check := within(st,c1,c2,flg);
   WHILE (st <> NIL) and (check = false) do
      begin
        st := st^.next;
        check := within(st,c1,c2,flg);
      end;
   IF    st = NIL
   THEN  parent_of := NIL
   ELSE  BEGIN
            if (flg = 1) and (((st^.ll.x = c1.x) and (c1.y >= st^.ll.y)
                                       and (c1.y <= st^.ur.y)) or
                    ((st^.ll.y = c1.y) and (c1.x >= st^.ll.x)
                                       and (c1.x <= st^.ur.x)) or
                    ((st^.ur.x = c1.x) and (c1.y >= st^.ll.y)
                                       and (c1.y <= st^.ur.y)) or
                    ((st^.ur.y = c1.y) and (c1.x >= st^.ll.x)
                                       and (c1.x <= st^.ur.x)))
                            THEN  parent_of := st
            ELSE  BEGIN
                    t1 := parent_of(st^.child,c1,c2,flg);
                    IF  t1 = NIL
                    THEN parent_of := st
                    ELSE parent_of := t1;
                  END;
         END;
RECOVER
   if escapecode=10 then escape(10) else
      begin
         show_error('Parent_of',false);
         escape (escapecode);
      end;
END;
$end$*/

/*******************************************************************************/
/*$if false$
PROCEDURE add_wire(p1,p2 : point;l : shortint; name : str_nodename; port : port_ptr);

VAR
   temp_val             : coord;
   t_np,l_to_scan,temp  : node_ptr;
   data_node,c_list     : node_ptr;
   port2, tmp_portlist  : port_ptr;
   old_ok_to_select     : boolean;
   coincident_boxes    : boolean;
   tmp_layer            : integer;
   tmp_name             : namestr;
   old_l_to_scan        : node_ptr;

BEGIN
   coincident_boxes := false;
   IF    p1.x > p2.x                   { Make p1 the lower left point of   }
   THEN  BEGIN                         { the box, and p2 the upper right.  }
            temp_val := p2.x;
            p2.x := p1.x;
            p1.x := temp_val;
         END;
   IF    p1.y > p2.y
   THEN  BEGIN
            temp_val := p2.y;
            p2.y := p1.y;
            p1.y := temp_val;
         END;

   {p1 := scale_down(p1);}   { Scale the two points           }
   {p2 := scale_down(p2);}

   data_node := get_a_node;               { Get a node }
   data_node^.ll := p1;                   { Stuff points, and layer in it }
   data_node^.ur := p2;
   data_node^.layer := l;
   data_node^.name := name;
     { duplicate port tree, if necessary }
   while port <> NIL do
     begin
       port2 := get_port_node;
       port2^ := port^;
       port2^.next := data_node^.portlist;
       data_node^.portlist := port2;
       port := port^.next;
     end;

   last_added := data_node;               { Set a global pointer (for move) }
   old_ok_to_select := ok_to_select;      { MAS }
   ok_to_select := false;
   t_np := parent_of(main,p1,p2,2);       { Find the node's parent        }
   ok_to_select := old_ok_to_select;

   data_node^.parent := t_np;
   IF    t_np = NIL                       { Set list of nodes to scan to see }
   THEN  BEGIN
            l_to_scan := main;             {  which are children of data_node }
          {  data_node^.parent := NIL; }     { Set the node's parent to NIL     }
         END
   ELSE  BEGIN
            l_to_scan := t_np^.child;     {  and which should be on same level}
          {  data_node^.parent := t_np;  }  { Set the data node's parent       }
         END;
   c_list := NIL;                         { List of children of data_node    }

   WHILE l_to_scan <> NIL do              { Check all nodes on same level as  }
      IF    within(data_node,l_to_scan^.ll,l_to_scan^.ur,2)  { data_node }
      THEN  BEGIN                         { If each is inside of data_node... }
    { attempt to make insertion of coincident objects nest
      outwards in order of insertion.... }

               if (data_node^.ll.x = l_to_scan^.ll.x) and
                  (data_node^.ll.y = l_to_scan^.ll.y) and
                  (data_node^.ur.x = l_to_scan^.ur.x) and
                  (data_node^.ur.y = l_to_scan^.ur.y) then
                  begin { add to OUTSIDE of object }
                     old_l_to_scan := l_to_scan;
                     coincident_boxes := true;
                  end;
$end$*/
/*$if false$
                     data_node^.child := l_to_scan;
                     data_node^.next := l_to_scan^.next;
                     data_node^.parent := l_to_scan^.parent;
                     l_to_scan^.parent := data_node;
                     c_list := l_to_scan;
                     l_to_scan^.next := NIL;
                     l_to_scan := NIL;   { we are done }
                  end
               else
$end$*/
/*$if false$
               begin  { add to INSIDE of object }
                temp := c_list;         {MAS}   { Append on to children list.       }
                c_list := l_to_scan;
                l_to_scan := l_to_scan^.next;
                c_list^.next := temp;
                c_list^.parent := data_node;
                if coincident_boxes then
                  begin  { swap layers, ports, and names }
                    tmp_layer := data_node^.layer;
                    data_node^.layer := old_l_to_scan^.layer;
                    old_l_to_scan^.layer := tmp_layer;
                    tmp_portlist := data_node^.portlist;
                    data_node^.portlist := old_l_to_scan^.portlist;
                    old_l_to_scan^.portlist := tmp_portlist;
                    tmp_name := data_node^.name;
                    data_node^.name := old_l_to_scan^.name;
                    old_l_to_scan^.name := tmp_name;
                  end;
               end;
            END
      ELSE  BEGIN                         { Otherwise, put on next list of data_node}
               temp := data_node^.next;
               data_node^.next := l_to_scan;
               l_to_scan := l_to_scan^.next;
               data_node^.next^.next := temp;
            END;

   IF    t_np = NIL                       { If there was no parent, set main  }
   THEN  main := data_node                { data_node, otherwise, set the     }
   ELSE  t_np^.child := data_node;        { parent^.child to data_node        }

   data_node^.child := c_list;            { Set data_node's children list.    }

END;
$end$*/


/*$if false$
  most recent
PROCEDURE add_wire(p1,p2 : point;l : shortint; name : str_nodename; port : port_ptr);

VAR
   temp_val             : coord;
   t_np,l_to_scan,temp  : node_ptr;
   data_node,c_list     : node_ptr;
   port2, tmp_portlist  : port_ptr;
   coincident_boxes     : boolean;

BEGIN
   writeln ('add_wire called');
   coincident_boxes := false;
   IF    p1.x > p2.x                   { Make p1 the lower left point of   }
   THEN  BEGIN                         { the box, and p2 the upper right.  }
            temp_val := p2.x;
            p2.x := p1.x;
            p1.x := temp_val;
         END;
   IF    p1.y > p2.y
   THEN  BEGIN
            temp_val := p2.y;
            p2.y := p1.y;
            p1.y := temp_val;
         END;

   data_node := get_a_node;               { Get a node }
   data_node^.ll := p1;                   { Stuff points, and layer in it }
   data_node^.ur := p2;
   data_node^.layer := l;
   data_node^.name := name;
     { duplicate port tree, if necessary }
   while port <> NIL do
     begin
       port2 := get_port_node;
       port2^ := port^;
       port2^.next := data_node^.portlist;
       data_node^.portlist := port2;
       port := port^.next;
     end;

   last_added := data_node;               { Set a global pointer (for move) }
   t_np := parent_of(main,p1,p2,2);       { Find the node's parent        }

   c_list := NIL;                         { List of children of data_node    }
   data_node^.parent := t_np;
   IF    t_np = NIL                       { Set list of nodes to scan to see }
     THEN  l_to_scan := main                {  which are children of data_node }
     ELSE begin
             { attempt to make insertion of coincident objects nest
               outwards in order of insertion.... }
             if (data_node^.ll.x = t_np^.ll.x) and (data_node^.ll.y = t_np^.ll.y) and
                (data_node^.ur.x = t_np^.ur.x) and (data_node^.ur.y = t_np^.ur.y) then
                begin { add to OUTSIDE of object }
                  coincident_boxes := true;
                  writeln ('found coincident box');
                  data_node^.parent := t_np^.parent;
                  if data_node^.parent = nil then begin
                              writeln ('data_node^.parent = nil');
                              main := data_node;
                           end
                     else if data_node^.parent^.child = t_np then
                             begin
                               writeln ('data_node^.parent^.child = t_np');
                               data_node^.parent^.child := data_node;
                             end
                          else begin
                               writeln ('data_node^.parent^.child <> t_np');
                                temp := data_node^.parent^.child;
                                while (temp <> NIL) and (temp^.next <> t_np)
                                  do temp := temp^.next;
                                if temp <> NIL then
                                  begin
                                    temp^.next := data_node;
                                  end;
                               end;
                  data_node^.next := t_np^.next;
                  t_np^.next := nil;
                  data_node^.child  := t_np;
                  t_np^.parent := data_node;
                end
              else
                begin
                  writeln ('boxes not coincident');
                  l_to_scan := t_np^.child;     {  and which should be on same level}
      end;      end;

   if not coincident_boxes then
     begin
        WHILE l_to_scan <> NIL do              { Check all nodes on same level as  }
           IF    within(data_node,l_to_scan^.ll,l_to_scan^.ur,2)  { data_node }
           THEN  BEGIN                         { If each is inside of data_node... }
                    { add to INSIDE of object, capturing any smaller boxes }
                     writeln ('  adding to inside of box, capturing smaller boxes');
                     temp := c_list;         {MAS}   { Append on to children list.       }
                     c_list := l_to_scan;
                     l_to_scan := l_to_scan^.next;
                     c_list^.next := temp;
                     c_list^.parent := data_node;
                 END
           ELSE  BEGIN                         { Otherwise, put on next list of data_node}
                    writeln ('  adding to .next list ');
                    temp := data_node^.next;
                    data_node^.next := l_to_scan;
                    l_to_scan := l_to_scan^.next;
                    data_node^.next^.next := temp;
                 END;

        IF    t_np = NIL                       { If there was no parent, set main  }
           THEN  main := data_node             { data_node, otherwise, set the     }
           ELSE  t_np^.child := data_node;     { parent^.child to data_node        }

        data_node^.child := c_list;            { Set data_node's children list.    }
     end;

END;
$end$*/

void add_wire(point p1, point p2, short l, Char *name, port_node *port)
{
  long temp_val;
  node *t_np, *l_to_scan, *temp, *data_node, *c_list;
  port_node *port2;
  boolean old_ok_to_select, coincident_boxes;

  coincident_boxes = false;
  if (p1.x > p2.x)   /* Make p1 the lower left point of   */
  {  /* the box, and p2 the upper right.  */
    temp_val = p2.x;
    p2.x = p1.x;
    p1.x = temp_val;
  }
  if (p1.y > p2.y) {
    temp_val = p2.y;
    p2.y = p1.y;
    p1.y = temp_val;
  }

  data_node = get_a_node();   /* Get a node */
  data_node->ll = p1;   /* Stuff points, and layer in it */
  data_node->ur = p2;
  data_node->layer = l;
  strcpy(data_node->name, name);
  /* duplicate port tree, if necessary */
  while (port != NULL) {
    port2 = get_port_node();
    *port2 = *port;
    port2->next = data_node->portlist;
    data_node->portlist = port2;
    port = port->next;
  }

  last_added = data_node;   /* Set a global pointer (for move) */
  old_ok_to_select = ok_to_select;   /* MAS */
  ok_to_select = false;
  t_np = parent_of(main_, p1, p2, 3);
      /* Find the node's parent -- NOTE FLG = 3*/
  ok_to_select = old_ok_to_select;

  /* check for coincident boxes */
  if (t_np != NULL && data_node->ll.x == t_np->ll.x &&
      data_node->ll.y == t_np->ll.y &&
      data_node->ur.x == t_np->ur.x && data_node->ur.y == t_np->ur.y)
  {  /* replace t_np with data_node */
    if (t_np->parent == NULL) {
      if (t_np == main_)
	main_ = data_node;   /* it is the root of the tree */
      else {  /* it is a top-level node */
	temp = main_;
	while (temp->next != t_np)
	  temp = temp->next;
	temp->next = data_node;
      }
    } else {
      temp = t_np->parent->child;
      if (temp == t_np)   /* it is the first son of a node */
	t_np->parent->child = data_node;
      else {  /* find the node in the .next list */
	while (temp->next != t_np)
	  temp = temp->next;
	temp->next = data_node;
      }
    }
    data_node->next = t_np->next;   /*update the nodes*/
    t_np->next = NULL;   /*so that data_node winds up outside*/
    data_node->parent = t_np->parent;
    t_np->parent = data_node;
    data_node->child = t_np;
    return;
  }
  data_node->parent = t_np;
  if (t_np == NULL)   /* Set list of nodes to scan to see */
    l_to_scan = main_;   /*  which are children of data_node */
  else
    l_to_scan = t_np->child;
  /*  and which should be on same level*/
  c_list = NULL;   /* List of children of data_node    */

  while (l_to_scan != NULL) {   /* Check all nodes on same level as  */
    if (!within(data_node, l_to_scan->ll, l_to_scan->ur, 2))   /* data_node */
    {  /* If each is inside of data_node... */
      temp = data_node->next;
      data_node->next = l_to_scan;
      l_to_scan = l_to_scan->next;
      data_node->next->next = temp;
      continue;
    }
    temp = c_list;   /*MAS*/
    /* Append on to children list.       */
    c_list = l_to_scan;
    l_to_scan = l_to_scan->next;
    c_list->next = temp;
    c_list->parent = data_node;
  }

  if (t_np == NULL)   /* If there was no parent, set main  */
    main_ = data_node;   /* data_node, otherwise, set the     */
  else
    t_np->child = data_node;
  /* parent^.child to data_node        */

  data_node->child = c_list;   /* Set data_node's children list.    */

  /* not coincident */
  /* Otherwise, put on next list of data_node*/
}


Local long MAX(long a, long b)
{
  if (a > b)
    return a;
  else
    return b;
}

Local long MIN(long a, long b)
{
  if (a < b)
    return a;
  else
    return b;
}


/*******************************************************************************/

boolean get_rbb(point *start, point *finish)
{
  /* draws an rbb on the screen, and returns its
     start and end positions in LAMBDA COORDINATES;
     when called, start is in SCREEN COORDINATES

     get_rbb returns TRUE if box successfully got, FALSE otherwise
  */
  boolean Result;
  point fin;
  tablet_info t_t;
  point lambda_start;

  gsave();

  making_wire = true;
  debounce();   /* Get the pen up                    */
  xor_on();   /* Rubber band boxes are in xor      */
  m_nocursor();   /* We know where the cursor is from  */
  lambda_start = scale_down(*start);
      /* MAS - save start in LAMBDA coords   */
  fin = *start;   /* Start off with an empty box       */
  *start = scale_up(lambda_start);
  draw_rbb(*start, fin);   /* Draw it to get in phase with loop */
  do {   /* Repeat RBB's                      */
    t_t = check_pen();   /* Find out where we are.            */
    if (t_t.y > TOP_OF_SCREEN)   /* If we are off the drawing pad,    */
      t_t = scale(t_t, 1);   /* Only scale the x coords           */
    else
      t_t = scale(t_t, 3);
    /* Otherwise scale both axes         */
    if (fin.x != t_t.x || fin.y != t_t.y)
    {  /* only redraw if you have actually moved */
      *start = scale_up(lambda_start);
      draw_rbb(*start, fin);   /* Erase the old RBB                 */
      fin.x = t_t.x;   /* Save this point for next go around*/
      fin.y = t_t.y;
      draw_rbb(*start, fin);   /* Draw the new RBB                  */
    }
  } while (!t_t.depressed && t_t.near_);
      /* Until they want to stop           */
  draw_rbb(*start, fin);   /* Erase the xor'ed RBB              */
  making_wire = false;
  m_nocursor();   /* GEG erase cursor                  */
  xor_off();   /* No more xor                       */
  if (t_t.near_ && t_t.y <= TOP_OF_SCREEN && start->x != fin.x &&
      start->y != fin.y)
      /* if pen not lifted (abort) and...  */
      {  /* area...                           */
    /* draw_rbb(start,fin);        { Don't draw the wire.              */
    finish->x = start->x;
    finish->y = start->y;   /* placeholders */
    start->x = MIN(start->x, fin.x);
    start->y = MIN(start->y, fin.y);
    finish->x = MAX(finish->x, fin.x);
    finish->y = MAX(finish->y, fin.y);

    *start = scale_down(*start);
    *finish = scale_down(*finish);
    Result = true;   /* successfully got rbb */
  } else
    Result = false;
  /* If they ended the box in the pad  */
  /* no zero width wires   */
  grestore();
  debounce();   /* make sure pen is up */
  return Result;
}


void make_wire(point start, short l)
{
  point finish;

  if (!get_rbb(&start, &finish))
    return;
  add_wire(start, finish, l, "", NULL);
  small_graphics();
  draw_wire(last_added->ll, last_added->ur);
  big_graphics();
}


/*$if false$
PROCEDURE make_wire(start : point; l : shortint);

VAR
   fin            :     point;
   t_t            :     tablet_info;
   lambda_start   :     point;

BEGIN
   making_wire := true;
   debounce;                              { Get the pen up                    }
   xor_on;                                { Rubber band boxes are in xor      }
   m_nocursor;                            { We know where the cursor is from  }
   lambda_start := scale_down (start);  { MAS - save start in LAMBDA coords   }
   fin := start;                          { Start off with an empty box       }
   start := scale_up (lambda_start);
   draw_rbb(start,fin);                   { Draw it to get in phase with loop }
   REPEAT                                 { Repeat RBB's                      }
      t_t := check_pen;                     { Find out where we are.            }
      IF    t_t.y > top_of_screen         { If we are off the drawing pad,    }
      THEN  t_t := scale(t_t,1)           { Only scale the x coords           }
      ELSE  t_t := scale(t_t,3);          { Otherwise scale both axes         }
      if (fin.x <> t_t.x) or (fin.y <> t_t.y) then
        begin                        { only redraw if you have actually moved }
           start := scale_up (lambda_start);
           draw_rbb(start,fin);           { Erase the old RBB                 }
           fin.x := t_t.x;                { Save this point for next go around}
           fin.y := t_t.y;
           draw_rbb(start,fin);           { Draw the new RBB                  }
        end;
   UNTIL t_t.depressed or not t_t.near;   { Until they want to stop           }
   draw_rbb(start,fin);                   { Erase the xor'ed RBB              }
   making_wire := false;
   m_nocursor;                            { GEG erase cursor                  }
   xor_off;                               { No more xor                       }
   IF t_t.near                            { if pen not lifted (abort) and...  }
     and (t_t.y <= top_of_screen)         { If they ended the box in the pad  }
     and (start.x <> fin.x) and (start.y <> fin.y)    { no zero width wires   }
   THEN  BEGIN                            { area...                           }
            draw_rbb(start,fin);          { Draw the wire.                    }
            add_wire(scale_down(start),   {   and add it to the tree.         }
                   scale_down(fin),l,'', nil);
         END;
   debounce;                              { make sure pen is up }
END;
$end$*/

/*******************************************************************************/

/* accepts two points on the screen-grid, and draws a box between them */
void draw_wire_box(long ax, long ay, long bx, long by)
{
  if (curr_style != 0)   /* start line style out at same point each time */
    m_linestyle(curr_style);
  switch (curr_fillp) {

  case -3:  /* Cross, same linestyle as border, black filled */
    filledbox(ax, ay, bx, by, 0);
    m_color(curr_fillc);
    m_drawline(ax, ay, bx, by);
    m_drawline(bx, ay, ax, by);
    m_color(curr_color);
    break;

  case -2:  /* Cross, same linestyle as border, not black filled */
    m_drawrect(ax, ay, bx, by);
    m_color(curr_fillc);
    m_drawline(ax, ay, bx, by);
    m_drawline(bx, ay, ax, by);
    m_color(curr_color);
    break;

  case -1:
    m_drawrect(ax, ay, bx, by);
    break;

  case 0:
    filledbox(ax, ay, bx, by, curr_fillc);
    break;

  case 1:
  case 2:
  case 3:
  case 4:
  case 5:
  case 6:
  case 7:
  case 8:
    pat_fillbox(ax, ay, bx, by, curr_fillp);
    break;

  default:
    printf("\007\007\007\007\007Illegal pattern in draw_wire_box=\007\007\007%d\n",
	   curr_fillp);
    break;
  }
}


/* Accepts two points on the lambda-grid, and draws a box between them */
void draw_wire(point a, point b)
{
  tr_4(a.x, a.y, b.x, b.y);
  draw_wire_box(tr_ax, tr_ay, tr_bx, tr_by);
}


/*******************************************************************************/

Static void start_deferred(void)
{
  last_deferred = deferred_stack;
}


Static void add_deferred(point ll, point ur, long layer)
{
  deferred_rec *tmp_deferred;

  if (last_deferred == NULL) {
    tmp_deferred = Malloc(sizeof(deferred_rec));
    check_mem();
    tmp_deferred->next = deferred_stack;
    deferred_stack = tmp_deferred;
  } else {
    tmp_deferred = last_deferred;
    last_deferred = last_deferred->next;
  }
  tmp_deferred->ll = ll;
  tmp_deferred->ur = ur;
  tmp_deferred->layer = layer;
  /*write(#139'Ad',layer:1,#136);*/
}


Static void end_deferred(void)
{
  deferred_rec *tmp_deferred, *WITH;

  tmp_deferred = deferred_stack;
  while (tmp_deferred != last_deferred) {
    WITH = tmp_deferred;
    if (redisp_last_layer != WITH->layer) {
      redisp_last_layer = WITH->layer;
      set_layer(redisp_last_layer);
    }
    draw_wire(WITH->ll, WITH->ur);
    tmp_deferred = WITH->next;
    /*write(#139'Dd',layer:1,#136);*/
  }
}


/*******************************************************************************/

Static void redisplay2(node *t_p)
{
  short qlayer;   /* quick (local) layer */
  bb_states bb_save;
  port_node *ports;
  point a, b;
  node *WITH;

  if (asm_memavail() < min_mem_recurse) {
    show_message("\007Low on memory in REDISPLAY", false);
    goto _L1;
  }

  bb_save = bb_state;
  while (t_p != NULL) {
    WITH = t_p;
    bb_state = bb_save;
    if (bb_state == bb_on || bb_check(WITH->ll, WITH->ur) != bb_off)
    {   /*t_p^.*/
      qlayer = WITH->layer;   /*t_p^.*/
      if (layers_on[qlayer - min_layer]) {
	if (deferred[qlayer - min_layer])   /*t_p^.*/
	  add_deferred(WITH->ll, WITH->ur, qlayer);
	else {
	  if (redisp_last_layer != qlayer) {   /*t_p^.*/
	    redisp_last_layer = qlayer;
	    set_layer(redisp_last_layer);
	  }
	  /*t_p^.*/
	  draw_wire(WITH->ll, WITH->ur);
	}
	/*t_p^.*/
      }
      ports = WITH->portlist;   /*t_p^.*/
      if (ports != NULL && display_ports) {   /*t_p^.*/
	while (ports != NULL) {
	  a = transform_point(ports->p1);   /* XFORM */
	  b = transform_point(ports->p2);
	  m_linestyle(0);
	  m_move(a.x, a.y);
	  m_color(BLACK);
	  m_draw(b.x, b.y);
	  m_move(a.x, a.y);

	  if (redisp_last_layer != ports->layerno)
	  {   /* restore current color */
	    redisp_last_layer = ports->layerno;
	    set_layer(redisp_last_layer);
	  } else
	    m_color(curr_color);

	  m_linestyle(2);   /* set coarse dotted style */
	  m_draw(b.x, b.y);
	  m_linestyle(curr_style);   /* restore current style */
	  ports = ports->next;
	}
      }
      if (WITH->child != NULL)   /*t_p^.*/
	redisplay2(WITH->child);
    }
    /*t_p^.*/
    /* else write(#137'G'#136) clipping in geometry */
    t_p = WITH->next;   /*t_p^.*/
  }
_L1: ;
}


void redisplay(node *t_p)
{
  redisp_last_layer = -1;
  start_deferred();
  redisplay2(t_p);
  end_deferred();
}


/*******************************************************************************/

void refresh_geom(void)
{
  gsave();
  xor_off();

  /* NEW - never screw with the menu!! */
  small_graphics();
  clear_screen();
  m_graphics_on();

  make_matrix();   /* re-make initial matrix */
  bb_state = bb_clip;
  redisplay(main_);
  big_graphics();
  if (!intzoom) {   /*show_message('Non-integer ZOOM',true);   */
    m_color(WHITE);
    m_move(20, 355);
    m_displaytext("Non-integer Zoom");
  }
  grestore();
}


/*******************************************************************************/

void small_clear(void)
{
  small_graphics();
  clear_screen();
  m_graphics_on();
  big_graphics();
}


/*******************************************************************************/

Static void SM(void)
{
  printf("|%6ld%6ld|    1\n", tr_ctm.a, tr_ctm.b);
  printf("|%6ld%6ld|   ---\n", tr_ctm.c, tr_ctm.d);
  printf("|%6ld%6ld|%6ld\n", tr_ctm.e, tr_ctm.f, tr_ctm.g);
}


Static void show_routing(routing *rt)
{
  /* draw the routing within a composition cell */
  routing *rt_ptr, *rt_horiz;
  point tp1, tp2;

  rt_horiz = rt;
  while (rt_horiz != NULL) {
    rt_ptr = rt_horiz;
    set_layer(rt_ptr->layer);
    while (rt_ptr != NULL) {
      /* writeln (rt_ptr^.pt1.x, rt_ptr^.pt1.y, rt_ptr^.pt2.x, rt_ptr^.pt2.y); */
      tp1 = transform_point(rt_ptr->pt1);
      tp2 = transform_point(rt_ptr->pt2);
      /* writeln (' ',tp1.x, tp1.y, tp2.x, tp2.y);    */
      m_move(tp1.x, tp1.y);
      m_draw(tp2.x, tp2.y);
      rt_ptr = rt_ptr->to_point;
    }

    rt_horiz = rt_horiz->next;
  }
}


void show_comp_2(c_cell *root)
{
  /* stacks up a transformation matrix */
  point tp1, tp2;
  long wid;
  Char pr_name[81];
  short ylabelpos;   /* y position (screen units) of label*/
  tr_matrix old_matrix;
  long t;
  bb_states bb_save;
  port_node *ports;
  Char STR2[256];

  old_matrix = tr_ctm;
  bb_save = bb_state;

  if (asm_memavail() < min_mem_recurse) {
    show_message("\007Low on memory in SHOW_COMP", false);
    return;
  }
  TRY(try2);
    while (root != NULL) {
      bb_state = bb_save;

      pl_tran(root->ll.x, root->ll.y);
      pl_xform(root->xform);

      /* TP2 is used in bounding box calculation and also if the cell is */
      /* closed. */
      tp2.x = root->ur.x - root->ll.x;
      tp2.y = root->ur.y - root->ll.y;

      if (bb_state == bb_on || bb_check(zero_point, tp2) != bb_off) {
	/* Ok to draw this cell */
	if (root->status == OPENED) {
	  if (root->tag == COMP) {
	    if (fleshed_out_routing)
	      draw_routing(root->UU.U1.c_d->routing_);
	    else
	      show_routing(root->UU.U1.c_d->routing_);
	    show_comp_2(root->UU.U1.c_d->data);
	  } else
	    redisplay(root->UU.g_d->data);
	} else {  /* cell must be closed, so draw it */
	  set_layer(comp_box);
	  draw_wire(zero_point, tp2);

	  if (root->tag == GEOM && display_ports) {
	    /* show ports if any */
	    ports = root->UU.g_d->portlist;
	    while (ports != NULL) {
	      m_color(BLACK);
	      draw_wire(ports->p1, ports->p2);
	      m_color(ports->layerno);
	      draw_wire(ports->p1, ports->p2);
	      ports = ports->next;
	    }
	  }

	  tp1 = transform_point(zero_point);
	  tp2 = transform_point(tp2);

	  if (tp1.x > tp2.x) {
	    t = tp1.x;
	    tp1.x = tp2.x;
	    tp2.x = t;
	  }
	  if (tp1.y > tp2.y) {
	    t = tp1.y;
	    tp1.y = tp2.y;
	    tp2.y = t;
	  }


	  if (tp2.y - tp1.y > 7) {
	    wid = tp2.x - tp1.x;
	    if (root->tag == GEOM)
	      strcpy(pr_name, root->UU.g_d->name);
	    else
	      strcpy(pr_name, root->UU.U1.c_d->name);
	    if (*root->prefix != '\0')
	      sprintf(pr_name + strlen(pr_name), ".%s", root->prefix);

	    if (root->xform > 0 && root->xform < 7)
	      m_color(root->xform);
	    else if (root->xform == 0)
	      m_color(BR_WHITE);
	    else
	      m_color(BUR);

	    if (gstate.ref_in_black)
	      m_color(0);

	    if (wid - 2 >= strlen(pr_name) * 7) {
	      ylabelpos = (tp1.y + tp2.y) / 2 - 3;
	      m_move(tp1.x + (wid - strlen(pr_name) * 7) / 2, ylabelpos);
	      if (ylabelpos < TOP_OF_SCREEN - 3)
		display_text(pr_name);
	    } else {
	      wid = (wid - 4) / 7;
	      ylabelpos = (tp1.y + tp2.y) / 2 - 3;
	      m_move(tp1.x + 2, ylabelpos);
	      if (ylabelpos < TOP_OF_SCREEN - 3) {
		sprintf(STR2, "%.*s", (int)wid, pr_name);
		display_text(STR2);
	      }
	    }
	  }

	}
      }
      root = root->next;   /* deal with its siblings */
      tr_ctm = old_matrix;   /* restore transformation matrix */
    }
  RECOVER(try2);
    if (P_escapecode == 10)
      _Escape(P_escapecode);
    else {
      show_error("Error in procedure SHOW_COMP_2", false);
      _Escape(10);
    }
  ENDTRY(try2);
}


void show_comp(void)
{
  tr_matrix old_mat;

  /*$if display_test$ TIME_start; $end$*/
  gsave();
  xor_off();
  make_matrix();   /* re-make initial matrix */
  old_mat = tr_ctm;   /* Save it for later */
  small_graphics();
  clear_screen();
  m_graphics_on();
  bb_state = bb_clip;
  /*$if display_test$ TIME_end('Setup and clear screen'); $end$*/

  /*$if display_test$ TIME_start; $end$*/
  if (fleshed_out_routing)
    draw_routing(comp_cells->routing_);
  else
    show_routing(comp_cells->routing_);
  /*$if display_test$ TIME_end('Routing'); $end$*/

  /*$if display_test$ TIME_start; $end$*/
  show_comp_2(comp_cells->data);
  /*$if display_test$ TIME_end('Drawing graphics'); $end$*/

  /*$if display_test$ TIME_start; $end$*/
  big_graphics();
  if (!intzoom) {   /*show_message('Non-integer ZOOM',true);   */
    m_color(WHITE);
    m_move(20, 355);
    m_displaytext("Non-integer Zoom");
  }
  grestore();
  /*$if display_test$ TIME_end('Shutdown time'); $end$*/
}  /* call show_comp_2 with root of tree */


/*******************************************************************************/

void fast_add(point p1, point p2, short l, Char *name, port_node *port)
{

  /* This routine adds a wire node into the tree.  It assumes that the nodes */
  /*  being added are in the order of top/down and then left to right (i.e.  */
  /*  Write node, recur down, recur across.  This is currently being used by */
  /*  the read routine and the NEW routine (for geometry cells).  In both    */
  /*  cases, we know the ordering of the nodes (and there is no tree before  */
  /*  the operation.                                                         */
  node *t_fa_p, *t_np, *t_op;
  boolean end_loop;
  port_node *port2;

  t_fa_p = get_a_node();
  t_fa_p->ll = p1;
  t_fa_p->ur = p2;
  t_fa_p->layer = l;
  t_fa_p->child = NULL;
  strcpy(t_fa_p->name, name);
  while (port != NULL) {
    port2 = get_port_node();
    *port2 = *port;
    port2->next = t_fa_p->portlist;
    t_fa_p->portlist = port2;
    port = port->next;
  }

  last_added = t_fa_p;

  t_np = main_;
  t_op = NULL;
  end_loop = false;
  do {
    if (t_np != NULL) {
      if (within(t_np, p1, p2, 2)) {
	t_op = t_np;
	t_np = t_np->child;
      } else
	end_loop = true;
    }
  } while (end_loop != true && t_np != NULL);

  t_fa_p->next = t_np;
  if (t_op == NULL) {
    main_ = t_fa_p;
    t_fa_p->parent = NULL;
  } else {
    t_op->child = t_fa_p;
    t_fa_p->parent = t_op;
  }
  t_fa_p->next = t_np;
}


/*******************************************************************************/

void reshuffle(node *shuf_obj)
{
  node *next_obj;

  /* Put last moved object(or any) back into tree one node at a time */
  if (asm_memavail() < min_mem_recurse) {
    show_message("\007Low on memory in RESHUFFLE", false);
    return;
  }
  while (shuf_obj != NULL) {
    /*reshuffle(shuf_obj^.child); */
    /* MAS--want it here for coincident boxes*/
    if (shuf_obj->layer != sel_box)
      add_wire(shuf_obj->ll, shuf_obj->ur, shuf_obj->layer, shuf_obj->name,
	       shuf_obj->portlist);
    reshuffle(shuf_obj->child);   /* so what if things get swizzled! */
    /* reshuffle(shuf_obj^.child);      { MAS */

    /* horizontal iteration vs. recursion */

    next_obj = shuf_obj->next;
    if (!retain_nodes) {
      shuf_obj->next = NULL;   /* Don't let recl_nodes get these!! */
      shuf_obj->child = NULL;
      recl_nodes(&shuf_obj);
    }
    shuf_obj = next_obj;
  }
}


/*******************************************************************************/

void fast_shuf(node *shuf_obj)
{

  /* This routine puts nodes back into the main tree (just like reshuffle), */
  /*  However, since it is only called when it is copying a tree, it knows  */
  /*  Already that it is in the right order, and does not need to check if  */
  /*   this node meets any criteria.                                        */
  if (asm_memavail() < min_mem_recurse) {
    show_message("\007Low on memory in FAST_SHUF", false);
    return;
  }
  while (shuf_obj != NULL) {
    if (shuf_obj->layer != sel_box)
      fast_add(shuf_obj->ll, shuf_obj->ur, shuf_obj->layer, shuf_obj->name,
	       shuf_obj->portlist);
    fast_shuf(shuf_obj->child);
    shuf_obj = shuf_obj->next;
  }
}


/*******************************************************************************/

void reinstate(void)
{
  /* Puts the last moved nodes back into the tree */
  if (last_moved->parent == NULL)
    main_ = last_moved->next;
  else
    last_moved->parent->child = last_moved->next;
  last_moved->next = NULL;
  reshuffle(last_moved);
  if (auto_refresh)
    refresh_geom();
  last_moved = NULL;
  /* disp_all_one_sel; */
  /* draw_menu_square(build_mode, PURPLE);     { Show which mode we are in   */
}


/*******************************************************************************/

void inc_nodes(node *n, long dx, long dy)
{
  port_node *port;
  node *WITH;

  if (asm_memavail() < min_mem_recurse) {
    show_message("\007Low on memory in INC_NODES", false);
    return;
  }
  while (n != NULL) {
    WITH = n;
    WITH->ll.x += dx;
    WITH->ll.y += dy;
    WITH->ur.x += dx;
    WITH->ur.y += dy;
    port = WITH->portlist;
    while (port != NULL) {
      port->p1.x += dx;
      port->p2.x += dx;
      port->p1.y += dy;
      port->p2.y += dy;
      port = port->next;
    }
    inc_nodes(WITH->child, dx, dy);
    n = WITH->next;
  }
}


/*******************************************************************************/


void node_out_of_tree(node *obj)
{  /*  it out of the tree             */
  node *obj_parent, *scan;

  /* Takes the passed node, and gets */
  obj_parent = obj->parent;
  if (obj_parent == NULL) {
    if (main_ == obj)
      main_ = obj->next;
    else {
      scan = main_;   /*####How do we know the loop will */
      while (scan->next != obj)   /* terminate??? */
	scan = scan->next;
      scan->next = obj->next;
    }
  } else if (obj->parent->child == obj)
    obj->parent->child = obj->next;
  else {
    scan = obj->parent->child;
    while (scan->next != obj)   /*####How do we know the loop will */
      scan = scan->next;
    /* terminate??? */
    scan->next = obj->next;
  }
  obj->next = NULL;
}


/*******************************************************************************/

void restore_in_tree(node *n)
{
  node *obj_parent;

  obj_parent = parent_of(main_, n->ll, n->ur, 2);
  n->parent = obj_parent;
  if (obj_parent == NULL) {
    n->next = main_;
    main_ = n;
  } else {
    n->next = obj_parent->child;
    obj_parent->child = n;
  }
}


/*******************************************************************************/


void move_a_tree(node *obj, point start)
{
  point old_pt, new_pt;
  tablet_info new_pen;

  xor_on();
  small_graphics();
  if (!point_to_point_move) {
    if (first_dotted)
      gstate.dotted = true;
    redisplay(obj);
    gstate.dotted = true;
    redisplay(obj);
  }
  old_pt = start;
  do {
    new_pen = check_pen();
    new_pt.x = new_pen.x;
    new_pt.y = new_pen.y;
    if (point_to_point_move) {
      new_pt = scale_down(new_pt);
      new_pt = scale_up(new_pt);   /* put it on lambda grid */
      m_cursor(new_pt.x, new_pt.y);   /* draw cursor */
    }
    new_pt = scale_down(new_pt);
    if (new_pt.x != old_pt.x || new_pt.y != old_pt.y || !new_pen.near_) {
      if (!point_to_point_move)   /* erase old shape */
	redisplay(obj);
      inc_nodes(obj, new_pt.x - old_pt.x, new_pt.y - old_pt.y);
      if (new_pen.near_ && !point_to_point_move)
	redisplay(obj);
      old_pt = new_pt;
    }
  } while (!new_pen.depressed && new_pen.near_);
  if (!new_pen.near_) {   /* move object back to where it started */
    inc_nodes(obj, start.x - new_pt.x, start.y - new_pt.y);
    if (!point_to_point_move)
      redisplay(obj);
  }
  xor_off();
  gstate.dotted = false;
  big_graphics();
}


Local double dist(long x1, long y1, long x2, long y2)
{
  long TEMP, TEMP1;

  TEMP = x2 - x1;
  TEMP1 = y2 - y1;
  return sqrt((double)(TEMP * TEMP + TEMP1 * TEMP1));
}



void draw_routing(routing *rt)
{
  boolean filled;
  double r;
  long dx, dy, radius;
  routing *rt2;
  point pt1, pt2;
  long xa[4], ya[4];   /* actually, 4 is enough */

  filled = (disp_style != 0);   /* this is not really correct */
  while (rt != NULL) {   /* rt will run across the list */
    rt2 = rt;   /* this pointer will run down the list */
    while (rt2 != NULL) {
      radius = (long)(zoom * rt2->width / 2);
      pt1 = transform_point(rt2->pt1);
      pt2 = transform_point(rt2->pt2);
      if (radius != 0) {
	r = dist(pt1.x, pt1.y, pt2.x, pt2.y) / radius;
	dx = pt2.x - pt1.x;
	dy = pt2.y - pt1.y;
	if (r == 0) {
	  dx = 0;
	  dy = 0;
	  r = 1.0;
	}
      } else {
	dx = 0;
	dy = 0;
	r = 1.0;
	radius = 0;
      }

      set_layer(rt2->layer);
      if (manhattan_routing) {  /* draw skeletally connected boxes */
	xa[0] = pt1.x - (long)floor(dx / r - dy / r + 0.5);
	    /* To extend wire ends */
	ya[0] = pt1.y - (long)floor(dy / r + dx / r + 0.5);
	    /* by 1 radius, remove */
	xa[1] = pt1.x - (long)floor(dx / r + dy / r + 0.5);
	    /* comment braces */
	ya[1] = pt1.y - (long)floor(dy / r - dx / r + 0.5);
	    /* around first 'round' */
	xa[2] = pt2.x + (long)floor(dx / r - dy / r + 0.5);   /* term */
	ya[2] = pt2.y + (long)floor(dy / r + dx / r + 0.5);
	xa[3] = pt2.x + (long)floor(dx / r + dy / r + 0.5);
	ya[3] = pt2.y + (long)floor(dy / r - dx / r + 0.5);
      } else {  /* draw circles */
	xa[0] = pt1.x - (long)floor(0.5 - dy / r);   /*dx/r*/
	/* To extend wire ends */
	ya[0] = pt1.y - (long)floor(dx / r + 0.5);   /*dy/r*/
	/* by 1 radius, remove */
	xa[1] = pt1.x - (long)floor(dy / r + 0.5);   /*dx/r*/
	/* comment braces */
	ya[1] = pt1.y - (long)floor(0.5 - dx / r);   /*dy/r*/
	/* around first 'round' */
	xa[2] = pt2.x + (long)floor(0.5 - dy / r);   /*dx/r*/
	/* term */
	ya[2] = pt2.y + (long)floor(dx / r + 0.5);   /*dy/r*/
	xa[3] = pt2.x + (long)floor(dy / r + 0.5);   /*dx/r*/
	ya[3] = pt2.y + (long)floor(0.5 - dx / r);   /*dy/r*/
      }
      if (filled) {  /* draw insides */
	if (curr_fillp == -1)
	  m_color(curr_color);
	else
	  m_color(curr_fillc);
	m_fillpoly(4, (int *) xa, (int *) ya);   /* draw the insides here */
      }
      m_color(curr_color);
      if (!filled)
	m_linestyle(curr_style);
      m_drawpoly(4, (int *) xa, (int *) ya);   /* draw the outline here */
      rt2 = rt2->to_point;
    }

    /* now draw circles */
    rt2 = rt;   /* traverse the tree again */
    /* draw the first circle in the path, if not Manhattan_routing */
    radius = (long)(zoom * rt2->width / 2);
    pt1 = transform_point(rt2->pt1);
    set_layer(rt2->layer);
    if (!manhattan_routing) {
      if (filled) {
	if (curr_fillp == -1)
	  m_color(curr_color);
	else
	  m_color(curr_fillc);
	m_ellipse(pt1.x, pt1.y, radius, radius, curr_color);
      } else {
	m_color(m_trans);   /* make inside transparent */
	if (!filled)
	  m_linestyle(curr_style);
	m_ellipse(pt1.x, pt1.y, radius, radius, curr_color);
      }
    }
    /* now draw the remaining circles, if necessary */
    while (rt2 != NULL) {
      radius = (long)(zoom * rt2->width / 2);
      pt1 = transform_point(rt2->pt1);
      pt2 = transform_point(rt2->pt2);
      set_layer(rt2->layer);
      if (!manhattan_routing) {
	if (filled) {
	  if (curr_fillp == -1)
	    m_color(curr_color);
	  else
	    m_color(curr_fillc);
	  m_ellipse(pt2.x, pt2.y, radius, radius, curr_color);
	} else {
	  m_color(m_trans);   /* make inside transparent */
	  if (!filled)
	    m_linestyle(curr_style);
	  m_ellipse(pt2.x, pt2.y, radius, radius, curr_color);
	}
      }
      rt2 = rt2->to_point;
    }
    rt = rt->next;   /* draw next routing wire */
  }
  m_linestyle(0);
}







/* End. */
