Logo Search packages:      
Sourcecode: xcircuit version File versions

elements.c

/*----------------------------------------------------------------------*/
/* elements.c --- xcircuit routines for creating basic elements         */
/* Copyright (c) 2002  Tim Edwards, Johns Hopkins University            */
/*----------------------------------------------------------------------*/

/*----------------------------------------------------------------------*/
/*      written by Tim Edwards, 8/13/93                                 */
/*----------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>

#ifdef TCL_WRAPPER 
#include <tk.h>
#else
#include "Xw/Xw.h"
#endif

/*----------------------------------------------------------------------*/
/* Local includes                                                       */
/*----------------------------------------------------------------------*/

#include "xcircuit.h"
#include "colordefs.h"

/*----------------------------------------------------------------------*/
/* Function prototype declarations                          */
/*----------------------------------------------------------------------*/
#include "prototypes.h"

/*----------------------------------------------------------------------*/
/* Global Variable definitions                                          */
/*----------------------------------------------------------------------*/

extern short eventmode;      /* keep track of the mode the screen is in */
extern short textpos, textend;  /* keep track of the cursor position in text */
extern Display  *dpy;    /* Works well to make this globally accessible */
extern int *appcolors;
extern Cursor   appcursors[NUM_CURSORS];
extern Clientdata areastruct;
extern Globaldata xobjs;
extern xcWidget   top;
extern fontinfo *fonts;
extern short fontcount;
extern short attachto;
extern char  _STR[150], _STR2[250];
extern stringpart *labelbuf;
extern int number_colors;

extern double atan2();

/*------------------------------------------------------------------------*/
/* Declarations of global variables                                       */
/*------------------------------------------------------------------------*/

short savedir = 0;      /* for rhomboid/manhattan editing */
char extchar[20];
double saveratio;
unsigned char texttype;

/*-------------------------------------*/
/* Sane values for a new path instance */
/*-------------------------------------*/

void pathdefaults(pathptr newpath, int x, int y)
{
   newpath->style = NORMAL;
   newpath->width = areastruct.linewidth;
   newpath->style = areastruct.style;
   newpath->color = areastruct.color;
   newpath->parts = 0;
   newpath->plist = (genericptr *)NULL;
   newpath->passed = NULL;
   newpath->num_params = 0;
}

/*---------------------------------------*/
/* Sane values for a new object instance */
/*---------------------------------------*/

void objectdefaults(objinstptr newinst, objectptr thisobj, int x, int y)
{
   newinst->position.x = x;
   newinst->position.y = y;
   newinst->rotation = 0;
   newinst->scale = 1.0;
   newinst->thisobject = thisobj;
   newinst->color = areastruct.color;
   newinst->params = NULL;
   newinst->passed = NULL;
   newinst->num_params = 0;

   newinst->bbox.lowerleft.x = thisobj->bbox.lowerleft.x;
   newinst->bbox.lowerleft.y = thisobj->bbox.lowerleft.y;
   newinst->bbox.width = thisobj->bbox.width;
   newinst->bbox.height = thisobj->bbox.height;

#ifdef SCHEMA
   newinst->schembbox = NULL;
#endif
}

/*--------------------------------------*/
/* Draw a dot at the current point.     */
/*--------------------------------------*/

void drawdot(int xpos, int ypos)
{
   arcptr *newarc;
   objinstptr *newdot;
   objectptr dotobj;
   
   /* Find the object "dot" in the builtin library, or else use an arc */
   
   if ((dotobj = finddot()) != (objectptr)NULL) {
      NEW_OBJINST(newdot, topobject);
      topobject->parts++;
      objectdefaults(*newdot, dotobj, xpos, ypos);
   }
   else {
      NEW_ARC(newarc, topobject);
      topobject->parts++;
      arcdefaults(*newarc, xpos, ypos);
      (*newarc)->radius = 6;
      (*newarc)->yaxis = 6;
      (*newarc)->width = 1.0;
      (*newarc)->style = FILLED | FILLSOLID | NOBORDER;
      (*newarc)->num_params = 0;
      (*newarc)->passed = NULL;
      calcarc(*newarc);
   }
   incr_changes(topobject);
}

/*----------------------*/
/* Start spline mode.   */
/*----------------------*/

void startspline(xcWidget button, caddr_t clientdata, caddr_t calldata)
{
   if (eventmode == NORMAL_MODE) {
      Wprintf("Click button1 and drag for spline.");
      eventmode = SPLINE0_MODE;
   }
}

/*----------------------*/
/* Start label mode.    */
/*----------------------*/

void starttext(xcWidget button, pointertype mode, caddr_t calldata)
{
   if (eventmode == NORMAL_MODE) {
      Wprintf("Click button1 for text.");
      texttype = (u_char)mode;
      eventmode = TEXT1_MODE;
   }
}

/*--------------------------------------*/
/* Sane default values for a label  */
/*--------------------------------------*/

void labeldefaults(labelptr newlabel, u_char dopin, int x, int y)
{
   newlabel->rotation = 0;
   newlabel->color = areastruct.color;
   newlabel->scale = areastruct.textscale;
   newlabel->string = (stringpart *)malloc(sizeof(stringpart));
   newlabel->num_params = 0;
   newlabel->passed = NULL;

   /* initialize string with font designator */
   newlabel->string->type = FONT_NAME;
   newlabel->string->data.font = areastruct.psfont;
   newlabel->string->nextpart = NULL;

#ifdef SCHEMA
   newlabel->pin = False;
   if (areastruct.schemon) {
      newlabel->pin = dopin;
      if (dopin == LOCAL) newlabel->color = LOCALPINCOLOR;
      else if (dopin == INFO) newlabel->color = INFOLABELCOLOR;
   }
#endif

   newlabel->justify = areastruct.justify;
   newlabel->position.x = x;
   newlabel->position.y = y;
}

/*--------------------------------------*/
/* Button handler when creating a label */
/*--------------------------------------*/

void textbutton(u_char dopin, XButtonEvent *event)
{
   labelptr *newlabel;
   XPoint userpt;
   short tmpheight;

   XDefineCursor(dpy, areastruct.areawin, TEXTPTR);
   Wprintf("Click to end or cancel.");

   if (fontcount == 0)
      Wprintf("Warning:  No fonts available!");

   NEW_LABEL(newlabel, topobject);
   areastruct.editpart = topobject->parts;
   snap(event->x, event->y, &userpt);
   labeldefaults(*newlabel, dopin, userpt.x, userpt.y);

   tmpheight = (short)(TEXTHEIGHT * (*newlabel)->scale);
   userpt.y -= ((*newlabel)->justify & NOTBOTTOM) ?
      (((*newlabel)->justify & TOP) ? tmpheight : tmpheight / 2) : 0;
   UDrawTLine(*newlabel);
   areastruct.origin.x = userpt.x;
   areastruct.origin.y = userpt.y;
   textpos = 1;  /* Text position is *after* the font declaration */
}

/*----------------------------------------------------------------------*/
/* Report on characters surrounding the current text position           */
/*----------------------------------------------------------------------*/

#define MAXCHARS 10

