/*
 * Copyright (c) 1992, The Geometry Center
 *                     University of Minnesota 
 *                     1300 South Second Street
 *                     Minneapolis, MN  55454
 *
 * email address: software@geom.umn.edu
 *
 * This software is copyrighted as noted above.  It may be freely copied,
 * modified, and redistributed, provided that the copyright notice is
 * preserved on all copies.
 *
 * There is no warranty or other guarantee of fitness for this software,
 * it is provided solely "as is".  Bug reports or fixes may be sent
 * to the authors, who may or may not act on them as they desire.
 *
 * You may not include this software in a program or other software product
 * without supplying the source, or without informing the end-user that the
 * source is available for no extra charge.
 *
 * If you modify this software, you should include a notice giving the
 * name of the person performing the modification, the date of modification,
 * and the reason for such modification.
 *
 *     The National Science and Technology Research Center for
 *      Computation and Visualization of Geometric Structures
 */

/* Generated by Interface Builder */

#import <objc/objc.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include "link_types.h"
#include "link_edit_types.h"
#include "link_edit_global.h"
#include "link.h"
#import "Link.h"
#import <appkit/Application.h>
#import <appkit/Window.h>
#import <appkit/Text.h>
#import <appkit/TextField.h>
#import <appkit/Matrix.h>
#import <appkit/Button.h>
#import <appkit/Cell.h>
#import <appkit/Panel.h>
#import <appkit/MenuCell.h>
#import "DisplayView.h"
#import "EditView.h"
#import "Distributor.h"
#import "PrefObj.h"
#import "fileops.h"
#import <zone.h>
#import <mach.h>
#include <memory.h>
#include <stdlib.h>
#import "HelpObj.h"

int isAnchorOrLooseEnd(LinkPointList *pnt)
{
  return (pnt->isAnchor ||
	  ((pnt->next == NULL || pnt->previous->previous == NULL)
	   && pnt->linkptr->closed == 0));
}


/* Counts crossings, labels them, and sets the linkptr fields */
int countCrossings(LinkStatus *gnrc, BOOL countAnchors)
{
  int numx = 0;
  LinkList *lnk;
  LinkPointList *pnt;
  LinkCrossingList *cross;

  lnk = gnrc->link.next;
  while(lnk != NULL) {
    pnt = lnk->point.next;
    while(pnt != NULL) {
      /* Count as a crossing if it's an anchor or a loose end */
      pnt->linkptr = lnk;
      if (isAnchorOrLooseEnd(pnt) && countAnchors)
	pnt->point_id = ++numx;
      cross = pnt->crossing.next;
      while(cross != NULL)
	{
	  if(cross->z > 0.0)
	    {
	      numx++;
	      /* Over crossing */
	      cross->id = numx;
	      /* Under crossing */
	      cross->crossing->id = numx;
	    }
	  cross = cross->next;
	}
      pnt = pnt->next;
    }
    lnk = lnk->next;
  }
  return numx;
}

void cleanLink(LinkStatus *gnrc)
{
  LinkList *lnk, *tl;
  lnk = gnrc->link.next;
  while (lnk != NULL)
    {
      tl = lnk->next;
      if (lnk->point.next == NULL)
	LinkDeleteLink(gnrc, lnk);
      lnk = tl;
    }
}  

int canRedrawLink(id linkObj)
{
  LinkStatus *gnrc;
  LinkList *lnk;
  LinkPointList *pnt;
  LinkCrossingList *cross;
  int retvalue = 1, numx = 0;

  gnrc = [linkObj get_status];

  /* First, clean up list of strands, to delete all empty strands */
  cleanLink(gnrc);
  
  lnk = gnrc->link.next;
  numx = 0;
  while(lnk != NULL) {
    if (lnk->closed == 0) retvalue = 0;
    pnt = lnk->point.next;
    while(pnt != NULL) {
      pnt->linkptr = lnk;
      cross = pnt->crossing.next;
      while(cross != NULL)
	{
	  if(cross->z > 0.0)
	    {
	      numx++;
	      /* Over crossing */
	      cross->id = numx;
	      /* Under crossing */
	      cross->crossing->id = numx;
	    }
	  cross = cross->next;
	}
      pnt = pnt->next;
    }
    lnk = lnk->next;
  }

  if (numx < 2) retvalue = 0;
  return retvalue;
}

/* Will take a Conway name string (like "3 4, 5") and convert spaces
   to underscores (3_4,_5) so it can be used in a filename */
/*char *spaceToUnderscore(char *s)
{
  char *t = (char *) malloc (sizeof(char) * (strlen(s) + 1));
  char *probe = t;
  int i, j = strlen(s);
  for (i=0; i<=j; i++)
    *probe++ = (*s++ == ' ') ? '_' : *(s-1);
  return t;
}*/

@implementation Link

- init
{
  [self initTitle:"Untitled"];
  return self;
}

- initTitle:(char *) title
{
  NXRect gbRect;
  NXZone *zone = [self zone];
  NXCoord x, y;
  
  /* Create window, display view, and editor view for link */
  [super init];
  [NXApp loadNibSection:"Link.nib" owner:self withNames:NO fromZone:zone];
  [window setTitle:title];
  [window setDelegate:self];

  /* Make edit and display views for Link */
  [graphicBox getFrame:&gbRect];
  gbRect.origin.x = gbRect.origin.y = 0;
  editView = [[EditView allocFromZone:zone] initFrame:&gbRect];
  displayView = [[DisplayView allocFromZone:zone] initFrame:&gbRect];

  /* Have views and controller point to me */
  [editView setLink:self];
  [displayView setLink:self];

  /* Start with editor view */
  [graphicBox setContentView:editView];

  /* Initialize editor variables */
  mygnrc = (LinkEditStatus *) NXZoneMalloc (zone, sizeof(LinkEditStatus));
  mylink_status = (LinkStatus *) NXZoneMalloc (zone, sizeof(LinkStatus));
  mydata = (LinkData *) NXZoneMalloc (zone, sizeof(LinkData));
  LinkSetUp(self);
  LinkInitialize(self);
  mygnrc->link_status = (void *) mylink_status;
  LinkEditInitialize(self);

  /* Initialize displayer variables */
  linkptr = NULL; matrix = NULL; row = NULL;

  /* Initialize other miscellaneous instance variables */
  linkfile = NULL;
  mode = EDITOR;
  isDirty = YES;
  redrawType = EVOLUTION;
  distributor = NXGetNamedObject("DistributorInstance", NXApp);
  [distributor getNextWinLoc :&x :&y];
  [window moveTo:x :y];
  tangle = 0;
  prefObj = NXGetNamedObject("PrefObjInstance", NXApp);
  name = NULL;
  currentType = EDITOR;
  parameters = (float **) NXZoneMalloc(zone, NUM_LOCAL_SETTINGS*sizeof(float *));
  parameters[0] = &amp;
  parameters[1] = &ampB;
  parameters[2] = &fffa;
  parameters[3] = &fffb;
  parameters[4] = &ppc;
  parameters[5] = &segMax;
  parameters[6] = &segScale;
  parameters[7] = &sep;
  parameters[8] = (float *) &texture;
  parameters[9] = (float *) &redraw;
  parameters[10] = (float *) &rulers;
  parameters[11] = (float *) &arrows;
  parameters[12] = (float *) &axes;
  parameters[13] = (float *) &vertices;
  parameters[14] = (float *) &anchorPoints;
  parameters[15] = (float *) &sparse;
  
  /* Initialize parameters */
  [self imbibeDefaults];
  [self initEditorSettings];

  [self printMessage:"Click to add point"];

  /* Draw them all */

  [window makeKeyAndOrderFront:self];
  [window display];
  [editView setModeEnter];
  [window display];
  return self;
}

