/*--------------------------------------------------------------*/ /* tclxcircuit.c: */ /* Tcl routines for xcircuit command-line functions */ /* Copyright (c) 2003 Tim Edwards, Johns Hopkins University */ /* Copyright (c) 2004 Tim Edwards, MultiGiG, Inc. */ /*--------------------------------------------------------------*/ #if defined(TCL_WRAPPER) && !defined(HAVE_PYTHON) #include #include /* for va_copy() */ #include /* for atoi() and others */ #include #include #include #include #include #include #ifdef HAVE_CAIRO #include #endif #ifndef _MSC_VER #include #include #endif #include "xcircuit.h" #include "colordefs.h" #include "menudep.h" #include "prototypes.h" Tcl_HashTable XcTagTable; extern Tcl_Interp *xcinterp; extern Tcl_Interp *consoleinterp; extern Display *dpy; extern Colormap cmap; extern Pixmap STIPPLE[STIPPLES]; /* Polygon fill-style stipple patterns */ extern char _STR[150], _STR2[250]; extern XCWindowData *areawin; extern Globaldata xobjs; extern int number_colors; extern colorindex *colorlist; extern Cursor appcursors[NUM_CURSORS]; extern ApplicationData appdata; extern fontinfo *fonts; extern short fontcount; extern u_char param_select[]; extern keybinding *keylist; extern Boolean spice_end; extern short flstart; extern int pressmode; extern u_char undo_collect; char STIPDATA[STIPPLES][4] = { "\000\004\000\001", "\000\005\000\012", "\001\012\005\010", "\005\012\005\012", "\016\005\012\007", "\017\012\017\005", "\017\012\017\016", "\000\000\000\000" }; short flags = -1; #define LIBOVERRIDE 1 #define LIBLOADED 2 #define COLOROVERRIDE 4 #define FONTOVERRIDE 8 #define KEYOVERRIDE 16 /*-----------------------*/ /* Tcl 8.4 compatibility */ /*-----------------------*/ #ifndef CONST84 #define CONST84 #endif /*----------------------------------------------------------------------*/ /* Procedure for waiting on X to map a window */ /* This code copied from Tk sources, where it is used for the "tkwait" */ /* command. */ /*----------------------------------------------------------------------*/ static void WaitVisibilityProc(ClientData clientData, XEvent *eventPtr) { int *donePtr = (int *) clientData; if (eventPtr->type == VisibilityNotify) { *donePtr = 1; } if (eventPtr->type == DestroyNotify) { *donePtr = 2; } } /*----------------------------------------------------------------------*/ /* Deal with systems which don't define va_copy(). */ /*----------------------------------------------------------------------*/ #ifndef HAVE_VA_COPY #ifdef HAVE___VA_COPY #define va_copy(a, b) __va_copy(a, b) #else #define va_copy(a, b) a = b #endif #endif #ifdef ASG extern int SetDebugLevel(int *level); #endif /*----------------------------------------------------------------------*/ /* Reimplement strdup() to use Tcl_Alloc(). */ /* Note that "strdup" is defined as "Tcl_Strdup" in xcircuit.h. */ /*----------------------------------------------------------------------*/ char *Tcl_Strdup(const char *s) { char *snew; int slen; slen = 1 + strlen(s); snew = Tcl_Alloc(slen); if (snew != NULL) memcpy(snew, s, slen); return snew; } /*----------------------------------------------------------------------*/ /* Reimplement vfprintf() as a call to Tcl_Eval(). */ /*----------------------------------------------------------------------*/ void tcl_vprintf(FILE *f, const char *fmt, va_list args_in) { va_list args; static char outstr[128] = "puts -nonewline std"; char *outptr, *bigstr = NULL, *finalstr = NULL; int i, nchars, result, escapes = 0; /* If we are printing an error message, we want to bring attention */ /* to it by mapping the console window and raising it, as necessary. */ /* I'd rather do this internally than by Tcl_Eval(), but I can't */ /* find the right window ID to map! */ if ((f == stderr) && (consoleinterp != xcinterp)) { Tk_Window tkwind; tkwind = Tk_MainWindow(consoleinterp); if ((tkwind != NULL) && (!Tk_IsMapped(tkwind))) result = Tcl_Eval(consoleinterp, "wm deiconify .\n"); result = Tcl_Eval(consoleinterp, "raise .\n"); } strcpy (outstr + 19, (f == stderr) ? "err \"" : "out \""); outptr = outstr; /* This mess circumvents problems with systems which do not have */ /* va_copy() defined. Some define __va_copy(); otherwise we must */ /* assume that args = args_in is valid. */ va_copy(args, args_in); nchars = vsnprintf(outptr + 24, 102, fmt, args); va_end(args); if (nchars >= 102) { va_copy(args, args_in); bigstr = Tcl_Alloc(nchars + 26); strncpy(bigstr, outptr, 24); outptr = bigstr; vsnprintf(outptr + 24, nchars + 2, fmt, args); va_end(args); } else if (nchars == -1) nchars = 126; for (i = 24; *(outptr + i) != '\0'; i++) { if (*(outptr + i) == '\"' || *(outptr + i) == '[' || *(outptr + i) == ']' || *(outptr + i) == '\\') escapes++; } if (escapes > 0) { finalstr = Tcl_Alloc(nchars + escapes + 26); strncpy(finalstr, outptr, 24); escapes = 0; for (i = 24; *(outptr + i) != '\0'; i++) { if (*(outptr + i) == '\"' || *(outptr + i) == '[' || *(outptr + i) == ']' || *(outptr + i) == '\\') { *(finalstr + i + escapes) = '\\'; escapes++; } *(finalstr + i + escapes) = *(outptr + i); } outptr = finalstr; } *(outptr + 24 + nchars + escapes) = '\"'; *(outptr + 25 + nchars + escapes) = '\0'; result = Tcl_Eval(consoleinterp, outptr); if (bigstr != NULL) Tcl_Free(bigstr); if (finalstr != NULL) Tcl_Free(finalstr); } /*------------------------------------------------------*/ /* Console output flushing which goes along with the */ /* routine tcl_vprintf() above. */ /*------------------------------------------------------*/ void tcl_stdflush(FILE *f) { Tcl_SavedResult state; static char stdstr[] = "::flush stdxxx"; char *stdptr = stdstr + 11; if ((f != stderr) && (f != stdout)) { fflush(f); } else { Tcl_SaveResult(xcinterp, &state); strcpy(stdptr, (f == stderr) ? "err" : "out"); Tcl_Eval(xcinterp, stdstr); Tcl_RestoreResult(xcinterp, &state); } } /*----------------------------------------------------------------------*/ /* Reimplement fprintf() as a call to Tcl_Eval(). */ /* Make sure that files (not stdout or stderr) get treated normally. */ /*----------------------------------------------------------------------*/ void tcl_printf(FILE *f, const char *format, ...) { va_list ap; va_start(ap, format); if ((f != stderr) && (f != stdout)) vfprintf(f, format, ap); else tcl_vprintf(f, format, ap); va_end(ap); } /*----------------------------------------------------------------------*/ /* Fill in standard areas of a key event structure. This includes */ /* everything necessary except type, keycode, and state (although */ /* state defaults to zero). This is also good for button events, which */ /* share the same structure as key events (except that keycode is */ /* changed to button). */ /*----------------------------------------------------------------------*/ void make_new_event(XKeyEvent *event) { XPoint newpos, wpoint; newpos = UGetCursorPos(); user_to_window(newpos, &wpoint); event->x = wpoint.x; event->y = wpoint.y; event->same_screen = TRUE; event->send_event = TRUE; event->display = dpy; event->window = Tk_WindowId(areawin->area); event->state = 0; } /*----------------------------------------------------------------------*/ /* Implement tag callbacks on functions */ /* Find any tags associated with a command and execute them. */ /*----------------------------------------------------------------------*/ int XcTagCallback(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { int objidx, result = TCL_OK; char *postcmd, *substcmd, *newcmd, *sptr, *sres; char *croot = Tcl_GetString(objv[0]); Tcl_HashEntry *entry; Tcl_SavedResult state; int reset = FALSE; int i, llen; /* Skip over technology qualifier, if any */ if (!strncmp(croot, "::", 2)) croot += 2; if (!strncmp(croot, "xcircuit::", 10)) croot += 10; entry = Tcl_FindHashEntry(&XcTagTable, croot); postcmd = (entry) ? (char *)Tcl_GetHashValue(entry) : NULL; if (postcmd) { substcmd = (char *)Tcl_Alloc(strlen(postcmd) + 1); strcpy(substcmd, postcmd); sptr = substcmd; /*--------------------------------------------------------------*/ /* Parse "postcmd" for Tk-substitution escapes */ /* Allowed escapes are: */ /* %W substitute the tk path of the calling window */ /* %r substitute the previous Tcl result string */ /* %R substitute the previous Tcl result string and */ /* reset the Tcl result. */ /* %[0-5] substitute the argument to the original command */ /* %N substitute all arguments as a list */ /* %% substitute a single percent character */ /* %# substitute the number of arguments passed */ /* %* (all others) no action: print as-is. */ /*--------------------------------------------------------------*/ while ((sptr = strchr(sptr, '%')) != NULL) { switch (*(sptr + 1)) { case 'W': { char *tkpath = NULL; Tk_Window tkwind = Tk_MainWindow(interp); if (tkwind != NULL) tkpath = Tk_PathName(tkwind); if (tkpath == NULL) newcmd = (char *)Tcl_Alloc(strlen(substcmd)); else newcmd = (char *)Tcl_Alloc(strlen(substcmd) + strlen(tkpath)); strcpy(newcmd, substcmd); if (tkpath == NULL) strcpy(newcmd + (int)(sptr - substcmd), sptr + 2); else { strcpy(newcmd + (int)(sptr - substcmd), tkpath); strcat(newcmd, sptr + 2); } Tcl_Free(substcmd); substcmd = newcmd; sptr = substcmd; } break; case 'R': reset = TRUE; case 'r': sres = (char *)Tcl_GetStringResult(interp); newcmd = (char *)Tcl_Alloc(strlen(substcmd) + strlen(sres) + 1); strcpy(newcmd, substcmd); sprintf(newcmd + (int)(sptr - substcmd), "\"%s\"", sres); strcat(newcmd, sptr + 2); Tcl_Free(substcmd); substcmd = newcmd; sptr = substcmd; break; case '#': if (objc < 100) { newcmd = (char *)Tcl_Alloc(strlen(substcmd) + 3); strcpy(newcmd, substcmd); sprintf(newcmd + (int)(sptr - substcmd), "%d", objc); strcat(newcmd, sptr + 2); Tcl_Free(substcmd); substcmd = newcmd; sptr = substcmd; } break; case '0': case '1': case '2': case '3': case '4': case '5': objidx = (int)(*(sptr + 1) - '0'); if ((objidx >= 0) && (objidx < objc)) { newcmd = (char *)Tcl_Alloc(strlen(substcmd) + strlen(Tcl_GetString(objv[objidx])) + 1); strcpy(newcmd, substcmd); strcpy(newcmd + (int)(sptr - substcmd), Tcl_GetString(objv[objidx])); strcat(newcmd, sptr + 2); Tcl_Free(substcmd); substcmd = newcmd; sptr = substcmd; } else if (objidx >= objc) { newcmd = (char *)Tcl_Alloc(strlen(substcmd) + 1); strcpy(newcmd, substcmd); strcpy(newcmd + (int)(sptr - substcmd), sptr + 2); Tcl_Free(substcmd); substcmd = newcmd; sptr = substcmd; } else sptr++; break; case 'N': llen = 1; for (i = 1; i < objc; i++) llen += (1 + strlen(Tcl_GetString(objv[i]))); newcmd = (char *)Tcl_Alloc(strlen(substcmd) + llen); strcpy(newcmd, substcmd); strcpy(newcmd + (int)(sptr - substcmd), "{"); for (i = 1; i < objc; i++) { strcat(newcmd, Tcl_GetString(objv[i])); if (i < (objc - 1)) strcat(newcmd, " "); } strcat(newcmd, "}"); strcat(newcmd, sptr + 2); Tcl_Free(substcmd); substcmd = newcmd; sptr = substcmd; break; case '%': newcmd = (char *)Tcl_Alloc(strlen(substcmd) + 1); strcpy(newcmd, substcmd); strcpy(newcmd + (int)(sptr - substcmd), sptr + 1); Tcl_Free(substcmd); substcmd = newcmd; sptr = substcmd; break; default: sptr++; break; } } /* Fprintf(stderr, "Substituted tag callback is \"%s\"\n", substcmd); */ /* Flush(stderr); */ Tcl_SaveResult(interp, &state); result = Tcl_Eval(interp, substcmd); if ((result == TCL_OK) && (reset == FALSE)) Tcl_RestoreResult(interp, &state); else Tcl_DiscardResult(&state); Tcl_Free(substcmd); } return result; } /*--------------------------------------------------------------*/ /* XcInternalTagCall --- */ /* */ /* Execute the tag callback for a command without actually */ /* evaluating the command itself. The command and arguments */ /* are passed as a variable number or char * arguments, since */ /* usually this routine will called with constant arguments */ /* (e.g., XcInternalTagCall(interp, 2, "set", "color");) */ /* */ /* objv declared static because this routine is used a lot */ /* (e.g., during select/unselect operations). */ /*--------------------------------------------------------------*/ int XcInternalTagCall(Tcl_Interp *interp, int argc, ...) { int i; static Tcl_Obj **objv = NULL; char *aptr; va_list ap; if (objv == (Tcl_Obj **)NULL) objv = (Tcl_Obj **)malloc(argc * sizeof(Tcl_Obj *)); else objv = (Tcl_Obj **)realloc(objv, argc * sizeof(Tcl_Obj *)); va_start(ap, argc); for (i = 0; i < argc; i++) { aptr = va_arg(ap, char *); /* We are depending on Tcl's heap allocation of objects */ /* so that we do not have to manage memory for these */ /* string representations. . . */ objv[i] = Tcl_NewStringObj(aptr, -1); } va_end(ap); return XcTagCallback(interp, argc, objv); } /*--------------------------------------------------------------*/ /* Return the event mode */ /* Event mode can be set in specific cases. */ /*--------------------------------------------------------------*/ int xctcl_eventmode(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { static char *modeNames[] = { "normal", "undo", "move", "copy", "pan", "selarea", "rescale", "catalog", "cattext", "fontcat", "efontcat", "text", "wire", "box", "arc", "spline", "etext", "epoly", "earc", "espline", "epath", "einst", "assoc", "catmove", NULL }; /* This routine is diagnostic only */ if (objc != 1) return TCL_ERROR; Tcl_SetResult(interp, modeNames[eventmode], NULL); return TCL_OK; } /*--------------------------------------------------------------*/ /* Add a command tag callback */ /*--------------------------------------------------------------*/ int xctcl_tag(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { Tcl_HashEntry *entry; char *hstring; int new; if (objc != 2 && objc != 3) return TCL_ERROR; entry = Tcl_CreateHashEntry(&XcTagTable, Tcl_GetString(objv[1]), &new); if (entry == NULL) return TCL_ERROR; hstring = (char *)Tcl_GetHashValue(entry); if (objc == 2) { Tcl_SetResult(interp, hstring, NULL); return TCL_OK; } if (strlen(Tcl_GetString(objv[2])) == 0) { Tcl_DeleteHashEntry(entry); } else { hstring = strdup(Tcl_GetString(objv[2])); Tcl_SetHashValue(entry, hstring); } return TCL_OK; } /*----------------------------------------------------------------------*/ /* Turn a selection list into a Tcl List object (may be empty list) */ /*----------------------------------------------------------------------*/ Tcl_Obj *SelectToTclList(Tcl_Interp *interp, short *slist, int snum) { int i; Tcl_Obj *objPtr, *listPtr; if (snum == 1) { objPtr = Tcl_NewHandleObj(SELTOGENERIC(slist)); return objPtr; } listPtr = Tcl_NewListObj(0, NULL); for (i = 0; i < snum; i++) { objPtr = Tcl_NewHandleObj(SELTOGENERIC(slist + i)); Tcl_ListObjAppendElement(interp, listPtr, objPtr); } return listPtr; } /*----------------------------------------------------------------------*/ /* Get an x,y position (as an XPoint structure) from a list of size 2 */ /*----------------------------------------------------------------------*/ int GetPositionFromList(Tcl_Interp *interp, Tcl_Obj *list, XPoint *rpoint) { int result, numobjs; Tcl_Obj *lobj, *tobj; int pos; if (!strcmp(Tcl_GetString(list), "here")) { if (rpoint) *rpoint = UGetCursorPos(); return TCL_OK; } result = Tcl_ListObjLength(interp, list, &numobjs); if (result != TCL_OK) return result; if (numobjs == 1) { /* Try decomposing the object into a list */ result = Tcl_ListObjIndex(interp, list, 0, &tobj); if (result == TCL_OK) { result = Tcl_ListObjLength(interp, tobj, &numobjs); if (numobjs == 2) list = tobj; } if (result != TCL_OK) Tcl_ResetResult(interp); } if (numobjs != 2) { Tcl_SetResult(interp, "list must contain x y positions", NULL); return TCL_ERROR; } result = Tcl_ListObjIndex(interp, list, 0, &lobj); if (result != TCL_OK) return result; result = Tcl_GetIntFromObj(interp, lobj, &pos); if (result != TCL_OK) return result; if (rpoint) rpoint->x = pos; result = Tcl_ListObjIndex(interp, list, 1, &lobj); if (result != TCL_OK) return result; result = Tcl_GetIntFromObj(interp, lobj, &pos); if (result != TCL_OK) return result; if (rpoint) rpoint->y = pos; return TCL_OK; } /*--------------------------------------------------------------*/ /* Convert color index to a list of 3 elements */ /* We assume that this color exists in the color table. */ /*--------------------------------------------------------------*/ Tcl_Obj *TclIndexToRGB(int cidx) { Tcl_Obj *RGBTuple; if (cidx < 0) { /* Handle "default color" */ return Tcl_NewStringObj("Default", 7); } else if (cidx >= number_colors) { Tcl_SetResult(xcinterp, "Bad color index", NULL); return NULL; } RGBTuple = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(xcinterp, RGBTuple, Tcl_NewIntObj((int)(colorlist[cidx].color.red / 256))); Tcl_ListObjAppendElement(xcinterp, RGBTuple, Tcl_NewIntObj((int)(colorlist[cidx].color.green / 256))); Tcl_ListObjAppendElement(xcinterp, RGBTuple, Tcl_NewIntObj((int)(colorlist[cidx].color.blue / 256))); return RGBTuple; } /*--------------------------------------------------------------*/ /* Convert a stringpart* to a Tcl list object */ /*--------------------------------------------------------------*/ Tcl_Obj *TclGetStringParts(stringpart *thisstring) { Tcl_Obj *lstr, *sdict, *stup; int i; stringpart *strptr; lstr = Tcl_NewListObj(0, NULL); for (strptr = thisstring, i = 0; strptr != NULL; strptr = strptr->nextpart, i++) { switch(strptr->type) { case TEXT_STRING: sdict = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(xcinterp, sdict, Tcl_NewStringObj("Text", 4)); Tcl_ListObjAppendElement(xcinterp, sdict, Tcl_NewStringObj(strptr->data.string, strlen(strptr->data.string))); Tcl_ListObjAppendElement(xcinterp, lstr, sdict); break; case PARAM_START: sdict = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(xcinterp, sdict, Tcl_NewStringObj("Parameter", 9)); Tcl_ListObjAppendElement(xcinterp, sdict, Tcl_NewStringObj(strptr->data.string, strlen(strptr->data.string))); Tcl_ListObjAppendElement(xcinterp, lstr, sdict); break; case PARAM_END: Tcl_ListObjAppendElement(xcinterp, lstr, Tcl_NewStringObj("End Parameter", 13)); break; case FONT_NAME: sdict = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(xcinterp, sdict, Tcl_NewStringObj("Font", 4)); Tcl_ListObjAppendElement(xcinterp, sdict, Tcl_NewStringObj(fonts[strptr->data.font].psname, strlen(fonts[strptr->data.font].psname))); Tcl_ListObjAppendElement(xcinterp, lstr, sdict); break; case FONT_SCALE: sdict = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(xcinterp, sdict, Tcl_NewStringObj("Font Scale", 10)); Tcl_ListObjAppendElement(xcinterp, sdict, Tcl_NewDoubleObj((double)strptr->data.scale)); Tcl_ListObjAppendElement(xcinterp, lstr, sdict); break; case KERN: sdict = Tcl_NewListObj(0, NULL); stup = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(xcinterp, stup, Tcl_NewIntObj((int)strptr->data.kern[0])); Tcl_ListObjAppendElement(xcinterp, stup, Tcl_NewIntObj((int)strptr->data.kern[1])); Tcl_ListObjAppendElement(xcinterp, sdict, Tcl_NewStringObj("Kern", 4)); Tcl_ListObjAppendElement(xcinterp, sdict, stup); Tcl_ListObjAppendElement(xcinterp, lstr, sdict); break; case FONT_COLOR: stup = TclIndexToRGB(strptr->data.color); if (stup != NULL) { sdict = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(xcinterp, sdict, Tcl_NewStringObj("Color", 5)); Tcl_ListObjAppendElement(xcinterp, sdict, stup); Tcl_ListObjAppendElement(xcinterp, lstr, sdict); } break; case MARGINSTOP: sdict = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(xcinterp, sdict, Tcl_NewStringObj("Margin Stop", 11)); Tcl_ListObjAppendElement(xcinterp, sdict, Tcl_NewIntObj((int)strptr->data.width)); Tcl_ListObjAppendElement(xcinterp, lstr, sdict); break; case TABSTOP: Tcl_ListObjAppendElement(xcinterp, lstr, Tcl_NewStringObj("Tab Stop", 8)); break; case TABFORWARD: Tcl_ListObjAppendElement(xcinterp, lstr, Tcl_NewStringObj("Tab Forward", 11)); break; case TABBACKWARD: Tcl_ListObjAppendElement(xcinterp, lstr, Tcl_NewStringObj("Tab Backward", 12)); break; case RETURN: // Don't show automatically interted line breaks if (strptr->data.flags == 0) Tcl_ListObjAppendElement(xcinterp, lstr, Tcl_NewStringObj("Return", 6)); break; case SUBSCRIPT: Tcl_ListObjAppendElement(xcinterp, lstr, Tcl_NewStringObj("Subscript", 9)); break; case SUPERSCRIPT: Tcl_ListObjAppendElement(xcinterp, lstr, Tcl_NewStringObj("Superscript", 11)); break; case NORMALSCRIPT: Tcl_ListObjAppendElement(xcinterp, lstr, Tcl_NewStringObj("Normalscript", 12)); break; case UNDERLINE: Tcl_ListObjAppendElement(xcinterp, lstr, Tcl_NewStringObj("Underline", 9)); break; case OVERLINE: Tcl_ListObjAppendElement(xcinterp, lstr, Tcl_NewStringObj("Overline", 8)); break; case NOLINE: Tcl_ListObjAppendElement(xcinterp, lstr, Tcl_NewStringObj("No Line", 7)); break; case HALFSPACE: Tcl_ListObjAppendElement(xcinterp, lstr, Tcl_NewStringObj("Half Space", 10)); break; case QTRSPACE: Tcl_ListObjAppendElement(xcinterp, lstr, Tcl_NewStringObj("Quarter Space", 13)); break; } } return lstr; } /*----------------------------------------------------------------------*/ /* Get a stringpart linked list from a Tcl list */ /*----------------------------------------------------------------------*/ int GetXCStringFromList(Tcl_Interp *interp, Tcl_Obj *list, stringpart **rstring) { int result, j, k, numobjs, idx, numparts, ptype, ival; Tcl_Obj *lobj, *pobj, *tobj, *t2obj; stringpart *newpart; char *fname; double fscale; static char *partTypes[] = {"Text", "Subscript", "Superscript", "Normalscript", "Underline", "Overline", "No Line", "Tab Stop", "Tab Forward", "Tab Backward", "Half Space", "Quarter Space", "Return", "Font", "Font Scale", "Color", "Margin Stop", "Kern", "Parameter", "End Parameter", "Special", NULL}; static int partTypesIdx[] = {TEXT_STRING, SUBSCRIPT, SUPERSCRIPT, NORMALSCRIPT, UNDERLINE, OVERLINE, NOLINE, TABSTOP, TABFORWARD, TABBACKWARD, HALFSPACE, QTRSPACE, RETURN, FONT_NAME, FONT_SCALE, FONT_COLOR, MARGINSTOP, KERN, PARAM_START, PARAM_END, SPECIAL}; /* No place to put result! */ if (rstring == NULL) return TCL_ERROR; result = Tcl_ListObjLength(interp, list, &numobjs); if (result != TCL_OK) return result; newpart = NULL; for (j = 0; j < numobjs; j++) { result = Tcl_ListObjIndex(interp, list, j, &lobj); if (result != TCL_OK) return result; result = Tcl_ListObjLength(interp, lobj, &numparts); if (result != TCL_OK) return result; result = Tcl_ListObjIndex(interp, lobj, 0, &pobj); if (result != TCL_OK) return result; /* Must define TCL_EXACT in flags, or else, for instance, "u" gets */ /* interpreted as "underline", which is usually not intended. */ if (pobj == NULL) return TCL_ERROR; else if (Tcl_GetIndexFromObj(interp, pobj, (CONST84 char **)partTypes, "string part types", TCL_EXACT, &idx) != TCL_OK) { Tcl_ResetResult(interp); idx = -1; // If there's only one object and the first item doesn't match // a stringpart itentifying word, then assume that "list" is a // single text string. if (numobjs == 1) tobj = list; else result = Tcl_ListObjIndex(interp, lobj, 0, &tobj); } else { result = Tcl_ListObjIndex(interp, lobj, (numparts > 1) ? 1 : 0, &tobj); } if (result != TCL_OK) return result; if (idx < 0) { if ((newpart == NULL) || (newpart->type != TEXT_STRING)) idx = 0; else { /* We have an implicit text string which should be appended */ /* to the previous text string with a space character. */ newpart->data.string = (char *)realloc(newpart->data.string, strlen(newpart->data.string) + strlen(Tcl_GetString(tobj)) + 2); strcat(newpart->data.string, " "); strcat(newpart->data.string, Tcl_GetString(tobj)); continue; } } ptype = partTypesIdx[idx]; newpart = makesegment(rstring, NULL); newpart->nextpart = NULL; newpart->type = ptype; switch(ptype) { case TEXT_STRING: case PARAM_START: newpart->data.string = strdup(Tcl_GetString(tobj)); break; case FONT_NAME: fname = Tcl_GetString(tobj); for (k = 0; k < fontcount; k++) { if (!strcmp(fonts[k].psname, fname)) { newpart->data.font = k; break; } } if (k == fontcount) { Tcl_SetResult(interp, "Bad font name", NULL); return TCL_ERROR; } break; case FONT_SCALE: result = Tcl_GetDoubleFromObj(interp, tobj, &fscale); if (result != TCL_OK) return result; newpart->data.scale = (float)fscale; break; case MARGINSTOP: result = Tcl_GetIntFromObj(interp, tobj, &ival); if (result != TCL_OK) return result; newpart->data.width = ival; break; case KERN: result = Tcl_ListObjLength(interp, tobj, &numparts); if (result != TCL_OK) return result; if (numparts != 2) { Tcl_SetResult(interp, "Bad kern list: need 2 values", NULL); return TCL_ERROR; } result = Tcl_ListObjIndex(interp, tobj, 0, &t2obj); if (result != TCL_OK) return result; result = Tcl_GetIntFromObj(interp, t2obj, &ival); if (result != TCL_OK) return result; newpart->data.kern[0] = (short)ival; result = Tcl_ListObjIndex(interp, tobj, 1, &t2obj); if (result != TCL_OK) return result; result = Tcl_GetIntFromObj(interp, t2obj, &ival); if (result != TCL_OK) return result; newpart->data.kern[1] = (short)ival; break; case FONT_COLOR: /* Not implemented: Need TclRGBToIndex() function */ break; /* All other types have no arguments */ } } return TCL_OK; } /*----------------------------------------------------------------------*/ /* Handle (integer representation of internal xcircuit object) checking */ /* if "checkobject" is NULL, then */ /*----------------------------------------------------------------------*/ genericptr *CheckHandle(pointertype eaddr, objectptr checkobject) { genericptr *gelem; int i, j; objectptr thisobj; Library *thislib; if (checkobject != NULL) { for (gelem = checkobject->plist; gelem < checkobject->plist + checkobject->parts; gelem++) if ((pointertype)(*gelem) == eaddr) goto exists; return NULL; } /* Look through all the pages. */ for (i = 0; i < xobjs.pages; i++) { if (xobjs.pagelist[i]->pageinst == NULL) continue; thisobj = xobjs.pagelist[i]->pageinst->thisobject; for (gelem = thisobj->plist; gelem < thisobj->plist + thisobj->parts; gelem++) if ((pointertype)(*gelem) == eaddr) goto exists; } /* Not found? Maybe in a library */ for (i = 0; i < xobjs.numlibs; i++) { thislib = xobjs.userlibs + i; for (j = 0; j < thislib->number; j++) { thisobj = thislib->library[j]; for (gelem = thisobj->plist; gelem < thisobj->plist + thisobj->parts; gelem++) if ((pointertype)(*gelem) == eaddr) goto exists; } } /* Either in the delete list (where we don't want to go) or */ /* is an invalid number. */ return NULL; exists: return gelem; } /*----------------------------------------------------------------------*/ /* Find the index into the "plist" list of elements */ /* Part number must be of a type in "mask" or no selection occurs. */ /* return values: -1 = no object found, -2 = found, but wrong type */ /*----------------------------------------------------------------------*/ short GetPartNumber(genericptr egen, objectptr checkobject, int mask) { genericptr *gelem; objectptr thisobject = checkobject; int i; if (checkobject == NULL) thisobject = topobject; for (i = 0, gelem = thisobject->plist; gelem < thisobject->plist + thisobject->parts; gelem++, i++) { if ((*gelem) == egen) { if ((*gelem)->type & mask) return i; else return -2; } } return -1; } /*----------------------------------------------------------------------*/ /* This routine is used by a number of menu functions. It looks for */ /* the arguments "selected" or an integer (object handle). If the */ /* argument is a valid object handle, it is added to the select list. */ /* The argument can be a list of handles, of which each is checked and */ /* added to the select list. */ /* "extra" indicates the number of required arguments beyond 2. */ /* "next" returns the integer of the argument after the handle, or the */ /* argument after the command, if there is no handle. If the handle is */ /* specified as a hierarchical list of element handles then */ /* areawin->hierstack contains the hierarchy of object instances. */ /*----------------------------------------------------------------------*/ int ParseElementArguments(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int *next, int mask) { short *newselect; char *argstr; int i, j, result, numobjs; pointertype ehandle; Tcl_Obj *lobj; int extra = 0, goodobjs = 0; if (next != NULL) { extra = *next; *next = 1; } if ((objc > (2 + extra)) || (objc == 1)) { Tcl_WrongNumArgs(interp, 1, objv, "[selected | ]