void charreport(labelptr curlabel)
{
   int i, locpos, cleft = 149;
   stringpart *strptr;

   _STR2[0] = '\0';
   for (i = textpos - MAXCHARS; i <= textpos + MAXCHARS - 1; i++) {
      if (i < 0) continue; 
      strptr = findstringpart(i, &locpos, curlabel->string, areastruct.topinstance);
      if (i == textpos) {
       strncat(_STR2, "| ", cleft);
       cleft -= 2;
      }
      if (strptr == NULL) break;
      charprint(_STR, strptr, locpos);
      cleft -= strlen(_STR);
      strncat(_STR2, _STR, cleft);
      strncat(_STR2, " ", --cleft);
      if (cleft <= 0) break;
   }
   Wprintf(_STR2);
}

/*----------------------------------------------------------------------*/
/* See if a (pin) label has a copy (at least one) in this drawing.      */
/*----------------------------------------------------------------------*/

labelptr findlabelcopy(labelptr curlabel, stringpart *curstring)
{
   genericptr *tgen;
   labelptr tlab;

   for (tgen = topobject->plist; tgen < topobject->plist + topobject->parts; tgen++) {
      if ((*tgen)->type == LABEL) {
         tlab = TOLABEL(tgen);
       if (tlab->pin == False) continue;
       else if (tlab == curlabel) continue;  /* Don't count self! */
         else if (!stringcomp(tlab->string, curstring)) return tlab;  
      }
   }
   return NULL;
}

/*--------------------------------------------------------------*/
/* Interpret string and add to current label.               */
/*    keypressed is a KeySym                          */
/*    clientdata can pass information for label controls    */
/*--------------------------------------------------------------*/