- initEditorSettings
{
  LinkSetRulers(mylink_status, (int) rulers);
  LinkSetArrows(mylink_status, (int) arrows);
  LinkSetAxes(mylink_status, (int) axes);
  LinkSetVertices(mylink_status, (int) vertices);
  LinkSetAnchors(mylink_status, (int) anchorPoints);
  return self;
}

- print
{
  switch(mode)
    {
    case EDITOR:
      [editView printPSCode:self];
      break;
    case DISPLAYER:
      [displayView printPSCode:self];
      break;
    }
  return self;
}

/*** File operations ***/

- saveLinktool:(const char *) filename
{
  NXTypedStream *outfile = NXOpenTypedStreamForFile(filename, NX_WRITEONLY);
  if (outfile == NULL)
    {
      NXRunAlertPanel("Save error",
		      "Could not save to file %s",
		      "OK", NULL, NULL, filename);
      return self;
    }
  NXWriteRootObject(outfile, self);
  NXCloseTypedStream(outfile);
  return self;
}

- setFileInfo:(const char *) filename :(char) file_type
{
  NXZone *zone = [self zone];
  if (linkfile)
    NXZoneFree(zone, (char *) linkfile);
  linkfile = (const char *) NXZoneMalloc(zone, strlen(filename)+1);
  strcpy((char *) linkfile, filename);
  linkfileType = file_type;
  return self;
}

- saveEditor:(const char *) filename
{
  char strng[100];
  if (LinkSaveAll (mylink_status, filename) == 0)
    sprintf(strng, "Saved editor format in %s", filename);
  else sprintf(strng, "Could not save editor format");
  [self printMessage:strng];
  return self;
}

- loadEditor:(const char *) filename
{
  LinkClearData(mylink_status);
  if (LinkGetFile(mylink_status,filename,[self zone]) != 0)
    [self printMessage:"Bad file; this window should be gone"];
  else [window display];
  [self setFileInfo: filename :FF_EDITOR];
  return self;
}

- saveMatrix:(const char *) filename
{
  saveMatrix(self, filename);
  return self;
}

- loadMatrix:(const char *) filename
{
  loadMatrix(self, filename, &row, &matrix, &numcross);
  currentType = DISPLAYER;
  [self setModeDisplayer:self];
  [self setFileInfo: filename :FF_MATRIX];
  return self;
}

- saveSB:(const char *) filename
{
  if (canRedrawLink(self))
    saveSB(mylink_status, filename);
  else NXRunAlertPanel("Save error",
		       "Link must be closed and have at least two crossings to save to StringBean format",
		       "OK", NULL, NULL);
  return self;
}

- saveThreeD: (const char *) filename
{
  FILE *ofp = fopen(filename, "w");
  crossing *xingptr;
  int dir, i, j, tdir, total, segs;
  double ox, oy, xx, yy, d;
  char currentRType, origRedraw;
  float **values;

  if (ofp == NULL)
    {
      NXRunAlertPanel("Save error",
		      "Could not save to file %s",
		      "OK", NULL, NULL, filename);
      return self;
    }
  currentRType = [self doDraw:self];           /* Update linkptr */

  /* Write header of 3D knot file */
  fprintf(ofp, "LINK\n");
  fprintf(ofp, "%d\n", linkptr->nstrands);
  for (j=0; j<linkptr->nstrands; j++)
    {
      total = 0;
      for(i=0, xingptr = linkptr->strandlist[j], dir = linkptr->dirlist[j];
	  !((i>0 && xingptr == linkptr->strandlist[j]
	     && dir == linkptr->dirlist[j]) ||
	    xingptr->orient == TERMINUS);
	  i++)
	{
	  tdir = (xingptr->ud[dir] == UP) ? N0 : N1;
	  ox = xingptr->x;
	  oy = xingptr->y;
	  xingptr = xingptr->nhbr[dir];
	  xx = xingptr->x;
	  yy = xingptr->y;
	  d = sqrt(((xx - ox) * (xx - ox)) + ((yy - oy) * (yy - oy)));
	  segs = d/.2 + 4;
	  if (segs > segMax) segs = segMax;
	  total += segs+1;       /* need +1 to cover the crossing point */
	  dir = tdir;
	}
      fprintf(ofp, "%d\n", total);
    }
  values = [displayView getValues];
  origRedraw = *(char *) values[9];
  *(char *) values[9] = currentRType;
  [displayView linkdraw:linkptr :ofp :NULL];   /* Output to file */
  *(char *) values[9] = origRedraw;
  fclose(ofp);
  return self;
}

- write: (NXTypedStream *) outfile
{
  int i;
  char c;
  [super write:outfile];

  [distributor readParametersTo:parameters];
  
  /* Save link in editor format */
  if (mylink_status == NULL || mylink_status->link.next == NULL
      || mylink_status->link.next->point.next == NULL)
    {
      c = 'N';
      NXWriteType(outfile, "c", &c);
    }
  else
    {
      c = 'Y';
      NXWriteType(outfile, "c", &c);
      LinkSaveAll2(mylink_status, (void *) outfile, DEST_TS);
    }

  /* Save matrix (only save if matrix is available.  Put 'Y' if it
     is to follow; otherwise put 'N') */
  if (matrix)
    {
      c = 'Y';
      NXWriteType(outfile, "c", &c);
      NXWriteType (outfile, "i", &numcross);
      for (i=0; i<numcross; i++)
	NXWriteArray(outfile, "i", 5, matrix[i]);
    }
  else
    {
      c = 'N';
      NXWriteType(outfile, "c", &c);
    }

  /* Save tangle, notation info & currentType (again, only if it's available) */
  {
    int i = [[notationRB selectedCell] tag];
    NXWriteTypes (outfile, "sicccc", &tangle, &i, &conwayType,
		  &braidType, &currentType, &isClosed);
  }
  if (name)
    {
      c = 'Y';
      NXWriteType(outfile, "c", &c);
      NXWriteType(outfile, "*", &name);
    }
  else
    {
      c = 'N';
      NXWriteType(outfile, "c", &c);
    }

  /* Save parameters */
  for (i=0; i<8; i++)
    NXWriteType(outfile, "f", parameters[i]);
  for (i=8; i<15; i++)
    NXWriteType(outfile, "c", (char *) parameters[i]);
  NXWriteType(outfile, "i", (int *) parameters[15]);
  
  return self;
}

