/*----------------------------------------------------------------------*/ /* elements.c --- xcircuit routines for creating basic elements */ /* Copyright (c) 2002 Tim Edwards, Johns Hopkins University */ /*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/ /* written by Tim Edwards, 8/13/93 */ /*----------------------------------------------------------------------*/ #include #include #include #ifndef XC_WIN32 #include #include #endif #ifdef TCL_WRAPPER #include #else #ifndef XC_WIN32 #include "Xw/Xw.h" #endif #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 int number_colors; extern double atan2(); /*------------------------------------------------------------------------*/ /* Declarations of global variables */ /*------------------------------------------------------------------------*/ short savedir = 0; /* for rhomboid/manhattan editing */ char extchar[20]; double saveratio; u_char texttype; /*--------------------------------------*/ /* Element constructor functions */ /*--------------------------------------*/ /*--------------------------------------------------------------*/ /* Label constructor: Create a new label element in the object */ /* whose instance is "destinst" and return a pointer to it. */ /* */ /* "destinst" is the destination instance. If NULL, the */ /* top-level instance (areastruct.topinstance) is used. */ /* "strptr" is a pointer to a stringpart string, and may */ /* be NULL. If non-NULL, should NOT be free'd by the */ /* calling routine. */ /* "pintype" is NORMAL, LOCAL, GLOBAL, or INFO */ /* "x" and "y" are the label coordinates. */ /* */ /* Other properties must be set individually by the calling */ /* routine. */ /* */ /* Return value is a pointer to the newly created label. */ /*--------------------------------------------------------------*/ labelptr new_label(objinstptr destinst, stringpart *strptr, int pintype, int x, int y) { labelptr *newlab; objectptr destobject; objinstptr locdestinst; locdestinst = (destinst == NULL) ? areastruct.topinstance : destinst; destobject = locdestinst->thisobject; NEW_LABEL(newlab, destobject); destobject->parts++; labeldefaults(*newlab, pintype, x, y); if (strptr->type == FONT_NAME) { free ((*newlab)->string); (*newlab)->string = strptr; } else (*newlab)->string->nextpart = strptr; calcbboxvalues(locdestinst, (genericptr *)newlab); updatepagebounds(destobject); incr_changes(destobject); return *newlab; } /*--------------------------------------------------------------*/ /* Variant of the above; creates a label from a (char *) string */ /* instead of a stringpart pointer. Like the stringpart */ /* pointer above, "cstr" should NOT be free'd by the calling */ /* routine. */ /*--------------------------------------------------------------*/ labelptr new_simple_label(objinstptr destinst, char *cstr, int pintype, int x, int y) { stringpart *strptr; strptr = (stringpart *)malloc(sizeof(stringpart)); strptr->type = TEXT_STRING; strptr->nextpart = NULL; strptr->data.string = cstr; return new_label(destinst, strptr, pintype, x, y); } /*--------------------------------------------------------------*/ /* Another variant of the above; creates a "temporary" label */ /* from a (char *) string. As above, "cstr" should NOT be */ /* free'd by the calling routine. The "temporary" label has no */ /* font information, and cannot be displayed nor saved/loaded */ /* from the PostScript output file. Used to name networks or */ /* to mark port positions. Pin type is always LOCAL. Does not */ /* require updating bounding box info since it cannot be */ /* displayed. Consequently, only requires passing the object */ /* to get the new label, not its instance. */ /*--------------------------------------------------------------*/ labelptr new_temporary_label(objectptr destobject, char *cstr, int x, int y) { labelptr *newlab; NEW_LABEL(newlab, destobject); destobject->parts++; labeldefaults(*newlab, LOCAL, x, y); (*newlab)->string->type = TEXT_STRING; /* overwrites FONT record */ (*newlab)->string->data.string = cstr; return *newlab; } /*--------------------------------------------------------------*/ /* Polygon constructor: Create a new polygon element in the */ /* object whose instance is "destinst" and return a pointer to */ /* it. */ /* */ /* "destinst" is the destination instance. If NULL, the */ /* top-level instance (areastruct.topinstance) is used. */ /* "points" is a list of XPoint pointers, should not be */ /* NULL. It is transferred to the polygon verbatim, */ /* and should NOT be free'd by the calling routine. */ /* "number" is the number of points in the list, or zero */ /* if "points" is NULL. */ /* */ /* Other properties must be set individually by the calling */ /* routine. */ /* */ /* Return value is a pointer to the newly created polygon. */ /*--------------------------------------------------------------*/ polyptr new_polygon(objinstptr destinst, pointlist *points, int number) { polyptr *newpoly; objectptr destobject; objinstptr locdestinst; locdestinst = (destinst == NULL) ? areastruct.topinstance : destinst; destobject = locdestinst->thisobject; NEW_POLY(newpoly, destobject); destobject->parts++; polydefaults(*newpoly, 0, 0, 0); (*newpoly)->number = number; (*newpoly)->points = *points; calcbboxvalues(locdestinst, (genericptr *)newpoly); updatepagebounds(destobject); incr_changes(destobject); return *newpoly; } /*--------------------------------------------------------------*/ /* Spline constructor: Create a new spline element in the */ /* object whose instance is "destinst" and return a pointer to */ /* it. */ /* */ /* "destinst" is the destination instance. If NULL, the */ /* top-level instance (areastruct.topinstance) is used. */ /* "points" is a array of 4 XPoints; should not be NULL. */ /* */ /* Other properties must be set individually by the calling */ /* routine. */ /* */ /* Return value is a pointer to the newly created spline. */ /*--------------------------------------------------------------*/ splineptr new_spline(objinstptr destinst, pointlist points) { splineptr *newspline; objectptr destobject; objinstptr locdestinst; int i; locdestinst = (destinst == NULL) ? areastruct.topinstance : destinst; destobject = locdestinst->thisobject; NEW_SPLINE(newspline, destobject); destobject->parts++; splinedefaults(*newspline, 0, 0); for (i = 0; i < 4; i++) (*newspline)->ctrl[i] = points[i]; calcspline(*newspline); calcbboxvalues(locdestinst, (genericptr *)newspline); updatepagebounds(destobject); incr_changes(destobject); return *newspline; } /*--------------------------------------------------------------*/ /* Arc constructor: Create a new arc element in the object */ /* whose instance is "destinst" and return a pointer to it. */ /* */ /* "destinst" is the destination instance. If NULL, the */ /* top-level instance (areastruct.topinstance) is used. */ /* "radius" is the radius of the (circular) arc. */ /* "x" and "y" represents the arc center position. */ /* */ /* Other properties must be set individually by the calling */ /* routine. */ /* */ /* Return value is a pointer to the newly created arc. */ /*--------------------------------------------------------------*/ arcptr new_arc(objinstptr destinst, int radius, int x, int y) { arcptr *newarc; objectptr destobject; objinstptr locdestinst; locdestinst = (destinst == NULL) ? areastruct.topinstance : destinst; destobject = locdestinst->thisobject; NEW_ARC(newarc, destobject); destobject->parts++; arcdefaults(*newarc, x, y); (*newarc)->radius = (*newarc)->yaxis = radius; calcarc(*newarc); calcbboxvalues(locdestinst, (genericptr *)newarc); updatepagebounds(destobject); incr_changes(destobject); return *newarc; } /*--------------------------------------------------------------*/ /* Instance constructor: Create a new object instance element */ /* in the object whose instance is "destinst" and return a */ /* pointer to it. */ /* */ /* "destinst" is the destination instance. If NULL, the */ /* top-level instance (areastruct.topinstance) is used. */ /* "srcinst" is the source instance of which this is a */ /* copy. */ /* "x" and "y" represents the instance position. */ /* */ /* Other properties must be set individually by the calling */ /* routine. */ /* */ /* Return value is a pointer to the newly created arc. */ /*--------------------------------------------------------------*/ objinstptr new_objinst(objinstptr destinst, objinstptr srcinst, int x, int y) { objinstptr *newobjinst; objectptr destobject; objinstptr locdestinst; locdestinst = (destinst == NULL) ? areastruct.topinstance : destinst; destobject = locdestinst->thisobject; NEW_OBJINST(newobjinst, destobject); destobject->parts++; instcopy(*newobjinst, srcinst); (*newobjinst)->position.x = x; (*newobjinst)->position.y = y; calcbboxvalues(locdestinst, (genericptr *)newobjinst); updatepagebounds(destobject); incr_changes(destobject); return *newobjinst; } /*--------------------------------------------------------------*/ /* Generic element destructor function */ /*--------------------------------------------------------------*/ void remove_element(objinstptr destinst, genericptr genelem) { objectptr destobject; objinstptr locdestinst; locdestinst = (destinst == NULL) ? areastruct.topinstance : destinst; destobject = locdestinst->thisobject; genelem->type &= REMOVE_TAG; delete_tagged(destobject); calcbboxvalues(locdestinst, (genericptr *)NULL); updatepagebounds(destobject); } /*-------------------------------------*/ /* 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; } /*---------------------------------------*/ /* Sane values for a new object instance */ /*---------------------------------------*/ void instancedefaults(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->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; newinst->schembbox = NULL; } /*--------------------------------------*/ /* 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++; instancedefaults(*newdot, dotobj, xpos, ypos); register_for_undo(XCF_Dot, UNDO_DONE, areastruct.topinstance, *newdot); } 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)->passed = NULL; calcarc(*newarc); register_for_undo(XCF_Arc, UNDO_DONE, areastruct.topinstance, *newarc); } incr_changes(topobject); } /*--------------------------------------*/ /* 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->passed = NULL; /* initialize string with font designator */ newlabel->string->type = FONT_NAME; newlabel->string->data.font = areastruct.psfont; newlabel->string->nextpart = NULL; newlabel->pin = False; if (areastruct.schemon) { newlabel->pin = dopin; if (dopin == LOCAL) newlabel->color = LOCALPINCOLOR; else if (dopin == GLOBAL) newlabel->color = GLOBALPINCOLOR; else if (dopin == INFO) newlabel->color = INFOLABELCOLOR; } newlabel->justify = areastruct.justify; newlabel->position.x = x; newlabel->position.y = y; } /*--------------------------------------*/ /* Button handler when creating a label */ /*--------------------------------------*/ void textbutton(u_char dopin, int x, int y) { 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(x, 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 (IS_LABEL(*tgen)) { tlab = TOLABEL(tgen); if (tlab->pin != LOCAL) 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 */ /* */ /* Return TRUE if labeltext handled the character, FALSE if the */ /* character was not recognized. */ /*--------------------------------------------------------------*/ Boolean labeltext(int keypressed, char *clientdata) { labelptr curlabel; stringpart *curpos, *labelbuf; int locpos; Boolean r, do_redraw = False; short tmplength, tmpheight, cfont; TextExtents tmpext; curlabel = TOLABEL(EDITPART); if (curlabel == NULL || textpos <= 0) { Wprintf("Error: Bad label string"); return FALSE; } /* find text segment of the current position */ curpos = findstringpart(textpos, &locpos, curlabel->string, areastruct.topinstance); UDrawTLine(curlabel); if (r = isbound(keypressed, XCF_Text_Delete)) { if (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 (r = isbound(keypressed, XCF_Text_Delete_Param)) { if (textpos > 1) { int curloc, strpos; stringpart *strptr; strptr = findstringpart(textpos - 1, &curloc, curlabel->string, areastruct.topinstance); if ((curloc < 0) && (strptr != NULL) && (strptr->type == PARAM_END)) { undrawtext(curlabel); while (strptr->type != PARAM_START) strptr = findstringpart(--strpos, &curloc, curlabel->string, areastruct.topinstance); unmakeparam(curlabel, strptr); do_redraw = True; } } } else if (r = isbound(keypressed, XCF_Text_Return)) { Boolean hasstuff = False; /* Check for null string */ 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, DEFAULTCURSOR); Wprintf(""); if (hasstuff && (eventmode != ETEXT_MODE && eventmode != CATTEXT_MODE)) { topobject->parts++; register_for_undo(XCF_Text, UNDO_DONE, areastruct.topinstance, curlabel); incr_changes(topobject); invalidate_netlist(topobject); } else if (!hasstuff && (eventmode == ETEXT_MODE)) { if (areastruct.editpart < topobject->parts) { short *sptr = allocselect(); *sptr = areastruct.editpart; /* Force the "delete" undo record to be a continuation of */ /* the undo series containing the edit. That way, "undo" */ /* does not generate a label with null text. */ xobjs.undostack->idx = -xobjs.undostack->idx; standard_element_delete(NORMAL); } else { /* Label had just been created; just delete it w/o undo */ freelabel(curlabel->string); free(curlabel); topobject->parts--; } } if ((!hasstuff) && (eventmode == CATTEXT_MODE)) { /* can't have null labels! */ undo_action(); XcSetFunction(GXcopy); redrawtext(curlabel); Wprintf("Object must have a name!"); eventmode = CATALOG_MODE; } else if (!hasstuff) { eventmode = NORMAL_MODE; } else if (eventmode == CATTEXT_MODE) { objectptr libobj; stringpart *oldname; /* Get the library object whose name matches the original string */ oldname = get_original_string(curlabel); libobj = NameToObject(oldname->nextpart->data.string, NULL, FALSE); if (libobj != NULL) { /* set name of object to new string */ strcpy(libobj->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(libobj)) { 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, libobj->name); XcSetFunction(GXcopy); redrawtext(curlabel); } } eventmode = CATALOG_MODE; unselect_all(); } else { /* (hasstuff && eventmode != CATTEXT_MODE) */ eventmode = NORMAL_MODE; incr_changes(topobject); if (curlabel->pin != False) invalidate_netlist(topobject); } setdefaultfontmarks(); setcolormark(areastruct.color); if ((labelbuf = get_original_string(curlabel)) != NULL) { /* 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 == LOCAL) && (topobject->symschem != NULL)) { if ((findlabelcopy(curlabel, labelbuf) == NULL) && (findlabelcopy(curlabel, curlabel->string) == NULL)) { changeotherpins(curlabel, labelbuf); if (topobject->schemtype == PRIMARY || topobject->schemtype == SECONDARY) Wprintf("Changed corresponding pin in associated symbol"); else Wprintf("Changed corresponding pin in associated schematic"); incr_changes(topobject->symschem); invalidate_netlist(topobject->symschem); } } resolveparams(areastruct.topinstance); updateinstparam(topobject); setobjecttype(topobject); } else calcbbox(areastruct.topinstance); return r; } else if (r = isbound(keypressed, XCF_Text_Right)) { if (curpos != NULL) textpos++; } else if (r = isbound(keypressed, XCF_Text_Left)) { if (textpos > 1) textpos--; } else if (r = 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 (r = 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 (r = isbound(keypressed, XCF_Text_Home)) textpos = 1; else if (r = isbound(keypressed, XCF_Text_End)) textpos = stringlength(curlabel->string, True, areastruct.topinstance); else if (r = 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) { oparamptr ops; 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.string = (char *)malloc(1 + strlen(clientdata)); strcpy(newpart->data.string, clientdata); ops = match_param(topobject, clientdata); if (ops == NULL) errcond = True; break; } if (errcond == True) { Wprintf("Error in insertion. Ignoring."); deletestring(newpart, &curlabel->string, areastruct.topinstance); } else { textpos++; r = TRUE; } 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; r = 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); if (curlabel->pin) pinadjust(curlabel->justify, &(areastruct.origin.x), &(areastruct.origin.y), 1); XcSetFunction(GXcopy); redrawtext(curlabel); } UDrawTLine(curlabel); if (r || do_redraw) { /* 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 r; } else setfontmarks(cfont, -1); textend = 0; } return r; } /*-------------------------------------*/ /* 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 == TEXT_MODE || eventmode == ETEXT_MODE) { curlabel = TOLABEL(EDITPART); UDrawTLine(curlabel); undrawtext(curlabel); jsave = curlabel->justify; curlabel->justify = transjust[mode] | (curlabel->justify & NONJUSTFIELD); 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 & NONJUSTFIELD); if (jsave != curlabel->justify) changed = True; } } if (eventmode != MOVE_MODE && eventmode != COPY_MODE) unselect_all(); else draw_all_selected(); } if (curlabel == NULL) Wprintf("No labels chosen to rejustify"); else if (changed) { pwriteback(areastruct.topinstance); calcbbox(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->passed = NULL; calcspline(newspline); } /*-------------------------*/ /* Start drawing a spline. */ /*-------------------------*/ void splinebutton(int x, int y) { splineptr *newspline; XPoint userpt; NEW_SPLINE(newspline, topobject); areastruct.editpart = topobject->parts; snap(x, 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; flusharea(); } /*--------------------------------------*/ /* 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->passed = NULL; calcarc(newarc); } /*-------------------------------------*/ /* Button handler when creating an arc */ /*-------------------------------------*/ void arcbutton(int x, int y) { arcptr *newarc; XPoint userpt; NEW_ARC(newarc, topobject); areastruct.editpart = topobject->parts; snap(x, 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; flusharea(); } /*--------------------------------------*/ /* 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->passed = NULL; if (number == 0) newpoly->points = NULL; else { newpoly->points = (pointlist) malloc(number * sizeof(XPoint)); for (pointptr = newpoly->points; pointptr < newpoly->points + number; pointptr++) { pointptr->x = x; pointptr->y = y; } } } /*------------------------------------*/ /* Button handler when creating a box */ /*------------------------------------*/ void boxbutton(int x, int y) { polyptr *newbox; XPoint userpt; NEW_POLY(newbox, topobject); areastruct.editpart = topobject->parts; snap(x, 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; flusharea(); } /*----------------------------------*/ /* 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); } flusharea(); } /*--------------------------*/ /* 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)->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; flusharea(); } /*-------------------------------------------------*/ /* Determine values of endpoints of an element */ /*-------------------------------------------------*/ void setendpoint(short *scnt, short direc, XPoint **endpoint, XPoint *arcpoint) { genericptr *sptr = topobject->plist + (*scnt); switch(ELEMENTTYPE(*sptr)) { 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 freepathparts(short *selectobj, short add) { genericptr *oldelem = topobject->plist + (*selectobj); switch(ELEMENTTYPE(*oldelem)) { case POLYGON: free((TOPOLY(oldelem))->points); 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) select_element(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 */ freepathparts(selectobj, 0); reviseselect(areastruct.selectlist, areastruct.selects, 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)->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); freepathparts(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)->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 (ELEMENTTYPE(*newelem)) { 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); } /*----------------------------------------------*/ /* Add a new point to a polygon */ /*----------------------------------------------*/ void poly_add_point(polyptr thispoly, XPoint *newpoint) { XPoint *tpoint; thispoly->number++; thispoly->points = (XPoint *)realloc(thispoly->points, thispoly->number * sizeof(XPoint)); tpoint = thispoly->points + thispoly->number - 1; tpoint->x = newpoint->x; tpoint->y = newpoint->y; } /*-------------------------------------------------*/ /* ButtonPress handler while a wire is being drawn */ /*-------------------------------------------------*/ void wire_op(int op, int x, int y) { XPoint userpt, *tpoint; polyptr newwire; snap(x, 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; /* cancel wire operation completely */ if (op == XCF_Cancel) { free(newwire->points); free(newwire); newwire = NULL; eventmode = NORMAL_MODE; } /* back up one point; prevent length zero wires */ else if ((op == XCF_Cancel_Last) || ((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 && (op == XCF_Wire || op == XCF_Continue_Element)) { if (newwire->number == 2) newwire->style = areastruct.style; poly_add_point(newwire, &userpt); } else if ((newwire == NULL) || op == XCF_Finish_Element || op == XCF_Cancel) xcRemoveEventHandler(areastruct.area, PointerMotionMask, False, (xcEventHandler)trackwire, NULL); if (newwire) { if (op == XCF_Finish_Element) { XcSetFunction(GXcopy); XcSetForeground(newwire->color); topobject->parts++; incr_changes(topobject); if (!nonnetwork(newwire)) invalidate_netlist(topobject); register_for_undo(XCF_Wire, UNDO_DONE, areastruct.topinstance, newwire); } UDrawPolygon(newwire); if (op == XCF_Cancel_Last) checkwarp(newwire->points + newwire->number - 1); } if (op == XCF_Finish_Element) { eventmode = NORMAL_MODE; singlebbox(EDITPART); } } /*-------------------------------------------------------------------------*/