void labeltext(int keypressed, char *clientdata)
{
   labelptr curlabel;
   stringpart *curpos;
   int locpos;
   Boolean do_redraw = False;
   short tmplength, tmpheight, cfont;
   TextExtents tmpext;

   curlabel = TOLABEL(EDITPART);

   if (curlabel == NULL || textpos <= 0) {
      Wprintf("Error:  Bad label string");
      return;
   }

   /* find text segment of the current position */
   curpos = findstringpart(textpos, &locpos, curlabel->string, areastruct.topinstance);

   UDrawTLine(curlabel);

   if (isbound(keypressed, XCF_Text_Delete) && textpos > 1) {
      int curloc, strpos;
      stringpart *strptr;

      if (textend == 0) textend = textpos - 1;

      undrawtext(curlabel);
      for (strpos = textpos - 1; strpos >= textend; strpos--) {
       strptr = findstringpart(strpos, &curloc, curlabel->string,
                   areastruct.topinstance);
       if (curloc >= 0) {
          memmove(strptr->data.string + curloc,
                  strptr->data.string + curloc + 1,
                  strlen(strptr->data.string + curloc + 1) + 1);
          if (strlen(strptr->data.string) == 0)
             deletestring(strptr, &curlabel->string, areastruct.topinstance);
       }

         /* Don't delete any parameter boundaries---must use      */
         /* "unparameterize" command for that.              */

       else if (strptr != NULL) {
          if ((strptr->type != PARAM_START) && (strptr->type != PARAM_END))
             deletestring(strptr, &curlabel->string, areastruct.topinstance);
          else
             textpos++;
       }
       else
          Fprintf(stdout, "Error:  Unexpected NULL string part\n");
         textpos--;
      }
      textend = 0;
      do_redraw = True;
   }
   else if (isbound(keypressed, XCF_Text_Return)) {
      Boolean hasstuff = False;   /* Check for null string */
      Boolean do_all = True;
      stringpart *tmppos;

      for (tmppos = curlabel->string; tmppos != NULL; tmppos = tmppos->nextpart) {
       if (tmppos->type == PARAM_START) hasstuff = True;
       else if (tmppos->type == TEXT_STRING) hasstuff = True;
      }
      XDefineCursor(dpy, areastruct.areawin, CROSS);

      Wprintf("");

      if (hasstuff && (eventmode != TEXT3_MODE && eventmode != CATTEXT_MODE)) {
       topobject->parts++;
       incr_changes(topobject);
#ifdef SCHEMA
         topobject->valid = False;
#endif

      }
      else if (!hasstuff && (eventmode == TEXT3_MODE)) topobject->parts--;

      if ((!hasstuff) && (eventmode == CATTEXT_MODE)) {  /* can't have null labels! */ 
        freelabel(curlabel->string);
        curlabel->string = stringcopyback(labelbuf, areastruct.topinstance);
        freelabel(labelbuf);
        labelbuf = NULL;
        resolveparams(areastruct.topinstance);
          XcSetFunction(GXcopy);
        redrawtext(curlabel);
        Wprintf("Object must have a name!");
        eventmode = CATALOG_MODE;
      }
      else if (!hasstuff) {
        freelabel(curlabel->string);
        free(curlabel);
        eventmode = NORMAL_MODE;
      }
      else if (eventmode == CATTEXT_MODE) {

         /* set name of object to new string */

       areastruct.save.y = curlabel->position.y + 100;
       areastruct.save.x = curlabel->position.x;
       objectselect(OBJECT);
       if (areastruct.selects == 1) {  /* this should be true! */
          short *selptr = areastruct.selectlist + areastruct.selects - 1;
          objinstptr selobj = SELTOOBJINST(selptr);
          strcpy(selobj->thisobject->name, curlabel->string->nextpart->data.string);
          /* If checkname() alters the name, it has to be copied back to */
          /* the catalog label for the object.                 */

          if (checkname(selobj->thisobject)) {
             undrawtext(curlabel);
             curlabel->string->nextpart->data.string = (char *)realloc(
                  curlabel->string->nextpart->data.string,
                  (strlen(_STR) + 1) * sizeof(char));
             strcpy(curlabel->string->nextpart->data.string,
                  selobj->thisobject->name);
               XcSetFunction(GXcopy);
             redrawtext(curlabel);
          }
       }
         eventmode = CATALOG_MODE;
       objectdeselect();
      }
      else {      /* (hasstuff && eventmode != CATTEXT_MODE) */
       eventmode = NORMAL_MODE;
       incr_changes(topobject);
#ifdef SCHEMA
       if (curlabel->pin != False) topobject->valid = False;
#endif
      }

      setdefaultfontmarks();
      setcolormark(areastruct.color);
      if (labelbuf != NULL) {

#ifdef SCHEMA
       /* If the original label (before modification) is a pin in a     */
       /* schematic/symbol with a matching symbol/schematic, and the    */
       /* name is unique, change every pin in the matching symbol/      */
       /* schematic to match the new text.                        */

       if ((areastruct.schemon == True) && (curlabel->pin != False) && 
            (topobject->symschem != NULL)) {
          if ((findlabelcopy(curlabel, labelbuf) == NULL)
                  && (findlabelcopy(curlabel, curlabel->string) == NULL)) {
             changeotherpins(curlabel, labelbuf);
             if (topobject->schemtype == SCHEMATIC)
                Wprintf("Changed corresponding pin in associated symbol");
             else
                Wprintf("Changed corresponding pin in associated schematic");
             incr_changes(topobject->symschem);
             topobject->symschem->valid = False;
          }
       }
#endif
      
       freelabel(labelbuf);
       labelbuf = NULL;
       resolveparams(areastruct.topinstance);
       if (do_all >= 0)
            updateinstparam(areastruct.topinstance, do_all);
       else /* do_all == -1 */
          calcbbox(areastruct.topinstance);
#ifdef SCHEMA
       setobjecttype(topobject);
#endif
      }
      else
         calcbbox(areastruct.topinstance);
      return;
   }
   else if (isbound(keypressed, XCF_Text_Right) && curpos != NULL)
      textpos++;
   else if (isbound(keypressed, XCF_Text_Left) && textpos > 1)
      textpos--;
   else if (isbound(keypressed, XCF_Text_Down)) {
      while (curpos != NULL) {
       textpos++;
       curpos = findstringpart(textpos, &locpos, curlabel->string,
                  areastruct.topinstance);
       if (curpos != NULL)
          if (curpos->type == RETURN)
             break;
      }
   }
   else if (isbound(keypressed, XCF_Text_Up)) {
      while (textpos > 1) {
       textpos--;
       curpos = findstringpart(textpos, &locpos, curlabel->string,
                  areastruct.topinstance);
       if (curpos->type == RETURN) {
          if (textpos > 1) textpos--;
          break;
       }
      }
   }
   else if (isbound(keypressed, XCF_Text_Home))
      textpos = 1;
   else if (isbound(keypressed, XCF_Text_End))
      textpos = stringlength(curlabel->string, True, areastruct.topinstance);
   else if (isbound(keypressed, XCF_Text_Split)) {
      labelptr *newlabel;
      XPoint points[4], points1[4], points2[4];

      /* Everything after the cursor gets dumped into a new label */

      if ((textpos > 1) && (curpos != NULL)) {
       labelbbox(curlabel, points, areastruct.topinstance);
         undrawtext(curlabel);
       NEW_LABEL(newlabel, topobject);
       labeldefaults(*newlabel, curlabel->pin, curlabel->position.x,
            curlabel->position.y);
         if (locpos > 0)
            curpos = splitstring(textpos, &curlabel->string, areastruct.topinstance);
       /* move back one position to find end of top part of string */
         curpos = splitstring(textpos - 1, &curlabel->string, areastruct.topinstance);
       if (curpos->nextpart->type == FONT_NAME) {
          freelabel((*newlabel)->string);
          (*newlabel)->string = curpos->nextpart;
       }
       else {
          (*newlabel)->string->data.font = curlabel->string->data.font;
          (*newlabel)->string->nextpart = curpos->nextpart;
       }
       curpos->nextpart = NULL;
       topobject->parts++;

       /* Adjust position of both labels to retain their original */
       /* relative positions.                               */

       labelbbox(curlabel, points1, areastruct.topinstance);
       labelbbox((*newlabel), points2, areastruct.topinstance);
       curlabel->position.x += (points[1].x - points1[1].x);
       curlabel->position.y += (points[1].y - points1[1].y);
       (*newlabel)->position.x += (points[3].x - points2[3].x);
       (*newlabel)->position.y += (points[3].y - points2[3].y);
      
         XcSetFunction(GXcopy);
         redrawtext(*newlabel);
         do_redraw = True;
      }
   }

   /* Write a font designator or other control into the string */

   if (clientdata != NULL) {
      stringpart *newpart;
      Boolean errcond = False;

      /* erase first before redrawing unless the string is empty */
      undrawtext(curlabel);
      if (locpos > 0) {
         curpos = splitstring(textpos, &curlabel->string, areastruct.topinstance);
       curpos = curpos->nextpart;
      }
      newpart = makesegment(&curlabel->string, curpos);
      newpart->type = keypressed;
      switch (keypressed) {
       case FONT_SCALE:
          newpart->data.scale = *((float *)clientdata);
          break;
       case KERN:
          newpart->data.kern[0] = *((short *)clientdata);
          newpart->data.kern[1] = *((short *)clientdata + 1);
          break;
       case FONT_COLOR:
          newpart->data.color = *((int *)clientdata);
          if (newpart->data.color >= number_colors) errcond = True;
          break;
       case FONT_NAME:
          newpart->data.font = *((int *)clientdata);
          if (newpart->data.font >= fontcount) errcond = True;
          break;
       case PARAM_START:
          newpart->data.paramno = *((int *)clientdata);
          if (newpart->data.paramno >= topobject->num_params) errcond = True;
          else if (topobject->params[newpart->data.paramno]->type != XC_STRING)
             errcond = True;
          break;
      }
      if (errcond == True) {
       Wprintf("Error in insertion.  Ignoring.");
       deletestring(newpart, &curlabel->string, areastruct.topinstance);
      }
      else
         textpos++;
      do_redraw = True;
   }

   /* Append the character to the string.  If the current label segment is    */
   /* not text, then create a text segment for it.                      */

   else if (keypressed > 0 && keypressed < 256) {
      stringpart *lastpos;

      /* erase first. */
      undrawtext(curlabel);

      /* Current position is not in a text string */
      if (locpos < 0) {

         /* Find part of string which is immediately in front of textpos */
         lastpos = findstringpart(textpos - 1, &locpos, curlabel->string, areastruct.topinstance);

       /* No text on either side to attach to: make a new text segment */
       if (locpos < 0) {
          curpos = makesegment(&curlabel->string, curpos);
          curpos->type = TEXT_STRING;
          curpos->data.string = (u_char *) malloc(2);
          curpos->data.string[0] = keypressed;
          curpos->data.string[1] = '\0';
       }
       else {           /* append to end of lastpos text string */
          int slen = strlen(lastpos->data.string);
          lastpos->data.string = (u_char *) realloc(lastpos->data.string,
            2 +  slen);
          *(lastpos->data.string + slen) = keypressed;
          *(lastpos->data.string + slen + 1) = '\0';
       }
      }
      else {      /* prepend to end of curpos text string */
         curpos->data.string = (u_char *) realloc(curpos->data.string, 
           2 + strlen(curpos->data.string));
       memmove(curpos->data.string + locpos + 1, curpos->data.string + locpos,
            strlen(curpos->data.string + locpos) + 1);
         *(curpos->data.string + locpos) = keypressed;
      }
      textpos++;  /* move forward to next text position */
      do_redraw = True;
   }

   /* Redraw the label */

   if (do_redraw) {
      short beglength;

      tmpext = ULength(curlabel->string, areastruct.topinstance, curlabel->scale, textpos, NULL);
      beglength = tmpext.width;
      
      tmpext = ULength(curlabel->string, areastruct.topinstance, curlabel->scale, 0, NULL);
      tmplength = tmpext.width;
      tmpheight = (short)(curlabel->scale * TEXTHEIGHT);
      areastruct.origin.x = curlabel->position.x + (curlabel->justify & NOTLEFT
        ? (curlabel->justify & RIGHT ? 0 : tmplength / 2) : tmplength)
        - (tmplength - beglength);
      areastruct.origin.y = curlabel->position.y + (curlabel->justify &
        NOTBOTTOM ? (curlabel->justify & TOP ? -tmpheight : -tmpheight / 2)
        : 0);
#ifdef SCHEMA
      if (curlabel->pin)
         pinadjust(curlabel->justify, &(areastruct.origin.x),
            &(areastruct.origin.y), 1);
#endif
      XcSetFunction(GXcopy);
      redrawtext(curlabel);
   }
   UDrawTLine(curlabel);

   /* Report on characters at the cursor position in the message window */

   charreport(curlabel);

   /* find current font and adjust menubuttons as necessary */

   cfont = findcurfont(textpos, curlabel->string, areastruct.topinstance);
   if (cfont < 0) {
      Wprintf("Error:  Illegal label string");
      return;
   }
   else
      setfontmarks(cfont, -1);

   textend = 0;
}