- read:(NXTypedStream *) infile
{
  NXZone *zone;
  int i;
  char c;
  [super read:infile];
  [self init];
  zone = [self zone];

  /* Load editor format */
  NXReadType(infile, "c", &c);
  if (c == 'Y')
    LinkGetFile2(mylink_status, (void *) infile, DEST_TS, zone);

  /* Load matrix */
  NXReadType (infile, "c", &c);
  if (c == 'Y')
    {
      NXReadType (infile, "i", &numcross);
      row = (int *) NXZoneMalloc(zone, 5*numcross*sizeof(int));
      matrix = (int **) NXZoneMalloc(zone, numcross*sizeof(int *));
      for(i=0; i<numcross; i++)
	matrix[i] = row + 5*i;
      for (i=0; i<numcross; i++)
	NXReadArray(infile, "i", 5, matrix[i]);
    }

  /* Load tangle, notation info & currentType */
  {
    int i;
    NXReadTypes (infile, "sicccc", &tangle, &i, &conwayType,
		 &braidType, &currentType, &isClosed);
    [notationRB selectCellWithTag:i];
    if (i==0)
      {
	[entryBox setTitle:"Conway name"];
	[RBBox setContentView:conwayRB];
      }
    else
      {
	[entryBox setTitle:"Braid name"];
	[RBBox setContentView:braidRB];
      }
  }
  NXReadType (infile, "c", &c);
  if (c == 'Y')
    NXReadType(infile, "*", &name);

  /* Load parameters */
  for (i=0; i<8; i++)
    NXReadType(infile, "f", parameters[i]);
  for (i=8; i<15; i++)
    NXReadType(infile, "c", (char *) parameters[i]);
  NXReadType(infile, "i", (int *) parameters[15]);

  /* Update appearances to reflect read-in values */
  [notationText setStringValue:name];
  [conwayRB selectCellWithTag:conwayType];
  [distributor writeParametersFrom:parameters];
  [self initEditorSettings];
  return self;
}


- showInspector:sender
{
  [distributor showInspector:self];
  return self;
}

- setModeEditor:sender
{
  if (mode == EDITOR) return self;
  if (mode == CONWAY)
    if ([self readNotation] == NULL) return self;
  [modeButton setTitle:"Editor"];
  [graphicBox setContentView:editView];
  [notices setStringValue:lastEMessage];
  if ([editView isEnterMode]) [editView setModeEnter];
  [window display];
  [distributor switchInspectorTo:EDITOR];
  mode = EDITOR;
  if (mylink_status == NULL || mylink_status->link.next == NULL
      || mylink_status->link.next->point.next == NULL)
    [self redrawToEditor];
  isDirty = YES;
  [window display];
  return self;
}

- setModeDisplayer:sender
{
  if (mode == DISPLAYER) return self;
  if (mode == EDITOR) [editView unsetModeEnter];
  if (mode == CONWAY)
    if ([self readNotation] == NULL) return self;
  if (mode == EDITOR && mylink_status->link.next
      && mylink_status->link.next->point.next) currentType = EDITOR;
  mode = DISPLAYER;
  [modeButton setTitle:"Displayer"];
  [graphicBox setContentView:displayView];
  [window display];
  [distributor switchInspectorTo:DISPLAYER];
  return self;
}

- setModeNotation:sender
{
  if (mode == CONWAY) return self;
  if (mode == EDITOR) [editView unsetModeEnter];
  mode = CONWAY;
  [modeButton setTitle:"Notation"];
  isDirty = YES;
  [graphicBox setContentView:[notationBox contentView]];
  [window display];
  [window flushWindow];
  [distributor switchInspectorTo:CONWAY];
  currentType = ([[notationRB selectedCell] tag]) ? BRAID : CONWAY;
  return self;
}

- setNotationConway:sender
{
  [entryBox setTitle:"Conway name"];
  [RBBox setContentView:conwayRB];
  [notationText setStringValue:""];
  currentType = CONWAY;
  [window display];
  return self;
}

- setNotationBraid:sender
{
  [entryBox setTitle:"Braid name"];
  [RBBox setContentView:braidRB];
  [notationText setStringValue:""];
  currentType = BRAID;
  [window display];
  return self;
}
  

/*** Editor methods ***/

- alternate
{
  /*******MUST CHECK FOR BOGUS FIELDS*********/
  LinkPointList *pnt;
  LinkCrossingList *xing;
  countCrossings(mylink_status, NO);    /* Set linkptr fields */
  cleanLink(mylink_status);
  resetVisited(mylink_status);
  if (mylink_status->link.next == NULL)
    {
      LinkPrintMessage("No link to alternate.");
      return self;
    }
  pnt = mylink_status->link.next->point.next;
  xing = getNextXing(mylink_status->link.next, &pnt);
  if (pnt == NULL || xing == NULL) return self;
  alternateFromCrossing(pnt, xing);
  [editView lockFocus];
  [editView clearScreen];
  [editView ReDrawLinkTopWindow:mylink_status];
  [editView unlockFocus];
  return self;
}

- reflect
{
  reflectLink(mylink_status);
  [editView lockFocus];
  [editView clearScreen];
  [editView ReDrawLinkTopWindow:mylink_status];
  [editView unlockFocus];
  return self;
}

- printMessage:(char *) s
{
  [notices setStringValue:s];
  if (mode == EDITOR) lastEMessage = s;
  return self;
}

- setEditorMode:(int) emode
{
  [distributor selectIfNecessary:emode];
  switch(emode)
    {
    case SELECT:
      [editView setModeSelect];
      break;
    case ENTER:
      [editView setModeEnter];
      break;
    case FLIP:
      [editView setModeFlip];
      break;
    case ZOOM:
      [editView setModeZoom];
      break;
    case TRANSLATE:
      [editView setModeTranslate];
      break;
    case ANCHORM:
      [editView setModeAnchor];
      break;
    }      
  return self;
}

/*** Displayer methods ***/


/* Also sets numcross counting anchors correctly */
- (char) whichToDisplay
{
  switch(currentType)
    {
    case CONWAY:
    case BRAID:
      if (name == NULL || !strcmp(name,""))
	return -1;
      return currentType;
    case EDITOR:
      if ((numcross = countCrossings(mylink_status, YES)) != 0)
	return EDITOR;
      /* Here, the currentType is EDITOR, and there's nothing in the editor */
      if (name == NULL || !strcmp(name,""))
	return -1;
      return ([[notationRB selectedCell] tag]) ? BRAID : CONWAY;
    default:
      return currentType;
    }
}


/* This is called by the distributor's "redraw", which is called by the
   inspector panel, or pop-list list; also called by displayView's drawSelf.
   Returns the redraw-type that must be used.  (Sometimes ANCHOR must be used,
   even though it's not set.) */
- (char) doDraw:sender
{
                            /* redraw type user currently selects */
  char currentRType = redraw = [distributor getRedrawType];
  char origRedraw;          /* To remember what the inspector was set to.
			       (So we TEMPORARILY change to ANCHOR if necessary.) */
  char silly_msg[100];
  BOOL isMessageSet = NO;
  char *msg = (char *) silly_msg;
  char which = [self whichToDisplay];
  float **values;
  if (mylink_status->num >= MAXSTRANDS)
    {
      [self printMessage:"Too many strands on link to display.  It would look simply horrible."];
      return -1;
    }
  if (which == -1)
    {
      [self printMessage:"No link to display"];
      return -1;
    }

  silly_msg[0] = '\0';
  
  /* Read display parameters */
  values = [displayView getValues];
  [distributor readParametersTo:values];

  if (currentType == DISPLAYER)
    {
      currentRType = [distributor getRedrawType];
      if (currentRType == ANCHOR)
	currentRType = EVOLUTION;
      [self convertMatrixToLinkptr];
    }
  else
    {
      if (which == CONWAY)
	{
	  [self nameProcess];
	  [self convertMatrixToLinkptr];
	  sprintf(msg, "Displaying Conway %s", (tangle) ? "tangle" : "link");
	  isMessageSet = YES;
	}
      else
	{
	  tangle = 0;
	  if (isDirty || currentRType != redrawType)
	    /* Then, we must convert edited link to redraw format and redraw */
	    {
	      /* Check to see if link can be redrawn.  If not, must
		 redraw with anchors instead */
	      if ( (currentRType == EVOLUTION || currentRType == EVOLUTION_FULL)
		  && !canRedrawLink(self))
		{
		  msg = "Link must have closed strands and at least two crossings to use evolution.  Using anchors instead.";
		  isMessageSet = YES;
		  currentRType = ANCHORS;
		}
	      if (currentRType == EVOLUTION || currentRType == EVOLUTION_FULL)
		{
		  [self convertEditToMatrix];
		  [self convertMatrixToLinkptr];
		  if (!isMessageSet)
		    {
		      msg = "Displaying edited link evolved";
		      isMessageSet = YES;
		    }
		}
	      else     /* currentRType is ANCHORS */
		{
		  [self convertUsingAnchors];
		  if (!isMessageSet)
		    {
		      msg = "Displaying edited link with anchors";
		      isMessageSet = YES;
		    }
		  
		}
	    }
	}
    }
  isDirty = NO; redrawType = currentRType;
  /* Now, set up display window and commence with linkdraw */
  /* But, if this was called solely to update linkptr (like say, to save
     a 3D file, or redraw to editor), then return now */
  if (sender == self) return currentRType;
  origRedraw = *(char *) values[9];
  *(char *)values[9] = currentRType;
  if (sender == distributor) [displayView lockFocus];
  [displayView setScale];
  [displayView linkdraw:linkptr :NULL :NULL];
  if (sender == distributor) [displayView unlockFocus];
  [window flushWindow];
  *(char *)values[9] = origRedraw;
  if (*msg != '\0') [self printMessage:(char *) msg];
  return currentRType;
}

- drawToStream:(NXStream *) stream
{
  /* Must temporarily put displayView in box if not yet there */
  id oldView = [graphicBox contentView];
  if (oldView != displayView)
    {
      [graphicBox setContentView:displayView];
      [displayView copyPSCodeInside:NULL to:stream];
      [graphicBox setContentView:oldView];
      return self;
    }
  [displayView copyPSCodeInside:NULL to:stream];
  return self;
}

/*** Conversion methods ***/

#define PI 3.14159265358979

char orient(double x1, double y1, double x2, double y2, double p, double q);