/*-------------------------------------*/
/* Initiate return from text edit mode */
/*-------------------------------------*/

void textreturn()
{
   int rkey;

   rkey = firstbinding(XCF_Text_Return);
   labeltext(rkey, NULL);
}

/*-------------------------------------*/
/* Change the justification of a label */
/*-------------------------------------*/

void rejustify(short mode)
{
   labelptr curlabel = NULL;
   short    *tsel;
   short jsave;
   Boolean changed = False;
   static short transjust[] = {15, 13, 12, 7, 5, 4, 3, 1, 0};

   if (eventmode == TEXT2_MODE || eventmode == TEXT3_MODE) {
      curlabel = TOLABEL(EDITPART);
      UDrawTLine(curlabel);
      undrawtext(curlabel);
      jsave = curlabel->justify;
      curlabel->justify = transjust[mode] |
            (curlabel->justify & (FLIPINV | PINVISIBLE));
      if (jsave != curlabel->justify) changed = True;
      redrawtext(curlabel);
      UDrawTLine(curlabel);

      setfontmarks(-1, curlabel->justify);
   }
   else {
      for (tsel = areastruct.selectlist; tsel < areastruct.selectlist +
            areastruct.selects; tsel++) {
       if (SELECTTYPE(tsel) == LABEL) {
          curlabel = SELTOLABEL(tsel);
            jsave = curlabel->justify;
          undrawtext(curlabel);
                curlabel->justify = transjust[mode] |
            (curlabel->justify & (FLIPINV | PINVISIBLE));
            if (jsave != curlabel->justify) changed = True;
          redrawtext(curlabel);
       }
      }
      if (eventmode != PRESS_MODE && eventmode != COPY2_MODE) objectdeselect();
   }
   if (curlabel == NULL)
      Wprintf("No labels chosen to rejustify");
   else if (changed) {
      pwriteback(areastruct.topinstance);
      incr_changes(topobject);

   }
}

/*----------------------------------*/
/* Sane default values for a spline */
/*----------------------------------*/

void splinedefaults(splineptr newspline, int x, int y)
{
   short j;

   for (j = 0; j < 4; j++) {
      newspline->ctrl[j].x = x;
      newspline->ctrl[j].y = y;
   }
   newspline->ctrl[1].x += (int)(xobjs.pagelist[areastruct.page]->gridspace / 2);
   newspline->ctrl[2].x -= (int)(xobjs.pagelist[areastruct.page]->gridspace / 2);
   newspline->width = areastruct.linewidth;
   newspline->style = areastruct.style;
   newspline->color = areastruct.color;
   newspline->num_params = 0;
   newspline->passed = NULL;
   calcspline(newspline);
}

/*-------------------------*/
/* Start drawing a spline. */
/*-------------------------*/

void splinebutton(XButtonEvent *event)
{
   splineptr *newspline;
   XPoint userpt;

   NEW_SPLINE(newspline, topobject);
   areastruct.editpart = topobject->parts;

   snap(event->x, event->y, &userpt);
   areastruct.editcycle = 3;
   splinedefaults(*newspline, userpt.x, userpt.y);

   XcSetXORFg(areastruct.color, BACKGROUND);
   XcSetFunction(GXxor);
   UDrawEditSpline(*newspline);

   xcAddEventHandler(areastruct.area, PointerMotionMask, False,
        (xcEventHandler)trackspline, NULL);

   eventmode = SPLINE_MODE;
}

/*------------------------------------*/
/* Track a spline during mouse motion */
/*------------------------------------*/

void trackspline(xcWidget w, caddr_t clientdata, caddr_t calldata)
{
   XPoint       newpos;
   splineptr    newspline;

   newspline = (eventmode == EPATH_MODE) ?
               (splineptr)(*((*((pathptr *)EDITPART))->plist
            + areastruct.editsubpart)) : TOSPLINE(EDITPART);
   newpos = UGetCursorPos();
   u2u_snap(&newpos);
   if (areastruct.save.x == newpos.x && areastruct.save.y == newpos.y) return;

   UDrawEditSpline(newspline);

   if (areastruct.editcycle == 0 || areastruct.editcycle == 3) {
      short cpoint = (areastruct.editcycle == 0) ? 1 : 2;
      newspline->ctrl[cpoint].x += (newpos.x - newspline->ctrl[areastruct.editcycle].x);
      newspline->ctrl[cpoint].y += (newpos.y - newspline->ctrl[areastruct.editcycle].y);
   }
   newspline->ctrl[areastruct.editcycle].x = newpos.x;
   newspline->ctrl[areastruct.editcycle].y = newpos.y;

   calcspline(newspline);
   UDrawEditSpline(newspline);

   printpos(newpos.x, newpos.y);
   areastruct.save.x = newpos.x;
   areastruct.save.y = newpos.y;
}

/*--------------------------*/
/* Start drawing a polygon. */
/*--------------------------*/

void startpoly(xcWidget button, caddr_t clientdata, caddr_t calldata)
{
   if (eventmode == NORMAL_MODE) {
      Wprintf("Click button1 to start the polygon.");
      eventmode = POLY_MODE;
   }
}

/*-----------------------*/
/* Start drawing an arc. */
/*-----------------------*/

void startarc(xcWidget button, caddr_t clientdata, caddr_t calldata)
{
   if (eventmode == NORMAL_MODE) {
      Wprintf("Click button1 and drag to make arc.");
      eventmode = ARC0_MODE;
   }
}

/*--------------------------------------*/
/* Set default values for an arc    */
/*--------------------------------------*/

void arcdefaults(arcptr newarc, int x, int y)
{
   newarc->style = areastruct.style;
   newarc->color = areastruct.color;
   newarc->position.x = x;
   newarc->position.y = y;
   newarc->width = areastruct.linewidth;
   newarc->radius = 0;
   newarc->yaxis = 0;
   newarc->angle1 = 0;
   newarc->angle2 = 360;
   newarc->num_params = 0;
   newarc->passed = NULL;
   calcarc(newarc);
}

/*-------------------------------------*/
/* Button handler when creating an arc */
/*-------------------------------------*/

void arcbutton(XButtonEvent *event)
{
   arcptr *newarc;
   XPoint userpt;

   NEW_ARC(newarc, topobject);
   areastruct.editpart = topobject->parts;
   snap(event->x, event->y, &userpt);
   areastruct.editcycle = 0;
   saveratio = 1.0;
   arcdefaults(*newarc, userpt.x, userpt.y);

   XcSetXORFg(areastruct.color, BACKGROUND);
   XcSetFunction(GXxor);
   UDrawArc(*newarc);
   UDrawXLine((*newarc)->position, (*newarc)->position);

   xcAddEventHandler(areastruct.area, PointerMotionMask, False,
        (xcEventHandler)trackarc, NULL);

   eventmode = ARC_MODE;
}

/*----------------------------------*/
/* Track an arc during mouse motion */
/*----------------------------------*/