- (BOOL) convertUsingAnchors
{
  crossing *xing_array;
  int i;
  LinkStatus *gnrc;
  LinkList *lnk, *tl;
  LinkPointList *pnt, *tp, *np;
  LinkCrossingList *cross, *tc;
  float x, y, scale, maxx, minx, maxy, miny, xoffset, yoffset;
  float prevx, prevy;

  NXZone *zone;

  zone = [self zone];
  gnrc = mylink_status;

  /* Get rid of matrix if there is one */
  if (matrix != NULL && row != NULL)
    {
      free(row);
      free(matrix);
      matrix = NULL;
      row = NULL;
    }

  numcross = countCrossings(mylink_status, YES);
  
  /* Create new link structure and traverse edited link into it */
  linkptr = linkalloc(zone);
  /*  if (reallyislink) linkp = linkalloc(1); */
  linkptr->label = "";
  linkptr->nxings = numcross;
  if ((xing_array = (struct crossing *)
       NXZoneMalloc(zone, sizeof(struct crossing)*linkptr->nxings)) == NULL)
    {
      perror ("unable to allocate xinglist.");
      return NO;
    }
  linkptr->xinglist = xing_array;
  for (i=0; i<linkptr->nxings; i++)
    resetEdgeRegions(&xing_array[i]);
  
  /* Fill in xing_array */
  lnk = gnrc->link.next;
  while(lnk != NULL) {
    pnt = lnk->point.next;
    while(pnt != NULL) {
      int type = NORMAL;
      /* If pnt is an anchor or loose end, record it as a
	 crossing and save necessary info */
      if (isAnchorOrLooseEnd(pnt))
	{
	  if ((pnt->next == NULL || pnt->previous->previous == NULL)
	      && pnt->linkptr->closed == 0)
	    type = LOOSE_END;
	  else type = ANCHOR;
	  i = pnt->point_id;
	  if ((cross = pnt->crossing.next) == NULL)
	    {
	      tp = pnt->next;
	      /* Find next point with crossing */
	      while (tp != NULL  && tp->crossing.next == NULL && tp->isAnchor == 0)
		tp = tp->next;
	      if (tp == NULL)    /* Find first crossing */
		if (pnt->linkptr->closed == 1)    /* Keep tp NULL if nothing next */
		  {
		    tp = lnk->point.next;
		    tc = tp->crossing.next;
		    while (!tp->isAnchor && tc == NULL)
		      {
			tp = tp->next;
			tc = tp->crossing.next;
		      }
		  }
	      if (tp == NULL)         /* If pnt is loose end, directed out */
		{
		  xing_array[i-1].type = type;
		  xing_array[i-1].ud[N0] =
		    xing_array[i-1].ud[N1] = UP;
		  xing_array[i-1].nhbr[N0] =
		    xing_array[i-1].nhbr[N1] = &xing_array[i-1];
		  xing_array[i-1].x = (float) pnt->x;
		  xing_array[i-1].y = (float) pnt->y;
		  xing_array[i-1].orient = TERMINUS;
		  xing_array[i-1].phase = atan2(prevy-pnt->y, prevx-pnt->x)-PI;
		  goto S2;
		}
	      if (tp->isAnchor)
		{
		  xing_array[i-1].type = type;
		  xing_array[i-1].ud[N0] =
		    xing_array[i-1].ud[N1] = UP;
		  xing_array[i-1].nhbr[N0] =
		    xing_array[i-1].nhbr[N1] = &xing_array[tp->point_id-1];
		  xing_array[tp->point_id-1].nhbr[P0] =
		    xing_array[tp->point_id-1].nhbr[P1] = &xing_array[i-1];
		  xing_array[tp->point_id-1].ud[P0] =
		    xing_array[tp->point_id-1].ud[P1] = UP;
		  /* Remember x & y */
		  xing_array[i-1].x = (float) pnt->x;
		  xing_array[i-1].y = (float) pnt->y;
		  xing_array[i-1].orient = RIGHT;
		  xing_array[i-1].phase = atan2(pnt->y-tp->y, pnt->x-tp->x)-PI;
		  goto S2;
		}
	      else tc = tp->crossing.next;
	    }
	  else tc = cross;
	  xing_array[i-1].type = type;
	  xing_array[i-1].nhbr[N0] = xing_array[i-1].nhbr[N1]
	    = &xing_array[tc->id-1];
	  xing_array[i-1].x = (float) pnt->x;
	  xing_array[i-1].y = (float) pnt->y;
	  /* Determine phase */
	  if (pnt->previous->previous)       /* If we read a point before... */
	    xing_array[i-1].phase = atan2(prevy-pnt->y, prevx-pnt->x)-PI;
	  else
	    xing_array[i-1].phase = atan2(pnt->y-pnt->next->y, pnt->x-pnt->next->x)-PI;
	  if (tc->z > 0)        /* becomes up strand */
	    {
	      xing_array[i-1].ud[N0] = xing_array[i-1].ud[N1] = UP;
	      xing_array[tc->id-1].nhbr[P0] = &xing_array[i-1];
	      xing_array[tc->id-1].ud[P0] = UP;
	    }
	  else                  /* becomes down strand */
	    {
	      xing_array[i-1].ud[N0] = xing_array[i-1].ud[N1] = DOWN;
	      xing_array[tc->id-1].nhbr[P1] = &xing_array[i-1];
	      xing_array[tc->id-1].ud[P1] = UP;
	    }
	}

      /* Now, we do the crossings after pnt */
S2:   prevx = pnt->x; prevy = pnt->y;			       
      cross = pnt->crossing.next;
      /* Get np for calculating orientation */
      np = (pnt->next == NULL) ? lnk->point.next : pnt->next;
      while(cross != NULL)
	{
	  i = cross->id;
	  if(cross->z > 0)
	    {
	      xing_array[i-1].type = NORMAL;
	      tl = cross->partner->linkptr;   /* tl = link of down strand */
	      printf("%d\n", i);
	      /* Determine where up strand goes, set tc to next crossing,
		 (np already set to immediate next point) */
	      if (cross->next == NULL)
		{
		  tp = pnt->next;
		  while(tp != NULL && tp->crossing.next == NULL && tp->isAnchor == 0
			&& !(tp->next == NULL && lnk->closed == 0))
		    tp = tp->next;     /* Find next point with a crossing */
		  if (tp == NULL)      /* Find first crossing */
		    {
		      tp = lnk->point.next;
		      tc = tp->crossing.next;
		      while (!tp->isAnchor && tc == NULL)
			{
			  tp = tp->next;
			  tc = tp->crossing.next;
			}
		    }
		  if (isAnchorOrLooseEnd(tp))
		    {
		      xing_array[i-1].ud[N0] = UP;
		      xing_array[i-1].nhbr[N0] =
			&xing_array[tp->point_id-1];
		      xing_array[tp->point_id-1].nhbr[P0]
			= xing_array[tp->point_id-1].nhbr[P1]
			  = &xing_array[i-1];
		      xing_array[tp->point_id-1].ud[P0]
			= xing_array[tp->point_id-1].ud[P1]
			  = UP;
		      /* Remember x & y */
		      xing_array[i-1].x = (float) cross->x;
		      xing_array[i-1].y = (float) cross->y;
		      xing_array[i-1].orient =
			(orient(pnt->x, pnt->y,
				np->x, np->y,
				cross->partner->x,
				cross->partner->y) == 'r') ? RIGHT : LEFT;
		      /* Determine phase */
		      xing_array[i-1].phase = atan2(prevy-cross->y, prevx-cross->x)-PI;
		      goto SORRY;
		    }
		  else tc = tp->crossing.next;
		}
	      else tc = cross->next;
	      xing_array[i-1].nhbr[N0] = &xing_array[tc->id-1];
	      if (tc->z > 0)        /* becomes up strand */
		{
		  xing_array[i-1].ud[N0] = UP;
		  xing_array[tc->id-1].nhbr[P0] = &xing_array[i-1];
		  xing_array[tc->id-1].ud[P0] = UP;
		}
	      else                  /* becomes down strand */
		{
		  xing_array[i-1].ud[N0] = DOWN;
		  xing_array[tc->id-1].nhbr[P1] = &xing_array[i-1];
		  xing_array[tc->id-1].ud[P1] = UP;
		}

	      /* Determine where down strand goes */
SORRY:	      if (cross->crossing->next == NULL)
		{
		  tp = cross->partner->next;
		  while (tp != NULL && tp->crossing.next == NULL && tp->isAnchor == 0
			 && !(tp->next == NULL && tl->closed == 0))
		    tp = tp->next;   /* Find next point with a crossing */
		  if (tp == NULL)    /* Find first crossing */
		    {
		      tp = tl->point.next;
		      tc = tp->crossing.next;
		      while (!tp->isAnchor && tc == NULL)
			{
			  tp = tp->next;
			  tc = tp->crossing.next;
			}
		    }
		  if (isAnchorOrLooseEnd(tp))
		    {
		      xing_array[i-1].ud[N1] = UP;
		      xing_array[i-1].nhbr[N1] =
			&xing_array[tp->point_id-1];
		      xing_array[tp->point_id-1].nhbr[P0]
			= xing_array[tp->point_id-1].nhbr[P1]
			  = &xing_array[i-1];
		      xing_array[tp->point_id-1].ud[P0]
			= xing_array[tp->point_id-1].ud[P1]
			  = DOWN;
		      /* Remember x & y */
		      xing_array[i-1].x = (float) cross->x;
		      xing_array[i-1].y = (float) cross->y;
		      xing_array[i-1].orient =
			(orient(pnt->x, pnt->y,
				np->x, np->y,
				cross->partner->x,
				cross->partner->y) == 'r') ? RIGHT : LEFT;
		      /* Determine phase */
		      xing_array[i-1].phase = atan2(prevy-cross->y, prevx-cross->x)-PI;
		      /* Here, we can finish looking at the crossings on
			 the edge, and go to the next edge. */
		      cross = cross->next;
		      continue;
		    }		  	  
		  else tc = tp->crossing.next;
		}
	      else tc = cross->crossing->next;
	      xing_array[i-1].nhbr[N1] = &xing_array[tc->id-1];
	      if (tc->z > 0)         /* Becomes the up strand */
		{
		  xing_array[i-1].ud[N1] = UP;
		  xing_array[tc->id-1].nhbr[P0] = &xing_array[i-1];
		  xing_array[tc->id-1].ud[P0] = DOWN;
		}
	      else                   /* Becomes the down strand */
		{
		  xing_array[i-1].ud[N1] = DOWN;
		  xing_array[tc->id-1].nhbr[P1] = &xing_array[i-1];
		  xing_array[tc->id-1].ud[P1] = DOWN;
		}

	      /* Determine orientation */
	      tp = (pnt->next == NULL) ? lnk->point.next : pnt->next;
	      xing_array[i-1].orient =
		(orient(pnt->x, pnt->y,
			tp->x, tp->y,
			cross->partner->x,
			cross->partner->y) == 'r') ? RIGHT : LEFT;
	      
	      /* Determine phase */
	      xing_array[i-1].phase = atan2(prevy-cross->y, prevx-cross->x)-PI;
	    }
	  /* Remember location of crossings */
	  xing_array[i-1].x = (float) cross->x;
	  xing_array[i-1].y = (float) cross->y;
	  cross = cross->next;
	}
      pnt = pnt->next;
    }
    lnk = lnk->next;
  }
  
  for (i=0; i<linkptr->nxings; i++)
    {
      xing_array[i].n = i;

      /* Linking xing list */
      if (i<linkptr->nxings-1)
	xing_array[i].next = &xing_array[i+1];
      else xing_array[i].next = &xing_array[0];
    }

  complete_processing(linkptr, tangle);

  /* Adjust coordinates of crossings to fit in square [-1,1] x [-1,1] */

  maxx = maxy = -1e+50;
  minx = miny = 1e+50;
  for (i=0; i<linkptr->nxings; i++)
    {
      x = xing_array[i].x; y = xing_array[i].y;
      if (x > maxx) maxx = x;
      if (x < minx) minx = x;
      if (y > maxy) maxy = y;
      if (y < miny) miny = y;
    }
  if (maxx-minx < maxy-miny)
    {
      xoffset = ((maxy-miny) - (maxx-minx)) / (2*(maxy-miny)) - 1;
      yoffset = -1;
      scale = 2/(maxy-miny);
    }
  else
    {
      if (maxx == minx)
	/* Uh oh, it would appear that we have only one anchor altogether */
	{
	  for (i=0; i<linkptr->nxings; i++)
	    {
	      xing_array[i].x = 0;
	      xing_array[i].y = 0;
	    }
	  return YES;
	}
      xoffset = -1;
      yoffset = ((maxx-minx) - (maxy-miny)) / (2*(maxx-minx)) - 1;
      scale = 2/(maxx-minx);
    }
  for (i=0; i<linkptr->nxings; i++)
    {
      xing_array[i].x = (xing_array[i].x - minx) * scale + xoffset;
      xing_array[i].y = (xing_array[i].y - miny) * scale + yoffset;
    }
  return YES;
}  