void trackarc(xcWidget w, caddr_t clientdata, caddr_t calldata)
{
   XPoint newpos;
   arcptr newarc;
   double adjrat;

   newarc = (eventmode == EPATH_MODE) ?
          (arcptr)(*((*((pathptr *)EDITPART))->plist
          + areastruct.editsubpart)) : TOARC(EDITPART);

   newpos = UGetCursorPos();
   u2u_snap(&newpos);
   if (areastruct.save.x == newpos.x && areastruct.save.y == newpos.y) return;

   UDrawArc(newarc);
   UDrawXLine(areastruct.save, newarc->position);

   if (areastruct.editcycle == 1 || areastruct.editcycle == 2) {
      float *angleptr, tmpang;

      adjrat = (newarc->yaxis == 0) ? 1 :
            (double)(abs(newarc->radius)) / (double)newarc->yaxis;
      angleptr = (areastruct.editcycle == 1) ? &newarc->angle1 : &newarc->angle2;
      tmpang = (float)(atan2((double)(newpos.y - newarc->position.y) * adjrat,
         (double)(newpos.x - newarc->position.x)) / RADFAC);
      if (areastruct.editcycle == 1) {
       if (tmpang > newarc->angle2) tmpang -= 360;
       else if (newarc->angle2 - tmpang > 360) newarc->angle2 -= 360;
      }
      else {
         if (tmpang < newarc->angle1) tmpang += 360;
       else if (tmpang - newarc->angle1 > 360) newarc->angle1 += 360;
      }
      *angleptr = tmpang;

      if (newarc->angle2 <= 0) {
       newarc->angle2 += 360;
       newarc->angle1 += 360;
      }
      if (newarc->angle2 <= newarc->angle1)
       newarc->angle1 -= 360;
   }
   else if (areastruct.editcycle == 0) {
      short direc = (newarc->radius < 0);
      newarc->radius = wirelength(&newpos, &(newarc->position));
      newarc->yaxis = (short)((double)newarc->radius * saveratio);
      if (direc) newarc->radius = -newarc->radius;
   }
   else {
      newarc->yaxis = wirelength(&newpos, &(newarc->position));
      saveratio = (double)newarc->yaxis / (double)newarc->radius;
   }

   calcarc(newarc);

   UDrawArc(newarc);
   UDrawXLine(newpos, newarc->position);
   printpos(newpos.x, newpos.y);

   areastruct.save.x = newpos.x;
   areastruct.save.y = newpos.y;
}

/*----------------------*/
/* Start drawing a box. */
/*----------------------*/

void startbox(xcWidget button, caddr_t clientdata, caddr_t calldata)
{
   if (eventmode == NORMAL_MODE) {
      Wprintf("Click button1 and drag to make box.");
      eventmode = BOX0_MODE;
   }
}

/*--------------------------------------*/
/* Sane default values for a polygon      */
/*--------------------------------------*/

void polydefaults(polyptr newpoly, int number, int x, int y)
{
   pointlist pointptr;

   newpoly->style = areastruct.style & ~UNCLOSED;
   newpoly->color = areastruct.color;
   newpoly->width = areastruct.linewidth;
   newpoly->number = number;
   newpoly->points = (pointlist) malloc(number * sizeof(XPoint));
   newpoly->num_params = 0;
   newpoly->passed = NULL;
   for (pointptr = newpoly->points; pointptr < newpoly->points + number;
      pointptr++) {
      pointptr->x = x;
      pointptr->y = y;
   }
}

/*------------------------------------*/
/* Button handler when creating a box */
/*------------------------------------*/

void boxbutton(XButtonEvent *event)
{
   polyptr *newbox;
   XPoint userpt;

   NEW_POLY(newbox, topobject);
   areastruct.editpart = topobject->parts;
   snap(event->x, event->y, &userpt);
   polydefaults(*newbox, 4, userpt.x, userpt.y);

   XcSetXORFg(areastruct.color, BACKGROUND);
   XcSetFunction(GXxor);
   UDrawPolygon(*newbox);

   xcAddEventHandler(areastruct.area, PointerMotionMask, False,
        (xcEventHandler)trackbox, NULL);

   eventmode = BOX_MODE;
}

/*---------------------------------*/
/* Track a box during mouse motion */
/*---------------------------------*/

void trackbox(xcWidget w, caddr_t clientdata, caddr_t calldata)
{
   XPoint newpos;
   polyptr      newbox;
   pointlist      pointptr;

   newbox = TOPOLY(EDITPART);
   newpos = UGetCursorPos();
   u2u_snap(&newpos);

   if (areastruct.save.x == newpos.x && areastruct.save.y == newpos.y) return;

   UDrawPolygon(newbox);

   pointptr = newbox->points + 1; pointptr->y = newpos.y;
   pointptr++; pointptr->y = newpos.y; pointptr->x = newpos.x;
   pointptr++; pointptr->x = newpos.x;
   
   UDrawPolygon(newbox);
   printpos(newpos.x, newpos.y);

   areastruct.save.x = newpos.x;
   areastruct.save.y = newpos.y;
}

/*----------------------------------*/
/* Track a wire during mouse motion */
/*----------------------------------*/

void trackwire(xcWidget w, caddr_t clientdata, caddr_t calldata)
{
   XPoint newpos, *tpoint;
   polyptr  newwire;

   newwire = TOPOLY(EDITPART);
   newpos = UGetCursorPos();
   u2u_snap(&newpos);
   if (areastruct.manhatn) manhattanize(&newpos, newwire);

   if (areastruct.save.x != newpos.x || areastruct.save.y != newpos.y) {
      tpoint = newwire->points + newwire->number - 1;
      UDrawPolygon(newwire);
      tpoint->x = newpos.x;
      tpoint->y = newpos.y;
      UDrawPolygon(newwire);
      areastruct.save.x = newpos.x;
      areastruct.save.y = newpos.y;
      printpos(newpos.x, newpos.y);
   }
}

/*--------------------------*/
/* Start drawing a polygon. */
/*--------------------------*/

void startwire(XPoint userpt)
{
   polyptr *newwire;
   pointlist pointptr;

   NEW_POLY(newwire, topobject);
   areastruct.editpart = topobject->parts;

   /* always start unfilled, unclosed; can fix on next button-push. */

   (*newwire)->style = UNCLOSED | (areastruct.style & (DASHED | DOTTED));
   (*newwire)->color = areastruct.color;
   (*newwire)->number = 2;
   (*newwire)->width = areastruct.linewidth;
   (*newwire)->points = (pointlist) malloc(2 * sizeof(XPoint));
   (*newwire)->num_params = 0;
   (*newwire)->passed = NULL;
   pointptr = (*newwire)->points;
   pointptr->x = (pointptr + 1)->x = areastruct.save.x = userpt.x;
   pointptr->y = (pointptr + 1)->y = areastruct.save.y = userpt.y;

   XcSetXORFg(areastruct.color, BACKGROUND);
   XcSetFunction(GXxor);
   UDrawPolygon(*newwire);

   xcAddEventHandler(areastruct.area, PointerMotionMask, False,
          (xcEventHandler)trackwire, NULL);
}

/*----------------------------------------------------------*/
/* Find which points should track along with the edit point */
/* in polygon RHOMBOID or MANHATTAN edit modes.           */
/* (point number is stored in areastruct.editcycle)       */
/*----------------------------------------------------------*/

void finddir(polyptr lastpoly)
{
   XPoint *savept, *npt, *lpt;

   savedir = NONE;
   if (areastruct.boxedit == NORMAL) return;

   savept = lastpoly->points + areastruct.editcycle;

   /* find points before and after the edit point */      

   lpt = (areastruct.editcycle == 0) ? ((lastpoly->style & UNCLOSED) ?
       NULL : lastpoly->points + lastpoly->number - 1) : savept - 1;
   npt = (areastruct.editcycle == lastpoly->number - 1) ?
       ((lastpoly->style & UNCLOSED) ? NULL : lastpoly->points) :
       savept + 1;

   /* two-point polygons (lines) are a degenerate case in RHOMBOID edit mode */

   if (areastruct.boxedit != MANHATTAN && lastpoly->number <= 2) return;

   /* This is complicated but logical:  in MANHATTAN mode, boxes maintain */
   /* box shape.  In RHOMBOID modes, parallelagrams maintain shape.  The  */
   /* "savedir" variable determines which coordinate(s) of which point(s) */
   /* should track along with the edit point.                       */

   if (areastruct.boxedit != RHOMBOIDY) {
      if (lpt != NULL) {
         if (lpt->y == savept->y) {
          savedir |= LASTY;
          if (areastruct.boxedit == RHOMBOIDX && lpt->x != savept->x)
             savedir |= LASTX;
          else if (areastruct.boxedit == RHOMBOIDA && npt != NULL) {
             if (npt->y != savept->y) savedir |= NEXTX;
          }
       }
      }
      if (npt != NULL) {
         if (npt->y == savept->y) {
          savedir |= NEXTY;
          if (areastruct.boxedit == RHOMBOIDX && npt->x != savept->x)
             savedir |= NEXTX;
          else if (areastruct.boxedit == RHOMBOIDA && lpt != NULL) {
             if (lpt->y != savept->y) savedir |= LASTX;
          }
       }
      }
   }
   if (areastruct.boxedit != RHOMBOIDX) {
      if (lpt != NULL) {
         if (lpt->x == savept->x) {
          savedir |= LASTX;
          if (areastruct.boxedit == RHOMBOIDY && lpt->y != savept->y)
             savedir |= LASTY;
          else if (areastruct.boxedit == RHOMBOIDA && npt != NULL) {
             if (npt->x != savept->x) savedir |= NEXTY;
          } 
       }
      }
      if (npt != NULL) {
         if (npt->x == savept->x) {
          savedir |= NEXTX;
          if (areastruct.boxedit == RHOMBOIDY && npt->y != savept->y)
             savedir |= NEXTY;
          else if (areastruct.boxedit == RHOMBOIDA && lpt != NULL) {
             if (lpt->x != savept->x) savedir |= LASTY;
          }
       }
      }
   }
}

/*--------------------------------------------------*/
/* Track movement of poly segments during edit mode */
/*--------------------------------------------------*/

void trackpoly(xcWidget w, caddr_t clientdata, caddr_t calldata)
{
   XPoint newpos, *curpt;
   polyptr      newpoly;
   int            nullint;

   newpoly = (eventmode == EPATH_MODE) ?
           (polyptr)(*((*((pathptr *)EDITPART))->plist
           + areastruct.editsubpart)) : TOPOLY(EDITPART);
   newpos = UGetCursorPos();
   u2u_snap(&newpos);
   if (areastruct.save.x == newpos.x && areastruct.save.y == newpos.y) return;

   UDrawPolygon(newpoly);

   /* find the point under consideration */
   curpt = newpoly->points + areastruct.editcycle;

   if (attachto) {
      findattach(curpt, &nullint, &newpos); 
   }
   else {

      /* find points to either side of the edit point */

      if (areastruct.boxedit != NORMAL) {
         XPoint *fpt = NULL, *bpt = NULL;
       int deltax = newpos.x - curpt->x;
       int deltay = newpos.y - curpt->y; 

         if (curpt > newpoly->points) bpt = curpt - 1;
         else if (!(newpoly->style & UNCLOSED))
          bpt = newpoly->points + newpoly->number - 1;
         if (curpt < newpoly->points + newpoly->number - 1) fpt = curpt + 1;
         else if (!(newpoly->style & UNCLOSED)) fpt = newpoly->points;

       /* enforce constraints */

       if (bpt != NULL) {
          if (savedir & LASTX) bpt->x += deltax;
          if (savedir & LASTY) bpt->y += deltay;
       }
       if (fpt != NULL) {
          if (savedir & NEXTX) fpt->x += deltax;
          if (savedir & NEXTY) fpt->y += deltay;
       }
      }

      /* update position of the point under consideration */

      curpt->x = newpos.x;
      curpt->y = newpos.y;
   }

   UDrawPolygon(newpoly);
   printpos(newpos.x, newpos.y);
   areastruct.save.x = newpos.x;
   areastruct.save.y = newpos.y;
}

/*-------------------------------------------------*/
/* Determine values of endpoints of an element     */
/*-------------------------------------------------*/

void setendpoint(short *scnt, short direc, XPoint **endpoint, XPoint *arcpoint)
{
   genericptr *sptr = topobject->plist + (*scnt);

   switch((*sptr)->type) {
      case POLYGON:
       if (direc)
          *endpoint = TOPOLY(sptr)->points + TOPOLY(sptr)->number - 1;
       else
          *endpoint = TOPOLY(sptr)->points;
       break;
      case SPLINE:
       if (direc)
          *endpoint = &(TOSPLINE(sptr)->ctrl[3]);
       else
          *endpoint = &(TOSPLINE(sptr)->ctrl[0]);
       break;
      case ARC:
       if (direc) {
          arcpoint->x = (short)(TOARC(sptr)->points[TOARC(sptr)->number - 1].x
            + 0.5);
          arcpoint->y = (short)(TOARC(sptr)->points[TOARC(sptr)->number - 1].y
            + 0.5);
       }
       else {
          arcpoint->x = (short)(TOARC(sptr)->points[0].x + 0.5);
          arcpoint->y = (short)(TOARC(sptr)->points[0].y + 0.5);
       }
       *endpoint = arcpoint;
       break;
   }
}

/*------------------------------------------------------------*/
/* Reverse points in a point list                     */
/*------------------------------------------------------------*/

void reversepoints(XPoint *plist, short number)
{
   XPoint hold, *ppt;
   XPoint *pend = plist + number - 1;
   short hnum = number >> 1;

   for (ppt = plist; ppt < plist + hnum; ppt++, pend--) {
      hold.x = ppt->x;
      hold.y = ppt->y;
      ppt->x = pend->x;
      ppt->y = pend->y;
      pend->x = hold.x;
      pend->y = hold.y;
   }
}

/*------------------------------------------------------------*/
/* Same as above for floating-point positions               */
/*------------------------------------------------------------*/

void reversefpoints(XfPoint *plist, short number)
{
   XfPoint hold, *ppt;
   XfPoint *pend = plist + number - 1;
   short hnum = number >> 1;

   for (ppt = plist; ppt < plist + hnum; ppt++, pend--) {
      hold.x = ppt->x;
      hold.y = ppt->y;
      ppt->x = pend->x;
      ppt->y = pend->y;
      pend->x = hold.x;
      pend->y = hold.y; 
   }
}

/*--------------------------------------------------------------*/
/* Permanently remove an element from the topobject plist   */
/*    add = 1 if plist has (parts + 1) elements       */
/*--------------------------------------------------------------*/