- convertEditToMatrix
{
  LinkList *lnk, *tl;
  LinkPointList *pnt, *tp;
  LinkCrossingList *cross, *tc;
  int i;
  NXZone *zone = [self zone];


  /* Check to see if a matrix was already calculated.  If so, free it up */
  if (matrix != NULL && row != NULL)
    {
      NXZoneFree(zone, row);
      NXZoneFree(zone, matrix);
    }


  /* Count and label crossings */
  numcross = countCrossings(mylink_status, NO);

  /* Allocate space for matrix */
  row = (int *) NXZoneMalloc(zone, 5*numcross*sizeof(int));
  matrix = (int **) NXZoneMalloc(zone, numcross*sizeof(int *));

  for(i=0; i<numcross; i++)
    matrix[i] = row + 5*i;

  /* Parse through mylink_status and fill in matrix */
  printf("Matrix:\n");
  lnk = mylink_status->link.next;
  while(lnk != NULL) {
    pnt = lnk->point.next;
    while(pnt != NULL) {
      cross = pnt->crossing.next;
      while(cross != NULL)
	{
	  if(cross->z > 0.0)
	    {
	      tl = cross->partner->linkptr;   /* tl = link of down strand */
	      i = cross->id;
	      printf("%d\n", i);
	      /* Determine where up strand goes */
	      if (cross->next == NULL)
		{
		  tp = pnt->next;
		  while(tp != NULL && tp->crossing.next == NULL)
		    tp = tp->next;     /* Find next point with a crossing */
		  if (tp == NULL)      /* Find first crossing */
		    {
		      tp = lnk->point.next;
		      tc = tp->crossing.next;
		      while (tc == NULL)
			{
			  tp = tp->next;
			  tc = tp->crossing.next;
			}
		    }
		  else tc = tp->crossing.next;
		}
	      else tc = cross->next;
	      matrix[i-1][UPN] = tc->id;
	      matrix[i-1][UPO] = (tc->z > 0) ? 'u' : 'd';

	      /* Determine where down strand goes */
	      if (cross->crossing->next == NULL)
		{
		  tp = cross->partner->next;
		  while (tp != NULL && tp->crossing.next == NULL)
		    tp = tp->next;   /* Find next point with a crossing */
		  if (tp == NULL)    /* Find first crossing */
		    {
		      tp = tl->point.next;
		      tc = tp->crossing.next;
		      while (tc == NULL)
			{
			  tp = tp->next;
			  tc = tp->crossing.next;
			}
		    }
		  else tc = tp->crossing.next;
		}
	      else tc = cross->crossing->next;
	      matrix[i-1][DNN] = tc->id;
	      matrix[i-1][DNO] = (tc->z > 0) ? 'u' : 'd';

	      /* Determine orientation */
	      tp = (pnt->next == NULL) ? lnk->point.next : pnt->next;
	      matrix[i-1][ORNT] = orient(pnt->x, pnt->y,
					 tp->x, tp->y,
					 cross->partner->x, cross->partner->y);
	      printf ("%c %d %c %d %c\n", matrix[i-1][ORNT], matrix[i-1][UPN],
		      matrix[i-1][UPO], matrix[i-1][DNN],
		      matrix[i-1][DNO]);
	    }
	  cross = cross->next;
	}
      pnt = pnt->next;
    }
    lnk = lnk->next;
  }
  return self;
}