void freeparts(short *selectobj, short add)
{
   genericptr *oldelem = topobject->plist + (*selectobj);
   switch((*oldelem)->type) {
      case POLYGON:
       free((TOPOLY(oldelem))->points);
       break;
      case PATH:
       free((TOPATH(oldelem))->plist);
       break;
      case LABEL:
       free((TOLABEL(oldelem))->string);
       break;
   }
   free(*oldelem);
   removep(selectobj, add);
}

/*--------------------------------------------------------------*/
/* Remove a part from an object                             */
/*    add = 1 if plist has (parts + 1) elements       */
/*--------------------------------------------------------------*/

void removep(short *selectobj, short add)
{
   genericptr *oldelem = topobject->plist + (*selectobj);

   for (++oldelem; oldelem < topobject->plist + topobject->parts + add; oldelem++)
          *(oldelem - 1) = *oldelem;

   topobject->parts--;
}

/*-------------------------------------------------*/
/* Break a path into its constituent components    */
/*-------------------------------------------------*/

void unjoin()
{
   short *selectobj;
   genericptr *genp, *newg;
   pathptr oldpath;

   if (areastruct.selects == 0) objectselect(PATH);
   if (areastruct.selects == 0) {
      Wprintf("No objects selected.");
      return;
   }

   /* for each selected path */

   XcSetFunction(GXcopy);

   for (selectobj = areastruct.selectlist; selectobj < areastruct.selectlist
        + areastruct.selects; selectobj++) {
      XSetForeground(dpy, areastruct.gc, BACKGROUND);
      if (SELECTTYPE(selectobj) == PATH) {
         oldpath = SELTOPATH(selectobj);

         /* undraw the path */
      
         UDrawPath(oldpath);
      
         /* move components to the top level */

       topobject->plist = (genericptr *)realloc(topobject->plist,
            (topobject->parts + oldpath->parts) * sizeof(genericptr));
       newg = topobject->plist + topobject->parts;
       for (genp = oldpath->plist; genp < oldpath->plist +
            oldpath->parts; genp++, newg++) {
          *newg = *genp;
       }
       topobject->parts += oldpath->parts;

         /* remove the path object and revise the selectlist */

         freeparts(selectobj, 0);
         reviseselect(selectobj);
      }
   }
   clearselects();
   drawarea(NULL, NULL, NULL);
}

/*-------------------------------------------------*/
/* Test if two points are near each other    */
/*-------------------------------------------------*/

Boolean neartest(XPoint *point1, XPoint *point2)
{
   short diff[2];

   diff[0] = point1->x - point2->x;
   diff[1] = point1->y - point2->y;
   diff[0] = abs(diff[0]);
   diff[1] = abs(diff[1]);

   if (diff[0] <= 2 && diff[1] <= 2) return True;
   else return False;
}


/*-------------------------------------------------*/
/* Join stuff together                       */
/*-------------------------------------------------*/