/* Process matrix into linkptr using evolution */
- convertMatrixToLinkptr
{
  NXZone *zone;
  zone = [self zone];
  /* Free up linkptr if already exists */
  if (linkptr != NULL)
    freelink(linkptr, zone);
  linkread(&linkptr, matrix, numcross, zone, &tangle);
  if (!tangle) mark_regions(linkptr);
  linkdyn(linkptr, tangle);
//  draw_all_regs = draw_all_rots = 0;
  return self;
}

/* Deletes all empty or one-point links */
void cleanUpGnrc(LinkStatus *gnrc)
{
  LinkList *lnk, *tmp, *prev;

  prev = &gnrc->link;
  lnk = gnrc->link.next;
  while (lnk != NULL)
    {
      tmp = lnk->next;
      if (lnk->point.next == NULL)
	{
	  prev->next = lnk->next;
	  free(lnk);
	  gnrc->num--;
	}
      else if (lnk->point.next->next == NULL)
	{
	  free(lnk->point.next);
	  prev->next = lnk->next;
	  free(lnk);
	  gnrc->num--;
	}
      else prev = lnk;
      lnk = tmp;
    }
}

- redrawToEditor
{
  int i;
  char currentRType, origRedraw;
  float **values;
  mylink_status->current_link = NULL;

  /* Set up view to be in box [-1,1] x [-1,1] */
  mylink_status->origin.dcx = mylink_status->width/2;
  mylink_status->origin.dcy = mylink_status->height/2;
  mylink_status->xscale = mylink_status->yscale =
    ((mylink_status->width > mylink_status->height)
     ? mylink_status->height : mylink_status->width) / (2*xppmm);
  
  currentRType = [self doDraw:self];
  if (linkptr == NULL) return self;
  values = [displayView getValues];
  origRedraw = *(char *) values[9];
  *(char *) values[9] = currentRType;
  [editView eraseWindow];
  [editView lockFocus];
  [displayView linkdraw:linkptr :NULL :mylink_status];
  *(char *) values[9] = origRedraw;
  cleanUpGnrc(mylink_status);
  LinkComputeDeviceCoords(mylink_status);
  for (i=0; i<5; i++)
    LinkCenterView(mylink_status);
  [editView clearScreen];
  [editView ReDrawLinkTopWindow:mylink_status];
  [editView unlockFocus];
  return self;
}



- imbibeDefaults
{
  float *defaults = [prefObj prepareValsFromCurrent];
  int i;
  for (i=0; i<8; i++)
    *parameters[i] = defaults[i];
  for (i=8; i<15; i++)
    *(char *)parameters[i] = (char) defaults[i];
  * (int *) parameters[15] = (int) defaults[15];
  return self;
}

/******* Read Notation View Stuff *********/

- readNotation
{
  int which = [[notationRB selectedCell] tag];
  int errval;
  braidType = (char) [[braidRB selectedCell] tag];
  conwayType = (char) [[conwayRB selectedCell] tag];
  if (which)
    /* Braid */
    {
      isClosed = braidType;
      name = [notationText stringValue];
      if (name == NULL) return self;
      if (!strcmp(name,""))
	{
	  name = NULL;
	  return self;
	}
      [graphicBox setContentView:editView];
      errval = (int) [editView processBraid :name :isClosed];
      if (errval == 0 || errval == 1)
	{
	  int response =
	    (errval == 0) ?
	      (NXRunAlertPanel("Braid notation error",
			       "%s not valid braid notation",
			       "Fix it", "Erase and ignore it", NULL, name))
		:(NXRunAlertPanel("Braid notation error",
				  "%s has more than 100 crossings.  I won't even think of trying to display it",
				  "Fix it", "Erase and ignore it", NULL, name));
	  if (response == NX_ALERTDEFAULT)
	    {
	      [modeButton setTitle:"Notation"];
	      [graphicBox setContentView:[notationBox contentView]];
	      return NULL;
	    }
	  name = NULL;
	  [notationText setStringValue:""];
	  return self;
	}
      currentType = BRAID;
    }
  else
    /* Conway */
    {
      isClosed = !conwayType;
      name = [notationText stringValue];
      if (name == NULL) return self;
      if (!strcmp(name,""))
	{
	  name = NULL;
	  return self;
	}
      if ((errval = [self checkConway]) != 0)
	{
	  int response =
	    (errval == 1) ?
	      (NXRunAlertPanel("Conway notation error",
			       "%s not valid Conway notation",
			       "Fix it", "Erase and ignore it", NULL, name))
	       : (NXRunAlertPanel("Conway notation error",
				  "%s has more than 100 crossings.  I won't even think of trying to display it",
				  "Fix it", "Erase and ignore it", NULL, name));
	  if (response == NX_ALERTDEFAULT)
	    {
	      [modeButton setTitle:"Notation"];
	      return NULL;
	    }
	  name = NULL;
	  [notationText setStringValue:""];
	  return self;
	}
      currentType = CONWAY;
      [graphicBox setContentView:editView];
      [editView eraseWindow];
      [graphicBox setContentView:[notationBox contentView]];  
    }
  return self;
}

- showNotationHelp:sender
{
  id helpObj = NXGetNamedObject("HelpInstance", NXApp);
  [helpObj showNotationHelp];
  return self;
}


#define NUMBER 1
#define COMMA  2
#define SPACE  3
#define MINUS  4
#define EYE    5

/* Returns 1 if invalid notation, and 2 if too many crossings */
- (int) checkConway
{
  int previous, error = 0, sum = 0, current = 0;
  char c;
  char *s = (char *) name;
  c = *s++;
  if ((c < '0' || c > '9') && c != '-')
    return 1;
  previous = (c == '-') ? MINUS : NUMBER;
  if (previous == NUMBER) current = current*10 + (c - '0');
  while ((c = *s++) != '\0' && (error == 0))
    switch (previous)
      {
      case NUMBER:
	if (c >= '0' && c <= '9')
	  {
	    current = current*10 + (c - '0');
	    previous = NUMBER;
	  }
	else if (c == ' ')
	  {
	    sum += current;
	    if (sum > 100) return 2;
	    current = 0;
	    previous = SPACE;
	  }
	else if (c == ',')
	  {
	    sum += current;
	    if (sum > 100) return 2;
	    current = 0;
	    previous = COMMA;
	  }
	else error = 1;
	break;
      case SPACE:
	if (c >= '0' && c <= '9')
	  {
	    current = current*10 + (c - '0');
	    previous = NUMBER;
	  }
	else if (c == '-') previous = MINUS;
	else error = 1;
	break;
      case COMMA:
	if (c == ' ') previous = SPACE;
	else error = 1;
	break;
      case MINUS:
	if (c < '0' || c > '9') error = 1;
	else
	  {
	    current = current*10 + (c - '0');
	    previous = NUMBER;
	  }
	break;
      }
  sum += current;
  if (sum > 100) return 2;
  if (*(s-2) < '0' || *(s-2) > '9') error = 1;
  return error;
}

/* Returns 1 if bad notation, 2 if too many crossings */
- (int) checkBraid:(const char *) s
{
  int max = 0, current = 0, previous;
  char c;

  while ((c = *s) == ' ') s++;
  if (c < '0' || c > '9') return 1;
  previous = NUMBER;
  s++;
  current = current*10 + (c - '0');
  while ((c = *s++) != '\0')
    switch(previous)
      {
      case NUMBER:
	if (c >= '0' && c <= '9')
	  {
	    current = current*10 + (c - '0');
	    previous = NUMBER;
	  }
	else if (c == ' ' || c == 'i')
	  {
	    if (current > max) max = current;
	    if (max > 100) return 2;
	    current = 0;
	  }
	else return 1;
	if (c == ' ') previous = SPACE;
	if (c == 'i') previous = EYE;
	break;
      case SPACE:
	if (c >= '0' && c <= '9')
	  {
	    current = current*10 + (c - '0');
	    previous = NUMBER;
	  }
	else if (c == ' ') previous = SPACE;
	else return 1;
	break;
      case EYE:
	if (c != ' ') return 1;
	previous = SPACE;
	break;
      }
  if (previous == NUMBER)
    if (current > max) max = current;
  if (max > 100) return 2;
  return 0;
}

/* Takes string name of Conway notation, and converts to matrix */
- nameProcess
{
  int i;
  char *s = (char *) name;
  char *matrixString;

  /* Set mode (i) to scheme routine comma correctly: */
  /* 1=comma, 2=comma-t, 3=multiply, 4=multiply-t  */
  if (index (s, ','))
    {
      if (conwayType == TANGLE)
	i=2;
      else i=1;
    }
  else
    {
      if (conwayType == TANGLE)
	i=4;
      else i=3;
    }
  
  /* Call the scheme procedure, place answer in matrixString */
  matrixString = (char *) sc_tscp_pointer(comma_comma(sc_cstringtostring(s),
						      sc_int_tscp(i)));
  
  [self convertStringToMatrix:matrixString];
  tangle = (conwayType == TANGLE);
  return self;
}

- convertStringToMatrix: (char *) mstring
{
  NXZone *zone;
  char a[2], b[2], c[2];
  int i;
  zone = [self zone];
  
  sscanf(mstring, "%d", &numcross);    /* Get numcross */
  while (*mstring++ != ' ');

  /* Check to see if a matrix was already calculated.  If so, free it up */
  if (matrix != NULL && row != NULL)
    {
      NXZoneFree(zone, row);
      NXZoneFree(zone, matrix);
    }

  /* Allocate space for matrix */
  row = (int *) NXZoneMalloc(zone, 5*numcross*sizeof(int));
  matrix = (int **) NXZoneMalloc(zone, numcross * sizeof(int *));
  for(i=0; i<numcross; i++)
    matrix[i] = row + 5*i;

  for (i=0; i<numcross; i++)
    {
      sscanf(mstring, "%s%d%s%d%s", a, &matrix[i][1], b, &matrix[i][3], c);
      matrix[i][0] = (int) a[0];
      matrix[i][2] = (int) b[0];
      matrix[i][4] = (int) c[0];
      while (*mstring++ != '\n');
    }
  return self;
}

- activateKeyEq
{
  [menu1 setKeyEquivalent:'1'];
  [menu2 setKeyEquivalent:'2'];
  [menu3 setKeyEquivalent:'3'];
  return self;
}

- inactivateKeyEq
{
  [menu1 setKeyEquivalent:0];
  [menu2 setKeyEquivalent:0];
  [menu3 setKeyEquivalent:0];
  return self;
}

/************ Delegate methods for inspectors *************/

- makeLinkActive
{
  currentLinkObj = self;
  link_status = mylink_status;
  gnrc = mygnrc;
  globalEditView = editView;
  globalDisplayView = displayView;
  [distributor writeParametersFrom:parameters];
  return self;
}

- makeLinkInactive
{
  [distributor readParametersTo:parameters];
  return self;
}

- windowDidBecomeKey:win
{
  /* Set global variables to point to new link */
  [self makeLinkActive];
  [self activateKeyEq];
  [distributor selectIfNecessary:[editView get_editMode]];
  return self;
}


- windowWillResize:win toSize:(NXSize *)frameSize
{
  if (frameSize->width < MINWIDTH)
    frameSize->width = MINWIDTH;
  if (frameSize->height < MINHEIGHT)
    frameSize->height = MINHEIGHT;
  return self;
}

- windowDidResignKey:sender
{
  [self makeLinkInactive];
  [self inactivateKeyEq];
  return self;
}

- windowWillClose:sender
{
  /*****INCLUDE ARE YOU SURE? STUFF HERE *****/
  [distributor deleteMeFromList:self];
  return self;
}

/************ Delegate methods for notation text fields *************/

- textDidChange:sender
{
  //  printf("delegate got from %d\n", (int) sender);
  NXSize size;
  [sender getMinSize:&size];
  printf("height = %f\n", size.height);
  return self;
}

/************ Extractor functions for instance variables **********/

/* Editor variables */

- (LinkEditStatus *) get_gnrc
{
  return mygnrc;
}

- (LinkStatus *) get_status
{
  return mylink_status;
}

- (LinkData *) get_data
{
  return mydata;
}

/* Other extractor functions */

- get_editView
{
  return editView;
}

- (char) get_mode
{
  return mode;
}

- get_window
{
  return window;
}

- (float **) get_parameters
{
  return parameters;
}

- (int) get_numcross
{
  return numcross;
}

- (short) get_tangle
{
  return tangle;
}

- (int **) get_matrix
{
  return matrix;
}

- (const char *) get_linkfile
{
  return linkfile;
}

- (char) get_linkfileType
{
  return linkfileType;
}

- (NXColor *) get_colors
{
  return [prefObj get_colors];
}

- dumpMatrix
{
  int i;
  for (i=0; i<numcross; i++)
    printf("%c %d %c %d %c\n", (char) matrix[i][0],
	    matrix[i][1], (char) matrix[i][2], matrix[i][3],
	    (char) matrix[i][4]);
  return self;
}
@end