void join()
{
   short     *selectobj;
   polyptr   *newpoly, nextwire;
   pathptr   *newpath;
   short     *scount, *sptr, *sptr2, *direc, *order;
   short     ordered, startpt = 0;
   short     numpolys, numlabels, numpoints, polytype;
   int           polycolor;
   float     polywidth;
   XPoint    *testpoint, *testpoint2, *begpoint, *endpoint, arcpoint[4];
   XPoint    *begpoint2, *endpoint2;
   Boolean   allpolys = True;

   numpolys = numlabels = 0;
   for (selectobj = areastruct.selectlist; selectobj < areastruct.selectlist
      + areastruct.selects; selectobj++) {
      if (SELECTTYPE(selectobj) == POLYGON) {
       /* arbitrary:  keep style of last polygon in selectlist */
       polytype = SELTOPOLY(selectobj)->style;
       polywidth = SELTOPOLY(selectobj)->width;
       polycolor = SELTOPOLY(selectobj)->color;
       numpolys++;
      }
      else if (SELECTTYPE(selectobj) == SPLINE) {
       polytype = SELTOSPLINE(selectobj)->style;
       polywidth = SELTOSPLINE(selectobj)->width;
       polycolor = SELTOSPLINE(selectobj)->color;
       numpolys++;
       allpolys = False;
      }
      else if (SELECTTYPE(selectobj) == ARC) {
       polytype = SELTOARC(selectobj)->style;
       polywidth = SELTOARC(selectobj)->width;
       polycolor = SELTOARC(selectobj)->color;
       numpolys++;
       allpolys = False;
      }
      else if (SELECTTYPE(selectobj) == LABEL)
       numlabels++;
   }
   if ((numpolys == 0) && (numlabels == 0)) {
      Wprintf("No elements selected for joining.");
      return;
   }
   else if ((numpolys == 1) || (numlabels == 1)) {
      Wprintf("Only one element: nothing to join to.");
      return;
   }
   else if ((numpolys > 1) && (numlabels > 1)) {
      Wprintf("Selection mixes labels and line segments.  Ignoring.");
      return;
   }
   else if (numlabels > 0) {
      joinlabels();
      return;
   }

   /* scount is a table of element numbers                        */
   /* order is an ordered table of end-to-end elements                  */
   /* direc is an ordered table of path directions (0=same as element,  */
   /*       1=reverse from element definition)                    */

   scount = (short *) malloc(numpolys * sizeof(short));
   order  = (short *) malloc(numpolys * sizeof(short));
   direc  = (short *) malloc(numpolys * sizeof(short));
   sptr = scount;
   numpoints = 1;

   /* make a record of the element instances involved */

   for (selectobj = areastruct.selectlist; selectobj < areastruct.selectlist
      + areastruct.selects; selectobj++) {
      if (SELECTTYPE(selectobj) == POLYGON) {
        numpoints += SELTOPOLY(selectobj)->number - 1;
        *(sptr++) = *selectobj;
      }
      else if (SELECTTYPE(selectobj) == SPLINE || SELECTTYPE(selectobj) == ARC)
        *(sptr++) = *selectobj;
   }

   /* Sort the elements by sorting the scount array:                    */
   /* Loop through each point as starting point in case of strangely connected      */
   /* structures. . . for normal structures it should break out on the first        */
   /* loop (startpt = 0).                                         */

   for (startpt = 0; startpt < numpolys; startpt++) {

      /* set first in ordered list */

      direc[0] = 0;
      order[0] = *(scount + startpt);

      for (ordered = 0; ordered < numpolys - 1; ordered++) {

         setendpoint(order + ordered, (1 ^ direc[ordered]), &endpoint2, &arcpoint[0]);
         setendpoint(order, (0 ^ direc[0]), &begpoint2, &arcpoint[1]);

         for (sptr = scount; sptr < scount + numpolys; sptr++) {

          /* don't compare with things already in the list */
          for (sptr2 = order; sptr2 <= order + ordered; sptr2++)
             if (*sptr == *sptr2) break;
          if (sptr2 != order + ordered + 1) continue;

            setendpoint(sptr, 0, &begpoint, &arcpoint[2]);
            setendpoint(sptr, 1, &endpoint, &arcpoint[3]);

          /* four cases of matching endpoint of one element to another */

          if (neartest(begpoint, endpoint2)) {
             order[ordered + 1] = *sptr;
             direc[ordered + 1] = 0;
             break;
          }
          else if (neartest(endpoint, endpoint2)) {
             order[ordered + 1] = *sptr;
             direc[ordered + 1] = 1;
             break;
          }
          else if (neartest(begpoint, begpoint2)) {
             for (sptr2 = order + ordered + 1; sptr2 > order; sptr2--)
                *sptr2 = *(sptr2 - 1);
             for (sptr2 = direc + ordered + 1; sptr2 > direc; sptr2--)
                *sptr2 = *(sptr2 - 1);
             order[0] = *sptr;
             direc[0] = 1;
             break;
          }
          else if (neartest(endpoint, begpoint2)) {
             for (sptr2 = order + ordered + 1; sptr2 > order; sptr2--) 
                *sptr2 = *(sptr2 - 1);
             for (sptr2 = direc + ordered + 1; sptr2 > direc; sptr2--)
                *sptr2 = *(sptr2 - 1);
             order[0] = *sptr;
             direc[0] = 0;
             break;
          }
         }
       if (sptr == scount + numpolys) break;
      }
      if (ordered == numpolys - 1) break;
   }

   if (startpt == numpolys) {
      Wprintf("Cannot join: Too many free endpoints");
      free(order);
      free(direc);
      free(scount);
      return;
   }

   XcSetFunction(GXcopy);
   XSetForeground(dpy, areastruct.gc, BACKGROUND);

   /* create the new polygon or path */

   if (allpolys) {
      NEW_POLY(newpoly, topobject);

      (*newpoly)->number = numpoints;
      (*newpoly)->points = (pointlist) malloc(numpoints * sizeof(XPoint));
      (*newpoly)->width  = polywidth;
      (*newpoly)->style  = polytype;
      (*newpoly)->color  = polycolor;
      (*newpoly)->num_params = 0;
      (*newpoly)->passed = NULL;

      /* insert the points into the new polygon */

      testpoint2 = (*newpoly)->points;
      for (sptr = order; sptr < order + numpolys; sptr++) {
         nextwire = SELTOPOLY(sptr);
       if (*(direc + (short)(sptr - order)) == 0) {
            for (testpoint = nextwire->points; testpoint < nextwire->points + 
               nextwire->number - 1; testpoint++) {
             testpoint2->x = testpoint->x;
             testpoint2->y = testpoint->y; 
             testpoint2++;
          }
         }
         else {
            for (testpoint = nextwire->points + nextwire->number - 1; testpoint
               > nextwire->points; testpoint--) {
             testpoint2->x = testpoint->x;
             testpoint2->y = testpoint->y; 
             testpoint2++;
          }
       }
      }
      /* pick up the last point */
      testpoint2->x = testpoint->x;
      testpoint2->y = testpoint->y;

      /* delete the old elements from the list */

      for (sptr = scount; sptr < scount + numpolys; sptr++) {
       easydraw(*sptr, DOFORALL);
       freeparts(sptr, 1);
       /* revise the list of path elements */
       for (sptr2 = sptr + 1; sptr2 < scount + numpolys; sptr2++)
          if (*sptr2 > *sptr) (*sptr2)--;
      }
      XcSetForeground((*newpoly)->color);
      UDrawPolygon(*newpoly);
   }
   else {   /* create a path */
      short newcount = 0;

      NEW_PATH(newpath, topobject);
      (*newpath)->style = polytype;
      (*newpath)->color = polycolor;
      (*newpath)->width = polywidth;
      (*newpath)->parts = numpolys;
      (*newpath)->plist = (genericptr *) malloc(numpolys * sizeof(genericptr));
      (*newpath)->num_params = 0;
      (*newpath)->passed = NULL;

      /* move the elements from the top level into the path structure */

      for (sptr = order; sptr < order + numpolys; sptr++, newcount++) {
       genericptr *oldelem = topobject->plist + *sptr;
       genericptr *newelem = (*newpath)->plist + newcount;
       *newelem = *oldelem;

       /* reverse point order if necessary */

         if (*(direc + (short)(sptr - order)) == 1) {
          switch ((*newelem)->type) {
             case POLYGON:
              reversepoints(TOPOLY(newelem)->points, TOPOLY(newelem)->number);
                break;
             case ARC:
              reversefpoints(TOARC(newelem)->points, TOARC(newelem)->number);
              TOARC(newelem)->radius = -TOARC(newelem)->radius;
                break;
             case SPLINE:
              reversepoints(TOSPLINE(newelem)->ctrl, 4);
              calcspline(TOSPLINE(newelem));
                break;
          }
       }
      }
      for (sptr = scount; sptr < scount + numpolys; sptr++) {
       easydraw(*sptr, DOFORALL);
       removep(sptr, 1);  /* close up list but do not delete elements */
       /* revise the list of path elements */
       for (sptr2 = sptr + 1; sptr2 < scount + numpolys; sptr2++)
          if (*sptr2 > *sptr) (*sptr2)--;
      }
      
      XcSetForeground((*newpath)->color);
      UDrawPath(*newpath);
   }

   /* clean up */

   topobject->parts++;
   incr_changes(topobject);
   clearselects();
   free(scount);
   free(order);
   free(direc);
}

/*-------------------------------------------------*/
/* ButtonPress handler while a wire is being drawn */
/*-------------------------------------------------*/

void wirebutton(XButtonEvent *event)
{
   XPoint userpt, *tpoint;
   polyptr newwire;

   snap(event->x, event->y, &userpt);

   newwire = TOPOLY(EDITPART);
   if (areastruct.manhatn) manhattanize(&userpt, newwire);
 
   /* This undraws the wire */

   UDrawPolygon(newwire);

   tpoint = newwire->points + newwire->number - 1;
   tpoint->x = userpt.x;
   tpoint->y = userpt.y;

   /* back up one point; prevent length zero wires */
   if ((event->button == Button3) || ((tpoint - 1)->x == userpt.x &&
         (tpoint - 1)->y == userpt.y)) {
      if (newwire->number <= 2) {
       free(newwire->points);
       free(newwire);
       newwire = NULL;
         eventmode = NORMAL_MODE;
      }
      else {
         if (--newwire->number == 2) newwire->style = UNCLOSED |
            (areastruct.style & (DASHED | DOTTED));
      }
   }

   if (newwire && event->button == Button1) {
      if (++newwire->number == 3) newwire->style = areastruct.style;
      newwire->points = (XPoint *)realloc(newwire->points, newwire->number
            * sizeof(XPoint));
      tpoint = newwire->points + newwire->number - 1;
      tpoint->x = userpt.x;
      tpoint->y = userpt.y;
   }
   else if (newwire == NULL || event->button != Button3)
      xcRemoveEventHandler(areastruct.area, PointerMotionMask, False,
         (xcEventHandler)trackwire, NULL);

   if (newwire) {
      if (event->button == Button2) {
         XcSetFunction(GXcopy);
         XcSetForeground(newwire->color);
       topobject->parts++;
       incr_changes(topobject);
#ifdef SCHEMA
       if (!nonnetwork(newwire)) topobject->valid = False;
#endif
      }
      UDrawPolygon(newwire);
      if (event->button == Button3)
       checkwarp(newwire->points + newwire->number - 1);
   }
}

/*-------------------------------------------------------------------------*/

Generated by  Doxygen 1.6.0   Back to index