Realized what the underlying cause of crashing on undefined "dpy"
[xcircuit.git] / tclxcircuit.c
blobd6b42bd7ed1ed6f488e4d1f7c21461e541e94b0b
1 /*--------------------------------------------------------------*/
2 /* tclxcircuit.c: */
3 /* Tcl routines for xcircuit command-line functions */
4 /* Copyright (c) 2003 Tim Edwards, Johns Hopkins University */
5 /* Copyright (c) 2004 Tim Edwards, MultiGiG, Inc. */
6 /*--------------------------------------------------------------*/
8 #if defined(TCL_WRAPPER) && !defined(HAVE_PYTHON)
10 #include <stdio.h>
11 #include <stdarg.h> /* for va_copy() */
12 #include <stdlib.h> /* for atoi() and others */
13 #include <ctype.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <errno.h>
19 #include <tk.h>
21 #ifdef HAVE_CAIRO
22 #include <cairo/cairo-xlib.h>
23 #endif
25 #ifndef _MSC_VER
26 #include <X11/Intrinsic.h>
27 #include <X11/StringDefs.h>
28 #endif
30 #include "xcircuit.h"
31 #include "colordefs.h"
32 #include "menudep.h"
33 #include "prototypes.h"
35 Tcl_HashTable XcTagTable;
37 extern Tcl_Interp *xcinterp;
38 extern Tcl_Interp *consoleinterp;
39 extern Display *dpy;
40 extern Colormap cmap;
41 extern Pixmap STIPPLE[STIPPLES]; /* Polygon fill-style stipple patterns */
42 extern char _STR[150], _STR2[250];
43 extern XCWindowData *areawin;
44 extern Globaldata xobjs;
45 extern int screenDPI;
46 extern int number_colors;
47 extern colorindex *colorlist;
48 extern Cursor appcursors[NUM_CURSORS];
49 extern ApplicationData appdata;
50 extern fontinfo *fonts;
51 extern short fontcount;
52 extern u_char param_select[];
53 extern keybinding *keylist;
54 extern Boolean spice_end;
55 extern short flstart;
56 extern int pressmode;
57 extern u_char undo_collect;
59 char STIPDATA[STIPPLES][4] = {
60 "\000\004\000\001",
61 "\000\005\000\012",
62 "\001\012\005\010",
63 "\005\012\005\012",
64 "\016\005\012\007",
65 "\017\012\017\005",
66 "\017\012\017\016",
67 "\000\000\000\000"
70 short flags = -1;
72 #define LIBOVERRIDE 1
73 #define LIBLOADED 2
74 #define COLOROVERRIDE 4
75 #define FONTOVERRIDE 8
76 #define KEYOVERRIDE 16
78 /*-----------------------*/
79 /* Tcl 8.4 compatibility */
80 /*-----------------------*/
82 #ifndef CONST84
83 #define CONST84
84 #endif
86 /*----------------------------------------------------------------------*/
87 /* Procedure for waiting on X to map a window */
88 /* This code copied from Tk sources, where it is used for the "tkwait" */
89 /* command. */
90 /*----------------------------------------------------------------------*/
92 static void
93 WaitVisibilityProc(ClientData clientData, XEvent *eventPtr)
95 int *donePtr = (int *) clientData;
97 if (eventPtr->type == VisibilityNotify) {
98 *donePtr = 1;
100 if (eventPtr->type == DestroyNotify) {
101 *donePtr = 2;
105 /*----------------------------------------------------------------------*/
106 /* Deal with systems which don't define va_copy(). */
107 /*----------------------------------------------------------------------*/
109 #ifndef HAVE_VA_COPY
110 #ifdef HAVE___VA_COPY
111 #define va_copy(a, b) __va_copy(a, b)
112 #else
113 #define va_copy(a, b) a = b
114 #endif
115 #endif
117 #ifdef ASG
118 extern int SetDebugLevel(int *level);
119 #endif
121 /*----------------------------------------------------------------------*/
122 /* Reimplement strdup() to use Tcl_Alloc(). */
123 /* Note that "strdup" is defined as "Tcl_Strdup" in xcircuit.h. */
124 /*----------------------------------------------------------------------*/
126 char *Tcl_Strdup(const char *s)
128 char *snew;
129 int slen;
131 slen = 1 + strlen(s);
132 snew = Tcl_Alloc(slen);
133 if (snew != NULL)
134 memcpy(snew, s, slen);
136 return snew;
139 /*----------------------------------------------------------------------*/
140 /* Reimplement vfprintf() as a call to Tcl_Eval(). */
141 /*----------------------------------------------------------------------*/
143 void tcl_vprintf(FILE *f, const char *fmt, va_list args_in)
145 va_list args;
146 static char outstr[128] = "puts -nonewline std";
147 char *outptr, *bigstr = NULL, *finalstr = NULL;
148 int i, nchars, result, escapes = 0;
150 /* If we are printing an error message, we want to bring attention */
151 /* to it by mapping the console window and raising it, as necessary. */
152 /* I'd rather do this internally than by Tcl_Eval(), but I can't */
153 /* find the right window ID to map! */
155 if ((f == stderr) && (consoleinterp != xcinterp)) {
156 Tk_Window tkwind;
157 tkwind = Tk_MainWindow(consoleinterp);
158 if ((tkwind != NULL) && (!Tk_IsMapped(tkwind)))
159 result = Tcl_Eval(consoleinterp, "wm deiconify .\n");
160 result = Tcl_Eval(consoleinterp, "raise .\n");
163 strcpy (outstr + 19, (f == stderr) ? "err \"" : "out \"");
164 outptr = outstr;
166 /* This mess circumvents problems with systems which do not have */
167 /* va_copy() defined. Some define __va_copy(); otherwise we must */
168 /* assume that args = args_in is valid. */
170 va_copy(args, args_in);
171 nchars = vsnprintf(outptr + 24, 102, fmt, args);
172 va_end(args);
174 if (nchars >= 102) {
175 va_copy(args, args_in);
176 bigstr = Tcl_Alloc(nchars + 26);
177 strncpy(bigstr, outptr, 24);
178 outptr = bigstr;
179 vsnprintf(outptr + 24, nchars + 2, fmt, args);
180 va_end(args);
182 else if (nchars == -1) nchars = 126;
184 for (i = 24; *(outptr + i) != '\0'; i++) {
185 if (*(outptr + i) == '\"' || *(outptr + i) == '[' ||
186 *(outptr + i) == ']' || *(outptr + i) == '\\')
187 escapes++;
190 if (escapes > 0) {
191 finalstr = Tcl_Alloc(nchars + escapes + 26);
192 strncpy(finalstr, outptr, 24);
193 escapes = 0;
194 for (i = 24; *(outptr + i) != '\0'; i++) {
195 if (*(outptr + i) == '\"' || *(outptr + i) == '[' ||
196 *(outptr + i) == ']' || *(outptr + i) == '\\') {
197 *(finalstr + i + escapes) = '\\';
198 escapes++;
200 *(finalstr + i + escapes) = *(outptr + i);
202 outptr = finalstr;
205 *(outptr + 24 + nchars + escapes) = '\"';
206 *(outptr + 25 + nchars + escapes) = '\0';
208 result = Tcl_Eval(consoleinterp, outptr);
210 if (bigstr != NULL) Tcl_Free(bigstr);
211 if (finalstr != NULL) Tcl_Free(finalstr);
214 /*------------------------------------------------------*/
215 /* Console output flushing which goes along with the */
216 /* routine tcl_vprintf() above. */
217 /*------------------------------------------------------*/
219 void tcl_stdflush(FILE *f)
221 Tcl_SavedResult state;
222 static char stdstr[] = "::flush stdxxx";
223 char *stdptr = stdstr + 11;
225 if ((f != stderr) && (f != stdout)) {
226 fflush(f);
228 else {
229 Tcl_SaveResult(xcinterp, &state);
230 strcpy(stdptr, (f == stderr) ? "err" : "out");
231 Tcl_Eval(xcinterp, stdstr);
232 Tcl_RestoreResult(xcinterp, &state);
236 /*----------------------------------------------------------------------*/
237 /* Reimplement fprintf() as a call to Tcl_Eval(). */
238 /* Make sure that files (not stdout or stderr) get treated normally. */
239 /*----------------------------------------------------------------------*/
241 void tcl_printf(FILE *f, const char *format, ...)
243 va_list ap;
245 va_start(ap, format);
246 if ((f != stderr) && (f != stdout))
247 vfprintf(f, format, ap);
248 else
249 tcl_vprintf(f, format, ap);
250 va_end(ap);
253 /*----------------------------------------------------------------------*/
254 /* Fill in standard areas of a key event structure. This includes */
255 /* everything necessary except type, keycode, and state (although */
256 /* state defaults to zero). This is also good for button events, which */
257 /* share the same structure as key events (except that keycode is */
258 /* changed to button). */
259 /*----------------------------------------------------------------------*/
261 void make_new_event(XKeyEvent *event)
263 XPoint newpos, wpoint;
265 newpos = UGetCursorPos();
266 user_to_window(newpos, &wpoint);
267 event->x = wpoint.x;
268 event->y = wpoint.y;
270 event->same_screen = TRUE;
271 event->send_event = TRUE;
272 event->display = dpy;
273 event->window = Tk_WindowId(areawin->area);
275 event->state = 0;
278 /*----------------------------------------------------------------------*/
279 /* Implement tag callbacks on functions */
280 /* Find any tags associated with a command and execute them. */
281 /*----------------------------------------------------------------------*/
283 int XcTagCallback(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
285 int objidx, result = TCL_OK;
286 char *postcmd, *substcmd, *newcmd, *sptr, *sres;
287 char *croot = Tcl_GetString(objv[0]);
288 Tcl_HashEntry *entry;
289 Tcl_SavedResult state;
290 int reset = FALSE;
291 int i, llen;
293 /* Skip over technology qualifier, if any */
295 if (!strncmp(croot, "::", 2)) croot += 2;
296 if (!strncmp(croot, "xcircuit::", 10)) croot += 10;
298 entry = Tcl_FindHashEntry(&XcTagTable, croot);
299 postcmd = (entry) ? (char *)Tcl_GetHashValue(entry) : NULL;
301 if (postcmd)
303 substcmd = (char *)Tcl_Alloc(strlen(postcmd) + 1);
304 strcpy(substcmd, postcmd);
305 sptr = substcmd;
307 /*--------------------------------------------------------------*/
308 /* Parse "postcmd" for Tk-substitution escapes */
309 /* Allowed escapes are: */
310 /* %W substitute the tk path of the calling window */
311 /* %r substitute the previous Tcl result string */
312 /* %R substitute the previous Tcl result string and */
313 /* reset the Tcl result. */
314 /* %[0-5] substitute the argument to the original command */
315 /* %N substitute all arguments as a list */
316 /* %% substitute a single percent character */
317 /* %# substitute the number of arguments passed */
318 /* %* (all others) no action: print as-is. */
319 /*--------------------------------------------------------------*/
321 while ((sptr = strchr(sptr, '%')) != NULL)
323 switch (*(sptr + 1))
325 case 'W': {
326 char *tkpath = NULL;
327 Tk_Window tkwind = Tk_MainWindow(interp);
328 if (tkwind != NULL) tkpath = Tk_PathName(tkwind);
329 if (tkpath == NULL)
330 newcmd = (char *)Tcl_Alloc(strlen(substcmd));
331 else
332 newcmd = (char *)Tcl_Alloc(strlen(substcmd) + strlen(tkpath));
334 strcpy(newcmd, substcmd);
336 if (tkpath == NULL)
337 strcpy(newcmd + (int)(sptr - substcmd), sptr + 2);
338 else
340 strcpy(newcmd + (int)(sptr - substcmd), tkpath);
341 strcat(newcmd, sptr + 2);
343 Tcl_Free(substcmd);
344 substcmd = newcmd;
345 sptr = substcmd;
346 } break;
348 case 'R':
349 reset = TRUE;
350 case 'r':
351 sres = (char *)Tcl_GetStringResult(interp);
352 newcmd = (char *)Tcl_Alloc(strlen(substcmd)
353 + strlen(sres) + 1);
354 strcpy(newcmd, substcmd);
355 sprintf(newcmd + (int)(sptr - substcmd), "\"%s\"", sres);
356 strcat(newcmd, sptr + 2);
357 Tcl_Free(substcmd);
358 substcmd = newcmd;
359 sptr = substcmd;
360 break;
362 case '#':
363 if (objc < 100) {
364 newcmd = (char *)Tcl_Alloc(strlen(substcmd) + 3);
365 strcpy(newcmd, substcmd);
366 sprintf(newcmd + (int)(sptr - substcmd), "%d", objc);
367 strcat(newcmd, sptr + 2);
368 Tcl_Free(substcmd);
369 substcmd = newcmd;
370 sptr = substcmd;
372 break;
374 case '0': case '1': case '2': case '3': case '4': case '5':
375 objidx = (int)(*(sptr + 1) - '0');
376 if ((objidx >= 0) && (objidx < objc))
378 newcmd = (char *)Tcl_Alloc(strlen(substcmd)
379 + strlen(Tcl_GetString(objv[objidx])) + 1);
380 strcpy(newcmd, substcmd);
381 strcpy(newcmd + (int)(sptr - substcmd),
382 Tcl_GetString(objv[objidx]));
383 strcat(newcmd, sptr + 2);
384 Tcl_Free(substcmd);
385 substcmd = newcmd;
386 sptr = substcmd;
388 else if (objidx >= objc)
390 newcmd = (char *)Tcl_Alloc(strlen(substcmd) + 1);
391 strcpy(newcmd, substcmd);
392 strcpy(newcmd + (int)(sptr - substcmd), sptr + 2);
393 Tcl_Free(substcmd);
394 substcmd = newcmd;
395 sptr = substcmd;
397 else sptr++;
398 break;
400 case 'N':
401 llen = 1;
402 for (i = 1; i < objc; i++)
403 llen += (1 + strlen(Tcl_GetString(objv[i])));
404 newcmd = (char *)Tcl_Alloc(strlen(substcmd) + llen);
405 strcpy(newcmd, substcmd);
406 strcpy(newcmd + (int)(sptr - substcmd), "{");
407 for (i = 1; i < objc; i++) {
408 strcat(newcmd, Tcl_GetString(objv[i]));
409 if (i < (objc - 1))
410 strcat(newcmd, " ");
412 strcat(newcmd, "}");
413 strcat(newcmd, sptr + 2);
414 Tcl_Free(substcmd);
415 substcmd = newcmd;
416 sptr = substcmd;
417 break;
419 case '%':
420 newcmd = (char *)Tcl_Alloc(strlen(substcmd) + 1);
421 strcpy(newcmd, substcmd);
422 strcpy(newcmd + (int)(sptr - substcmd), sptr + 1);
423 Tcl_Free(substcmd);
424 substcmd = newcmd;
425 sptr = substcmd;
426 break;
428 default:
429 sptr++;
430 break;
434 /* Fprintf(stderr, "Substituted tag callback is \"%s\"\n", substcmd); */
435 /* Flush(stderr); */
437 Tcl_SaveResult(interp, &state);
438 result = Tcl_Eval(interp, substcmd);
439 if ((result == TCL_OK) && (reset == FALSE))
440 Tcl_RestoreResult(interp, &state);
441 else
442 Tcl_DiscardResult(&state);
444 Tcl_Free(substcmd);
446 return result;
449 /*--------------------------------------------------------------*/
450 /* XcInternalTagCall --- */
451 /* */
452 /* Execute the tag callback for a command without actually */
453 /* evaluating the command itself. The command and arguments */
454 /* are passed as a variable number or char * arguments, since */
455 /* usually this routine will called with constant arguments */
456 /* (e.g., XcInternalTagCall(interp, 2, "set", "color");) */
457 /* */
458 /* objv declared static because this routine is used a lot */
459 /* (e.g., during select/unselect operations). */
460 /*--------------------------------------------------------------*/
462 int XcInternalTagCall(Tcl_Interp *interp, int argc, ...)
464 int i;
465 static Tcl_Obj **objv = NULL;
466 char *aptr;
467 va_list ap;
470 if (objv == (Tcl_Obj **)NULL)
471 objv = (Tcl_Obj **)malloc(argc * sizeof(Tcl_Obj *));
472 else
473 objv = (Tcl_Obj **)realloc(objv, argc * sizeof(Tcl_Obj *));
475 va_start(ap, argc);
476 for (i = 0; i < argc; i++) {
477 aptr = va_arg(ap, char *);
478 /* We are depending on Tcl's heap allocation of objects */
479 /* so that we do not have to manage memory for these */
480 /* string representations. . . */
482 objv[i] = Tcl_NewStringObj(aptr, -1);
484 va_end(ap);
486 return XcTagCallback(interp, argc, objv);
489 /*--------------------------------------------------------------*/
490 /* Return the event mode */
491 /* Event mode can be set in specific cases. */
492 /*--------------------------------------------------------------*/
494 int xctcl_eventmode(ClientData clientData,
495 Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
497 static char *modeNames[] = {
498 "normal", "undo", "move", "copy", "pan",
499 "selarea", "rescale", "catalog", "cattext",
500 "fontcat", "efontcat", "text", "wire", "box",
501 "arc", "spline", "etext", "epoly", "earc",
502 "espline", "epath", "einst", "assoc", "catmove",
503 NULL
506 /* This routine is diagnostic only */
508 if (objc != 1) return TCL_ERROR;
510 Tcl_SetResult(interp, modeNames[eventmode], NULL);
511 return TCL_OK;
514 /*--------------------------------------------------------------*/
515 /* Add a command tag callback */
516 /*--------------------------------------------------------------*/
518 int xctcl_tag(ClientData clientData,
519 Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
521 Tcl_HashEntry *entry;
522 char *hstring;
523 int new;
525 if (objc != 2 && objc != 3)
526 return TCL_ERROR;
528 entry = Tcl_CreateHashEntry(&XcTagTable, Tcl_GetString(objv[1]), &new);
529 if (entry == NULL) return TCL_ERROR;
531 hstring = (char *)Tcl_GetHashValue(entry);
532 if (objc == 2)
534 Tcl_SetResult(interp, hstring, NULL);
535 return TCL_OK;
538 if (strlen(Tcl_GetString(objv[2])) == 0)
540 Tcl_DeleteHashEntry(entry);
542 else
544 hstring = strdup(Tcl_GetString(objv[2]));
545 Tcl_SetHashValue(entry, hstring);
547 return TCL_OK;
550 /*----------------------------------------------------------------------*/
551 /* Turn a selection list into a Tcl List object (may be empty list) */
552 /*----------------------------------------------------------------------*/
554 Tcl_Obj *SelectToTclList(Tcl_Interp *interp, short *slist, int snum)
556 int i;
557 Tcl_Obj *objPtr, *listPtr;
559 if (snum == 1) {
560 objPtr = Tcl_NewHandleObj(SELTOGENERIC(slist));
561 return objPtr;
564 listPtr = Tcl_NewListObj(0, NULL);
565 for (i = 0; i < snum; i++) {
566 objPtr = Tcl_NewHandleObj(SELTOGENERIC(slist + i));
567 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
569 return listPtr;
572 /*----------------------------------------------------------------------*/
573 /* Get an x,y position (as an XPoint structure) from a list of size 2 */
574 /*----------------------------------------------------------------------*/
576 int GetPositionFromList(Tcl_Interp *interp, Tcl_Obj *list, XPoint *rpoint)
578 int result, numobjs;
579 Tcl_Obj *lobj, *tobj;
580 int pos;
582 if (!strcmp(Tcl_GetString(list), "here")) {
583 if (rpoint) *rpoint = UGetCursorPos();
584 return TCL_OK;
586 result = Tcl_ListObjLength(interp, list, &numobjs);
587 if (result != TCL_OK) return result;
589 if (numobjs == 1) {
590 /* Try decomposing the object into a list */
591 result = Tcl_ListObjIndex(interp, list, 0, &tobj);
592 if (result == TCL_OK) {
593 result = Tcl_ListObjLength(interp, tobj, &numobjs);
594 if (numobjs == 2)
595 list = tobj;
597 if (result != TCL_OK) Tcl_ResetResult(interp);
599 if (numobjs != 2) {
600 Tcl_SetResult(interp, "list must contain x y positions", NULL);
601 return TCL_ERROR;
603 result = Tcl_ListObjIndex(interp, list, 0, &lobj);
604 if (result != TCL_OK) return result;
605 result = Tcl_GetIntFromObj(interp, lobj, &pos);
606 if (result != TCL_OK) return result;
607 if (rpoint) rpoint->x = pos;
609 result = Tcl_ListObjIndex(interp, list, 1, &lobj);
610 if (result != TCL_OK) return result;
611 result = Tcl_GetIntFromObj(interp, lobj, &pos);
612 if (result != TCL_OK) return result;
613 if (rpoint) rpoint->y = pos;
615 return TCL_OK;
618 /*--------------------------------------------------------------*/
619 /* Convert color index to a list of 3 elements */
620 /* We assume that this color exists in the color table. */
621 /*--------------------------------------------------------------*/
623 Tcl_Obj *TclIndexToRGB(int cidx)
625 Tcl_Obj *RGBTuple;
627 if (cidx < 0) { /* Handle "default color" */
628 return Tcl_NewStringObj("Default", 7);
630 else if (cidx >= number_colors) {
631 Tcl_SetResult(xcinterp, "Bad color index", NULL);
632 return NULL;
635 RGBTuple = Tcl_NewListObj(0, NULL);
636 Tcl_ListObjAppendElement(xcinterp, RGBTuple,
637 Tcl_NewIntObj((int)(colorlist[cidx].color.red / 256)));
638 Tcl_ListObjAppendElement(xcinterp, RGBTuple,
639 Tcl_NewIntObj((int)(colorlist[cidx].color.green / 256)));
640 Tcl_ListObjAppendElement(xcinterp, RGBTuple,
641 Tcl_NewIntObj((int)(colorlist[cidx].color.blue / 256)));
642 return RGBTuple;
646 /*--------------------------------------------------------------*/
647 /* Convert a stringpart* to a Tcl list object */
648 /*--------------------------------------------------------------*/
650 Tcl_Obj *TclGetStringParts(stringpart *thisstring)
652 Tcl_Obj *lstr, *sdict, *stup;
653 int i;
654 stringpart *strptr;
656 lstr = Tcl_NewListObj(0, NULL);
657 for (strptr = thisstring, i = 0; strptr != NULL;
658 strptr = strptr->nextpart, i++) {
659 switch(strptr->type) {
660 case TEXT_STRING:
661 sdict = Tcl_NewListObj(0, NULL);
662 Tcl_ListObjAppendElement(xcinterp, sdict, Tcl_NewStringObj("Text", 4));
663 Tcl_ListObjAppendElement(xcinterp, sdict,
664 Tcl_NewStringObj(strptr->data.string,
665 strlen(strptr->data.string)));
666 Tcl_ListObjAppendElement(xcinterp, lstr, sdict);
667 break;
668 case PARAM_START:
669 sdict = Tcl_NewListObj(0, NULL);
670 Tcl_ListObjAppendElement(xcinterp, sdict, Tcl_NewStringObj("Parameter", 9));
671 Tcl_ListObjAppendElement(xcinterp, sdict,
672 Tcl_NewStringObj(strptr->data.string,
673 strlen(strptr->data.string)));
674 Tcl_ListObjAppendElement(xcinterp, lstr, sdict);
675 break;
676 case PARAM_END:
677 Tcl_ListObjAppendElement(xcinterp, lstr,
678 Tcl_NewStringObj("End Parameter", 13));
679 break;
680 case FONT_NAME:
681 sdict = Tcl_NewListObj(0, NULL);
682 Tcl_ListObjAppendElement(xcinterp, sdict, Tcl_NewStringObj("Font", 4));
683 Tcl_ListObjAppendElement(xcinterp, sdict,
684 Tcl_NewStringObj(fonts[strptr->data.font].psname,
685 strlen(fonts[strptr->data.font].psname)));
686 Tcl_ListObjAppendElement(xcinterp, lstr, sdict);
687 break;
688 case FONT_SCALE:
689 sdict = Tcl_NewListObj(0, NULL);
690 Tcl_ListObjAppendElement(xcinterp, sdict,
691 Tcl_NewStringObj("Font Scale", 10));
692 Tcl_ListObjAppendElement(xcinterp, sdict,
693 Tcl_NewDoubleObj((double)strptr->data.scale));
694 Tcl_ListObjAppendElement(xcinterp, lstr, sdict);
695 break;
696 case KERN:
697 sdict = Tcl_NewListObj(0, NULL);
698 stup = Tcl_NewListObj(0, NULL);
699 Tcl_ListObjAppendElement(xcinterp, stup,
700 Tcl_NewIntObj((int)strptr->data.kern[0]));
701 Tcl_ListObjAppendElement(xcinterp, stup,
702 Tcl_NewIntObj((int)strptr->data.kern[1]));
704 Tcl_ListObjAppendElement(xcinterp, sdict, Tcl_NewStringObj("Kern", 4));
705 Tcl_ListObjAppendElement(xcinterp, sdict, stup);
706 Tcl_ListObjAppendElement(xcinterp, lstr, sdict);
707 break;
708 case FONT_COLOR:
709 stup = TclIndexToRGB(strptr->data.color);
710 if (stup != NULL) {
711 sdict = Tcl_NewListObj(0, NULL);
712 Tcl_ListObjAppendElement(xcinterp, sdict,
713 Tcl_NewStringObj("Color", 5));
714 Tcl_ListObjAppendElement(xcinterp, sdict, stup);
715 Tcl_ListObjAppendElement(xcinterp, lstr, sdict);
717 break;
718 case MARGINSTOP:
719 sdict = Tcl_NewListObj(0, NULL);
720 Tcl_ListObjAppendElement(xcinterp, sdict,
721 Tcl_NewStringObj("Margin Stop", 11));
722 Tcl_ListObjAppendElement(xcinterp, sdict,
723 Tcl_NewIntObj((int)strptr->data.width));
724 Tcl_ListObjAppendElement(xcinterp, lstr, sdict);
725 break;
726 case TABSTOP:
727 Tcl_ListObjAppendElement(xcinterp, lstr,
728 Tcl_NewStringObj("Tab Stop", 8));
729 break;
730 case TABFORWARD:
731 Tcl_ListObjAppendElement(xcinterp, lstr,
732 Tcl_NewStringObj("Tab Forward", 11));
733 break;
734 case TABBACKWARD:
735 Tcl_ListObjAppendElement(xcinterp, lstr,
736 Tcl_NewStringObj("Tab Backward", 12));
737 break;
738 case RETURN:
739 // Don't show automatically interted line breaks
740 if (strptr->data.flags == 0)
741 Tcl_ListObjAppendElement(xcinterp, lstr,
742 Tcl_NewStringObj("Return", 6));
743 break;
744 case SUBSCRIPT:
745 Tcl_ListObjAppendElement(xcinterp, lstr,
746 Tcl_NewStringObj("Subscript", 9));
747 break;
748 case SUPERSCRIPT:
749 Tcl_ListObjAppendElement(xcinterp, lstr,
750 Tcl_NewStringObj("Superscript", 11));
751 break;
752 case NORMALSCRIPT:
753 Tcl_ListObjAppendElement(xcinterp, lstr,
754 Tcl_NewStringObj("Normalscript", 12));
755 break;
756 case UNDERLINE:
757 Tcl_ListObjAppendElement(xcinterp, lstr,
758 Tcl_NewStringObj("Underline", 9));
759 break;
760 case OVERLINE:
761 Tcl_ListObjAppendElement(xcinterp, lstr,
762 Tcl_NewStringObj("Overline", 8));
763 break;
764 case NOLINE:
765 Tcl_ListObjAppendElement(xcinterp, lstr,
766 Tcl_NewStringObj("No Line", 7));
767 break;
768 case HALFSPACE:
769 Tcl_ListObjAppendElement(xcinterp, lstr,
770 Tcl_NewStringObj("Half Space", 10));
771 break;
772 case QTRSPACE:
773 Tcl_ListObjAppendElement(xcinterp, lstr,
774 Tcl_NewStringObj("Quarter Space", 13));
775 break;
778 return lstr;
781 /*----------------------------------------------------------------------*/
782 /* Get a stringpart linked list from a Tcl list */
783 /*----------------------------------------------------------------------*/
785 int GetXCStringFromList(Tcl_Interp *interp, Tcl_Obj *list, stringpart **rstring)
787 int result, j, k, numobjs, idx, numparts, ptype, ival;
788 Tcl_Obj *lobj, *pobj, *tobj, *t2obj;
789 stringpart *newpart;
790 char *fname;
791 double fscale;
793 static char *partTypes[] = {"Text", "Subscript", "Superscript",
794 "Normalscript", "Underline", "Overline", "No Line", "Tab Stop",
795 "Tab Forward", "Tab Backward", "Half Space", "Quarter Space",
796 "Return", "Font", "Font Scale", "Color", "Margin Stop", "Kern",
797 "Parameter", "End Parameter", "Special", NULL};
799 static int partTypesIdx[] = {TEXT_STRING, SUBSCRIPT, SUPERSCRIPT,
800 NORMALSCRIPT, UNDERLINE, OVERLINE, NOLINE, TABSTOP, TABFORWARD,
801 TABBACKWARD, HALFSPACE, QTRSPACE, RETURN, FONT_NAME, FONT_SCALE,
802 FONT_COLOR, MARGINSTOP, KERN, PARAM_START, PARAM_END, SPECIAL};
804 /* No place to put result! */
805 if (rstring == NULL) return TCL_ERROR;
807 result = Tcl_ListObjLength(interp, list, &numobjs);
808 if (result != TCL_OK) return result;
810 newpart = NULL;
811 for (j = 0; j < numobjs; j++) {
812 result = Tcl_ListObjIndex(interp, list, j, &lobj);
813 if (result != TCL_OK) return result;
815 result = Tcl_ListObjLength(interp, lobj, &numparts);
816 if (result != TCL_OK) return result;
818 result = Tcl_ListObjIndex(interp, lobj, 0, &pobj);
819 if (result != TCL_OK) return result;
821 /* Must define TCL_EXACT in flags, or else, for instance, "u" gets */
822 /* interpreted as "underline", which is usually not intended. */
824 if (pobj == NULL)
825 return TCL_ERROR;
826 else if (Tcl_GetIndexFromObj(interp, pobj, (CONST84 char **)partTypes,
827 "string part types", TCL_EXACT, &idx) != TCL_OK) {
828 Tcl_ResetResult(interp);
829 idx = -1;
831 // If there's only one object and the first item doesn't match
832 // a stringpart itentifying word, then assume that "list" is a
833 // single text string.
835 if (numobjs == 1)
836 tobj = list;
837 else
838 result = Tcl_ListObjIndex(interp, lobj, 0, &tobj);
840 else {
841 result = Tcl_ListObjIndex(interp, lobj, (numparts > 1) ? 1 : 0, &tobj);
843 if (result != TCL_OK) return result;
845 if (idx < 0) {
846 if ((newpart == NULL) || (newpart->type != TEXT_STRING))
847 idx = 0;
848 else {
849 /* We have an implicit text string which should be appended */
850 /* to the previous text string with a space character. */
851 newpart->data.string = (char *)realloc(newpart->data.string,
852 strlen(newpart->data.string) + strlen(Tcl_GetString(tobj))
853 + 2);
854 strcat(newpart->data.string, " ");
855 strcat(newpart->data.string, Tcl_GetString(tobj));
856 continue;
859 ptype = partTypesIdx[idx];
861 newpart = makesegment(rstring, NULL);
862 newpart->nextpart = NULL;
863 newpart->type = ptype;
865 switch(ptype) {
866 case TEXT_STRING:
867 case PARAM_START:
868 newpart->data.string = strdup(Tcl_GetString(tobj));
869 break;
870 case FONT_NAME:
871 fname = Tcl_GetString(tobj);
872 for (k = 0; k < fontcount; k++) {
873 if (!strcmp(fonts[k].psname, fname)) {
874 newpart->data.font = k;
875 break;
878 if (k == fontcount) {
879 Tcl_SetResult(interp, "Bad font name", NULL);
880 return TCL_ERROR;
882 break;
883 case FONT_SCALE:
884 result = Tcl_GetDoubleFromObj(interp, tobj, &fscale);
885 if (result != TCL_OK) return result;
886 newpart->data.scale = (float)fscale;
887 break;
888 case MARGINSTOP:
889 result = Tcl_GetIntFromObj(interp, tobj, &ival);
890 if (result != TCL_OK) return result;
891 newpart->data.width = ival;
892 break;
893 case KERN:
894 result = Tcl_ListObjLength(interp, tobj, &numparts);
895 if (result != TCL_OK) return result;
896 if (numparts != 2) {
897 Tcl_SetResult(interp, "Bad kern list: need 2 values", NULL);
898 return TCL_ERROR;
900 result = Tcl_ListObjIndex(interp, tobj, 0, &t2obj);
901 if (result != TCL_OK) return result;
902 result = Tcl_GetIntFromObj(interp, t2obj, &ival);
903 if (result != TCL_OK) return result;
904 newpart->data.kern[0] = (short)ival;
906 result = Tcl_ListObjIndex(interp, tobj, 1, &t2obj);
907 if (result != TCL_OK) return result;
908 result = Tcl_GetIntFromObj(interp, t2obj, &ival);
909 if (result != TCL_OK) return result;
910 newpart->data.kern[1] = (short)ival;
912 break;
913 case FONT_COLOR:
914 /* Not implemented: Need TclRGBToIndex() function */
915 break;
917 /* All other types have no arguments */
920 return TCL_OK;
923 /*----------------------------------------------------------------------*/
924 /* Handle (integer representation of internal xcircuit object) checking */
925 /* if "checkobject" is NULL, then */
926 /*----------------------------------------------------------------------*/
928 genericptr *CheckHandle(pointertype eaddr, objectptr checkobject)
930 genericptr *gelem;
931 int i, j;
932 objectptr thisobj;
933 Library *thislib;
935 if (checkobject != NULL) {
936 for (gelem = checkobject->plist; gelem < checkobject->plist +
937 checkobject->parts; gelem++)
938 if ((pointertype)(*gelem) == eaddr) goto exists;
939 return NULL;
942 /* Look through all the pages. */
944 for (i = 0; i < xobjs.pages; i++) {
945 if (xobjs.pagelist[i]->pageinst == NULL) continue;
946 thisobj = xobjs.pagelist[i]->pageinst->thisobject;
947 for (gelem = thisobj->plist; gelem < thisobj->plist + thisobj->parts; gelem++)
948 if ((pointertype)(*gelem) == eaddr) goto exists;
951 /* Not found? Maybe in a library */
953 for (i = 0; i < xobjs.numlibs; i++) {
954 thislib = xobjs.userlibs + i;
955 for (j = 0; j < thislib->number; j++) {
956 thisobj = thislib->library[j];
957 for (gelem = thisobj->plist; gelem < thisobj->plist + thisobj->parts; gelem++)
958 if ((pointertype)(*gelem) == eaddr) goto exists;
962 /* Either in the delete list (where we don't want to go) or */
963 /* is an invalid number. */
964 return NULL;
966 exists:
967 return gelem;
970 /*----------------------------------------------------------------------*/
971 /* Find the index into the "plist" list of elements */
972 /* Part number must be of a type in "mask" or no selection occurs. */
973 /* return values: -1 = no object found, -2 = found, but wrong type */
974 /*----------------------------------------------------------------------*/
976 short GetPartNumber(genericptr egen, objectptr checkobject, int mask)
978 genericptr *gelem;
979 objectptr thisobject = checkobject;
980 int i;
982 if (checkobject == NULL) thisobject = topobject;
984 for (i = 0, gelem = thisobject->plist; gelem < thisobject->plist +
985 thisobject->parts; gelem++, i++) {
986 if ((*gelem) == egen) {
987 if ((*gelem)->type & mask)
988 return i;
989 else
990 return -2;
993 return -1;
996 /*----------------------------------------------------------------------*/
997 /* This routine is used by a number of menu functions. It looks for */
998 /* the arguments "selected" or an integer (object handle). If the */
999 /* argument is a valid object handle, it is added to the select list. */
1000 /* The argument can be a list of handles, of which each is checked and */
1001 /* added to the select list. */
1002 /* "extra" indicates the number of required arguments beyond 2. */
1003 /* "next" returns the integer of the argument after the handle, or the */
1004 /* argument after the command, if there is no handle. If the handle is */
1005 /* specified as a hierarchical list of element handles then */
1006 /* areawin->hierstack contains the hierarchy of object instances. */
1007 /*----------------------------------------------------------------------*/
1009 int ParseElementArguments(Tcl_Interp *interp, int objc,
1010 Tcl_Obj *CONST objv[], int *next, int mask) {
1012 short *newselect;
1013 char *argstr;
1014 int i, j, result, numobjs;
1015 pointertype ehandle;
1016 Tcl_Obj *lobj;
1017 int extra = 0, goodobjs = 0;
1019 if (next != NULL) {
1020 extra = *next;
1021 *next = 1;
1024 if ((objc > (2 + extra)) || (objc == 1)) {
1025 Tcl_WrongNumArgs(interp, 1, objv, "[selected | <element_handle>] <option>");
1026 return TCL_ERROR;
1028 else if (objc == 1) {
1029 *next = 0;
1030 return TCL_OK;
1032 else {
1033 argstr = Tcl_GetString(objv[1]);
1035 if (strcmp(argstr, "selected")) {
1037 /* check for object handle (special type) */
1039 result = Tcl_ListObjLength(interp, objv[1], &numobjs);
1040 if (result != TCL_OK) return result;
1041 goodobjs = 0;
1043 /* Non-integer, non-list types: assume operation is to be applied */
1044 /* to currently selected elements, and return to caller. */
1046 if (numobjs == 1) {
1047 result = Tcl_GetHandleFromObj(interp, objv[1], (void *)&ehandle);
1048 if (result != TCL_OK) {
1049 Tcl_ResetResult(interp);
1050 return TCL_OK;
1053 if (numobjs == 0) {
1054 Tcl_SetResult(interp, "No elements.", NULL);
1055 return TCL_ERROR;
1057 else
1058 newselect = (short *)malloc(numobjs * sizeof(short));
1060 /* Prepare a new selection, in case the new selection is */
1061 /* smaller than the original selection, but don't blanket */
1062 /* delete an existing selection, which will destroy cycle */
1063 /* information. */
1065 for (j = 0; j < numobjs; j++) {
1066 result = Tcl_ListObjIndex(interp, objv[1], j, &lobj);
1067 if (result != TCL_OK) {
1068 free(newselect);
1069 return result;
1071 result = Tcl_GetHandleFromObj(interp, lobj, (void *)&ehandle);
1072 if (result != TCL_OK) {
1073 free(newselect);
1074 return result;
1076 if (areawin->hierstack != NULL)
1077 i = GetPartNumber((genericptr)ehandle,
1078 areawin->hierstack->thisinst->thisobject, mask);
1079 else
1080 i = GetPartNumber((genericptr)ehandle, topobject, mask);
1082 if (i == -1) {
1083 free_stack(&areawin->hierstack);
1084 Tcl_SetResult(interp, "No such element exists.", NULL);
1085 free(newselect);
1086 return TCL_ERROR;
1088 else if (i >= 0) {
1089 *(newselect + goodobjs) = i;
1090 if (next != NULL) *next = 2;
1091 goodobjs++;
1094 if (goodobjs == 0) {
1095 Tcl_SetResult(interp, "No element matches required type.", NULL);
1096 unselect_all();
1097 free(newselect);
1098 return TCL_ERROR;
1100 else {
1101 selection aselect, bselect;
1103 /* To avoid unnecessarily blasting the existing selection */
1104 /* and its cycles, we compare the two selection lists. */
1105 /* This is not an excuse for not fixing the selection list */
1106 /* mess in general! */
1108 aselect.selectlist = newselect;
1109 aselect.selects = goodobjs;
1110 bselect.selectlist = areawin->selectlist;
1111 bselect.selects = areawin->selects;
1112 if (compareselection(&aselect, &bselect)) {
1113 free(newselect);
1115 else {
1116 unselect_all();
1117 areawin->selects = goodobjs;
1118 areawin->selectlist = newselect;
1122 draw_normal_selected(topobject, areawin->topinstance);
1124 else if (next != NULL) *next = 2;
1126 return TCL_OK;
1129 /*----------------------------------------------------------------------*/
1130 /* Generate a transformation matrix according to the object instance */
1131 /* hierarchy left on the hierstack. */
1132 /*----------------------------------------------------------------------*/
1134 void MakeHierCTM(Matrix *hierCTM)
1136 objinstptr thisinst;
1137 pushlistptr cs;
1139 UResetCTM(hierCTM);
1140 for (cs = areawin->hierstack; cs != NULL; cs = cs->next) {
1141 thisinst = cs->thisinst;
1142 UMultCTM(hierCTM, thisinst->position, thisinst->scale, thisinst->rotation);
1146 /*----------------------------------------------------------------------*/
1147 /* This routine is similar to ParseElementArguments. It looks for a */
1148 /* page number or page name in the second argument position. If it */
1149 /* finds one, it sets the page number in the return value. Otherwise, */
1150 /* it sets the return value to the value of areawin->page. */
1151 /*----------------------------------------------------------------------*/
1153 int ParsePageArguments(Tcl_Interp *interp, int objc,
1154 Tcl_Obj *CONST objv[], int *next, int *pageret) {
1156 char *pagename;
1157 int i, page, result;
1158 Tcl_Obj *objPtr;
1160 if (next != NULL) *next = 1;
1161 if (pageret != NULL) *pageret = areawin->page; /* default */
1163 if ((objc == 1) || ((objc == 2) && !strcmp(Tcl_GetString(objv[1]), ""))) {
1164 objPtr = Tcl_NewIntObj(areawin->page + 1);
1165 Tcl_SetObjResult(interp, objPtr);
1166 if (next) *next = -1;
1167 return TCL_OK;
1169 else {
1170 pagename = Tcl_GetString(objv[1]);
1171 if (strcmp(pagename, "directory")) {
1173 /* check for page number (integer) */
1175 result = Tcl_GetIntFromObj(interp, objv[1], &page);
1176 if (result != TCL_OK) {
1177 Tcl_ResetResult(interp);
1179 /* check for page name (string) */
1181 for (i = 0; i < xobjs.pages; i++) {
1182 if (xobjs.pagelist[i]->pageinst == NULL) continue;
1183 if (!strcmp(pagename, xobjs.pagelist[i]->pageinst->thisobject->name)) {
1184 if (pageret) *pageret = i;
1185 break;
1188 if (i == xobjs.pages) {
1189 if (next != NULL) *next = 0;
1192 else {
1193 if (page < 1) {
1194 Tcl_SetResult(interp, "Illegal page number: zero or negative", NULL);
1195 return TCL_ERROR;
1197 else if (page > xobjs.pages) {
1198 Tcl_SetResult(interp, "Illegal page number: page does not exist", NULL);
1199 if (pageret) *pageret = (page - 1);
1200 return TCL_ERROR;
1202 else if (pageret) *pageret = (page - 1);
1205 else {
1206 *next = 0;
1209 return TCL_OK;
1212 /*----------------------------------------------------------------------*/
1213 /* This routine is similar to ParsePageArguments. It looks for a */
1214 /* library number or library name in the second argument position. If */
1215 /* it finds one, it sets the page number in the return value. */
1216 /* Otherwise, if a library page is currently being viewed, it sets the */
1217 /* return value to that library. Otherwise, it sets the return value */
1218 /* to the User Library. */
1219 /*----------------------------------------------------------------------*/
1221 int ParseLibArguments(Tcl_Interp *interp, int objc,
1222 Tcl_Obj *CONST objv[], int *next, int *libret) {
1224 char *libname;
1225 int library, result;
1226 Tcl_Obj *objPtr;
1228 if (next != NULL) *next = 1;
1230 if (objc == 1) {
1231 library = is_library(topobject);
1232 if (library < 0) {
1233 Tcl_SetResult(interp, "No current library.", NULL);
1234 return TCL_ERROR;
1236 objPtr = Tcl_NewIntObj(library + 1);
1237 Tcl_SetObjResult(interp, objPtr);
1238 if (next) *next = -1;
1239 return TCL_OK;
1241 else {
1242 libname = Tcl_GetString(objv[1]);
1243 if (strcmp(libname, "directory")) {
1245 /* check for library number (integer) or name */
1247 result = Tcl_GetIntFromObj(interp, objv[1], &library);
1248 if (result != TCL_OK) {
1249 Tcl_ResetResult(xcinterp);
1250 *libret = NameToLibrary(libname);
1251 if (*libret < 0) {
1252 *libret = -1;
1253 if (next != NULL) *next = 0;
1256 else {
1257 if (library < 1) {
1258 Tcl_SetResult(interp, "Illegal library number: zero or negative", NULL);
1259 return TCL_ERROR;
1261 else if (library > xobjs.numlibs) {
1262 Tcl_SetResult(interp, "Illegal library number: library "
1263 "does not exist", NULL);
1264 return TCL_ERROR;
1266 else *libret = (library - 1);
1269 else *next = 0;
1271 return TCL_OK;
1274 /*----------------------------------------------------------------------*/
1275 /* Schematic and symbol creation and association */
1276 /*----------------------------------------------------------------------*/
1278 int xctcl_symschem(ClientData clientData, Tcl_Interp *interp,
1279 int objc, Tcl_Obj *CONST objv[])
1281 int i, idx, result, stype;
1282 objectptr otherobj = NULL;
1283 char *objname;
1285 static char *subCmds[] = {
1286 "associate", "disassociate", "make", "goto", "get", "type", NULL
1288 enum SubIdx {
1289 AssocIdx, DisAssocIdx, MakeIdx, GoToIdx, NameIdx, TypeIdx
1292 /* The order of these must match the definitions in xcircuit.h */
1293 static char *schemTypes[] = {
1294 "primary", "secondary", "trivial", "symbol", "fundamental",
1295 "nonetwork", NULL /* (jdk) */
1298 if (objc == 1 || objc > 4) {
1299 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
1300 return TCL_ERROR;
1302 else if ((result = Tcl_GetIndexFromObj(interp, objv[1],
1303 (CONST84 char **)subCmds, "option", 0, &idx)) != TCL_OK) {
1304 return result;
1307 switch(idx) {
1308 case AssocIdx:
1309 if (objc == 3) {
1311 /* To do: accept name for association */
1312 objname = Tcl_GetString(objv[2]);
1314 if (topobject->schemtype == PRIMARY) {
1316 /* Name has to be that of a library object */
1318 otherobj = NameToObject(Tcl_GetString(objv[2]), NULL, FALSE);
1319 if (otherobj == NULL) {
1320 Tcl_SetResult(interp, "Name is not a known object", NULL);
1321 return TCL_ERROR;
1324 else {
1326 /* Name has to be that of a page label */
1328 objectptr pageobj;
1329 for (i = 0; i < xobjs.pages; i++) {
1330 pageobj = xobjs.pagelist[i]->pageinst->thisobject;
1331 if (!strcmp(objname, pageobj->name)) {
1332 otherobj = pageobj;
1333 break;
1336 if (otherobj == NULL)
1338 Tcl_SetResult(interp, "Name is not a known page label", NULL);
1339 return TCL_ERROR;
1342 if (schemassoc(topobject, otherobj) == False)
1343 return TCL_ERROR;
1345 else
1346 startschemassoc(NULL, 0, NULL);
1347 break;
1348 case DisAssocIdx:
1349 schemdisassoc();
1350 break;
1351 case MakeIdx:
1352 if (topobject->symschem != NULL)
1353 Wprintf("Error: Schematic already has an associated symbol.");
1354 else if (topobject->schemtype != PRIMARY)
1355 Wprintf("Error: Current page is not a primary schematic.");
1356 else if (!strncmp(topobject->name, "Page ", 5))
1357 Wprintf("Error: Schematic page must have a valid name.");
1358 else {
1359 int libnum = -1;
1360 if (objc >= 3) {
1362 objname = Tcl_GetString(objv[2]);
1364 if (objc == 4) {
1365 ParseLibArguments(xcinterp, 2, &objv[2], NULL, &libnum);
1366 if (libnum < 0) {
1367 Tcl_SetResult(interp, "Invalid library name.", NULL);
1368 return TCL_ERROR;
1372 else {
1373 /* Use this error condition to generate the popup prompt */
1374 Tcl_SetResult(interp, "Must supply a name for the page", NULL);
1375 return TCL_ERROR;
1377 swapschem(1, libnum, objname);
1378 return TCL_OK;
1380 return TCL_ERROR;
1381 break;
1382 case GoToIdx:
1383 /* This is supposed to specifically go to the specified type, */
1384 /* so don't call swapschem to change views if we're already */
1385 /* on the right view. */
1387 if (topobject->schemtype == PRIMARY || topobject->schemtype == SECONDARY) {
1388 if (!strncmp(Tcl_GetString(objv[0]), "sym", 3)) {
1389 swapschem(0, -1, NULL);
1392 else {
1393 if (!strncmp(Tcl_GetString(objv[0]), "sch", 3)) {
1394 swapschem(0, -1, NULL);
1397 break;
1398 case NameIdx:
1399 if (topobject->symschem != NULL)
1400 Tcl_AppendElement(interp, topobject->symschem->name);
1401 break;
1402 case TypeIdx:
1403 if (objc == 3) {
1404 if (topobject->schemtype == PRIMARY || topobject->schemtype == SECONDARY) {
1405 Tcl_SetResult(interp, "Make object to change from schematic to symbol",
1406 NULL);
1407 return TCL_ERROR;
1409 if ((result = Tcl_GetIndexFromObj(interp, objv[2],
1410 (CONST84 char **)schemTypes, "schematic types",
1411 0, &stype)) != TCL_OK)
1412 return result;
1413 if (stype == PRIMARY || stype == SECONDARY) {
1414 Tcl_SetResult(interp, "Cannot change symbol into a schematic", NULL);
1415 return TCL_ERROR;
1417 topobject->schemtype = stype;
1418 if (topobject->symschem) schemdisassoc();
1420 else
1421 Tcl_AppendElement(interp, schemTypes[topobject->schemtype]);
1423 break;
1425 return XcTagCallback(interp, objc, objv);
1428 /*----------------------------------------------------------------------*/
1429 /* Generate netlist into a Tcl hierarchical list */
1430 /* (plus other netlist functions) */
1431 /*----------------------------------------------------------------------*/
1433 int xctcl_netlist(ClientData clientData, Tcl_Interp *interp,
1434 int objc, Tcl_Obj *CONST objv[])
1436 Tcl_Obj *rdict;
1437 int idx, result, mpage, spage, bvar, j;
1438 Boolean valid, quiet;
1439 char *option, *extension, *mode = NULL;
1440 pushlistptr stack;
1441 objectptr master, slave;
1442 objinstptr schemtopinst;
1444 static char *subCmds[] = {
1445 "write", "highlight", "unhighlight", "goto", "get", "select", "parse",
1446 "position", "make", "connect", "unconnect", "autonumber", "ratsnest",
1447 "update", NULL
1449 enum SubIdx {
1450 WriteIdx, HighLightIdx, UnHighLightIdx, GoToIdx, GetIdx, SelectIdx,
1451 ParseIdx, PositionIdx, MakeIdx, ConnectIdx, UnConnectIdx,
1452 AutoNumberIdx, RatsNestIdx, UpdateIdx
1455 if (objc == 1) {
1456 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
1457 return TCL_ERROR;
1459 else if ((result = Tcl_GetIndexFromObj(interp, objv[1],
1460 (CONST84 char **)subCmds, "option", 0, &idx)) != TCL_OK) {
1461 return result;
1464 /* Look for the "-quiet" option (more options processed by "netlist get") */
1466 j = 1;
1467 quiet = FALSE;
1468 while (option = Tcl_GetString(objv[objc - (j++)]), option[0] == '-') {
1469 if (!strncmp(option + 1, "quiet", 5))
1470 quiet = TRUE;
1473 /* Make sure a valid netlist exists for the current schematic */
1474 /* for those commands which require a valid netlist (non-ASG */
1475 /* functions). Some functions (e.g., "parse") require that */
1476 /* the next object up in the hierarchy have a valid netlist, */
1477 /* if we have descended to the current symbol from there. */
1479 valid = False;
1480 switch(idx) {
1481 case RatsNestIdx:
1482 /* Specifically avoid calling updatenets() */
1483 if ((topobject->labels != NULL) || (topobject->polygons != NULL))
1484 valid = True;
1485 break;
1488 if (!valid) {
1489 objinstptr tinst;
1491 /* Ignore libraries */
1492 if (is_library(topobject) >= 0 || (eventmode == CATALOG_MODE))
1493 return TCL_ERROR;
1495 if ((topobject->schemtype) != PRIMARY && (areawin->stack != NULL))
1496 tinst = areawin->stack->thisinst;
1497 else
1498 tinst = areawin->topinstance;
1500 if ((result = updatenets(tinst, quiet)) < 0) {
1501 Tcl_SetResult(interp, "Check circuit for infinite recursion.", NULL);
1502 return TCL_ERROR;
1504 else if (result == 0) {
1505 Tcl_SetResult(interp, "No netlist.", NULL);
1506 return TCL_ERROR;
1510 switch(idx) {
1511 case WriteIdx: /* write netlist formats */
1512 if (objc < 3) {
1513 Tcl_WrongNumArgs(interp, 1, objv, "write format [extension] "
1514 "[spice_end] [-option]");
1515 return TCL_ERROR;
1518 /* Check for forcing option */
1520 option = Tcl_GetString(objv[objc - 1]);
1521 if (option[0] == '-')
1523 option++;
1524 if (!strncmp(option, "flat", 4) || !strncmp(option, "pseu", 4))
1526 mode = (char *)malloc(5 + strlen(Tcl_GetString(objv[2])));
1527 option[4] = '\0';
1528 sprintf(mode, "%s%s", option, Tcl_GetString(objv[2]));
1530 else if (strncmp(option, "hier", 4))
1532 Tcl_SetResult(interp, "Unknown netlist option.", NULL);
1533 return TCL_ERROR;
1535 objc--;
1538 if ((result = Tcl_GetBooleanFromObj(interp, objv[objc - 1], &bvar))
1539 != TCL_OK) {
1540 spice_end = True;
1541 Tcl_ResetResult(interp);
1543 else {
1544 spice_end = (Boolean)bvar;
1545 objc--;
1548 /* If no extension is specified, the extension is the same as */
1549 /* the format name. */
1551 if (objc == 3)
1552 extension = Tcl_GetString(objv[2]);
1553 else
1554 extension = Tcl_GetString(objv[3]);
1555 writenet(topobject, (mode == NULL) ? Tcl_GetString(objv[2]) : mode,
1556 extension);
1557 if (mode != NULL) free(mode);
1558 break;
1560 case GoToIdx: /* go to top-level page having specified name */
1561 if (objc != 2 && objc != 3) {
1562 Tcl_WrongNumArgs(interp, 1, objv, "goto [hierarchical-network-name]");
1563 return TCL_ERROR;
1566 /* Find the top of the schematic hierarchy, regardless of */
1567 /* where the current page is in it. */
1569 if (areawin->stack == NULL)
1570 schemtopinst = areawin->topinstance;
1571 else {
1572 pushlistptr sstack = areawin->stack;
1573 while (sstack->next != NULL) sstack = sstack->next;
1574 schemtopinst = sstack->thisinst;
1577 stack = NULL;
1578 push_stack(&stack, schemtopinst, NULL);
1579 valid = TRUE;
1580 if (objc == 3)
1581 valid = HierNameToObject(schemtopinst, Tcl_GetString(objv[2]), &stack);
1583 if (valid) {
1584 /* Add the current edit object to the push stack, then append */
1585 /* the new push stack */
1586 free_stack(&areawin->stack);
1587 topobject->viewscale = areawin->vscale;
1588 topobject->pcorner = areawin->pcorner;
1589 areawin->topinstance = stack->thisinst;
1590 pop_stack(&stack);
1591 areawin->stack = stack;
1592 setpage(TRUE);
1593 transferselects();
1594 refresh(NULL, NULL, NULL);
1595 setsymschem();
1597 /* If the current object is a symbol that has a schematic, */
1598 /* go to the schematic. */
1600 if (topobject->schemtype != PRIMARY && topobject->symschem != NULL)
1601 swapschem(0, -1, NULL);
1603 else {
1604 Tcl_SetResult(interp, "Not a valid network.", NULL);
1605 return TCL_ERROR;
1607 break;
1609 case GetIdx: { /* return hierarchical name of selected network */
1610 int stype, netid, lbus;
1611 Boolean uplevel, hier, canon;
1612 char *prefix = NULL;
1613 Matrix locctm;
1614 short *newselect;
1615 Genericlist *netlist;
1616 CalllistPtr calls;
1617 objinstptr refinstance;
1618 objectptr refobject;
1619 XPoint refpoint, *refptptr;
1620 stringpart *ppin;
1621 char *snew;
1622 buslist *sbus;
1623 Tcl_Obj *tlist;
1625 option = Tcl_GetString(objv[objc - 1]);
1626 uplevel = FALSE;
1627 hier = FALSE;
1628 canon = FALSE;
1629 quiet = FALSE;
1630 while (option[0] == '-') {
1631 if (!strncmp(option + 1, "up", 2)) {
1632 uplevel = TRUE;
1634 else if (!strncmp(option + 1, "hier", 4)) {
1635 hier = TRUE;
1637 else if (!strncmp(option + 1, "canon", 5)) {
1638 canon = TRUE;
1640 else if (!strncmp(option + 1, "quiet", 5)) {
1641 quiet = TRUE;
1643 else if (sscanf(option, "%hd", &refpoint.x) == 1) {
1644 break; /* This is probably a negative point position! */
1646 objc--;
1647 option = Tcl_GetString(objv[objc - 1]);
1650 refinstance = (areawin->hierstack) ? areawin->hierstack->thisinst
1651 : areawin->topinstance;
1653 if (uplevel) {
1654 if (areawin->hierstack == NULL) {
1655 if (areawin->stack == NULL) {
1656 if (quiet) return TCL_OK;
1657 Fprintf(stderr, "Option \"up\" used, but current page is the"
1658 " top of the schematic\n");
1659 return TCL_ERROR;
1661 else {
1662 UResetCTM(&locctm);
1663 UPreMultCTM(&locctm, refinstance->position, refinstance->scale,
1664 refinstance->rotation);
1665 refinstance = areawin->stack->thisinst;
1666 refobject = refinstance->thisobject;
1669 else {
1670 if (areawin->hierstack->next == NULL) {
1671 if (quiet) return TCL_OK;
1672 Fprintf(stderr, "Option \"up\" used, but current page is the"
1673 " top of the drawing stack\n");
1674 return TCL_ERROR;
1676 else {
1677 UResetCTM(&locctm);
1678 UPreMultCTM(&locctm, refinstance->position, refinstance->scale,
1679 refinstance->rotation);
1680 refinstance = areawin->hierstack->next->thisinst;
1681 refobject = refinstance->thisobject;
1685 else {
1686 refobject = topobject;
1688 if ((objc != 2) && (objc != 3)) {
1689 Tcl_WrongNumArgs(interp, 1, objv,
1690 "get [selected|here|<name>] [-up][-hier][-canon][-quiet]");
1691 return TCL_ERROR;
1693 if ((objc == 3) && !strcmp(Tcl_GetString(objv[2]), "here")) {
1694 /* If "here", make a selection. */
1695 areawin->save = UGetCursorPos();
1696 newselect = select_element(POLYGON | LABEL | OBJINST);
1697 objc--;
1699 if ((objc == 2) || (!strcmp(Tcl_GetString(objv[2]), "selected"))) {
1700 /* If no argument, or "selected", use the selected element */
1701 newselect = areawin->selectlist;
1702 if (areawin->selects == 0) {
1703 if (hier) {
1704 Tcl_SetResult(interp, GetHierarchy(&areawin->stack, canon),
1705 TCL_DYNAMIC);
1706 break;
1708 else {
1709 Fprintf(stderr, "Either select an element or use \"-hier\"\n");
1710 return TCL_ERROR;
1713 if (areawin->selects != 1) {
1714 Fprintf(stderr, "Choose only one network element\n");
1715 return TCL_ERROR;
1717 else {
1718 stype = SELECTTYPE(newselect);
1719 if (stype == LABEL) {
1720 labelptr nlabel = SELTOLABEL(newselect);
1721 refptptr = &(nlabel->position);
1722 if ((nlabel->pin != LOCAL) && (nlabel->pin != GLOBAL)) {
1723 Fprintf(stderr, "Selected label is not a pin\n");
1724 return TCL_ERROR;
1727 else if (stype == POLYGON) {
1728 polyptr npoly = SELTOPOLY(newselect);
1729 refptptr = npoly->points;
1730 if (nonnetwork(npoly)) {
1731 Fprintf(stderr, "Selected polygon is not a wire\n");
1732 return TCL_ERROR;
1735 else if (stype == OBJINST) {
1736 objinstptr ninst = SELTOOBJINST(newselect);
1737 char *devptr;
1739 for (calls = topobject->calls; calls != NULL; calls = calls->next)
1740 if (calls->callinst == ninst)
1741 break;
1742 if (calls == NULL) {
1743 Fprintf(stderr, "Selected instance is not a circuit component\n");
1744 return TCL_ERROR;
1746 else if (calls->devindex == -1) {
1747 cleartraversed(topobject);
1748 resolve_indices(topobject, FALSE);
1750 push_stack(&areawin->stack, ninst, NULL);
1751 prefix = GetHierarchy(&areawin->stack, canon);
1752 pop_stack(&areawin->stack);
1753 if (prefix == NULL) break;
1754 devptr = prefix;
1755 if (!hier) {
1756 devptr = strrchr(prefix, '/');
1757 if (devptr == NULL)
1758 devptr = prefix;
1759 else
1760 devptr++;
1762 Tcl_SetResult(interp, devptr, TCL_VOLATILE);
1763 free(prefix);
1764 break;
1768 else if ((objc == 3) && (result = GetPositionFromList(interp, objv[2],
1769 &refpoint)) == TCL_OK) {
1770 /* Find net at indicated position in reference object. */
1771 /* This allows us to query points without generating a pin */
1772 /* at the position, which can alter the netlist under */
1773 /* observation. */
1774 refptptr = &refpoint;
1776 else {
1777 /* If a name, find the pin label element matching the name */
1778 int x, y;
1779 objinstptr instofname = (areawin->hierstack) ?
1780 areawin->hierstack->thisinst :
1781 areawin->topinstance;
1783 Tcl_ResetResult(interp);
1785 if (NameToPinLocation(instofname, Tcl_GetString(objv[2]),
1786 &x, &y) == 0) {
1787 refpoint.x = x; /* conversion from int to short */
1788 refpoint.y = y;
1789 refptptr = &refpoint;
1791 else {
1792 /* This is not necessarily an error. Use "-quiet" to shut it up */
1793 if (quiet) return TCL_OK;
1794 Tcl_SetResult(interp, "Cannot find position for pin ", NULL);
1795 Tcl_AppendElement(interp, Tcl_GetString(objv[2]));
1796 return TCL_ERROR;
1800 /* Now that we have a reference point, convert it to a netlist */
1801 if (uplevel) {
1802 UTransformbyCTM(&locctm, refptptr, &refpoint, 1);
1803 refptptr = &refpoint;
1805 netlist = pointtonet(refobject, refinstance, refptptr);
1806 if (netlist == NULL) {
1807 if (quiet) return TCL_OK;
1808 Fprintf(stderr, "Error: No network found!\n");
1809 return TCL_ERROR;
1812 /* If refobject is a secondary schematic, we need to find the */
1813 /* corresponding primary page to call nettopin(). */
1814 master = (refobject->schemtype == SECONDARY) ?
1815 refobject->symschem : refobject;
1817 /* Now that we have a netlist, convert it to a name */
1818 /* Need to get prefix from the current call stack so we */
1819 /* can represent flat names as well as hierarchical names. */
1821 if (hier) {
1822 int plen;
1823 prefix = GetHierarchy(&areawin->stack, canon);
1824 if (prefix) {
1825 plen = strlen(prefix);
1826 if (*(prefix + plen - 1) != '/') {
1827 prefix = realloc(prefix, plen + 2);
1828 strcat(prefix, "/");
1833 if (netlist->subnets == 0) {
1834 netid = netlist->net.id;
1835 ppin = nettopin(netid, master, (prefix == NULL) ? "" : prefix);
1836 snew = textprint(ppin, refinstance);
1837 Tcl_SetResult(interp, snew, TCL_DYNAMIC);
1839 else if (netlist->subnets == 1) {
1841 /* Need to get prefix from the current call stack! */
1842 sbus = netlist->net.list;
1843 netid = sbus->netid;
1844 ppin = nettopin(netid, master, (prefix == NULL) ? "" : prefix);
1845 snew = textprintsubnet(ppin, refinstance, sbus->subnetid);
1846 Tcl_SetResult(interp, snew, TCL_DYNAMIC);
1848 else {
1849 tlist = Tcl_NewListObj(0, NULL);
1850 for (lbus = 0; lbus < netlist->subnets; lbus++) {
1851 sbus = netlist->net.list + lbus;
1852 netid = sbus->netid;
1853 ppin = nettopin(netid, master, (prefix == NULL) ? "" : prefix);
1854 snew = textprintsubnet(ppin, refinstance, sbus->subnetid);
1855 Tcl_ListObjAppendElement(interp, tlist, Tcl_NewStringObj(snew, -1));
1856 Tcl_SetObjResult(interp, tlist);
1857 free(snew);
1860 if (prefix != NULL) free(prefix);
1861 } break;
1863 case ParseIdx: { /* generate output from info labels */
1864 char *mode, *snew;
1865 objectptr cfrom;
1867 if (objc != 3) {
1868 Tcl_WrongNumArgs(interp, 1, objv, "parse <mode>");
1869 return TCL_ERROR;
1871 mode = Tcl_GetString(objv[2]);
1872 master = topobject;
1873 if ((master->schemtype == SECONDARY) && (master->symschem != NULL))
1874 master = master->symschem;
1876 if (master->schemtype != PRIMARY && areawin->stack != NULL) {
1877 cfrom = areawin->stack->thisinst->thisobject;
1878 snew = parseinfo(cfrom, master, cfrom->calls, NULL, mode, FALSE, TRUE);
1880 else {
1881 Calllist loccalls;
1883 loccalls.cschem = NULL;
1884 loccalls.callobj = master;
1885 loccalls.callinst = areawin->topinstance;
1886 loccalls.devindex = -1;
1887 loccalls.ports = NULL;
1888 loccalls.next = NULL;
1890 snew = parseinfo(NULL, master, &loccalls, NULL, mode, FALSE, TRUE);
1892 Tcl_SetResult(interp, snew, TCL_DYNAMIC);
1894 } break;
1896 case UnConnectIdx: /* disassociate the page with another one */
1897 if ((objc != 2) && (objc != 3)) {
1898 Tcl_WrongNumArgs(interp, 1, objv, "unconnect [<secondary>]");
1899 return TCL_ERROR;
1901 else if (objc == 3) {
1902 result = Tcl_GetIntFromObj(interp, objv[2], &spage);
1903 if (result != TCL_OK) {
1904 Tcl_ResetResult(interp);
1905 slave = NameToPageObject(Tcl_GetString(objv[2]), NULL, &spage);
1907 else {
1908 if (spage >= xobjs.pages) {
1909 Tcl_SetResult(interp, "Bad page number for secondary schematic", NULL);
1910 return TCL_ERROR;
1912 slave = xobjs.pagelist[spage]->pageinst->thisobject;
1914 if ((slave == NULL) || (is_page(slave) < 0)) {
1915 Tcl_SetResult(interp, "Error determining secondary schematic", NULL);
1916 return TCL_ERROR;
1919 else {
1920 slave = topobject;
1921 spage = areawin->page;
1923 if (slave->symschem == NULL || slave->symschem->schemtype !=
1924 PRIMARY) {
1925 Tcl_SetResult(interp, "Page is not a secondary schematic", NULL);
1926 return TCL_ERROR;
1929 destroynets(slave->symschem);
1930 slave->schemtype = PRIMARY;
1931 slave->symschem = NULL;
1932 break;
1934 case ConnectIdx: /* associate the page with another one */
1935 if ((objc != 3) && (objc != 4)) {
1936 Tcl_WrongNumArgs(interp, 1, objv, "connect <primary> [<secondary>]");
1937 return TCL_ERROR;
1939 else if (objc == 4) {
1940 result = Tcl_GetIntFromObj(interp, objv[3], &spage);
1941 if (result != TCL_OK) {
1942 Tcl_ResetResult(interp);
1943 slave = NameToPageObject(Tcl_GetString(objv[3]), NULL, &spage);
1945 else {
1946 if (spage >= xobjs.pages) {
1947 Tcl_SetResult(interp, "Bad page number for secondary schematic", NULL);
1948 return TCL_ERROR;
1950 slave = xobjs.pagelist[spage]->pageinst->thisobject;
1952 if ((slave == NULL) || (is_page(slave) < 0)) {
1953 Tcl_SetResult(interp, "Error determining secondary schematic", NULL);
1954 return TCL_ERROR;
1957 else {
1958 slave = topobject;
1959 spage = areawin->page;
1960 destroynets(slave);
1963 result = Tcl_GetIntFromObj(interp, objv[2], &mpage);
1964 if (result != TCL_OK) {
1965 Tcl_ResetResult(interp);
1966 master = NameToPageObject(Tcl_GetString(objv[2]), NULL, &mpage);
1968 else
1969 mpage--;
1971 if ((mpage >= xobjs.pages) || (xobjs.pagelist[mpage]->pageinst == NULL)) {
1972 Tcl_SetResult(interp, "Bad page number for master schematic", NULL);
1973 return TCL_ERROR;
1975 else if (mpage == areawin->page) {
1976 Tcl_SetResult(interp, "Attempt to specify schematic "
1977 "as its own master", NULL);
1978 return TCL_ERROR;
1980 if (xobjs.pagelist[mpage]->pageinst->thisobject->symschem == slave) {
1981 Tcl_SetResult(interp, "Attempt to create recursive "
1982 "primary/secondary schematic relationship", NULL);
1983 return TCL_ERROR;
1985 master = xobjs.pagelist[mpage]->pageinst->thisobject;
1986 destroynets(master);
1988 if ((master == NULL) || (is_page(master) < 0)) {
1989 Tcl_SetResult(interp, "Error determining master schematic", NULL);
1990 return TCL_ERROR;
1993 slave->schemtype = SECONDARY;
1994 slave->symschem = master;
1995 break;
1997 case UnHighLightIdx: /* remove network connectivity highlight */
1998 if (objc == 2) {
1999 highlightnetlist(topobject, areawin->topinstance, 0);
2001 else {
2002 Tcl_WrongNumArgs(interp, 1, objv, "(no options)");
2003 return TCL_ERROR;
2005 break;
2007 case HighLightIdx: /* highlight network connectivity */
2008 if (objc == 2) {
2009 startconnect(NULL, NULL, NULL);
2010 break;
2012 /* drop through */
2013 case PositionIdx:
2014 case SelectIdx: /* select the first element in the indicated net */
2016 int netid, lbus, i;
2017 XPoint newpos, *netpos;
2018 char *tname;
2019 Genericlist *lnets, *netlist;
2020 buslist *sbus;
2021 LabellistPtr llist;
2022 PolylistPtr plist;
2023 short *newselect;
2025 if (objc < 3) {
2026 Tcl_WrongNumArgs(interp, 1, objv, "network");
2027 return TCL_ERROR;
2030 result = GetPositionFromList(interp, objv[2], &newpos);
2031 if (result == TCL_OK) { /* find net at indicated position */
2032 areawin->save = newpos;
2033 connectivity(NULL, NULL, NULL);
2034 /* should there be any result here? */
2035 break;
2037 else { /* assume objv[2] is net name */
2038 Tcl_ResetResult(interp);
2039 tname = Tcl_GetString(objv[2]);
2040 lnets = nametonet(topobject, areawin->topinstance, tname);
2041 if (lnets == NULL) {
2042 Tcl_SetResult(interp, "No such network ", NULL);
2043 Tcl_AppendElement(interp, tname);
2044 break;
2046 switch (idx) {
2047 case HighLightIdx:
2048 netlist = (Genericlist *)malloc(sizeof(Genericlist));
2050 /* Erase any existing highlights first */
2051 highlightnetlist(topobject, areawin->topinstance, 0);
2052 netlist->subnets = 0;
2053 copy_bus(netlist, lnets);
2054 topobject->highlight.netlist = netlist;
2055 topobject->highlight.thisinst = areawin->topinstance;
2056 highlightnetlist(topobject, areawin->topinstance, 1);
2057 if (netlist->subnets == 0) {
2058 netid = netlist->net.id;
2059 Tcl_SetObjResult(interp, Tcl_NewIntObj(netlist->net.id));
2061 else {
2062 rdict = Tcl_NewListObj(0, NULL);
2063 for (lbus = 0; lbus < netlist->subnets; lbus++) {
2064 sbus = netlist->net.list + lbus;
2065 netid = sbus->netid;
2066 Tcl_ListObjAppendElement(interp, rdict, Tcl_NewIntObj(netid));
2068 Tcl_SetObjResult(interp, rdict);
2070 break;
2072 /* Return a position belonging to the net. If this is a bus, */
2073 /* we return the position of the 1st subnet. At some point, */
2074 /* this should be expanded to return a point per subnet. */
2076 case PositionIdx:
2077 if (lnets->subnets == 0)
2078 netid = lnets->net.id;
2079 else
2080 netid = (lnets->net.list)->netid;
2082 netpos = NetToPosition(lnets->net.id, topobject);
2083 rdict = Tcl_NewListObj(0, NULL);
2084 Tcl_ListObjAppendElement(interp, rdict, Tcl_NewIntObj(netpos->x));
2085 Tcl_ListObjAppendElement(interp, rdict, Tcl_NewIntObj(netpos->y));
2086 Tcl_SetObjResult(interp, rdict);
2087 break;
2089 /* Select everything in the network. To-do: allow specific */
2090 /* selection of labels, wires, or a single element in the net */
2092 case SelectIdx:
2093 unselect_all();
2094 rdict = Tcl_NewListObj(0, NULL);
2095 for (llist = topobject->labels; llist != NULL;
2096 llist = llist->next) {
2097 if (match_buses((Genericlist *)llist, (Genericlist *)lnets, 0)) {
2098 i = GetPartNumber((genericptr)llist->label, topobject, LABEL);
2099 if (i >= 0) {
2100 newselect = allocselect();
2101 *newselect = i;
2102 Tcl_ListObjAppendElement(interp, rdict,
2103 Tcl_NewHandleObj((genericptr)llist->label));
2107 for (plist = topobject->polygons; plist != NULL;
2108 plist = plist->next) {
2109 if (match_buses((Genericlist *)plist, (Genericlist *)lnets, 0)) {
2110 i = GetPartNumber((genericptr)plist->poly, topobject, POLYGON);
2111 if (i >= 0) {
2112 newselect = allocselect();
2113 *newselect = i;
2114 Tcl_ListObjAppendElement(interp, rdict,
2115 Tcl_NewHandleObj((genericptr)plist->poly));
2119 Tcl_SetObjResult(interp, rdict);
2120 refresh(NULL, NULL, NULL);
2121 break;
2124 } break;
2126 case UpdateIdx: /* destroy and regenerate the current netlist */
2127 destroynets(areawin->topinstance->thisobject);
2128 if ((result = updatenets(areawin->topinstance, quiet)) < 0) {
2129 Tcl_SetResult(interp, "Check circuit for infinite recursion.", NULL);
2130 return TCL_ERROR;
2132 else if (result == 0) {
2133 Tcl_SetResult(interp, "Failure to generate a network.", NULL);
2134 return TCL_ERROR;
2136 break;
2138 case MakeIdx: /* generate Tcl-list netlist */
2139 rdict = Tcl_NewListObj(0, NULL);
2140 Tcl_ListObjAppendElement(interp, rdict, Tcl_NewStringObj("globals", 7));
2141 Tcl_ListObjAppendElement(interp, rdict, tclglobals(areawin->topinstance));
2142 Tcl_ListObjAppendElement(interp, rdict, Tcl_NewStringObj("circuit", 7));
2143 Tcl_ListObjAppendElement(interp, rdict, tcltoplevel(areawin->topinstance));
2145 Tcl_SetObjResult(interp, rdict);
2146 break;
2148 case AutoNumberIdx: /* auto-number circuit components */
2149 if (checkvalid(topobject) == -1) {
2150 destroynets(topobject);
2151 createnets(areawin->topinstance, FALSE);
2153 else {
2154 cleartraversed(topobject);
2155 clear_indices(topobject);
2157 if ((objc == 3) && !strcmp(Tcl_GetString(objv[2]), "-forget")) {
2158 cleartraversed(topobject);
2159 unnumber(topobject);
2161 else {
2162 cleartraversed(topobject);
2163 resolve_indices(topobject, FALSE); /* Do fixed assignments first */
2164 cleartraversed(topobject);
2165 resolve_indices(topobject, TRUE); /* Now do the auto-numbering */
2167 break;
2169 case RatsNestIdx:
2170 /* Experimental netlist stuff! */
2171 ratsnest(areawin->topinstance);
2172 break;
2174 return XcTagCallback(interp, objc, objv);
2177 /*----------------------------------------------------------------------*/
2178 /* Return current position */
2179 /*----------------------------------------------------------------------*/
2181 int xctcl_here(ClientData clientData, Tcl_Interp *interp,
2182 int objc, Tcl_Obj *CONST objv[])
2184 Tcl_Obj *listPtr, *objPtr;
2185 XPoint newpos;
2187 if (objc != 1) {
2188 Tcl_WrongNumArgs(interp, 0, objv, "(no arguments)");
2189 return TCL_ERROR;
2191 newpos = UGetCursorPos();
2193 listPtr = Tcl_NewListObj(0, NULL);
2194 objPtr = Tcl_NewIntObj((int)newpos.x);
2195 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
2197 objPtr = Tcl_NewIntObj((int)newpos.y);
2198 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
2200 Tcl_SetObjResult(interp, listPtr);
2202 return XcTagCallback(interp, objc, objv);
2206 /*----------------------------------------------------------------------*/
2207 /* Argument-converting wrappers from Tcl command callback to xcircuit */
2208 /*----------------------------------------------------------------------*/
2210 int xctcl_pan(ClientData clientData, Tcl_Interp *interp,
2211 int objc, Tcl_Obj *CONST objv[])
2213 int result, idx;
2214 double frac = 0.0;
2215 XPoint newpos, wpoint;
2216 static char *directions[] = {"here", "left", "right", "up", "down",
2217 "center", "follow", NULL};
2218 enum DirIdx {
2219 DirHere, DirLeft, DirRight, DirUp, DirDown, DirCenter, DirFollow
2222 if (objc != 2 && objc != 3) {
2223 Tcl_WrongNumArgs(interp, 0, objv, "option ?arg ...?");
2224 return TCL_ERROR;
2227 /* Check against keywords */
2229 if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)directions,
2230 "option", 0, &idx) != TCL_OK) {
2231 result = GetPositionFromList(interp, objv[1], &newpos);
2232 if (result != TCL_OK) return result;
2233 idx = 5;
2235 else
2236 newpos = UGetCursorPos();
2238 user_to_window(newpos, &wpoint);
2240 switch(idx) {
2241 case DirHere:
2242 case DirCenter:
2243 case DirFollow:
2244 if (objc != 2) {
2245 Tcl_WrongNumArgs(interp, 0, objv, "(no arguments)");
2247 break;
2248 default:
2249 if (objc == 2) frac = 0.3;
2250 else
2251 Tcl_GetDoubleFromObj(interp, objv[2], &frac);
2254 panbutton((u_int)idx, wpoint.x, wpoint.y, (float)frac);
2255 return XcTagCallback(interp, objc, objv);
2258 /*----------------------------------------------------------------------*/
2260 int xctcl_zoom(ClientData clientData, Tcl_Interp *interp,
2261 int objc, Tcl_Obj *CONST objv[])
2263 int result, idx;
2264 float save;
2265 double factor;
2266 XPoint newpos, wpoint;
2268 static char *subCmds[] = {"in", "out", "view", "factor", NULL};
2269 enum SubIdx {
2270 InIdx, OutIdx, ViewIdx, FactorIdx
2273 newpos = UGetCursorPos();
2274 user_to_window(newpos, &wpoint);
2276 if (objc == 1)
2277 zoomview(NULL, NULL, NULL);
2278 else if ((result = Tcl_GetDoubleFromObj(interp, objv[1], &factor)) != TCL_OK)
2280 Tcl_ResetResult(interp);
2281 if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)subCmds,
2282 "option", 0, &idx) != TCL_OK) {
2283 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
2284 return TCL_ERROR;
2286 switch(idx) {
2287 case InIdx:
2288 zoominrefresh(wpoint.x, wpoint.y);
2289 break;
2290 case OutIdx:
2291 zoomoutrefresh(wpoint.x, wpoint.y);
2292 break;
2293 case ViewIdx:
2294 zoomview(NULL, NULL, NULL);
2295 break;
2296 case FactorIdx:
2297 if (objc == 2) {
2298 Tcl_Obj *objPtr = Tcl_NewDoubleObj((double)areawin->zoomfactor);
2299 Tcl_SetObjResult(interp, objPtr);
2300 break;
2302 else if (objc != 3) {
2303 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
2304 return TCL_ERROR;
2306 if (!strcmp(Tcl_GetString(objv[2]), "default"))
2307 factor = SCALEFAC;
2308 else {
2309 result = Tcl_GetDoubleFromObj(interp, objv[2], &factor);
2310 if (result != TCL_OK) return result;
2311 if (factor <= 0) {
2312 Tcl_SetResult(interp, "Negative/Zero zoom factors not allowed.",
2313 NULL);
2314 return TCL_ERROR;
2316 if (factor < 1.0) factor = 1.0 / factor;
2318 if ((float)factor == areawin->zoomfactor) break;
2319 Wprintf("Zoom factor changed from %2.1f to %2.1f",
2320 areawin->zoomfactor, (float)factor);
2321 areawin->zoomfactor = (float) factor;
2322 break;
2325 else {
2326 save = areawin->zoomfactor;
2328 if (factor < 1.0) {
2329 areawin->zoomfactor = (float)(1.0 / factor);
2330 zoomout(wpoint.x, wpoint.y);
2332 else {
2333 areawin->zoomfactor = (float)factor;
2334 zoomin(wpoint.x, wpoint.y);
2336 refresh(NULL, NULL, NULL);
2337 areawin->zoomfactor = save;
2339 return XcTagCallback(interp, objc, objv);
2342 /*----------------------------------------------------------------------*/
2343 /* Get a color, either by name or by integer index. */
2344 /* If "append" is TRUE, then if the color is not in the existing list */
2345 /* of colors, it will be added to the list. */
2346 /*----------------------------------------------------------------------*/
2348 int GetColorFromObj(Tcl_Interp *interp, Tcl_Obj *obj, int *cindex, Boolean append)
2350 char *cname;
2351 int result;
2353 if (cindex == NULL) return TCL_ERROR;
2355 cname = Tcl_GetString(obj);
2356 if (!strcmp(cname, "inherit")) {
2357 *cindex = DEFAULTCOLOR;
2359 else {
2360 result = Tcl_GetIntFromObj(interp, obj, cindex);
2361 if (result != TCL_OK) {
2362 Tcl_ResetResult(interp);
2363 *cindex = query_named_color(cname);
2364 if (*cindex == BADCOLOR) {
2365 *cindex = ERRORCOLOR;
2366 Tcl_SetResult(interp, "Unknown color name ", NULL);
2367 Tcl_AppendElement(interp, cname);
2368 return TCL_ERROR;
2370 else if (*cindex == ERRORCOLOR) {
2371 if (append)
2372 *cindex = addnewcolorentry(xc_alloccolor(cname));
2373 else {
2374 Tcl_SetResult(interp, "Color ", NULL);
2375 Tcl_AppendElement(interp, cname);
2376 Tcl_AppendElement(interp, "is not in the color table.");
2377 return TCL_ERROR;
2380 return TCL_OK;
2383 if ((*cindex >= number_colors) || (*cindex < DEFAULTCOLOR)) {
2384 Tcl_SetResult(interp, "Color index out of range", NULL);
2385 return TCL_ERROR;
2388 return TCL_OK;
2391 /*----------------------------------------------------------------------*/
2393 int xctcl_color(ClientData clientData, Tcl_Interp *interp,
2394 int objc, Tcl_Obj *CONST objv[])
2396 int result, nidx, cindex, ccol, idx, i;
2397 char *colorname, *option;
2399 static char *subCmds[] = {"set", "index", "value", "get", "add",
2400 "override", NULL};
2401 enum SubIdx { SetIdx, IndexIdx, ValueIdx, GetIdx, AddIdx, OverrideIdx };
2403 nidx = 2;
2404 result = ParseElementArguments(interp, objc, objv, &nidx, ALL_TYPES);
2405 if (result != TCL_OK) return result;
2407 if ((result = Tcl_GetIndexFromObj(interp, objv[nidx],
2408 (CONST84 char **)subCmds, "option", 0,
2409 &idx)) != TCL_OK)
2410 return result;
2412 switch (idx) {
2413 case SetIdx:
2414 if ((objc - nidx) == 2) {
2415 result = GetColorFromObj(interp, objv[nidx + 1], &cindex, TRUE);
2416 if (result != TCL_OK) return result;
2417 setcolor((Tk_Window)clientData, cindex);
2418 /* Tag callback performed by setcolormarks() via setcolor() */
2419 return TCL_OK;
2421 else {
2422 Tcl_WrongNumArgs(interp, 1, objv, "set <color> | inherit");
2423 return TCL_ERROR;
2425 break;
2427 case IndexIdx:
2428 /* Return the index of the color. For use with parameterized color */
2429 if ((objc - nidx) == 2) {
2430 result = GetColorFromObj(interp, objv[nidx + 1], &cindex, TRUE);
2431 if (result != TCL_OK) return result;
2432 Tcl_SetObjResult(interp, Tcl_NewIntObj(cindex));
2433 return TCL_OK;
2435 else {
2436 Tcl_WrongNumArgs(interp, 1, objv, "index <color> | inherit");
2437 return TCL_ERROR;
2439 break;
2441 case ValueIdx:
2442 /* Return the value of the color as an {R G B} list */
2443 if ((objc - nidx) == 2) {
2444 result = GetColorFromObj(interp, objv[nidx + 1], &cindex, TRUE);
2445 if (result != TCL_OK) return result;
2446 else if (cindex < 0 || cindex >= number_colors) {
2447 Tcl_SetResult(interp, "Color index out of range", NULL);
2448 return TCL_ERROR;
2450 Tcl_SetObjResult(interp, TclIndexToRGB(cindex));
2451 return TCL_OK;
2453 else {
2454 Tcl_WrongNumArgs(interp, 1, objv, "value <color>");
2455 return TCL_ERROR;
2457 break;
2459 case GetIdx:
2460 /* Check for "-all" switch */
2461 if ((objc - nidx) == 2) {
2462 option = Tcl_GetString(objv[nidx + 1]);
2463 if (!strncmp(option, "-all", 4)) {
2464 for (i = NUMBER_OF_COLORS; i < number_colors; i++) {
2465 char colorstr[14];
2466 sprintf(colorstr, "#%04x%04x%04x",
2467 colorlist[i].color.red,
2468 colorlist[i].color.green,
2469 colorlist[i].color.blue);
2470 Tcl_AppendElement(interp, colorstr);
2473 else {
2474 Tcl_WrongNumArgs(interp, 1, objv, "get [-all]");
2475 return TCL_ERROR;
2477 break;
2480 if (areawin->selects > 0) { /* operation on element */
2481 genericptr genobj = SELTOGENERIC(areawin->selectlist);
2482 ccol = (int)genobj->color;
2484 else /* global setting */
2485 ccol = areawin->color;
2487 /* Find and return the index of the color */
2488 if (ccol == DEFAULTCOLOR)
2489 Tcl_SetObjResult(interp, Tcl_NewStringObj("inherit", 7));
2490 else {
2491 for (i = NUMBER_OF_COLORS; i < number_colors; i++)
2492 if (colorlist[i].color.pixel == ccol)
2493 break;
2494 Tcl_SetObjResult(interp, Tcl_NewIntObj(i));
2496 break;
2498 case AddIdx:
2499 if ((objc - nidx) == 2) {
2500 colorname = Tcl_GetString(objv[nidx + 1]);
2501 if (strlen(colorname) == 0) return TCL_ERROR;
2502 cindex = addnewcolorentry(xc_alloccolor(colorname));
2503 Tcl_SetObjResult(interp, Tcl_NewIntObj(cindex));
2505 else {
2506 Tcl_WrongNumArgs(interp, 1, objv, "add <color_name>");
2507 return TCL_ERROR;
2509 break;
2511 case OverrideIdx:
2512 flags |= COLOROVERRIDE;
2513 return TCL_OK; /* no tag callback */
2514 break;
2516 return XcTagCallback(interp, objc, objv);
2519 /*----------------------------------------------------------------------*/
2521 int xctcl_delete(ClientData clientData, Tcl_Interp *interp,
2522 int objc, Tcl_Obj *CONST objv[])
2524 int result = ParseElementArguments(interp, objc, objv, NULL, ALL_TYPES);
2526 if (result != TCL_OK) return result;
2528 /* delete element (call library delete if in catalog) */
2529 if (areawin->selects > 0) {
2530 if (eventmode == CATALOG_MODE)
2531 catdelete();
2532 else
2533 deletebutton(0, 0); /* Note: arguments are not used */
2536 return XcTagCallback(interp, objc, objv);
2539 /*----------------------------------------------------------------------*/
2540 /* Note that when using "undo series", it is the responsibility of the */
2541 /* caller to make sure that every "start" is matched by an "end". */
2542 /*----------------------------------------------------------------------*/
2544 int xctcl_undo(ClientData clientData, Tcl_Interp *interp,
2545 int objc, Tcl_Obj *CONST objv[])
2547 if ((objc == 3) && !strcmp(Tcl_GetString(objv[1]), "series")) {
2549 if (!strcmp(Tcl_GetString(objv[2]), "start")) {
2550 if (undo_collect < 255) undo_collect++;
2552 else if (!strcmp(Tcl_GetString(objv[2]), "end")) {
2553 if (undo_collect > 0) undo_collect--;
2554 undo_finish_series();
2556 else if (!strcmp(Tcl_GetString(objv[2]), "cancel")) {
2557 undo_collect = (u_char)0;
2558 undo_finish_series();
2560 else {
2561 Tcl_SetResult(interp, "Usage: undo series <start|end|cancel>", NULL);
2562 return TCL_ERROR;
2565 else if (objc == 1) {
2566 undo_action();
2568 else {
2569 Tcl_WrongNumArgs(interp, 1, objv, "[series <start|end>");
2570 return TCL_ERROR;
2572 return XcTagCallback(interp, objc, objv);
2575 /*----------------------------------------------------------------------*/
2577 int xctcl_redo(ClientData clientData, Tcl_Interp *interp,
2578 int objc, Tcl_Obj *CONST objv[])
2580 if (objc != 1) {
2581 Tcl_WrongNumArgs(interp, 1, objv, "(no arguments)");
2582 return TCL_ERROR;
2584 redo_action();
2585 return XcTagCallback(interp, objc, objv);
2588 /*----------------------------------------------------------------------*/
2590 int xctcl_move(ClientData clientData, Tcl_Interp *interp,
2591 int objc, Tcl_Obj *CONST objv[])
2593 XPoint position;
2594 int nidx = 3;
2595 int result = ParseElementArguments(interp, objc, objv, &nidx, ALL_TYPES);
2597 if (result != TCL_OK) return result;
2599 if (areawin->selects == 0) {
2600 Tcl_SetResult(interp, "Error in move setup: nothing selected.", NULL);
2601 return TCL_ERROR;
2604 if ((objc - nidx) == 0) {
2605 eventmode = MOVE_MODE;
2606 u2u_snap(&areawin->save);
2607 Tk_CreateEventHandler(areawin->area, PointerMotionMask,
2608 (Tk_EventProc *)xctk_drag, NULL);
2610 else if ((objc - nidx) >= 1) {
2611 if ((objc - nidx) == 2) {
2612 if (!strcmp(Tcl_GetString(objv[nidx]), "relative")) {
2613 if ((result = GetPositionFromList(interp, objv[nidx + 1],
2614 &position)) != TCL_OK) {
2615 Tcl_SetResult(interp, "Position must be {x y} list", NULL);
2616 return TCL_ERROR;
2619 else {
2620 Tcl_WrongNumArgs(interp, 1, objv, "relative {x y}");
2621 return TCL_ERROR;
2624 else {
2625 if ((result = GetPositionFromList(interp, objv[nidx],
2626 &position)) != TCL_OK) {
2627 Tcl_SetResult(interp, "Position must be {x y} list", NULL);
2628 return TCL_ERROR;
2630 position.x -= areawin->save.x;
2631 position.y -= areawin->save.y;
2633 placeselects(position.x, position.y, NULL);
2635 else {
2636 Tcl_WrongNumArgs(interp, 1, objv, "[relative] {x y}");
2637 return TCL_ERROR;
2639 return XcTagCallback(interp, objc, objv);
2642 /*----------------------------------------------------------------------*/
2644 int xctcl_copy(ClientData clientData, Tcl_Interp *interp,
2645 int objc, Tcl_Obj *CONST objv[])
2647 XPoint position;
2648 Tcl_Obj *listPtr;
2649 int nidx = 3;
2650 int result = ParseElementArguments(interp, objc, objv, &nidx, ALL_TYPES);
2652 if (result != TCL_OK) return result;
2654 if ((objc - nidx) == 0) {
2655 if (areawin->selects > 0) {
2656 createcopies();
2657 copydrag();
2660 else if ((objc - nidx) >= 1) {
2661 if (areawin->selects == 0) {
2662 Tcl_SetResult(interp, "Error in copy: nothing selected.", NULL);
2663 return TCL_ERROR;
2665 if ((objc - nidx) == 2) {
2666 if (!strcmp(Tcl_GetString(objv[nidx]), "relative")) {
2667 if ((result = GetPositionFromList(interp, objv[nidx + 1],
2668 &position)) != TCL_OK) {
2669 Tcl_SetResult(interp, "Position must be {x y} list", NULL);
2670 return TCL_ERROR;
2673 else {
2674 Tcl_WrongNumArgs(interp, 1, objv, "relative {x y}");
2675 return TCL_ERROR;
2678 else {
2679 if ((result = GetPositionFromList(interp, objv[nidx],
2680 &position)) != TCL_OK) {
2681 Tcl_SetResult(interp, "Position must be {x y} list", NULL);
2682 return TCL_ERROR;
2684 position.x -= areawin->save.x;
2685 position.y -= areawin->save.y;
2687 createcopies();
2689 listPtr = SelectToTclList(interp, areawin->selectlist, areawin->selects);
2690 Tcl_SetObjResult(interp, listPtr);
2692 placeselects(position.x, position.y, NULL);
2694 else {
2695 Tcl_WrongNumArgs(interp, 1, objv, "[relative] {x y}");
2696 return TCL_ERROR;
2698 return XcTagCallback(interp, objc, objv);
2701 /*----------------------------------------------------------------------*/
2703 int xctcl_flip(ClientData clientData, Tcl_Interp *interp,
2704 int objc, Tcl_Obj *CONST objv[])
2706 char *teststr;
2707 int nidx = 2;
2708 int result = ParseElementArguments(interp, objc, objv, &nidx, ALL_TYPES);
2709 XPoint position;
2711 if (result != TCL_OK) return result;
2713 if ((objc - nidx) == 2) {
2714 if ((result = GetPositionFromList(interp, objv[nidx + 1],
2715 &position)) != TCL_OK)
2716 return result;
2718 else if ((objc - nidx) == 1) {
2719 if (areawin->selects > 1)
2720 position = UGetCursorPos();
2722 else {
2723 Tcl_WrongNumArgs(interp, 1, objv, "horizontal|vertical [<center>]");
2724 return TCL_ERROR;
2727 teststr = Tcl_GetString(objv[nidx]);
2729 switch(teststr[0]) {
2730 case 'h': case 'H':
2731 elementflip(&position);
2732 break;
2733 case 'v': case 'V':
2734 elementvflip(&position);
2735 break;
2736 default:
2737 Tcl_SetResult(interp, "Error: options are horizontal or vertical", NULL);
2738 return TCL_ERROR;
2740 return XcTagCallback(interp, objc, objv);
2743 /*----------------------------------------------------------------------*/
2745 int xctcl_rotate(ClientData clientData, Tcl_Interp *interp,
2746 int objc, Tcl_Obj *CONST objv[])
2748 int rval, nidx = 2;
2749 int result = ParseElementArguments(interp, objc, objv, &nidx, ALL_TYPES);
2750 XPoint position;
2752 if (result != TCL_OK) return result;
2754 /* No options --- return the rotation value(s) */
2755 if ((objc - nidx) == 0) {
2756 int i, numfound = 0;
2757 Tcl_Obj *listPtr, *objPtr;
2758 for (i = 0; i < areawin->selects; i++) {
2759 objPtr = NULL;
2760 if (SELECTTYPE(areawin->selectlist + i) == OBJINST) {
2761 objinstptr pinst = SELTOOBJINST(areawin->selectlist + i);
2762 objPtr = Tcl_NewDoubleObj((double)(pinst->rotation));
2764 else if (SELECTTYPE(areawin->selectlist + i) == LABEL) {
2765 labelptr plab = SELTOLABEL(areawin->selectlist + i);
2766 objPtr = Tcl_NewDoubleObj((double)(plab->rotation));
2768 else if (SELECTTYPE(areawin->selectlist + i) == GRAPHIC) {
2769 graphicptr gp = SELTOGRAPHIC(areawin->selectlist + i);
2770 objPtr = Tcl_NewDoubleObj((double)(gp->rotation));
2772 if (objPtr != NULL) {
2773 if (numfound > 0)
2774 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
2775 if ((++numfound) == 1)
2776 listPtr = objPtr;
2779 switch (numfound) {
2780 case 0:
2781 Tcl_SetResult(interp, "Error: no object instances, graphic "
2782 "images, or labels selected", NULL);
2783 return TCL_ERROR;
2784 break;
2785 case 1:
2786 Tcl_SetObjResult(interp, objPtr);
2787 break;
2788 default:
2789 Tcl_SetObjResult(interp, listPtr);
2790 break;
2792 return XcTagCallback(interp, objc, objv);
2795 result = Tcl_GetIntFromObj(interp, objv[nidx], &rval);
2796 if (result != TCL_OK) return result;
2798 if ((objc - nidx) == 2) {
2799 if ((result = GetPositionFromList(interp, objv[nidx + 1],
2800 &position)) != TCL_OK)
2801 return result;
2802 else {
2803 elementrotate(rval, &position);
2804 return XcTagCallback(interp, objc, objv);
2807 else if ((objc - nidx) == 1) {
2808 position = UGetCursorPos();
2809 elementrotate(rval, &position);
2810 return XcTagCallback(interp, objc, objv);
2813 Tcl_WrongNumArgs(interp, 1, objv, "<angle> [<center>]");
2814 return TCL_ERROR;
2817 /*----------------------------------------------------------------------*/
2819 int xctcl_edit(ClientData clientData, Tcl_Interp *interp,
2820 int objc, Tcl_Obj *CONST objv[])
2822 int result = ParseElementArguments(interp, objc, objv, NULL, ALL_TYPES);
2824 if (result != TCL_OK) return result;
2826 /* To be done---edit element */
2828 return XcTagCallback(interp, objc, objv);
2831 /*----------------------------------------------------------------------*/
2832 /* Support procedure for xctcl_param: Given a pointer to a parameter, */
2833 /* return the value of the parameter as a pointer to a Tcl object. */
2834 /* This takes care of the fact that the parameter value can be a */
2835 /* string, integer, or float, depending on the parameter type. */
2836 /* */
2837 /* If "verbatim" is true, then expression parameters return the string */
2838 /* representation of the expression, not the result, and indirect */
2839 /* parameters return the parameter name referenced, not the value. */
2840 /* */
2841 /* refinst, if non-NULL, is the instance containing ops, used when */
2842 /* "verbatim" is true and the parameter is indirectly referenced. */
2843 /*----------------------------------------------------------------------*/
2845 Tcl_Obj *GetParameterValue(objectptr refobj, oparamptr ops, Boolean verbatim,
2846 objinstptr refinst)
2848 Tcl_Obj *robj;
2849 char *refkey;
2851 if (verbatim && (refinst != NULL) &&
2852 ((refkey = find_indirect_param(refinst, ops->key)) != NULL)) {
2853 robj = Tcl_NewStringObj(refkey, strlen(refkey));
2854 return robj;
2857 switch (ops->type) {
2858 case XC_STRING:
2859 robj = TclGetStringParts(ops->parameter.string);
2860 break;
2861 case XC_EXPR:
2862 if (verbatim)
2863 robj = Tcl_NewStringObj(ops->parameter.expr,
2864 strlen(ops->parameter.expr));
2865 else
2866 robj = evaluate_raw(refobj, ops, refinst, NULL);
2867 break;
2868 case XC_INT:
2869 robj = Tcl_NewIntObj(ops->parameter.ivalue);
2870 break;
2871 case XC_FLOAT:
2872 robj = Tcl_NewDoubleObj((double)ops->parameter.fvalue);
2873 break;
2875 return robj;
2878 /*----------------------------------------------------------------------*/
2879 /* Given a pointer to a parameter and a Tcl object, set the parameter */
2880 /* to the value of the object. Return the standard Tcl return type */
2881 /* */
2882 /* If searchinst is non-NULL, then it refers to the level above in the */
2883 /* hierarchy, and we are supposed to set an indirect reference. */
2884 /*----------------------------------------------------------------------*/
2886 int SetParameterValue(Tcl_Interp *interp, oparamptr ops, Tcl_Obj *objv)
2888 int result, ivalue;
2889 double dvalue;
2890 stringpart *strptr = NULL, *newpart;
2892 if (ops == NULL) {
2893 Tcl_SetResult(interp, "Cannot set parameter value", NULL);
2894 return TCL_ERROR;
2896 switch (ops->type) {
2897 case XC_FLOAT:
2898 result = Tcl_GetDoubleFromObj(interp, objv, &dvalue);
2899 if (result != TCL_OK) return result;
2900 ops->parameter.fvalue = (float)dvalue;
2901 break;
2902 case XC_INT:
2903 result = Tcl_GetIntFromObj(interp, objv, &ivalue);
2904 if (result != TCL_OK) return result;
2905 ops->parameter.ivalue = ivalue;
2906 break;
2907 case XC_EXPR:
2908 ops->parameter.expr = strdup(Tcl_GetString(objv));
2909 break;
2910 case XC_STRING:
2911 result = GetXCStringFromList(interp, objv, &strptr);
2912 if (result != TCL_OK) return result;
2913 freelabel(ops->parameter.string);
2914 /* Must add a "param end" */
2915 newpart = makesegment(&strptr, NULL);
2916 newpart->nextpart = NULL;
2917 newpart->type = PARAM_END;
2918 newpart->data.string = (u_char *)NULL;
2919 ops->parameter.string = strptr;
2920 break;
2922 return TCL_OK;
2925 /*----------------------------------------------------------------------*/
2926 /* Translate the numeric parameter types to a string that the Tcl */
2927 /* "parameter" routine will recognize from the command line. */
2928 /*----------------------------------------------------------------------*/
2930 char *
2931 translateparamtype(int type)
2933 const char *param_types[] = {"numeric", "substring", "x position",
2934 "y position", "style", "anchoring", "start angle", "end angle",
2935 "radius", "minor axis", "rotation", "scale", "linewidth", "color",
2936 "expression", "position", NULL};
2938 if (type < 0) return NULL;
2939 return (char *)param_types[type];
2942 /*----------------------------------------------------------------------*/
2943 /* Parameter command: */
2944 /* */
2945 /* Normally, a selected element will produce a list of backwards- */
2946 /* referenced parameters (eparam). However, it is useful to pick up */
2947 /* the forwards-referenced parameters of an object instance, so that */
2948 /* parameters can be modified from the level above (e.g., to change */
2949 /* circuit component values, component indices, etc.). The optional */
2950 /* final argument "-forward" can be used to access this mode. */
2951 /*----------------------------------------------------------------------*/
2953 int xctcl_param(ClientData clientData, Tcl_Interp *interp,
2954 int objc, Tcl_Obj *CONST objv[])
2956 int i, j, value, idx, nidx = 4;
2957 int result = ParseElementArguments(interp, objc, objv, &nidx, ALL_TYPES);
2958 oparamptr ops, instops;
2959 oparam temps;
2960 eparamptr epp;
2961 genericptr thiselem = NULL;
2962 Tcl_Obj *plist, *kpair, *exprres;
2963 objinstptr refinst;
2964 objectptr refobj;
2965 char *dash_opt;
2966 Boolean verbatim = FALSE, indirection = FALSE, forwarding = FALSE;
2968 static char *subCmds[] = {"allowed", "get", "type", "default", "set", "make",
2969 "replace", "forget", "delete", NULL};
2970 enum SubIdx {
2971 AllowedIdx, GetIdx, TypeIdx, DefaultIdx, SetIdx, MakeIdx, ReplaceIdx,
2972 ForgetIdx, DeleteIdx
2975 /* The order of these type names must match the enumeration in xcircuit.h */
2977 static char *param_types[] = {"numeric", "substring", "x position",
2978 "y position", "style", "anchoring", "start angle", "end angle",
2979 "radius", "minor axis", "rotation", "scale", "linewidth", "color",
2980 "expression", "position", NULL}; /* (jdk) */
2982 /* The first object instance in the select list becomes "thiselem", */
2983 /* if such exists. Otherwise, it remains null. */
2985 for (j = 0; j < areawin->selects; j++) {
2986 if (SELECTTYPE(areawin->selectlist + j) == OBJINST) {
2987 thiselem = SELTOGENERIC(areawin->selectlist + j);
2988 break;
2992 if (objc - nidx < 1)
2993 idx = GetIdx;
2994 else {
2995 dash_opt = Tcl_GetString(objv[nidx]);
2996 if (*dash_opt == '-')
2997 idx = GetIdx;
2998 else {
2999 if ((result = Tcl_GetIndexFromObj(interp, objv[nidx],
3000 (CONST84 char **)subCmds, "option", 0, &idx)) != TCL_OK)
3001 return result;
3005 /* Use the topobject by default */
3006 refinst = areawin->topinstance;
3007 refobj = topobject;
3009 /* command-line switches */
3011 dash_opt = Tcl_GetString(objv[objc - 1]);
3012 while (*dash_opt == '-') {
3014 /* If an object instance is selected, we list backwards-referenced */
3015 /* (eparam) parameters, unless the command ends in "-forward". */
3017 if (!strncmp(dash_opt + 1, "forw", 4)) {
3018 switch (idx) {
3019 case SetIdx:
3020 case GetIdx:
3021 case TypeIdx:
3022 case MakeIdx:
3023 case DeleteIdx:
3024 case ForgetIdx:
3025 case DefaultIdx:
3026 if (thiselem && IS_OBJINST(thiselem)) {
3027 refinst = (objinstptr)thiselem;
3028 refobj = refinst->thisobject;
3029 thiselem = NULL;
3030 forwarding = TRUE;
3032 break;
3035 else if (!strncmp(dash_opt + 1, "verb", 4)) {
3036 verbatim = TRUE;
3038 else if (!strncmp(dash_opt + 1, "ind", 3)) {
3039 indirection = TRUE;
3042 objc--;
3043 if (objc == 0) {
3044 Tcl_SetResult(interp, "Must have a valid option", NULL);
3045 return TCL_ERROR;
3047 dash_opt = Tcl_GetString(objv[objc - 1]);
3051 switch (idx) {
3052 case AllowedIdx:
3053 for (i = 0; i < (sizeof(param_types) / sizeof(char *)); i++)
3054 if ((thiselem == NULL) || (param_select[i] & thiselem->type))
3055 Tcl_AppendElement(interp, param_types[i]);
3057 break;
3059 case GetIdx:
3060 case TypeIdx:
3062 if (objc == nidx + 2) {
3064 /* Check argument against all parameter keys */
3065 ops = find_param(refinst, Tcl_GetString(objv[nidx + 1]));
3066 if (ops == NULL) {
3067 /* Otherwise, the argument must be a parameter type. */
3068 if ((result = Tcl_GetIndexFromObj(interp, objv[nidx + 1],
3069 (CONST84 char **)param_types, "parameter type",
3070 0, &value)) != TCL_OK) {
3071 Tcl_SetResult(interp, "Must have a valid key or parameter type",
3072 NULL);
3073 return result;
3077 /* Return the value of the indicated parameter */
3079 plist = Tcl_NewListObj(0, NULL);
3080 if (thiselem == NULL) {
3081 if (ops != NULL) {
3082 if (idx == GetIdx)
3083 Tcl_ListObjAppendElement(interp, plist,
3084 GetParameterValue(refobj, ops, verbatim, refinst));
3085 else
3086 Tcl_ListObjAppendElement(interp, plist,
3087 Tcl_NewStringObj(param_types[ops->which],
3088 strlen(param_types[ops->which])));
3090 else {
3091 for (ops = refobj->params; ops != NULL; ops = ops->next) {
3092 instops = find_param(refinst, ops->key);
3093 if (instops->which == value) {
3094 kpair = Tcl_NewListObj(0, NULL);
3095 Tcl_ListObjAppendElement(interp, kpair,
3096 Tcl_NewStringObj(instops->key, strlen(instops->key)));
3097 if (idx == GetIdx)
3098 Tcl_ListObjAppendElement(interp, kpair,
3099 GetParameterValue(refobj, instops, verbatim,
3100 refinst));
3101 else
3102 Tcl_ListObjAppendElement(interp, kpair,
3103 Tcl_NewStringObj(param_types[instops->which],
3104 strlen(param_types[instops->which])));
3105 Tcl_ListObjAppendElement(interp, plist, kpair);
3110 else {
3111 for (epp = thiselem->passed; epp != NULL; epp = epp->next) {
3112 instops = find_param(refinst, epp->key);
3113 if (instops->which == value) {
3114 if (idx == GetIdx)
3115 Tcl_ListObjAppendElement(interp, plist,
3116 GetParameterValue(refobj, instops, verbatim, refinst));
3117 else
3118 Tcl_ListObjAppendElement(interp, plist,
3119 Tcl_NewStringObj(param_types[instops->which],
3120 strlen(param_types[instops->which])));
3124 /* Search label for parameterized substrings. These are */
3125 /* backwards-referenced parameters, although they are */
3126 /* not stored in the eparam record of the label. */
3128 if ((value == P_SUBSTRING) && IS_LABEL(thiselem)) {
3129 stringpart *cstr;
3130 labelptr clab = (labelptr)thiselem;
3131 for (cstr = clab->string; cstr != NULL; cstr = cstr->nextpart) {
3132 if (cstr->type == PARAM_START) {
3133 kpair = Tcl_NewListObj(0, NULL);
3134 ops = find_param(refinst, cstr->data.string);
3135 Tcl_ListObjAppendElement(interp, kpair,
3136 Tcl_NewStringObj(ops->key, strlen(ops->key)));
3137 if (idx == GetIdx)
3138 Tcl_ListObjAppendElement(interp, kpair,
3139 GetParameterValue(refobj, ops, verbatim,
3140 refinst));
3141 else
3142 Tcl_ListObjAppendElement(interp, kpair,
3143 Tcl_NewStringObj(param_types[ops->which],
3144 strlen(param_types[ops->which])));
3145 Tcl_ListObjAppendElement(interp, plist, kpair);
3150 Tcl_SetObjResult(interp, plist);
3152 else {
3153 plist = Tcl_NewListObj(0, NULL);
3154 if (thiselem == NULL) {
3155 for (ops = refobj->params; ops != NULL; ops = ops->next) {
3156 kpair = Tcl_NewListObj(0, NULL);
3157 Tcl_ListObjAppendElement(interp, kpair,
3158 Tcl_NewStringObj(ops->key, strlen(ops->key)));
3159 if (idx == GetIdx) {
3160 instops = find_param(refinst, ops->key);
3161 Tcl_ListObjAppendElement(interp, kpair,
3162 GetParameterValue(refobj, instops, verbatim, refinst));
3164 else
3165 Tcl_ListObjAppendElement(interp, kpair,
3166 Tcl_NewStringObj(param_types[ops->which],
3167 strlen(param_types[ops->which])));
3168 Tcl_ListObjAppendElement(interp, plist, kpair);
3171 else {
3172 for (epp = thiselem->passed; epp != NULL; epp = epp->next) {
3173 kpair = Tcl_NewListObj(0, NULL);
3174 ops = find_param(refinst, epp->key);
3175 Tcl_ListObjAppendElement(interp, kpair,
3176 Tcl_NewStringObj(ops->key, strlen(ops->key)));
3177 if (idx == GetIdx)
3178 Tcl_ListObjAppendElement(interp, kpair,
3179 GetParameterValue(refobj, ops, verbatim, refinst));
3180 else
3181 Tcl_ListObjAppendElement(interp, kpair,
3182 Tcl_NewStringObj(param_types[ops->which],
3183 strlen(param_types[ops->which])));
3184 Tcl_ListObjAppendElement(interp, plist, kpair);
3187 /* Search label for parameterized substrings. These are */
3188 /* backwards-referenced parameters, although they are */
3189 /* not stored in the eparam record of the label. */
3191 if (IS_LABEL(thiselem)) {
3192 stringpart *cstr;
3193 labelptr clab = (labelptr)thiselem;
3194 for (cstr = clab->string; cstr != NULL; cstr = cstr->nextpart) {
3195 if (cstr->type == PARAM_START) {
3196 kpair = Tcl_NewListObj(0, NULL);
3197 ops = find_param(refinst, cstr->data.string);
3198 Tcl_ListObjAppendElement(interp, kpair,
3199 Tcl_NewStringObj(ops->key, strlen(ops->key)));
3200 if (idx == GetIdx)
3201 Tcl_ListObjAppendElement(interp, kpair,
3202 GetParameterValue(refobj, ops, verbatim,
3203 refinst));
3204 else
3205 Tcl_ListObjAppendElement(interp, kpair,
3206 Tcl_NewStringObj(param_types[ops->which],
3207 strlen(param_types[ops->which])));
3208 Tcl_ListObjAppendElement(interp, plist, kpair);
3213 Tcl_SetObjResult(interp, plist);
3215 break;
3217 case DefaultIdx:
3218 if (objc == nidx + 2) {
3219 /* Check against keys */
3220 ops = match_param(refobj, Tcl_GetString(objv[nidx + 1]));
3221 if (ops == NULL) {
3222 if ((result = Tcl_GetIndexFromObj(interp, objv[nidx + 1],
3223 (CONST84 char **)param_types, "parameter type",
3224 0, &value)) != TCL_OK) {
3225 Tcl_SetResult(interp, "Must have a valid key or parameter type",
3226 NULL);
3227 return result;
3230 else { /* get default value(s) */
3231 plist = Tcl_NewListObj(0, NULL);
3232 if (thiselem == NULL) {
3233 if (ops != NULL) {
3234 Tcl_ListObjAppendElement(interp, plist,
3235 GetParameterValue(refobj, ops, verbatim, refinst));
3237 else {
3238 for (ops = refobj->params; ops != NULL; ops = ops->next) {
3239 if (ops->which == value) {
3240 Tcl_ListObjAppendElement(interp, plist,
3241 GetParameterValue(refobj, ops, verbatim, refinst));
3246 else {
3247 for (epp = thiselem->passed; epp != NULL; epp = epp->next) {
3248 ops = match_param(refobj, epp->key);
3249 if (ops->which == value) {
3250 Tcl_ListObjAppendElement(interp, plist,
3251 GetParameterValue(refobj, ops, verbatim, refinst));
3255 /* search label for parameterized substrings */
3257 if ((value == P_SUBSTRING) && IS_LABEL(thiselem)) {
3258 stringpart *cstr;
3259 labelptr clab = (labelptr)thiselem;
3260 for (cstr = clab->string; cstr != NULL; cstr = cstr->nextpart) {
3261 if (cstr->type == PARAM_START) {
3262 ops = match_param(refobj, cstr->data.string);
3263 if (ops != NULL)
3264 Tcl_ListObjAppendElement(interp, plist,
3265 GetParameterValue(refobj, ops, verbatim,
3266 refinst));
3271 Tcl_SetObjResult(interp, plist);
3274 else if (objc == nidx + 1) { /* list all parameters and their defaults */
3275 plist = Tcl_NewListObj(0, NULL);
3276 for (ops = refobj->params; ops != NULL; ops = ops->next) {
3277 kpair = Tcl_NewListObj(0, NULL);
3278 Tcl_ListObjAppendElement(interp, kpair,
3279 Tcl_NewStringObj(ops->key, strlen(ops->key)));
3280 Tcl_ListObjAppendElement(interp, kpair,
3281 GetParameterValue(refobj, ops, verbatim, refinst));
3282 Tcl_ListObjAppendElement(interp, plist, kpair);
3284 Tcl_SetObjResult(interp, plist);
3286 else {
3287 Tcl_WrongNumArgs(interp, 1, objv, "default <type|key> [<value>]");
3288 return TCL_ERROR;
3290 break;
3292 case SetIdx: /* currently, instances only. . .*/
3293 if (objc == nidx + 3) { /* possibly to be expanded. . . */
3294 char *key = Tcl_GetString(objv[nidx + 1]);
3295 objinstptr searchinst = NULL;
3297 /* Allow option "set" to act on more than one selection */
3299 if (areawin->selects == 0) goto keycheck;
3301 while (j < areawin->selects) {
3303 refinst = SELTOOBJINST(areawin->selectlist + j);
3304 refobj = refinst->thisobject;
3306 /* Check against keys */
3307 keycheck:
3308 instops = match_instance_param(refinst, key);
3309 ops = match_param(refobj, key);
3310 if (instops == NULL) {
3311 if (ops == NULL) {
3312 if (!forwarding || (areawin->selects <= 1)) {
3313 Tcl_SetResult(interp, "Invalid key ", NULL);
3314 Tcl_AppendElement(interp, key);
3315 return TCL_ERROR;
3317 else
3318 goto next_param;
3320 copyparams(refinst, refinst);
3321 instops = match_instance_param(refinst, key);
3323 else if (ops->type == XC_EXPR) {
3324 /* If the expression is currently the default expression */
3325 /* but the instance value is holding the last evaluated */
3326 /* result, then we have to delete and regenerate the */
3327 /* existing instance parameter ("verbatim" assumed even */
3328 /* if not declared because you can't change the result */
3329 /* of the expression). */
3331 free_instance_param(refinst, instops);
3332 instops = copyparameter(ops);
3333 instops->next = refinst->params;
3334 refinst->params = instops;
3336 if (indirection) {
3337 char *refkey = Tcl_GetString(objv[nidx + 2]);
3339 if (refinst != areawin->topinstance)
3340 searchinst = areawin->topinstance;
3341 else if (areawin->stack) {
3342 searchinst = areawin->stack->thisinst;
3344 else {
3345 resolveparams(refinst);
3346 Tcl_SetResult(interp, "On top-level page: "
3347 "no indirection possible!", NULL);
3348 return TCL_ERROR;
3350 if (match_param(searchinst->thisobject, refkey) == NULL) {
3351 resolveparams(refinst);
3352 Tcl_SetResult(interp, "Invalid indirect reference key", NULL);
3353 return TCL_ERROR;
3355 /* Create an eparam record in the instance */
3356 epp = make_new_eparam(refkey);
3357 epp->flags |= P_INDIRECT;
3358 epp->pdata.refkey = strdup(key);
3359 epp->next = refinst->passed;
3360 refinst->passed = epp;
3362 else
3363 SetParameterValue(interp, instops, objv[nidx + 2]);
3364 resolveparams(refinst);
3366 /* Check if there are more selections to modify */
3368 next_param:
3369 if (!forwarding) break;
3370 while (++j != areawin->selects)
3371 if (SELECTTYPE(areawin->selectlist + j) == OBJINST)
3372 break;
3375 /* Redraw everything (this could be finessed. . .) */
3376 areawin->redraw_needed = True;
3377 drawarea(areawin->area, (caddr_t)NULL, (caddr_t)NULL);
3379 else {
3380 Tcl_WrongNumArgs(interp, 1, objv, "set <key>");
3381 return TCL_ERROR;
3383 break;
3385 case MakeIdx:
3386 if (objc >= (nidx + 2) && objc <= (nidx + 4)) {
3387 if ((result = Tcl_GetIndexFromObj(interp, objv[nidx + 1],
3388 (CONST84 char **)param_types, "parameter type",
3389 0, &value)) != TCL_OK)
3390 return result;
3392 if ((value == P_SUBSTRING) && (objc == (nidx + 4))) {
3393 stringpart *strptr = NULL, *newpart;
3394 result = GetXCStringFromList(interp, objv[nidx + 3], &strptr);
3395 if (result != TCL_ERROR) {
3396 if (makestringparam(refobj, Tcl_GetString(objv[nidx + 2]),
3397 strptr) == -1)
3398 return TCL_ERROR;
3399 /* Add the "parameter end" marker to this string */
3400 newpart = makesegment(&strptr, NULL);
3401 newpart->nextpart = NULL;
3402 newpart->type = PARAM_END;
3403 newpart->data.string = (u_char *)NULL;
3406 else if (value == P_SUBSTRING) {
3407 /* Get parameter value from selection */
3408 startparam((Tk_Window)clientData, (pointertype)value,
3409 (caddr_t)Tcl_GetString(objv[nidx + 2]));
3411 else if ((value == P_EXPRESSION) && (objc == (nidx + 4))) {
3412 temps.type = XC_EXPR;
3413 temps.parameter.expr = Tcl_GetString(objv[nidx + 3]);
3414 exprres = evaluate_raw(refobj, &temps, refinst, &result);
3416 if (result != TCL_OK) {
3417 Tcl_SetResult(xcinterp, "Bad result from expression!", NULL);
3418 /* Not fatal to have a bad expression result. . . */
3419 /* return result; */
3421 if (makeexprparam(refobj, Tcl_GetString(objv[nidx + 2]),
3422 temps.parameter.expr, P_EXPRESSION) == NULL)
3423 return TCL_ERROR;
3426 /* All other types are parsed as either a numeric value */
3427 /* (integer or float), or an expression that evaluates */
3428 /* to a numeric value. */
3430 else if (((value == P_NUMERIC) && (objc == (nidx + 4))) ||
3431 objc == (nidx + 3)) {
3432 double tmpdbl;
3434 i = (value == P_NUMERIC) ? 3 : 2;
3436 result = Tcl_GetDoubleFromObj(interp, objv[nidx + i], &tmpdbl);
3437 if (result != TCL_ERROR) {
3438 if (makefloatparam(refobj, Tcl_GetString(objv[nidx + i - 1]),
3439 (float)tmpdbl) == -1)
3440 return TCL_ERROR;
3442 else {
3443 char *newkey;
3445 /* This may be an expression. Do a quick check to */
3446 /* see if the string can be evaluated as a Tcl */
3447 /* expression. If it returns a valid numeric result, */
3448 /* then accept the expression. */
3450 Tcl_ResetResult(interp);
3451 temps.type = XC_EXPR;
3452 temps.parameter.expr = Tcl_GetString(objv[nidx + i]);
3454 exprres = evaluate_raw(refobj, &temps, refinst, &result);
3455 if (result != TCL_OK) {
3456 Tcl_SetResult(xcinterp, "Bad result from expression!", NULL);
3457 return result;
3459 result = Tcl_GetDoubleFromObj(interp, exprres, &tmpdbl);
3460 if (result != TCL_ERROR) {
3461 if ((newkey = makeexprparam(refobj, (value == P_NUMERIC) ?
3462 Tcl_GetString(objv[nidx + i - 1]) : NULL,
3463 temps.parameter.expr, value)) == NULL)
3464 return TCL_ERROR;
3465 else if (value != P_NUMERIC) {
3466 /* Link the expression parameter to the element */
3467 /* To-do: Handle cycles (one extra argument) */
3468 genericptr pgen;
3469 for (i = 0; i < areawin->selects; i++) {
3470 pgen = SELTOGENERIC(areawin->selectlist + i);
3471 makenumericalp(&pgen, value, newkey, 0);
3475 else {
3476 Tcl_SetResult(xcinterp, "Expression evaluates to "
3477 "non-numeric type!", NULL);
3478 return result;
3482 else if (((value != P_NUMERIC) && (objc == (nidx + 4))) ||
3483 objc == (nidx + 3)) {
3484 int cycle;
3485 i = objc - 1;
3486 if (value == P_POSITION || value == P_POSITION_X ||
3487 value == P_POSITION_Y) {
3488 if (objc == nidx + 4) {
3489 result = Tcl_GetIntFromObj(interp, objv[i - 1], &cycle);
3490 if (result == TCL_ERROR) {
3491 Tcl_ResetResult(interp);
3492 startparam((Tk_Window)clientData, (pointertype)value,
3493 Tcl_GetString(objv[i]));
3495 else {
3496 parameterize(value, NULL, (short)cycle);
3499 else {
3500 Tcl_WrongNumArgs(interp, 1, objv, "make position cycle <value>");
3501 return TCL_ERROR;
3504 else {
3505 if (objc == nidx + 3)
3506 startparam((Tk_Window)clientData, (pointertype)value,
3507 Tcl_GetString(objv[i]));
3508 else {
3509 Tcl_WrongNumArgs(interp, 1, objv, "make <numeric_type> <value>");
3510 return TCL_ERROR;
3514 else {
3515 if ((value == P_SUBSTRING) || (value == P_NUMERIC) ||
3516 (value == P_EXPRESSION)) {
3517 Tcl_WrongNumArgs(interp, 1, objv,
3518 "make substring|numeric|expression <key>");
3519 return TCL_ERROR;
3521 else
3522 startparam((Tk_Window)clientData, (pointertype)value, NULL);
3525 else {
3526 Tcl_WrongNumArgs(interp, 1, objv, "make <type> [<key>]");
3527 return TCL_ERROR;
3529 break;
3531 case ReplaceIdx:
3532 /* Calls unparameterize---replaces text with the instance value, */
3533 /* or replaces a numeric parameter with the instance values by */
3534 /* unparameterizing the element. Don't use with parameter keys. */
3536 if (objc == nidx + 2) {
3537 if ((result = Tcl_GetIndexFromObj(interp, objv[nidx + 1],
3538 (CONST84 char **)param_types, "parameter type",
3539 0, &value)) != TCL_OK)
3540 return result;
3541 unparameterize(value);
3543 else {
3544 Tcl_WrongNumArgs(interp, 1, objv, "replace <type>");
3545 return TCL_ERROR;
3547 break;
3549 case DeleteIdx:
3550 case ForgetIdx:
3552 if (objc == nidx + 2) {
3553 /* Check against keys */
3554 ops = match_param(refobj, Tcl_GetString(objv[nidx + 1]));
3555 if (ops == NULL) {
3556 Tcl_SetResult(interp, "Invalid parameter key", NULL);
3557 return TCL_ERROR;
3559 else {
3560 free_object_param(refobj, ops);
3561 /* Redraw everything */
3562 drawarea(areawin->area, (caddr_t)NULL, (caddr_t)NULL);
3565 else {
3566 Tcl_WrongNumArgs(interp, 1, objv, "forget <key>");
3567 return TCL_ERROR;
3569 break;
3571 return XcTagCallback(interp, objc, objv);
3574 /*----------------------------------------------------------------------*/
3576 int xctcl_select(ClientData clientData, Tcl_Interp *interp,
3577 int objc, Tcl_Obj *CONST objv[])
3579 char *argstr;
3580 short *newselect;
3581 int selected_prior, selected_new, nidx, result;
3582 Tcl_Obj *listPtr;
3583 XPoint newpos;
3585 if (objc == 1) {
3586 /* Special case: "select" by itself returns the number of */
3587 /* selected objects. */
3588 Tcl_SetObjResult(interp, Tcl_NewIntObj((int)areawin->selects));
3589 return XcTagCallback(interp, objc, objv);
3591 else {
3592 nidx = 1;
3593 result = ParseElementArguments(interp, objc, objv, &nidx, ALL_TYPES);
3594 if (result != TCL_OK) return result;
3597 if (objc != 2) {
3598 Tcl_WrongNumArgs(interp, 1, objv, "here | get | <element_handle>");
3599 return TCL_ERROR;
3602 if (nidx == 1) {
3603 argstr = Tcl_GetString(objv[1]);
3604 if (!strcmp(argstr, "here")) {
3605 newpos = UGetCursorPos();
3606 areawin->save = newpos;
3607 selected_prior = areawin->selects;
3608 newselect = select_element(ALL_TYPES);
3609 selected_new = areawin->selects - selected_prior;
3611 else if (!strcmp(argstr, "get")) {
3612 newselect = areawin->selectlist;
3613 selected_new = areawin->selects;
3615 else {
3616 Tcl_WrongNumArgs(interp, 1, objv, "here | get | <object_handle>");
3617 return TCL_ERROR;
3620 listPtr = SelectToTclList(interp, newselect, selected_new);
3621 Tcl_SetObjResult(interp, listPtr);
3623 return XcTagCallback(interp, objc, objv);
3626 /*----------------------------------------------------------------------*/
3628 int xctcl_deselect(ClientData clientData, Tcl_Interp *interp,
3629 int objc, Tcl_Obj *CONST objv[])
3631 int i, j, k, result, numobjs;
3632 pointertype ehandle;
3633 char *argstr;
3634 Tcl_Obj *lobj;
3636 if (objc > 3) {
3637 Tcl_WrongNumArgs(interp, 1, objv, "[element_handle]");
3638 return TCL_ERROR;
3640 else if (objc == 3 || (objc == 2 && !strcmp(Tcl_GetString(objv[0]), "deselect"))) {
3642 argstr = Tcl_GetString(objv[1]);
3643 if (strcmp(argstr, "selected")) {
3645 /* check for object handles (integer list) */
3647 result = Tcl_ListObjLength(interp, objv[1], &numobjs);
3648 if (result != TCL_OK) return result;
3650 for (j = 0; j < numobjs; j++) {
3651 result = Tcl_ListObjIndex(interp, objv[1], j, &lobj);
3652 if (result != TCL_OK) return result;
3653 result = Tcl_GetHandleFromObj(interp, lobj, (void *)&ehandle);
3654 if (result != TCL_OK) return result;
3655 i = GetPartNumber((genericptr)ehandle, topobject, ALL_TYPES);
3656 if (i == -1) {
3657 Tcl_SetResult(interp, "No such element exists.", NULL);
3658 return TCL_ERROR;
3660 for (i = 0; i < areawin->selects; i++) {
3661 short *newselect = areawin->selectlist + i;
3662 if ((genericptr)ehandle == SELTOGENERIC(newselect)) {
3663 XTopSetForeground(SELTOCOLOR(newselect));
3664 geneasydraw(*newselect, DEFAULTCOLOR, topobject,
3665 areawin->topinstance);
3667 areawin->selects--;
3668 for (k = i; k < areawin->selects; k++)
3669 *(areawin->selectlist + k) = *(areawin->selectlist + k + 1);
3670 if (areawin->selects == 0) {
3671 free(areawin->selectlist);
3672 freeselects(); /* specifically, free hierstack */
3678 else
3679 unselect_all();
3681 else
3682 startdesel((Tk_Window)clientData, NULL, NULL);
3684 return XcTagCallback(interp, objc, objv);
3687 /*----------------------------------------------------------------------*/
3689 int xctcl_push(ClientData clientData, Tcl_Interp *interp,
3690 int objc, Tcl_Obj *CONST objv[])
3692 int result = ParseElementArguments(interp, objc, objv, NULL, OBJINST);
3694 if (result != TCL_OK) return result;
3696 pushobject(NULL);
3698 return XcTagCallback(interp, objc, objv);
3701 /*----------------------------------------------------------------------*/
3703 int xctcl_pop(ClientData clientData, Tcl_Interp *interp,
3704 int objc, Tcl_Obj *CONST objv[])
3706 if (objc != 1) {
3707 Tcl_WrongNumArgs(interp, 1, objv, "(no arguments)");
3708 return TCL_ERROR;
3710 popobject((Tk_Window)clientData, 0, NULL);
3712 return XcTagCallback(interp, objc, objv);
3715 /*----------------------------------------------------------------------*/
3716 /* Object queries */
3717 /*----------------------------------------------------------------------*/
3719 int xctcl_object(ClientData clientData, Tcl_Interp *interp,
3720 int objc, Tcl_Obj *CONST objv[])
3722 int i, j, idx, result, nidx, libno;
3723 genericptr egen;
3724 Tcl_Obj **newobjv, *ilist, *plist, *hobj;
3725 pointertype ehandle;
3726 objinstptr thisinst;
3727 Boolean forceempty = FALSE;
3729 static char *subCmds[] = {"make", "name", "parts", "library",
3730 "handle", "hide", "unhide", "bbox", NULL};
3731 enum SubIdx {
3732 MakeIdx, NameIdx, PartsIdx, LibraryIdx, HandleIdx, HideIdx,
3733 UnhideIdx, BBoxIdx
3736 /* Check for option "-force" (create an object even if it has no contents) */
3737 if (!strncmp(Tcl_GetString(objv[objc - 1]), "-forc", 5)) {
3738 forceempty = TRUE;
3739 objc--;
3742 /* (revision) "object handle <name>" returns a handle (or null), so */
3743 /* all commands can unambiguously operate on a handle (or nothing) */
3744 /* in the second position. */
3746 nidx = 0;
3748 /* 2nd argument may be a handle, object name, or nothing. */
3749 /* If nothing, the instance of the top-level page is assumed. */
3751 if (objc < 2) {
3752 Tcl_WrongNumArgs(interp, 0, objv, "object [handle] <option> ...");
3753 return TCL_ERROR;
3756 result = Tcl_GetHandleFromObj(interp, objv[1], (void *)&ehandle);
3757 if (result != TCL_OK) {
3758 Tcl_ResetResult(interp);
3759 ehandle = (pointertype)(areawin->topinstance);
3761 else {
3762 nidx = 1;
3763 objc--;
3765 egen = (genericptr)ehandle;
3767 if (ELEMENTTYPE(egen) != OBJINST) {
3768 Tcl_SetResult(interp, "handle does not point to an object instance!", NULL);
3769 return TCL_ERROR;
3771 if (objc < 2) {
3772 Tcl_WrongNumArgs(interp, 0, objv, "object <handle> <option> ...");
3773 return TCL_ERROR;
3775 thisinst = (objinstptr)egen;
3777 if ((result = Tcl_GetIndexFromObj(interp, objv[1 + nidx],
3778 (CONST84 char **)subCmds, "option", 0, &idx)) != TCL_OK)
3779 return result;
3781 switch (idx) {
3782 case LibraryIdx:
3783 case HideIdx:
3784 case UnhideIdx:
3786 if ((libno = libfindobject(thisinst->thisobject, &j)) < 0) {
3787 Tcl_SetResult(interp, "No such object.", NULL);
3788 return TCL_ERROR;
3790 break;
3793 switch (idx) {
3794 case BBoxIdx:
3795 ilist = Tcl_NewListObj(0, NULL);
3796 hobj = Tcl_NewIntObj((int)thisinst->thisobject->bbox.lowerleft.x);
3797 Tcl_ListObjAppendElement(interp, ilist, hobj);
3798 hobj = Tcl_NewIntObj((int)thisinst->thisobject->bbox.lowerleft.y);
3799 Tcl_ListObjAppendElement(interp, ilist, hobj);
3800 hobj = Tcl_NewIntObj((int)(thisinst->thisobject->bbox.lowerleft.x +
3801 thisinst->thisobject->bbox.width));
3802 Tcl_ListObjAppendElement(interp, ilist, hobj);
3803 hobj = Tcl_NewIntObj((int)(thisinst->thisobject->bbox.lowerleft.y +
3804 thisinst->thisobject->bbox.height));
3805 Tcl_ListObjAppendElement(interp, ilist, hobj);
3806 Tcl_SetObjResult(interp, ilist);
3807 break;
3809 case HandleIdx:
3810 if ((objc == 3) && (!NameToObject(Tcl_GetString(objv[nidx + 2]),
3811 (objinstptr *)&ehandle, TRUE))) {
3812 Tcl_SetResult(interp, "Object is not loaded.", NULL);
3813 return TCL_ERROR;
3815 else {
3816 Tcl_SetObjResult(interp, Tcl_NewHandleObj((genericptr)ehandle));
3818 break;
3820 case LibraryIdx:
3821 if (objc == 3) {
3822 int libtarget;
3823 if (ParseLibArguments(xcinterp, 2, &objv[objc - 2 + nidx], NULL,
3824 &libtarget) == TCL_ERROR)
3825 return TCL_ERROR;
3826 else if (libno != libtarget) {
3827 libmoveobject(thisinst->thisobject, libtarget);
3828 /* Regenerate the source and target library pages */
3829 composelib(libno + LIBRARY);
3830 composelib(libtarget + LIBRARY);
3833 Tcl_SetObjResult(interp, Tcl_NewIntObj(libno + 1));
3834 break;
3836 case HideIdx:
3837 thisinst->thisobject->hidden = True;
3838 composelib(libno + LIBRARY);
3839 break;
3841 case UnhideIdx:
3842 thisinst->thisobject->hidden = False;
3843 composelib(libno + LIBRARY);
3844 break;
3846 case MakeIdx:
3848 if ((areawin->selects == 0) && (nidx == 0)) {
3849 /* h = object make "name" [{element_list}] [library]*/
3850 newobjv = (Tcl_Obj **)(&objv[2]);
3851 result = ParseElementArguments(interp, objc - 2, newobjv, NULL, ALL_TYPES);
3852 if (forceempty && result != TCL_OK) Tcl_ResetResult(interp);
3853 else if (!forceempty && result == TCL_OK && areawin->selects == 0)
3855 Tcl_SetResult(interp, "Cannot create empty object. Use "
3856 "\"-force\" option.", NULL);
3857 return TCL_ERROR;
3859 else if (result != TCL_OK) return result;
3861 else if (nidx == 1) {
3862 Tcl_SetResult(interp, "\"object <handle> make\" is illegal", NULL);
3863 return TCL_ERROR;
3865 else if (objc < 3) {
3866 Tcl_WrongNumArgs(interp, 1, objv, "make <name> [element_list] [<library>]");
3867 return TCL_ERROR;
3869 if (objc >= 4)
3870 ParseLibArguments(xcinterp, 2, &objv[objc - 2], NULL, &libno);
3871 else
3872 libno = -1;
3873 thisinst = domakeobject(libno, Tcl_GetString(objv[nidx + 2]), forceempty);
3874 Tcl_SetObjResult(interp, Tcl_NewHandleObj(thisinst));
3875 break;
3877 case NameIdx:
3878 if (nidx == 1 || areawin->selects == 0) {
3879 if (objc == 3) {
3880 sprintf(thisinst->thisobject->name, Tcl_GetString(objv[nidx + 2]));
3881 checkname(thisinst->thisobject);
3883 Tcl_AppendElement(interp, thisinst->thisobject->name);
3885 else {
3886 for (i = 0; i < areawin->selects; i++) {
3887 if (SELECTTYPE(areawin->selectlist + i) == OBJINST) {
3888 thisinst = SELTOOBJINST(areawin->selectlist + i);
3889 Tcl_AppendElement(interp, thisinst->thisobject->name);
3893 break;
3894 case PartsIdx:
3895 /* Make a list of the handles of all parts in the object */
3896 if (nidx == 1 || areawin->selects == 0) {
3897 plist = Tcl_NewListObj(0, NULL);
3898 for (j = 0; j < thisinst->thisobject->parts; j++) {
3899 hobj = Tcl_NewHandleObj(*(thisinst->thisobject->plist + j));
3900 Tcl_ListObjAppendElement(interp, plist, hobj);
3902 Tcl_SetObjResult(interp, plist);
3904 else {
3905 ilist = Tcl_NewListObj(0, NULL);
3906 for (i = 0; i < areawin->selects; i++) {
3907 if (SELECTTYPE(areawin->selectlist + i) == OBJINST) {
3908 objinstptr thisinst = SELTOOBJINST(areawin->selectlist + i);
3909 Tcl_ListObjAppendElement(interp, ilist,
3910 Tcl_NewStringObj(thisinst->thisobject->name,
3911 strlen(thisinst->thisobject->name)));
3912 plist = Tcl_NewListObj(0, NULL);
3913 for (j = 0; j < thisinst->thisobject->parts; j++) {
3914 hobj = Tcl_NewHandleObj(*(thisinst->thisobject->plist + j));
3915 Tcl_ListObjAppendElement(interp, plist, hobj);
3917 Tcl_ListObjAppendElement(interp, ilist, plist);
3920 Tcl_SetObjResult(interp, ilist);
3922 break;
3924 return XcTagCallback(interp, objc, objv);
3927 /*----------------------------------------------------------------------*/
3928 /* Get anchoring (or associated fields) global setting, or apply */
3929 /* to selected labels. */
3930 /*----------------------------------------------------------------------*/
3933 getanchoring(Tcl_Interp *interp, short bitfield)
3935 int i, rval;
3936 labelptr tlab;
3938 if (areawin->selects == 0) {
3939 if (bitfield & RIGHT) {
3940 Tcl_AppendElement(interp, (areawin->anchor & RIGHT) ?
3941 "right" : (areawin->anchor & NOTLEFT) ? "center" : "left");
3943 else if (bitfield & TOP) {
3944 Tcl_AppendElement(interp, (areawin->anchor & TOP) ?
3945 "top" : (areawin->anchor & NOTBOTTOM) ? "middle" : "bottom");
3947 else if (bitfield & JUSTIFYRIGHT) {
3948 Tcl_AppendElement(interp, (areawin->anchor & JUSTIFYRIGHT) ? "right" :
3949 (areawin->anchor & TEXTCENTERED) ? "center" :
3950 (areawin->anchor & JUSTIFYBOTH) ? "both" :
3951 "left");
3953 else {
3954 Tcl_AppendElement(interp, (areawin->anchor & bitfield) ?
3955 "true" : "false");
3957 return (areawin->anchor & bitfield);
3959 for (i = 0; i < areawin->selects; i++) {
3960 if (SELECTTYPE(areawin->selectlist + i) != LABEL) continue;
3961 tlab = SELTOLABEL(areawin->selectlist + i);
3962 if (bitfield == PINVISIBLE && tlab->pin == NORMAL) continue;
3963 if (bitfield & RIGHT) {
3964 Tcl_AppendElement(interp, (tlab->anchor & RIGHT) ?
3965 "right" : (tlab->anchor & NOTLEFT) ? "center" : "left");
3967 else if (bitfield & TOP) {
3968 Tcl_AppendElement(interp, (tlab->anchor & TOP) ?
3969 "top" : (tlab->anchor & NOTBOTTOM) ? "middle" : "bottom");
3971 else if (bitfield & JUSTIFYRIGHT) {
3972 Tcl_AppendElement(interp, (tlab->anchor & JUSTIFYRIGHT) ? "right" :
3973 (tlab->anchor & TEXTCENTERED) ? "center" :
3974 (tlab->anchor & JUSTIFYBOTH) ? "both" :
3975 "left");
3977 else {
3978 Tcl_AppendElement(interp, (tlab->anchor & bitfield) ? "true" : "false");
3980 rval = tlab->anchor;
3982 return (rval & bitfield);
3986 /*----------------------------------------------------------------------*/
3987 /* Set anchoring (and associated fields) global setting, or apply */
3988 /* to selected labels. */
3989 /*----------------------------------------------------------------------*/
3991 void
3992 setanchoring(short bitfield, short value)
3994 int i;
3995 labelptr tlab;
3997 if (areawin->selects == 0) {
3998 areawin->anchor &= (~bitfield);
3999 if (value > 0) areawin->anchor |= value;
4000 return;
4002 for (i = 0; i < areawin->selects; i++) {
4003 if (SELECTTYPE(areawin->selectlist + i) != LABEL) continue;
4004 tlab = SELTOLABEL(areawin->selectlist + i);
4005 if (bitfield == PINVISIBLE && tlab->pin == NORMAL) continue;
4006 tlab->anchor &= (~bitfield);
4007 if (value > 0) tlab->anchor |= value;
4011 /*----------------------------------------------------------------------*/
4012 /* Translate the label encoding bits to a string that the Tcl routine */
4013 /* will recognize from the command line. */
4014 /* */
4015 /* (note to self---is there a good way to not have to declare these */
4016 /* constant character arrays twice in two different routines?) */
4017 /*----------------------------------------------------------------------*/
4019 char *
4020 translateencoding(int psfont)
4022 const char *encValues[] = {"Standard", "special", "ISOLatin1",
4023 "ISOLatin2", "ISOLatin3", "ISOLatin4", "ISOLatin5",
4024 "ISOLatin6", "ISO8859-5", NULL};
4025 int i;
4027 i = (fonts[psfont].flags & 0xf80) >> 7;
4028 if (i < 0) return NULL;
4029 return (char *)encValues[i];
4032 /*----------------------------------------------------------------------*/
4033 /* Translate the label style bits to a string that the Tcl routine */
4034 /* will recognize from the command line. */
4035 /*----------------------------------------------------------------------*/
4037 char *
4038 translatestyle(int psfont)
4040 const char *styValues[] = {"normal", "bold", "italic", "bolditalic", NULL};
4041 int i;
4043 i = fonts[psfont].flags & 0x3;
4044 if (i < 0) return NULL;
4045 return (char *)styValues[i];
4048 /*----------------------------------------------------------------------*/
4049 /* Individual element handling. */
4050 /*----------------------------------------------------------------------*/
4052 int xctcl_label(ClientData clientData, Tcl_Interp *interp,
4053 int objc, Tcl_Obj *CONST objv[])
4055 int i, idx, idx2, nidx, result, value, jval, jval2;
4056 double tmpdbl;
4057 char *tmpstr;
4058 Tcl_Obj *objPtr, *listPtr;
4059 labelptr tlab;
4061 static char *subCmds[] = {"make", "type", "insert", "anchor", "justify",
4062 "flipinvariant", "visible", "font", "scale", "encoding", "style",
4063 "family", "substring", "text", "latex", "list", "replace", "position",
4064 NULL};
4065 enum SubIdx {
4066 MakeIdx, TypeIdx, InsertIdx, AnchorIdx, JustifyIdx, FlipIdx, VisibleIdx,
4067 FontIdx, ScaleIdx, EncodingIdx, StyleIdx, FamilyIdx, SubstringIdx,
4068 TextIdx, LaTeXIdx, ListIdx, ReplaceIdx, PositionIdx
4071 /* These must match the order of string part types defined in xcircuit.h */
4072 static char *subsubCmds[] = {"text", "subscript", "superscript",
4073 "normalscript", "underline", "overline", "noline", "stop",
4074 "forward", "backward", "halfspace", "quarterspace", "return",
4075 "name", "scale", "color", "margin", "kern", "parameter",
4076 "special", NULL};
4078 static char *pinTypeNames[] = {"normal", "text", "local", "pin", "global",
4079 "info", "netlist", NULL};
4081 static int pinTypes[] = {NORMAL, NORMAL, LOCAL, LOCAL, GLOBAL, INFO, INFO};
4083 static char *anchorValues[] = {"left", "center", "right", "top", "middle",
4084 "bottom", NULL};
4086 static char *justifyValues[] = {"left", "center", "right", "both", NULL};
4088 const char *styValues[] = {"normal", "bold", "italic", "bolditalic", NULL};
4090 const char *encValues[] = {"Standard", "special", "ISOLatin1",
4091 "ISOLatin2", "ISOLatin3", "ISOLatin4", "ISOLatin5",
4092 "ISOLatin6", "ISO8859-5", NULL};
4094 /* Tk "label" has been renamed to "tcl_label", but we want to */
4095 /* consider the "label" command to be overloaded, such that the */
4096 /* command "label" may be used without reference to technology. */
4098 Tcl_Obj **newobjv = (Tcl_Obj **)Tcl_Alloc(objc * sizeof(Tcl_Obj *));
4100 newobjv[0] = Tcl_NewStringObj("tcl_label", 9);
4101 Tcl_IncrRefCount(newobjv[0]);
4102 for (i = 1; i < objc; i++) {
4103 if (Tcl_IsShared(objv[i]))
4104 newobjv[i] = Tcl_DuplicateObj(objv[i]);
4105 else
4106 newobjv[i] = objv[i];
4107 Tcl_IncrRefCount(newobjv[i]);
4110 result = Tcl_EvalObjv(interp, objc, newobjv, 0);
4112 for (i = 0; i < objc; i++)
4113 Tcl_DecrRefCount(newobjv[i]);
4114 Tcl_Free((char *)newobjv);
4116 if (result == TCL_OK) return result;
4117 Tcl_ResetResult(interp);
4119 /* Now, assuming that Tcl didn't like the syntax, we continue on with */
4120 /* our own version. */
4122 nidx = 4;
4123 result = ParseElementArguments(interp, objc, objv, &nidx, LABEL);
4124 if (result != TCL_OK) return result;
4126 if ((result = Tcl_GetIndexFromObj(interp, objv[nidx],
4127 (CONST84 char **)subCmds, "option", 0, &idx)) != TCL_OK)
4128 return result;
4130 /* If there are no selections at this point, check if the command is */
4131 /* appropriate for setting a default value. */
4133 switch (idx) {
4134 case MakeIdx:
4135 if ((areawin->selects == 0) && (nidx == 1)) {
4136 if (objc != 2) {
4137 result = Tcl_GetIndexFromObj(interp, objv[2],
4138 (CONST84 char **)pinTypeNames, "pin type", 0, &idx2);
4139 if (result != TCL_OK) {
4140 if (objc == 3) return result;
4141 else {
4142 Tcl_ResetResult(interp);
4143 idx2 = 0;
4146 else {
4147 nidx++;
4148 idx2 = pinTypes[idx2]; /* idx2 now matches defs in xcircuit.h */
4151 if ((objc != 4) && (objc != 5)) {
4152 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
4153 return TCL_ERROR;
4155 else {
4156 labelptr newlab;
4157 stringpart *strptr = NULL;
4158 XPoint position;
4160 if ((result = GetXCStringFromList(interp, objv[nidx + 1],
4161 &strptr)) != TCL_OK)
4162 return result;
4164 /* Should probably have some mechanism to create an empty */
4165 /* string from a script, even though empty strings are */
4166 /* disallowed from the GUI. */
4168 if (strptr == NULL) {
4169 Tcl_SetResult(interp, "Empty string. No element created.", NULL);
4170 break;
4172 if ((objc - nidx) <= 2) {
4173 Tcl_WrongNumArgs(interp, 3, objv, "<text> {position}");
4174 return TCL_ERROR;
4177 if ((result = GetPositionFromList(interp, objv[nidx + 2],
4178 &position)) != TCL_OK)
4179 return result;
4181 newlab = new_label(NULL, strptr, idx2, position.x, position.y,
4182 (u_char)1);
4183 singlebbox((genericptr *)&newlab);
4184 objPtr = Tcl_NewHandleObj(newlab);
4185 Tcl_SetObjResult(interp, objPtr);
4188 else if (nidx == 2) {
4189 Tcl_SetResult(interp, "\"label <handle> make\" is illegal", NULL);
4190 return TCL_ERROR;
4192 else {
4193 Tcl_SetResult(interp, "No selections allowed", NULL);
4194 return TCL_ERROR;
4196 break;
4198 case ScaleIdx:
4199 if (objc == 2) {
4200 if ((areawin->selects == 0) && (nidx == 1) &&
4201 eventmode != TEXT_MODE && eventmode != ETEXT_MODE) {
4202 objPtr = Tcl_NewDoubleObj((double)areawin->textscale);
4203 Tcl_SetObjResult(interp, objPtr);
4205 else {
4206 float *floatptr;
4207 gettextsize(&floatptr);
4208 objPtr = Tcl_NewDoubleObj((double)((float)(*floatptr)));
4209 Tcl_SetObjResult(interp, objPtr);
4212 else if (objc >= 3) {
4213 result = Tcl_GetDoubleFromObj(interp, objv[nidx + 1], &tmpdbl);
4214 if (result != TCL_OK) return result;
4215 if (tmpdbl <= 0.0) {
4216 Tcl_SetResult(interp, "Illegal scale value", NULL);
4217 return TCL_ERROR;
4220 if ((areawin->selects == 0) && (nidx == 1) && (eventmode != TEXT_MODE)
4221 && (eventmode != ETEXT_MODE))
4222 areawin->textscale = (float)tmpdbl;
4223 else
4224 changetextscale((float)tmpdbl);
4226 break;
4228 case FontIdx:
4229 if (objc == 2) {
4230 tmpstr = fonts[areawin->psfont].psname;
4231 objPtr = Tcl_NewStringObj(tmpstr, strlen(tmpstr));
4232 Tcl_SetObjResult(interp, objPtr);
4234 else {
4235 tmpstr = Tcl_GetString(objv[2]);
4236 for (i = 0; i < fontcount; i++)
4237 if (!strcmp(fonts[i].psname, tmpstr)) break;
4238 setfont((Tk_Window)clientData, (u_int)i, NULL);
4240 break;
4242 case FamilyIdx:
4244 /* Check for "-all" switch */
4245 if ((objc - nidx) == 2) {
4246 tmpstr = Tcl_GetString(objv[nidx + 1]);
4247 if (!strncmp(tmpstr, "-all", 4)) {
4249 /* Create a list of all font families. This does a simple */
4250 /* check against contiguous entries, but the result is not */
4251 /* guaranteed to be a list of unique entries (i.e., the */
4252 /* calling script should sort the list) */
4254 for (i = 0; i < fontcount; i++) {
4255 if (i == 0 || strcmp(fonts[i].family, fonts[i-1].family))
4256 Tcl_AppendElement(interp, fonts[i].family);
4258 break;
4262 if (objc == 2) {
4263 tmpstr = fonts[areawin->psfont].family;
4264 objPtr = Tcl_NewStringObj(tmpstr, strlen(tmpstr));
4265 Tcl_SetObjResult(interp, objPtr);
4267 else {
4268 tmpstr = Tcl_GetString(objv[2]);
4269 for (i = 0; i < fontcount; i++)
4270 if (!strcmp(fonts[i].family, tmpstr)) break;
4271 setfont((Tk_Window)clientData, (u_int)i, NULL);
4273 break;
4275 case EncodingIdx:
4276 if (objc == 2) {
4277 tmpstr = translateencoding(areawin->psfont);
4278 objPtr = Tcl_NewStringObj(tmpstr, -1);
4279 Tcl_SetObjResult(interp, objPtr);
4281 else {
4282 if (Tcl_GetIndexFromObj(interp, objv[2],
4283 (CONST84 char **)encValues, "encodings", 0,
4284 &idx2) != TCL_OK) {
4285 return TCL_ERROR;
4287 fontencoding((Tk_Window)clientData, idx2, NULL);
4288 refresh(NULL, NULL, NULL);
4290 break;
4292 case StyleIdx:
4293 if (objc == 2) {
4294 tmpstr = translatestyle(areawin->psfont);
4295 objPtr = Tcl_NewStringObj(tmpstr, -1);
4296 Tcl_SetObjResult(interp, objPtr);
4298 else {
4299 if (Tcl_GetIndexFromObj(interp, objv[2],
4300 (CONST84 char **)styValues,
4301 "styles", 0, &idx2) != TCL_OK) {
4302 return TCL_ERROR;
4304 fontstyle((Tk_Window)clientData, idx2, NULL);
4306 break;
4308 case TypeIdx: /* Change type of label */
4309 if ((areawin->selects == 0) && (nidx == 1)) {
4310 Tcl_SetResult(interp, "Must have a label selection.", NULL);
4311 return TCL_ERROR;
4313 if (objc == nidx + 1) { /* Return pin type(s) */
4314 for (i = 0; i < areawin->selects; i++) {
4315 if (SELECTTYPE(areawin->selectlist + i) != LABEL) continue;
4316 tlab = SELTOLABEL(areawin->selectlist + i);
4317 for (idx2 = 0; idx2 < sizeof(pinTypeNames); idx2++) {
4318 if (tlab->pin == pinTypes[idx2]) {
4319 Tcl_AppendElement(interp, pinTypeNames[idx2]);
4320 break;
4325 else {
4326 if (Tcl_GetIndexFromObj(interp, objv[nidx + 1],
4327 (CONST84 char **)pinTypeNames,
4328 "pin types", 0, &idx2) != TCL_OK) {
4329 return TCL_ERROR;
4331 for (i = 0; i < areawin->selects; i++) {
4332 if (SELECTTYPE(areawin->selectlist + i) != LABEL) continue;
4333 tlab = SELTOLABEL(areawin->selectlist + i);
4334 tlab->pin = pinTypes[idx2];
4335 pinconvert(tlab, tlab->pin);
4336 setobjecttype(topobject);
4339 break;
4341 case InsertIdx: /* Text insertion */
4342 if (nidx != 1) {
4343 Tcl_SetResult(interp, "Insertion into handle or selection"
4344 " not supported (yet)", NULL);
4345 return TCL_ERROR;
4347 if (eventmode != TEXT_MODE && eventmode != ETEXT_MODE) {
4348 Tcl_SetResult(interp, "Must be in edit mode to insert into label.",
4349 NULL);
4350 return TCL_ERROR;
4352 if (objc <= nidx + 1) {
4353 Tcl_WrongNumArgs(interp, 2, objv, "insert_type");
4354 return TCL_ERROR;
4356 if (Tcl_GetIndexFromObj(interp, objv[nidx + 1],
4357 (CONST84 char **)subsubCmds,
4358 "insertions", 0, &idx2) != TCL_OK) {
4359 return TCL_ERROR;
4361 if ((idx2 > TEXT_STRING) && (idx2 < FONT_NAME) && (objc - nidx == 2)) {
4362 labeltext(idx2, (char *)1);
4364 else if (idx2 == MARGINSTOP) {
4365 if (objc - nidx == 3) {
4366 result = Tcl_GetIntFromObj(interp, objv[nidx + 2], &value);
4367 if (result != TCL_OK) return result;
4369 else value = 1;
4370 labeltext(idx2, (char *)&value);
4372 else if ((idx2 == PARAM_START) && (objc - nidx == 3)) {
4373 labeltext(idx2, Tcl_GetString(objv[nidx + 2]));
4375 else if ((idx2 == FONT_COLOR) && (objc - nidx == 3)) {
4376 result = GetColorFromObj(interp, objv[nidx + 2], &value, TRUE);
4377 if (result != TCL_OK) return result;
4378 labeltext(idx2, (char *)&value);
4380 else if ((idx2 == FONT_NAME) && (objc - nidx == 3)) {
4381 tmpstr = Tcl_GetString(objv[nidx + 2]);
4382 for (i = 0; i < fontcount; i++)
4383 if (!strcmp(fonts[i].psname, tmpstr)) break;
4384 if (i == fontcount) {
4385 Tcl_SetResult(interp, "Invalid font name.", NULL);
4386 return TCL_ERROR;
4388 else
4389 labeltext(idx2, (char *)&i);
4391 else if ((idx2 == FONT_SCALE) && (objc - nidx == 3)) {
4392 float fvalue;
4393 double dvalue;
4394 result = Tcl_GetDoubleFromObj(interp, objv[nidx + 2], &dvalue);
4395 if (result != TCL_OK) return result;
4396 fvalue = (float)dvalue;
4397 labeltext(idx2, (char *)&fvalue);
4399 else if ((idx2 == KERN) && (objc - nidx == 3)) {
4400 strcpy(_STR2, Tcl_GetString(objv[nidx + 2]));
4401 setkern(NULL, NULL);
4403 else if ((idx2 == TEXT_STRING) && (objc - nidx == 3)) {
4404 char *substring = Tcl_GetString(objv[nidx + 2]);
4405 for (i = 0; i < strlen(substring); i++) {
4406 /* Special handling allows newlines from cutbuffer selections */
4407 /* to be translated into embedded carriage returns. */
4408 if (substring[i] == '\012')
4409 labeltext(RETURN, (char *)1);
4410 else
4411 labeltext(substring[i], NULL);
4415 /* PARAM_END in xcircuit.h is actually mapped to the same */
4416 /* position as "special" in subsubCommands[] above; don't */
4417 /* be confused. . . */
4419 else if ((idx2 == PARAM_END) && (objc - nidx == 2)) {
4420 dospecial();
4422 else if ((idx2 == PARAM_END) && (objc - nidx == 3)) {
4423 result = Tcl_GetIntFromObj(interp, objv[nidx + 2], &value);
4424 if (result != TCL_OK) return result;
4425 labeltext(value, NULL);
4427 else {
4428 Tcl_WrongNumArgs(interp, 2, objv, "insertion_type ?arg ...?");
4429 return TCL_ERROR;
4431 break;
4433 case SubstringIdx:
4434 objPtr = Tcl_NewListObj(0, NULL);
4435 if (areawin != NULL && areawin->selects == 1) {
4436 if (SELECTTYPE(areawin->selectlist) == LABEL) {
4437 Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewIntObj(areawin->textend));
4438 Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewIntObj(areawin->textpos));
4441 Tcl_SetObjResult(interp, objPtr);
4442 break;
4444 /* Fixed issue where LaTeX mode wasn't assigned to labels */
4445 /* by Agustín Campeny, April 2020 */
4447 case VisibleIdx: /* Change visibility of pin */
4448 if (objc == nidx + 1)
4449 jval = getanchoring(interp, PINVISIBLE);
4450 else {
4451 if ((result = Tcl_GetBooleanFromObj(interp, objv[nidx + 1],
4452 &value)) != TCL_OK)
4453 return result;
4454 setanchoring(PINVISIBLE, (value) ? PINVISIBLE : NORMAL);
4456 break;
4458 case FlipIdx:
4459 if (objc == nidx + 1)
4460 jval = getanchoring(interp, FLIPINV);
4461 else {
4462 if ((result = Tcl_GetBooleanFromObj(interp, objv[nidx + 1],
4463 &value)) != TCL_OK)
4464 return result;
4465 setanchoring(FLIPINV, (value) ? FLIPINV : NORMAL);
4467 break;
4469 case LaTeXIdx:
4470 if (objc == nidx + 1)
4471 jval = getanchoring(interp, LATEXLABEL);
4472 else {
4473 if ((result = Tcl_GetBooleanFromObj(interp, objv[nidx + 1],
4474 &value)) != TCL_OK)
4475 return result;
4476 setanchoring(LATEXLABEL, (value) ? LATEXLABEL : NORMAL);
4478 break;
4480 case JustifyIdx:
4481 if (objc == nidx + 1) {
4482 jval = getanchoring(interp, JUSTIFYRIGHT | JUSTIFYBOTH | TEXTCENTERED);
4484 else {
4485 if (Tcl_GetIndexFromObj(interp, objv[nidx + 1],
4486 (CONST84 char **)justifyValues,
4487 "justification", 0, &idx2) != TCL_OK) {
4488 return TCL_ERROR;
4490 switch (idx2) {
4491 case 0: value = NORMAL; break;
4492 case 1: value = TEXTCENTERED; break;
4493 case 2: value = JUSTIFYRIGHT; break;
4494 case 3: value = JUSTIFYBOTH; break;
4496 setanchoring(JUSTIFYRIGHT | JUSTIFYBOTH | TEXTCENTERED, value);
4497 refresh(NULL, NULL, NULL);
4499 break;
4501 case AnchorIdx:
4502 if (objc == nidx + 1) {
4503 jval = getanchoring(interp, RIGHT | NOTLEFT);
4504 jval2 = getanchoring(interp, TOP | NOTBOTTOM);
4506 else {
4507 if (Tcl_GetIndexFromObj(interp, objv[nidx + 1],
4508 (CONST84 char **)anchorValues,
4509 "anchoring", 0, &idx2) != TCL_OK) {
4510 return TCL_ERROR;
4512 switch (idx2) {
4513 case 0: value = NORMAL; break;
4514 case 1: value = NOTLEFT; break;
4515 case 2: value = NOTLEFT | RIGHT; break;
4516 case 3: value = NOTBOTTOM | TOP; break;
4517 case 4: value = NOTBOTTOM; break;
4518 case 5: value = NORMAL; break;
4520 switch (idx2) {
4521 case 0: case 1: case 2:
4522 setanchoring(RIGHT | NOTLEFT, value);
4523 refresh(NULL, NULL, NULL);
4524 break;
4525 case 3: case 4: case 5:
4526 setanchoring(TOP | NOTBOTTOM, value);
4527 refresh(NULL, NULL, NULL);
4528 break;
4531 break;
4533 case TextIdx:
4534 if ((areawin->selects == 0) && (nidx == 1)) {
4535 Tcl_SetResult(interp, "Must have a label selection.", NULL);
4536 return TCL_ERROR;
4538 if (objc == nidx + 1) { /* Return label as printable string */
4539 char *tstr;
4540 objPtr = Tcl_NewListObj(0, NULL);
4541 for (i = 0; i < areawin->selects; i++) {
4542 if (SELECTTYPE(areawin->selectlist + i) != LABEL) continue;
4543 tlab = SELTOLABEL(areawin->selectlist + i);
4544 tstr = textprint(tlab->string, areawin->topinstance);
4545 Tcl_ListObjAppendElement(interp, objPtr,
4546 Tcl_NewStringObj(tstr, strlen(tstr)));
4547 free(tstr);
4549 Tcl_SetObjResult(interp, objPtr);
4551 break;
4553 case ListIdx:
4554 if ((areawin->selects == 0) && (nidx == 1)) {
4555 Tcl_SetResult(interp, "Must have a label selection.", NULL);
4556 return TCL_ERROR;
4558 if (objc == nidx + 1) { /* Return label as printable string */
4559 listPtr = Tcl_NewListObj(0, NULL);
4560 for (i = 0; i < areawin->selects; i++) {
4561 if (SELECTTYPE(areawin->selectlist + i) != LABEL) continue;
4562 tlab = SELTOLABEL(areawin->selectlist + i);
4563 objPtr = TclGetStringParts(tlab->string);
4564 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
4566 Tcl_SetObjResult(interp, listPtr);
4568 break;
4570 case ReplaceIdx: /* the opposite of "list" */
4571 if ((areawin->selects == 0) && (nidx == 1)) {
4572 Tcl_SetResult(interp, "Must have a label selection.", NULL);
4573 return TCL_ERROR;
4575 if (objc == nidx + 2) { /* Replace string from list */
4576 stringpart *strptr = NULL;
4578 if ((result = GetXCStringFromList(interp, objv[nidx + 1],
4579 &strptr)) != TCL_OK)
4580 return result;
4582 for (i = 0; i < areawin->selects; i++) {
4583 if (SELECTTYPE(areawin->selectlist + i) != LABEL) continue;
4584 tlab = SELTOLABEL(areawin->selectlist + i);
4585 register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance, tlab);
4586 freelabel(tlab->string);
4587 tlab->string = stringcopy(strptr);
4589 freelabel(strptr);
4590 undo_finish_series();
4591 refresh(NULL, NULL, NULL);
4593 break;
4595 case PositionIdx:
4596 if ((areawin->selects == 0) && (nidx == 1)) {
4597 Tcl_SetResult(interp, "Must have a label selection.", NULL);
4598 return TCL_ERROR;
4600 if (objc == nidx + 1) { /* Return position of label */
4601 Tcl_Obj *cpair;
4603 listPtr = Tcl_NewListObj(0, NULL);
4604 for (i = 0; i < areawin->selects; i++) {
4605 if (SELECTTYPE(areawin->selectlist + i) != LABEL) continue;
4606 tlab = SELTOLABEL(areawin->selectlist + i);
4607 cpair = Tcl_NewListObj(0, NULL);
4608 objPtr = Tcl_NewIntObj((int)tlab->position.x);
4609 Tcl_ListObjAppendElement(interp, cpair, objPtr);
4610 objPtr = Tcl_NewIntObj((int)tlab->position.y);
4611 Tcl_ListObjAppendElement(interp, cpair, objPtr);
4612 Tcl_ListObjAppendElement(interp, listPtr, cpair);
4614 Tcl_SetObjResult(interp, listPtr);
4616 else if (objc == nidx + 2) { /* Change position of label */
4617 XPoint position;
4619 if ((areawin->selects != 1) || (SELECTTYPE(areawin->selectlist)
4620 != LABEL)) {
4621 Tcl_SetResult(interp, "Must have exactly one selected label", NULL);
4622 return TCL_ERROR;
4624 if ((result = GetPositionFromList(interp, objv[nidx + 1],
4625 &position)) != TCL_OK)
4626 return result;
4628 tlab = SELTOLABEL(areawin->selectlist);
4629 tlab->position.x = position.x;
4630 tlab->position.y = position.y;
4632 break;
4634 return XcTagCallback(interp, objc, objv);
4637 /*----------------------------------------------------------------------*/
4638 /* Element Fill Styles */
4639 /*----------------------------------------------------------------------*/
4641 int xctcl_dofill(ClientData clientData, Tcl_Interp *interp,
4642 int objc, Tcl_Obj *CONST objv[])
4644 u_int value;
4645 int i, idx, result, rval = -1;
4647 static char *Styles[] = {"opaque", "transparent", "filled", "unfilled",
4648 "solid", NULL};
4649 enum StylesIdx {
4650 OpaqueIdx, TransparentIdx, FilledIdx, UnfilledIdx, SolidIdx
4653 if (objc == 1) {
4654 value = areawin->style;
4655 Tcl_AppendElement(interp, ((value & OPAQUE) ? "opaque" : "transparent"));
4656 if (value & FILLED) {
4657 Tcl_AppendElement(interp, "filled");
4658 switch (value & FILLSOLID) {
4659 case 0:
4660 Tcl_AppendElement(interp, "12"); break;
4661 case STIP0:
4662 Tcl_AppendElement(interp, "25"); break;
4663 case STIP1:
4664 Tcl_AppendElement(interp, "37"); break;
4665 case STIP1 | STIP0:
4666 Tcl_AppendElement(interp, "50"); break;
4667 case STIP2:
4668 Tcl_AppendElement(interp, "62"); break;
4669 case STIP2 | STIP0:
4670 Tcl_AppendElement(interp, "75"); break;
4671 case STIP2 | STIP1:
4672 Tcl_AppendElement(interp, "87"); break;
4673 case FILLSOLID:
4674 Tcl_AppendElement(interp, "solid"); break;
4677 else {
4678 Tcl_AppendElement(interp, "unfilled");
4680 return TCL_OK;
4683 for (i = 1; i < objc; i++) {
4684 if (Tcl_GetIndexFromObj(interp, objv[i],
4685 (CONST84 char **)Styles, "fill styles",
4686 0, &idx) != TCL_OK) {
4687 Tcl_ResetResult(interp);
4688 result = Tcl_GetIntFromObj(interp, objv[i], &value);
4689 if (result != TCL_OK) {
4690 Tcl_SetResult(interp, "Expected fill style or fillfactor 0 to 100", NULL);
4691 return result;
4693 else {
4694 if (value >= 0 && value < 6) value = FILLSOLID;
4695 else if (value >= 6 && value < 19) value = FILLED;
4696 else if (value >= 19 && value < 31) value = FILLED | STIP0;
4697 else if (value >= 31 && value < 44) value = FILLED | STIP1;
4698 else if (value >= 44 && value < 56) value = FILLED | STIP0 | STIP1;
4699 else if (value >= 56 && value < 69) value = FILLED | STIP2;
4700 else if (value >= 69 && value < 81) value = FILLED | STIP2 | STIP0;
4701 else if (value >= 81 && value < 94) value = FILLED | STIP2 | STIP1;
4702 else if (value >= 94 && value <= 100) value = FILLED | FILLSOLID;
4703 else {
4704 Tcl_SetResult(interp, "Fill value should be 0 to 100", NULL);
4705 return TCL_ERROR;
4707 rval = setelementstyle((Tk_Window)clientData, (pointertype)value,
4708 FILLED | FILLSOLID);
4711 else {
4712 switch(idx) {
4713 case OpaqueIdx:
4714 rval = setelementstyle((Tk_Window)clientData, OPAQUE, OPAQUE);
4715 break;
4716 case TransparentIdx:
4717 rval = setelementstyle((Tk_Window)clientData, NORMAL, OPAQUE);
4718 break;
4719 case UnfilledIdx:
4720 rval = setelementstyle((Tk_Window)clientData, FILLSOLID,
4721 FILLED | FILLSOLID);
4722 break;
4723 case SolidIdx:
4724 rval = setelementstyle((Tk_Window)clientData, FILLED | FILLSOLID,
4725 FILLED | FILLSOLID);
4726 break;
4727 case FilledIdx:
4728 break;
4732 if (rval < 0)
4733 return TCL_ERROR;
4735 return XcTagCallback(interp, objc, objv);
4738 /*----------------------------------------------------------------------*/
4739 /* Element border styles */
4740 /*----------------------------------------------------------------------*/
4742 int xctcl_doborder(ClientData clientData, Tcl_Interp *interp,
4743 int objc, Tcl_Obj *CONST objv[])
4745 int result, i, idx, value, rval = -1;
4746 u_short mask;
4747 double wvalue;
4749 static char *borderStyles[] = {"solid", "dashed", "dotted", "none",
4750 "unbordered", "unclosed", "closed", "bbox", "set", "get", "square",
4751 "round", "clipmask", NULL};
4752 enum StyIdx {
4753 SolidIdx, DashedIdx, DottedIdx, NoneIdx, UnborderedIdx,
4754 UnclosedIdx, ClosedIdx, BBoxIdx, SetIdx, GetIdx, SquareIdx,
4755 RoundIdx, ClipMaskIdx
4758 if (objc == 1) {
4759 Tcl_Obj *listPtr;
4760 listPtr = Tcl_NewListObj(0, NULL);
4761 value = areawin->style;
4762 wvalue = (double)areawin->linewidth;
4763 switch (value & (DASHED | DOTTED | NOBORDER | SQUARECAP)) {
4764 case NORMAL:
4765 Tcl_ListObjAppendElement(interp, listPtr,
4766 Tcl_NewStringObj("solid", 5)); break;
4767 case DASHED:
4768 Tcl_ListObjAppendElement(interp, listPtr,
4769 Tcl_NewStringObj("dashed", 6)); break;
4770 case DOTTED:
4771 Tcl_ListObjAppendElement(interp, listPtr,
4772 Tcl_NewStringObj("dotted", 6)); break;
4773 case NOBORDER:
4774 Tcl_ListObjAppendElement(interp, listPtr,
4775 Tcl_NewStringObj("unbordered", 10)); break;
4776 case SQUARECAP:
4777 Tcl_ListObjAppendElement(interp, listPtr,
4778 Tcl_NewStringObj("square-endcaps", 10)); break;
4780 if (value & UNCLOSED)
4781 Tcl_ListObjAppendElement(interp, listPtr, Tcl_NewStringObj("unclosed", 8));
4782 else
4783 Tcl_ListObjAppendElement(interp, listPtr, Tcl_NewStringObj("closed", 6));
4785 if (value & BBOX)
4786 Tcl_ListObjAppendElement(interp, listPtr,
4787 Tcl_NewStringObj("bounding box", 12));
4789 if (value & CLIPMASK)
4790 Tcl_ListObjAppendElement(interp, listPtr,
4791 Tcl_NewStringObj("clipmask", 8));
4793 Tcl_ListObjAppendElement(interp, listPtr, Tcl_NewDoubleObj(wvalue));
4794 Tcl_SetObjResult(interp, listPtr);
4795 return TCL_OK;
4798 for (i = 1; i < objc; i++) {
4799 result = Tcl_GetIndexFromObj(interp, objv[i],
4800 (CONST84 char **)borderStyles,
4801 "border style", 0, &idx);
4802 if (result != TCL_OK)
4803 return result;
4805 switch (idx) {
4806 case GetIdx:
4808 int j, numfound = 0;
4809 genericptr setel;
4810 Tcl_Obj *objPtr, *listPtr = NULL;
4812 for (j = 0; j < areawin->selects; j++) {
4813 setel = SELTOGENERIC(areawin->selectlist + j);
4814 if (IS_ARC(setel) || IS_POLYGON(setel) ||
4815 IS_SPLINE(setel) || IS_PATH(setel)) {
4816 switch(ELEMENTTYPE(setel)) {
4817 case ARC: wvalue = ((arcptr)setel)->width; break;
4818 case POLYGON: wvalue = ((polyptr)setel)->width; break;
4819 case SPLINE: wvalue = ((splineptr)setel)->width; break;
4820 case PATH: wvalue = ((pathptr)setel)->width; break;
4822 if ((++numfound) == 2) {
4823 listPtr = Tcl_NewListObj(0, NULL);
4824 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
4826 objPtr = Tcl_NewDoubleObj(wvalue);
4827 if (numfound > 1)
4828 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
4831 switch (numfound) {
4832 case 0:
4833 objPtr = Tcl_NewDoubleObj(areawin->linewidth);
4834 /* fall through */
4835 case 1:
4836 Tcl_SetObjResult(interp, objPtr);
4837 break;
4838 default:
4839 Tcl_SetObjResult(interp, listPtr);
4840 break;
4843 break;
4844 case SetIdx:
4845 if ((objc - i) != 2) {
4846 Tcl_SetResult(interp, "Error: no linewidth given.", NULL);
4847 return TCL_ERROR;
4849 result = Tcl_GetDoubleFromObj(interp, objv[++i], &wvalue);
4850 if (result == TCL_OK) {
4851 sprintf(_STR2, "%f", wvalue);
4852 setwwidth((Tk_Window)clientData, NULL);
4854 else {
4855 Tcl_SetResult(interp, "Error: invalid border linewidth.", NULL);
4856 return TCL_ERROR;
4858 break;
4859 case SolidIdx: value = NORMAL; mask = DASHED | DOTTED | NOBORDER; break;
4860 case DashedIdx: value = DASHED; mask = DASHED | DOTTED | NOBORDER; break;
4861 case DottedIdx: value = DOTTED; mask = DASHED | DOTTED | NOBORDER; break;
4862 case NoneIdx: case UnborderedIdx:
4863 value = NOBORDER; mask = DASHED | DOTTED | NOBORDER; break;
4864 case UnclosedIdx: value = UNCLOSED; mask = UNCLOSED; break;
4865 case ClosedIdx: value = NORMAL; mask = UNCLOSED; break;
4866 case SquareIdx: value = SQUARECAP; mask = SQUARECAP; break;
4867 case RoundIdx: value = NORMAL; mask = SQUARECAP; break;
4868 case BBoxIdx:
4869 mask = BBOX;
4870 if ((objc - i) < 2) value = BBOX;
4871 else {
4872 char *yesno = Tcl_GetString(objv[++i]);
4873 value = (tolower(yesno[0]) == 'y' || tolower(yesno[0]) == 't') ?
4874 BBOX : NORMAL;
4876 break;
4877 case ClipMaskIdx:
4878 mask = CLIPMASK;
4879 if ((objc - i) < 2) value = CLIPMASK;
4880 else {
4881 char *yesno = Tcl_GetString(objv[++i]);
4882 value = (tolower(yesno[0]) == 'y' || tolower(yesno[0]) == 't') ?
4883 CLIPMASK : NORMAL;
4885 break;
4887 if (idx != SetIdx && idx != GetIdx)
4888 rval = setelementstyle((Tk_Window)clientData, (u_short)value, mask);
4891 return XcTagCallback(interp, objc, objv);
4894 /*----------------------------------------------------------------------*/
4896 int xctcl_polygon(ClientData clientData, Tcl_Interp *interp,
4897 int objc, Tcl_Obj *CONST objv[])
4899 int idx, nidx, result, npoints, j;
4900 polyptr newpoly, ppoly;
4901 XPoint ppt;
4902 pointlist points;
4903 Tcl_Obj *objPtr, *coord, *cpair, **newobjv;
4904 Boolean is_box = FALSE;
4905 Matrix hierCTM;
4907 static char *subCmds[] = {"make", "border", "fill", "points", "number", NULL};
4908 enum SubIdx {
4909 MakeIdx, BorderIdx, FillIdx, PointsIdx, NumberIdx
4912 nidx = 255;
4913 result = ParseElementArguments(interp, objc, objv, &nidx, POLYGON);
4914 if (result != TCL_OK) return result;
4916 if ((result = Tcl_GetIndexFromObj(interp, objv[nidx],
4917 (CONST84 char **)subCmds,
4918 "option", 0, &idx)) != TCL_OK)
4919 return result;
4921 switch (idx) {
4922 case MakeIdx:
4923 if ((areawin->selects == 0) && (nidx == 1)) {
4924 if (objc < 5) {
4925 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
4926 return TCL_ERROR;
4928 if (!strcmp(Tcl_GetString(objv[2]), "box")) {
4929 npoints = objc - 3;
4930 is_box = TRUE;
4931 if (npoints != 4 && npoints != 2) {
4932 Tcl_SetResult(interp, "Box must have 2 or 4 points", NULL);
4933 return TCL_ERROR;
4936 else {
4937 result = Tcl_GetIntFromObj(interp, objv[2], &npoints);
4938 if (result != TCL_OK) return result;
4940 if (objc != npoints + 3) {
4941 Tcl_WrongNumArgs(interp, 1, objv, "N {x1 y1}...{xN yN}");
4942 return TCL_ERROR;
4944 points = (pointlist)malloc(npoints * sizeof(XPoint));
4945 for (j = 0; j < npoints; j++) {
4946 result = GetPositionFromList(interp, objv[3 + j], &ppt);
4947 if (result == TCL_OK) {
4948 points[j].x = ppt.x;
4949 points[j].y = ppt.y;
4952 if (is_box && (npoints == 2)) {
4953 npoints = 4;
4954 points = (pointlist)realloc(points, npoints * sizeof(XPoint));
4955 points[2].x = points[1].x;
4956 points[2].y = points[1].y;
4957 points[1].y = points[0].y;
4958 points[3].x = points[0].x;
4959 points[3].y = points[2].y;
4961 newpoly = new_polygon(NULL, &points, npoints);
4962 if (!is_box) newpoly->style |= UNCLOSED;
4963 singlebbox((genericptr *)&newpoly);
4965 objPtr = Tcl_NewHandleObj(newpoly);
4966 Tcl_SetObjResult(interp, objPtr);
4968 else if (nidx == 2) {
4969 Tcl_SetResult(interp, "\"polygon <handle> make\" is illegal", NULL);
4970 return TCL_ERROR;
4972 else {
4973 Tcl_SetResult(interp, "No selections allowed", NULL);
4974 return TCL_ERROR;
4976 break;
4978 case BorderIdx:
4979 newobjv = (Tcl_Obj **)(&objv[nidx]);
4980 result = xctcl_doborder(clientData, interp, objc - nidx, newobjv);
4981 break;
4983 case FillIdx:
4984 newobjv = (Tcl_Obj **)(&objv[nidx]);
4985 result = xctcl_dofill(clientData, interp, objc - nidx, newobjv);
4986 break;
4988 case NumberIdx:
4989 if (areawin->selects != 1) {
4990 Tcl_SetResult(interp, "Must have exactly one selection to "
4991 "query points", NULL);
4992 return TCL_ERROR;
4994 else {
4995 if (SELECTTYPE(areawin->selectlist) != POLYGON) {
4996 Tcl_SetResult(interp, "Selected element is not a polygon", NULL);
4997 return TCL_ERROR;
4999 else
5000 ppoly = SELTOPOLY(areawin->selectlist);
5002 if ((objc - nidx) == 1) {
5003 objPtr = Tcl_NewIntObj(ppoly->number);
5004 Tcl_SetObjResult(interp, objPtr);
5006 else
5008 Tcl_SetResult(interp, "Cannot change number of points.\n", NULL);
5009 return TCL_ERROR;
5012 break;
5014 case PointsIdx:
5015 if (areawin->selects != 1) {
5016 Tcl_SetResult(interp, "Must have exactly one selection to "
5017 "query or manipulate points", NULL);
5018 return TCL_ERROR;
5020 else {
5021 ppoly = SELTOPOLY(areawin->selectlist);
5022 MakeHierCTM(&hierCTM);
5023 if (ppoly->type != POLYGON) {
5024 Tcl_SetResult(interp, "Selected element is not a polygon", NULL);
5025 return TCL_ERROR;
5027 points = ppoly->points;
5029 if ((objc - nidx) == 1) /* Return a list of all points */
5031 objPtr = Tcl_NewListObj(0, NULL);
5032 for (npoints = 0; npoints < ppoly->number; npoints++) {
5033 cpair = Tcl_NewListObj(0, NULL);
5034 UTransformbyCTM(&hierCTM, points + npoints, &ppt, 1);
5035 coord = Tcl_NewIntObj((int)ppt.x);
5036 Tcl_ListObjAppendElement(interp, cpair, coord);
5037 coord = Tcl_NewIntObj((int)ppt.y);
5038 Tcl_ListObjAppendElement(interp, cpair, coord);
5039 Tcl_ListObjAppendElement(interp, objPtr, cpair);
5041 Tcl_SetObjResult(interp, objPtr);
5043 else if ((objc - nidx) == 2) /* Return a specific point */
5045 result = Tcl_GetIntFromObj(interp, objv[2], &npoints);
5046 if (result != TCL_OK) return result;
5047 if (npoints >= ppoly->number) {
5048 Tcl_SetResult(interp, "Point number out of range", NULL);
5049 return TCL_ERROR;
5051 objPtr = Tcl_NewListObj(0, NULL);
5052 UTransformbyCTM(&hierCTM, points + npoints, &ppt, 1);
5053 coord = Tcl_NewIntObj((int)ppt.x);
5054 Tcl_ListObjAppendElement(interp, objPtr, coord);
5055 coord = Tcl_NewIntObj((int)ppt.y);
5056 Tcl_ListObjAppendElement(interp, objPtr, coord);
5057 Tcl_SetObjResult(interp, objPtr);
5059 else
5061 Tcl_SetResult(interp, "Individual point setting unimplemented\n", NULL);
5062 return TCL_ERROR;
5065 break;
5067 return XcTagCallback(interp, objc, objv);
5070 /*----------------------------------------------------------------------*/
5072 int xctcl_spline(ClientData clientData, Tcl_Interp *interp,
5073 int objc, Tcl_Obj *CONST objv[])
5075 int idx, nidx, result, j, npoints;
5076 splineptr newspline, pspline;
5077 XPoint ppt, ctrlpoints[4];
5078 Tcl_Obj *objPtr, *cpair, *coord, **newobjv;
5079 Matrix hierCTM;
5081 static char *subCmds[] = {"make", "border", "fill", "points", NULL};
5082 enum SubIdx {
5083 MakeIdx, BorderIdx, FillIdx, PointsIdx
5086 nidx = 5;
5087 result = ParseElementArguments(interp, objc, objv, &nidx, SPLINE);
5088 if (result != TCL_OK) return result;
5090 if ((result = Tcl_GetIndexFromObj(interp, objv[nidx],
5091 (CONST84 char **)subCmds,
5092 "option", 0, &idx)) != TCL_OK)
5093 return result;
5095 /* h = spline make {x1 y1} ... {x4 y4} */
5097 switch (idx) {
5098 case MakeIdx:
5099 if ((areawin->selects == 0) && (nidx == 1)) {
5100 if (objc != 6) {
5101 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
5102 return TCL_ERROR;
5104 for (j = 0; j < 4; j++) {
5105 result = GetPositionFromList(interp, objv[2 + j], &ppt);
5106 if (result == TCL_OK) {
5107 ctrlpoints[j].x = ppt.x;
5108 ctrlpoints[j].y = ppt.y;
5111 newspline = new_spline(NULL, ctrlpoints);
5112 singlebbox((genericptr *)&newspline);
5114 objPtr = Tcl_NewHandleObj(newspline);
5115 Tcl_SetObjResult(interp, objPtr);
5117 else if (areawin->selects == 1) {
5118 if (ELEMENTTYPE(*(topobject->plist + (*areawin->selectlist))) == POLYGON) {
5119 converttocurve();
5121 else {
5122 Tcl_SetResult(interp, "\"spline make\": must have a polygon selected",
5123 NULL);
5124 return TCL_ERROR;
5127 else if (nidx == 2) {
5128 Tcl_SetResult(interp, "\"spline <handle> make\" is illegal", NULL);
5129 return TCL_ERROR;
5131 else {
5132 Tcl_SetResult(interp, "No selections allowed except single polygon", NULL);
5133 return TCL_ERROR;
5135 break;
5137 case BorderIdx:
5138 newobjv = (Tcl_Obj **)(&objv[nidx]);
5139 result = xctcl_doborder(clientData, interp, objc - nidx, newobjv);
5140 break;
5142 case FillIdx:
5143 newobjv = (Tcl_Obj **)(&objv[nidx]);
5144 result = xctcl_dofill(clientData, interp, objc - nidx, newobjv);
5145 break;
5147 case PointsIdx:
5148 if (areawin->selects != 1) {
5149 Tcl_SetResult(interp, "Must have exactly one selection to "
5150 "query or manipulate points", NULL);
5151 return TCL_ERROR;
5153 else {
5154 /* check for ESPLINE mode? */
5155 if (SELECTTYPE(areawin->selectlist) != SPLINE) {
5156 Tcl_SetResult(interp, "Selected element is not a spline", NULL);
5157 return TCL_ERROR;
5159 else
5160 pspline = SELTOSPLINE(areawin->selectlist);
5162 MakeHierCTM(&hierCTM);
5164 if ((objc - nidx) == 1) /* Return a list of all points */
5166 objPtr = Tcl_NewListObj(0, NULL);
5167 for (npoints = 0; npoints < 4; npoints++) {
5168 cpair = Tcl_NewListObj(0, NULL);
5169 UTransformbyCTM(&hierCTM, pspline->ctrl + npoints, &ppt, 1);
5170 coord = Tcl_NewIntObj((int)ppt.x);
5171 Tcl_ListObjAppendElement(interp, cpair, coord);
5172 coord = Tcl_NewIntObj((int)ppt.y);
5173 Tcl_ListObjAppendElement(interp, cpair, coord);
5174 Tcl_ListObjAppendElement(interp, objPtr, cpair);
5176 Tcl_SetObjResult(interp, objPtr);
5178 else if ((objc - nidx) == 2) /* Return a specific point */
5180 result = Tcl_GetIntFromObj(interp, objv[objc - nidx + 1], &npoints);
5181 if (result != TCL_OK) return result;
5182 if (npoints >= 4) {
5183 Tcl_SetResult(interp, "Point number out of range", NULL);
5184 return TCL_ERROR;
5186 objPtr = Tcl_NewListObj(0, NULL);
5187 UTransformbyCTM(&hierCTM, pspline->ctrl + npoints, &ppt, 1);
5188 coord = Tcl_NewIntObj((int)ppt.x);
5189 Tcl_ListObjAppendElement(interp, objPtr, coord);
5190 coord = Tcl_NewIntObj((int)ppt.y);
5191 Tcl_ListObjAppendElement(interp, objPtr, coord);
5192 Tcl_SetObjResult(interp, objPtr);
5194 else
5196 Tcl_SetResult(interp, "Individual control point setting "
5197 "unimplemented\n", NULL);
5198 return TCL_ERROR;
5202 return XcTagCallback(interp, objc, objv);
5205 /*----------------------------------------------------------------------*/
5207 int xctcl_graphic(ClientData clientData, Tcl_Interp *interp,
5208 int objc, Tcl_Obj *CONST objv[])
5210 int i, idx, nidx, result;
5211 double dvalue;
5212 graphicptr newgp, gp;
5213 XPoint ppt;
5214 Tcl_Obj *objPtr, *listPtr;
5215 char *filename;
5217 static char *subCmds[] = {"make", "scale", "position", NULL};
5218 enum SubIdx {
5219 MakeIdx, ScaleIdx, PositionIdx
5222 nidx = 7;
5223 result = ParseElementArguments(interp, objc, objv, &nidx, GRAPHIC);
5224 if (result != TCL_OK) return result;
5226 if ((result = Tcl_GetIndexFromObj(interp, objv[nidx],
5227 (CONST84 char **)subCmds,
5228 "option", 0, &idx)) != TCL_OK)
5229 return result;
5231 switch (idx) {
5232 case MakeIdx:
5233 if ((areawin->selects == 0) && (nidx == 1)) {
5234 if ((objc != 5) && (objc != 7)) {
5235 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
5236 return TCL_ERROR;
5239 filename = Tcl_GetString(objv[2]);
5241 result = GetPositionFromList(interp, objv[3], &ppt);
5242 if (result != TCL_OK) return result;
5244 result = Tcl_GetDoubleFromObj(interp, objv[4], &dvalue);
5245 if (result != TCL_OK) return result;
5247 if (!strcmp(filename, "gradient")) {
5248 if (objc == 7) {
5249 int c1, c2;
5250 result = GetColorFromObj(interp, objv[5], &c1, TRUE);
5251 if (result != TCL_OK) return result;
5252 result = GetColorFromObj(interp, objv[6], &c2, TRUE);
5253 if (result != TCL_OK) return result;
5254 newgp = gradient_field(NULL, ppt.x, ppt.y, c1, c2);
5256 else
5257 newgp = gradient_field(NULL, ppt.x, ppt.y, 0, 1);
5259 else if (objc != 5) {
5260 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
5261 return TCL_ERROR;
5263 else
5264 newgp = new_graphic(NULL, filename, ppt.x, ppt.y);
5266 if (newgp == NULL) return TCL_ERROR;
5268 newgp->scale = (float)dvalue;
5269 singlebbox((genericptr *)&newgp);
5271 objPtr = Tcl_NewHandleObj(newgp);
5272 Tcl_SetObjResult(interp, objPtr);
5274 else if (nidx == 2) {
5275 Tcl_SetResult(interp, "\"graphic <handle> make\" is illegal", NULL);
5276 return TCL_ERROR;
5278 else {
5279 Tcl_SetResult(interp, "No selections allowed", NULL);
5280 return TCL_ERROR;
5282 break;
5284 case ScaleIdx:
5285 case PositionIdx:
5286 if ((areawin->selects == 0) && (nidx == 1)) {
5287 Tcl_SetResult(interp, "Must have a graphic selection.", NULL);
5288 return TCL_ERROR;
5290 if (objc == nidx + 1) { /* Return position of graphic origin */
5291 Tcl_Obj *cpair;
5292 graphicptr gp;
5294 listPtr = Tcl_NewListObj(0, NULL);
5295 for (i = 0; i < areawin->selects; i++) {
5296 if (SELECTTYPE(areawin->selectlist + i) != GRAPHIC) continue;
5297 gp = SELTOGRAPHIC(areawin->selectlist + i);
5299 switch (idx) {
5300 case ScaleIdx:
5301 objPtr = Tcl_NewDoubleObj(gp->scale);
5302 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
5303 break;
5304 case PositionIdx:
5305 cpair = Tcl_NewListObj(0, NULL);
5306 objPtr = Tcl_NewIntObj((int)gp->position.x);
5307 Tcl_ListObjAppendElement(interp, cpair, objPtr);
5308 objPtr = Tcl_NewIntObj((int)gp->position.y);
5309 Tcl_ListObjAppendElement(interp, cpair, objPtr);
5310 Tcl_ListObjAppendElement(interp, listPtr, cpair);
5311 break;
5314 Tcl_SetObjResult(interp, listPtr);
5316 else if (objc == nidx + 2) { /* Change position or scale */
5317 if (idx == ScaleIdx) {
5318 result = Tcl_GetDoubleFromObj(interp, objv[nidx + 1], &dvalue);
5319 if (result == TCL_OK) {
5320 for (i = 0; i < areawin->selects; i++) {
5321 float oldscale;
5323 if (SELECTTYPE(areawin->selectlist + i) != GRAPHIC) continue;
5324 gp = SELTOGRAPHIC(areawin->selectlist + i);
5325 oldscale = gp->scale;
5326 gp->scale = (float)dvalue;
5327 if (gp->scale != oldscale) {
5328 #ifndef HAVE_CAIRO
5329 gp->valid = False;
5330 #endif /* !HAVE_CAIRO */
5331 drawarea(areawin->area, (caddr_t)clientData, (caddr_t)NULL);
5332 calcbboxvalues(areawin->topinstance,
5333 topobject->plist + *(areawin->selectlist + i));
5334 register_for_undo(XCF_Rescale, UNDO_MORE, areawin->topinstance,
5335 (genericptr)gp, (double)oldscale);
5338 undo_finish_series();
5341 else {
5342 result = GetPositionFromList(interp, objv[nidx + 1], &ppt);
5343 if (result == TCL_OK) {
5344 for (i = 0; i < areawin->selects; i++) {
5345 if (SELECTTYPE(areawin->selectlist + i) != GRAPHIC) continue;
5346 gp = SELTOGRAPHIC(areawin->selectlist + i);
5347 gp->position.x = ppt.x;
5348 gp->position.y = ppt.y;
5349 calcbboxvalues(areawin->topinstance,
5350 topobject->plist + *(areawin->selectlist + i));
5354 updatepagebounds(topobject);
5355 incr_changes(topobject);
5357 break;
5359 return XcTagCallback(interp, objc, objv);
5362 /*----------------------------------------------------------------------*/
5364 int xctcl_arc(ClientData clientData, Tcl_Interp *interp,
5365 int objc, Tcl_Obj *CONST objv[])
5367 int idx, nidx, result, value;
5368 double angle;
5369 arcptr newarc;
5370 XPoint ppt;
5371 Tcl_Obj *objPtr, *listPtr, **newobjv;
5373 static char *subCmds[] = {"make", "border", "fill", "radius", "minor",
5374 "angle", "position", NULL};
5375 enum SubIdx {
5376 MakeIdx, BorderIdx, FillIdx, RadiusIdx, MinorIdx, AngleIdx,
5377 PositionIdx
5380 nidx = 7;
5381 result = ParseElementArguments(interp, objc, objv, &nidx, ARC);
5382 if (result != TCL_OK) return result;
5384 if ((result = Tcl_GetIndexFromObj(interp, objv[nidx],
5385 (CONST84 char **)subCmds,
5386 "option", 0, &idx)) != TCL_OK)
5387 return result;
5389 switch (idx) {
5390 case MakeIdx:
5391 if ((areawin->selects == 0) && (nidx == 1)) {
5392 if ((objc < 4) || (objc > 7)) {
5393 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
5394 return TCL_ERROR;
5396 result = GetPositionFromList(interp, objv[2], &ppt);
5397 if (result != TCL_OK) return result;
5399 result = Tcl_GetIntFromObj(interp, objv[3], &value);
5400 if (result != TCL_OK) return result;
5402 newarc = new_arc(NULL, value, ppt.x, ppt.y);
5404 switch (objc) {
5405 case 6:
5406 result = Tcl_GetDoubleFromObj(interp, objv[4], &angle);
5407 if (result == TCL_OK) newarc->angle1 = (float)angle;
5408 result = Tcl_GetDoubleFromObj(interp, objv[5], &angle);
5409 if (result == TCL_OK) newarc->angle2 = (float)angle;
5410 break;
5411 case 7:
5412 result = Tcl_GetDoubleFromObj(interp, objv[5], &angle);
5413 if (result == TCL_OK) newarc->angle1 = (float)angle;
5414 result = Tcl_GetDoubleFromObj(interp, objv[6], &angle);
5415 if (result == TCL_OK) newarc->angle2 = (float)angle;
5416 case 5:
5417 result = Tcl_GetIntFromObj(interp, objv[4], &value);
5418 if (result == TCL_OK) newarc->yaxis = value;
5419 break;
5421 if (objc >= 6) {
5422 /* Check that angle2 > angle1. Swap if necessary. */
5423 if (newarc->angle2 < newarc->angle1) {
5424 int tmp = newarc->angle2;
5425 newarc->angle2 = newarc->angle1;
5426 newarc->angle1 = tmp;
5429 /* Check for 0 length chords (assume full circle was intended) */
5430 if (newarc->angle1 == newarc->angle2) {
5431 Tcl_SetResult(interp, "Changed zero-length arc chord!\n", NULL);
5432 newarc->angle2 = newarc->angle1 + 360;
5435 /* Normalize */
5436 if (newarc->angle1 >= 360) {
5437 newarc->angle1 -= 360;
5438 newarc->angle2 -= 360;
5440 else if (newarc->angle2 <= 0) {
5441 newarc->angle1 += 360;
5442 newarc->angle2 += 360;
5445 if (objc >= 5) {
5446 calcarc(newarc);
5447 singlebbox((genericptr *)&newarc);
5449 objPtr = Tcl_NewHandleObj(newarc);
5450 Tcl_SetObjResult(interp, objPtr);
5452 else if (nidx == 2) {
5453 Tcl_SetResult(interp, "\"arc <handle> make\" is illegal", NULL);
5454 return TCL_ERROR;
5456 else {
5457 Tcl_SetResult(interp, "No selections allowed", NULL);
5458 return TCL_ERROR;
5460 break;
5462 case BorderIdx:
5463 newobjv = (Tcl_Obj **)(&objv[nidx]);
5464 result = xctcl_doborder(clientData, interp, objc - nidx, newobjv);
5465 break;
5467 case FillIdx:
5468 newobjv = (Tcl_Obj **)(&objv[nidx]);
5469 result = xctcl_dofill(clientData, interp, objc - nidx, newobjv);
5470 break;
5472 case RadiusIdx:
5473 case MinorIdx:
5474 case AngleIdx:
5475 case PositionIdx:
5476 if ((areawin->selects == 0) && (nidx == 1)) {
5477 Tcl_SetResult(interp, "Must have an arc selection.", NULL);
5478 return TCL_ERROR;
5480 if (objc == nidx + 1) { /* Return position of arc center */
5481 Tcl_Obj *cpair;
5482 int i;
5483 arcptr parc;
5485 listPtr = Tcl_NewListObj(0, NULL);
5486 for (i = 0; i < areawin->selects; i++) {
5487 if (SELECTTYPE(areawin->selectlist + i) != ARC) continue;
5488 parc = SELTOARC(areawin->selectlist + i);
5490 switch (idx) {
5491 case RadiusIdx:
5492 objPtr = Tcl_NewIntObj(parc->radius);
5493 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
5494 break;
5495 case MinorIdx:
5496 objPtr = Tcl_NewIntObj(parc->yaxis);
5497 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
5498 break;
5499 case AngleIdx:
5500 cpair = Tcl_NewListObj(0, NULL);
5501 objPtr = Tcl_NewDoubleObj(parc->angle1);
5502 Tcl_ListObjAppendElement(interp, cpair, objPtr);
5503 objPtr = Tcl_NewDoubleObj(parc->angle2);
5504 Tcl_ListObjAppendElement(interp, cpair, objPtr);
5505 Tcl_ListObjAppendElement(interp, listPtr, cpair);
5506 break;
5507 case PositionIdx:
5508 cpair = Tcl_NewListObj(0, NULL);
5509 objPtr = Tcl_NewIntObj((int)parc->position.x);
5510 Tcl_ListObjAppendElement(interp, cpair, objPtr);
5511 objPtr = Tcl_NewIntObj((int)parc->position.y);
5512 Tcl_ListObjAppendElement(interp, cpair, objPtr);
5513 Tcl_ListObjAppendElement(interp, listPtr, cpair);
5514 break;
5517 Tcl_SetObjResult(interp, listPtr);
5519 break;
5521 return XcTagCallback(interp, objc, objv);
5524 /*----------------------------------------------------------------------*/
5526 int xctcl_path(ClientData clientData, Tcl_Interp *interp,
5527 int objc, Tcl_Obj *CONST objv[])
5529 int idx, nidx, result, j, i;
5530 genericptr newgen, *eptr;
5531 pathptr ppath;
5532 Tcl_Obj *elist, *objPtr, *cpair, *coord, **newobjv;
5533 XPoint ppt;
5534 Matrix hierCTM;
5536 static char *subCmds[] = {"join", "make", "border", "fill", "point", "unjoin",
5537 "points", NULL};
5538 enum SubIdx {
5539 JoinIdx, MakeIdx, BorderIdx, FillIdx, PointIdx, UnJoinIdx, PointsIdx
5542 nidx = 5;
5543 result = ParseElementArguments(interp, objc, objv, &nidx, PATH);
5544 if (result != TCL_OK) return result;
5546 if ((result = Tcl_GetIndexFromObj(interp, objv[nidx],
5547 (CONST84 char **)subCmds,
5548 "option", 0, &idx)) != TCL_OK)
5549 return result;
5551 switch (idx) {
5552 case MakeIdx: case JoinIdx:
5553 if ((areawin->selects == 0) && (nidx == 1)) {
5554 /* h = path make {element_list} */
5555 newobjv = (Tcl_Obj **)(&objv[1]);
5556 result = ParseElementArguments(interp, objc - 1, newobjv, NULL,
5557 POLYGON | ARC | SPLINE | PATH);
5558 if (result != TCL_OK) return result;
5560 else if (nidx == 2) {
5561 Tcl_SetResult(interp, "\"path <handle> make\" is illegal", NULL);
5562 return TCL_ERROR;
5564 /* h = path make */
5565 join();
5566 newgen = *(topobject->plist + topobject->parts - 1);
5567 objPtr = Tcl_NewHandleObj(newgen);
5568 Tcl_SetObjResult(interp, objPtr);
5569 break;
5571 case BorderIdx:
5572 newobjv = (Tcl_Obj **)(&objv[nidx]);
5573 result = xctcl_doborder(clientData, interp, objc - nidx, newobjv);
5574 break;
5576 case FillIdx:
5577 newobjv = (Tcl_Obj **)(&objv[nidx]);
5578 result = xctcl_dofill(clientData, interp, objc - nidx, newobjv);
5579 break;
5581 case PointIdx:
5582 Tcl_SetResult(interp, "Unimplemented function.", NULL);
5583 return TCL_ERROR;
5584 break;
5586 case UnJoinIdx:
5587 unjoin();
5588 /* Would be nice to return the list of constituent elements. . . */
5589 break;
5591 case PointsIdx:
5592 /* Make a list of the polygon and spline elements in the path, */
5593 /* returning a nested list enumerating the points. This is */
5594 /* ad-hoc, as it does not match any other method of returning */
5595 /* point information about a part. This is because returning */
5596 /* a handle list is useless, since the handles cannot be */
5597 /* accessed directly. */
5599 if (areawin->selects != 1) {
5600 Tcl_SetResult(interp, "Must have exactly one selection to "
5601 "query parts", NULL);
5602 return TCL_ERROR;
5604 else {
5605 if (SELECTTYPE(areawin->selectlist) != PATH) {
5606 Tcl_SetResult(interp, "Selected element is not a path", NULL);
5607 return TCL_ERROR;
5609 else
5610 ppath = SELTOPATH(areawin->selectlist);
5612 MakeHierCTM(&hierCTM);
5614 objPtr = Tcl_NewListObj(0, NULL);
5615 for (j = 0; j < ppath->parts; j++) {
5616 eptr = (genericptr *)(ppath->plist + j);
5617 elist = Tcl_NewListObj(0, NULL);
5618 if ((*eptr)->type == POLYGON) {
5619 polyptr ppoly;
5620 ppoly = (polyptr)(*eptr);
5621 Tcl_ListObjAppendElement(interp, elist,
5622 Tcl_NewStringObj("polygon", -1));
5623 for (i = 0; i < ppoly->number; i++) {
5624 cpair = Tcl_NewListObj(0, NULL);
5625 UTransformbyCTM(&hierCTM, ppoly->points + i, &ppt, 1);
5626 coord = Tcl_NewIntObj((int)ppt.x);
5627 Tcl_ListObjAppendElement(interp, cpair, coord);
5628 coord = Tcl_NewIntObj((int)ppt.y);
5629 Tcl_ListObjAppendElement(interp, cpair, coord);
5630 Tcl_ListObjAppendElement(interp, elist, cpair);
5633 else {
5634 splineptr pspline;
5635 pspline = (splineptr)(*eptr);
5636 Tcl_ListObjAppendElement(interp, elist,
5637 Tcl_NewStringObj("spline", -1));
5638 for (i = 0; i < 4; i++) {
5639 cpair = Tcl_NewListObj(0, NULL);
5640 UTransformbyCTM(&hierCTM, pspline->ctrl + i, &ppt, 1);
5641 coord = Tcl_NewIntObj((int)ppt.x);
5642 Tcl_ListObjAppendElement(interp, cpair, coord);
5643 coord = Tcl_NewIntObj((int)ppt.y);
5644 Tcl_ListObjAppendElement(interp, cpair, coord);
5645 Tcl_ListObjAppendElement(interp, elist, cpair);
5648 Tcl_ListObjAppendElement(interp, objPtr, elist);
5650 Tcl_SetObjResult(interp, objPtr);
5652 break;
5654 return XcTagCallback(interp, objc, objv);
5657 /*----------------------------------------------------------------------*/
5659 int xctcl_instance(ClientData clientData, Tcl_Interp *interp,
5660 int objc, Tcl_Obj *CONST objv[])
5662 int i, numfound, idx, nidx, result;
5663 objectptr pobj;
5664 objinstptr pinst, newinst;
5665 short *newselect;
5666 XPoint newpos, ppt;
5667 Tcl_Obj *objPtr;
5668 Matrix hierCTM;
5670 static char *subCmds[] = {"make", "object", "scale", "center", "linewidth",
5671 "bbox", "netlist", NULL};
5672 enum SubIdx {
5673 MakeIdx, ObjectIdx, ScaleIdx, CenterIdx, LineWidthIdx, BBoxIdx, NetListIdx
5676 static char *lwsubCmds[] = {"scale_variant", "variant", "scale_invariant",
5677 "invariant", NULL};
5679 nidx = 3;
5680 result = ParseElementArguments(interp, objc, objv, &nidx, OBJINST);
5681 if (result != TCL_OK) return result;
5683 if ((result = Tcl_GetIndexFromObj(interp, objv[nidx],
5684 (CONST84 char **)subCmds,
5685 "option", 0, &idx)) != TCL_OK)
5686 return result;
5688 switch (idx) {
5689 case MakeIdx:
5690 if ((areawin->selects == 0) && (nidx == 1)) {
5691 if (objc == 3) {
5692 pobj = NameToObject(Tcl_GetString(objv[2]), &pinst, False);
5693 if (pobj == NULL) {
5694 Tcl_SetResult(interp, "no such object ", NULL);
5695 Tcl_AppendResult(interp, Tcl_GetString(objv[2]), NULL);
5696 return TCL_ERROR;
5698 newpos = UGetCursorPos();
5699 u2u_snap(&newpos);
5700 newinst = new_objinst(NULL, pinst, newpos.x, newpos.y);
5701 newinst->color = areawin->color;
5702 newselect = allocselect();
5703 *newselect = (short)(topobject->parts - 1);
5704 draw_normal_selected(topobject, areawin->topinstance);
5705 eventmode = COPY_MODE;
5706 Tk_CreateEventHandler(areawin->area, PointerMotionMask,
5707 (Tk_EventProc *)xctk_drag, NULL);
5708 return XcTagCallback(interp, objc, objv);
5710 else if (objc != 4) {
5711 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
5712 return TCL_ERROR;
5714 pobj = NameToObject(Tcl_GetString(objv[2]), &pinst, False);
5715 if (pobj == NULL) {
5716 Tcl_SetResult(interp, "no such object ", NULL);
5717 Tcl_AppendResult(interp, Tcl_GetString(objv[2]), NULL);
5718 return TCL_ERROR;
5720 result = GetPositionFromList(interp, objv[3], &newpos);
5721 if (result != TCL_OK) return result;
5723 newinst = new_objinst(NULL, pinst, newpos.x, newpos.y);
5724 newinst->color = areawin->color;
5725 singlebbox((genericptr *)&newinst);
5726 objPtr = Tcl_NewHandleObj(newinst);
5727 Tcl_SetObjResult(interp, objPtr);
5729 else if (nidx == 2) {
5730 Tcl_SetResult(interp, "\"instance <handle> make\" is illegal", NULL);
5731 return TCL_ERROR;
5733 else {
5734 Tcl_SetResult(interp, "No selections allowed.", NULL);
5735 return TCL_ERROR;
5737 break;
5739 case ObjectIdx:
5740 if ((objc - nidx) == 1) {
5741 Tcl_Obj *listPtr;
5742 numfound = 0;
5743 for (i = 0; i < areawin->selects; i++) {
5744 if (SELECTTYPE(areawin->selectlist + i) == OBJINST) {
5745 pinst = SELTOOBJINST(areawin->selectlist + i);
5746 objPtr = Tcl_NewStringObj(pinst->thisobject->name, -1);
5747 if (numfound > 0)
5748 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
5749 if ((++numfound) == 1)
5750 listPtr = objPtr;
5753 switch (numfound) {
5754 case 0:
5755 Tcl_SetResult(interp, "Error: no object instances selected", NULL);
5756 return TCL_ERROR;
5757 break;
5758 case 1:
5759 Tcl_SetObjResult(interp, objPtr);
5760 break;
5761 default:
5762 Tcl_SetObjResult(interp, listPtr);
5763 break;
5766 else {
5767 Tcl_Obj *listPtr;
5768 int listlen;
5769 objectptr pobj;
5771 /* If the number of additional arguments matches the number */
5772 /* of selected items, or if there is one additional item */
5773 /* that is a list with a number of items equal to the */
5774 /* number of selected items, then change each element to */
5775 /* the corresponding object in the list. If there is only */
5776 /* one additional item, change all elements to that object. */
5778 if ((objc - nidx) == 1 + areawin->selects) {
5779 // Change each element in turn to the corresponding object
5780 // taken from the command arguments
5781 for (i = 0; i < areawin->selects; i++) {
5782 pobj = NameToObject(Tcl_GetString(objv[2 + i]), NULL, FALSE);
5783 if (pobj == NULL) {
5784 Tcl_SetResult(interp, "Name is not a known object", NULL);
5785 return TCL_ERROR;
5787 pinst = SELTOOBJINST(areawin->selectlist + i);
5788 pinst->thisobject = pobj;
5789 calcbboxinst(pinst);
5792 else if ((objc - nidx) == 2) {
5793 result = Tcl_ListObjLength(interp, objv[2], &listlen);
5794 if (result != TCL_OK) return result;
5795 if (listlen == 1) {
5796 // Check if the indicated object exists
5797 pobj = NameToObject(Tcl_GetString(objv[2]), NULL, FALSE);
5798 if (pobj == NULL) {
5799 Tcl_SetResult(interp, "Name is not a known object", NULL);
5800 return TCL_ERROR;
5803 // Change all selected elements to the object specified
5804 for (i = 0; i < areawin->selects; i++) {
5805 pinst = SELTOOBJINST(areawin->selectlist + i);
5806 pinst->thisobject = pobj;
5807 calcbboxinst(pinst);
5810 else if (listlen != areawin->selects) {
5811 Tcl_SetResult(interp, "Error: list length does not match"
5812 "the number of selected elements.", NULL);
5813 return TCL_ERROR;
5815 else {
5816 // Change each element in turn to the corresponding object
5817 // in the list
5818 for (i = 0; i < areawin->selects; i++) {
5819 result = Tcl_ListObjIndex(interp, objv[2], i, &listPtr);
5820 if (result != TCL_OK) return result;
5822 pobj = NameToObject(Tcl_GetString(listPtr), NULL, FALSE);
5823 if (pobj == NULL) {
5824 Tcl_SetResult(interp, "Name is not a known object", NULL);
5825 return TCL_ERROR;
5827 pinst = SELTOOBJINST(areawin->selectlist + i);
5828 pinst->thisobject = pobj;
5829 calcbboxinst(pinst);
5833 drawarea(areawin->area, NULL, NULL);
5835 break;
5837 case ScaleIdx:
5838 if ((objc - nidx) == 1) {
5839 Tcl_Obj *listPtr;
5840 numfound = 0;
5841 for (i = 0; i < areawin->selects; i++) {
5842 if (SELECTTYPE(areawin->selectlist + i) == OBJINST) {
5843 pinst = SELTOOBJINST(areawin->selectlist + i);
5844 objPtr = Tcl_NewDoubleObj(pinst->scale);
5845 if (numfound > 0)
5846 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
5847 if ((++numfound) == 1)
5848 listPtr = objPtr;
5851 switch (numfound) {
5852 case 0:
5853 Tcl_SetResult(interp, "Error: no object instances selected", NULL);
5854 return TCL_ERROR;
5855 break;
5856 case 1:
5857 Tcl_SetObjResult(interp, objPtr);
5858 break;
5859 default:
5860 Tcl_SetObjResult(interp, listPtr);
5861 break;
5864 else {
5865 strcpy(_STR2, Tcl_GetString(objv[2]));
5866 setosize((Tk_Window)clientData, NULL);
5868 break;
5870 case CenterIdx:
5872 if ((objc - nidx) == 1) {
5873 Tcl_Obj *listPtr, *coord;
5874 numfound = 0;
5875 for (i = 0; i < areawin->selects; i++) {
5876 if (SELECTTYPE(areawin->selectlist + i) == OBJINST) {
5877 pinst = SELTOOBJINST(areawin->selectlist + i);
5878 MakeHierCTM(&hierCTM);
5879 objPtr = Tcl_NewListObj(0, NULL);
5880 UTransformbyCTM(&hierCTM, &pinst->position, &ppt, 1);
5881 coord = Tcl_NewIntObj((int)ppt.x);
5882 Tcl_ListObjAppendElement(interp, objPtr, coord);
5883 coord = Tcl_NewIntObj((int)ppt.y);
5884 Tcl_ListObjAppendElement(interp, objPtr, coord);
5885 if (numfound > 0)
5886 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
5887 if ((++numfound) == 1)
5888 listPtr = objPtr;
5891 switch (numfound) {
5892 case 0:
5893 Tcl_SetResult(interp, "Error: no object instances selected", NULL);
5894 return TCL_ERROR;
5895 break;
5896 case 1:
5897 Tcl_SetObjResult(interp, objPtr);
5898 break;
5899 default:
5900 Tcl_SetObjResult(interp, listPtr);
5901 break;
5904 else if (((objc - nidx) == 2) && (areawin->selects == 1)) {
5905 result = GetPositionFromList(interp, objv[objc - 1], &newpos);
5906 if (result != TCL_OK) return result;
5907 if (SELECTTYPE(areawin->selectlist) == OBJINST) {
5908 pinst = SELTOOBJINST(areawin->selectlist);
5909 MakeHierCTM(&hierCTM);
5910 UTransformbyCTM(&hierCTM, &newpos, &pinst->position, 1);
5913 else {
5914 Tcl_SetResult(interp, "Usage: instance center {x y}; only one"
5915 "instance should be selected.", NULL);
5916 return TCL_ERROR;
5918 break;
5920 case LineWidthIdx:
5921 if ((objc - nidx) == 1) {
5922 Tcl_Obj *listPtr;
5923 numfound = 0;
5924 for (i = 0; i < areawin->selects; i++) {
5925 if (SELECTTYPE(areawin->selectlist + i) == OBJINST) {
5926 pinst = SELTOOBJINST(areawin->selectlist + i);
5927 if (pinst->style & LINE_INVARIANT)
5928 objPtr = Tcl_NewStringObj("scale_invariant", -1);
5929 else
5930 objPtr = Tcl_NewStringObj("scale_variant", -1);
5931 if (numfound > 0)
5932 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
5933 if ((++numfound) == 1)
5934 listPtr = objPtr;
5937 switch (numfound) {
5938 case 0:
5939 Tcl_SetResult(interp, "Error: no object instances selected", NULL);
5940 return TCL_ERROR;
5941 break;
5942 case 1:
5943 Tcl_SetObjResult(interp, objPtr);
5944 break;
5945 default:
5946 Tcl_SetObjResult(interp, listPtr);
5947 break;
5950 else {
5951 int subidx;
5952 if ((result = Tcl_GetIndexFromObj(interp, objv[nidx + 1],
5953 (CONST84 char **)lwsubCmds,
5954 "value", 0, &subidx)) == TCL_OK) {
5955 for (i = 0; i < areawin->selects; i++) {
5956 if (SELECTTYPE(areawin->selectlist + i) == OBJINST) {
5957 pinst = SELTOOBJINST(areawin->selectlist + i);
5958 if (subidx < 2)
5959 pinst->style &= ~LINE_INVARIANT;
5960 else
5961 pinst->style |= LINE_INVARIANT;
5966 break;
5968 case NetListIdx:
5969 if ((objc - nidx) == 1) {
5970 Tcl_Obj *listPtr;
5971 numfound = 0;
5972 for (i = 0; i < areawin->selects; i++) {
5973 if (SELECTTYPE(areawin->selectlist + i) == OBJINST) {
5974 pinst = SELTOOBJINST(areawin->selectlist + i);
5975 objPtr = Tcl_NewBooleanObj((pinst->style & INST_NONETLIST) ?
5976 FALSE : TRUE);
5977 if (numfound > 0)
5978 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
5979 if ((++numfound) == 1)
5980 listPtr = objPtr;
5983 switch (numfound) {
5984 case 0:
5985 Tcl_SetResult(interp, "Error: no object instances selected", NULL);
5986 return TCL_ERROR;
5987 break;
5988 case 1:
5989 Tcl_SetObjResult(interp, objPtr);
5990 break;
5991 default:
5992 Tcl_SetObjResult(interp, listPtr);
5993 break;
5996 else {
5997 int value;
5998 if ((result = Tcl_GetBooleanFromObj(interp, objv[nidx + 1], &value))
5999 == TCL_OK) {
6000 for (i = 0; i < areawin->selects; i++) {
6001 if (SELECTTYPE(areawin->selectlist + i) == OBJINST) {
6002 pinst = SELTOOBJINST(areawin->selectlist + i);
6003 if (value)
6004 pinst->style &= ~INST_NONETLIST;
6005 else
6006 pinst->style |= INST_NONETLIST;
6011 break;
6013 case BBoxIdx:
6014 if ((objc - nidx) == 1) {
6015 Tcl_Obj *listPtr, *coord;
6016 numfound = 0;
6017 for (i = 0; i < areawin->selects; i++) {
6018 if (SELECTTYPE(areawin->selectlist + i) == OBJINST) {
6019 pinst = SELTOOBJINST(areawin->selectlist + i);
6020 objPtr = Tcl_NewListObj(0, NULL);
6021 coord = Tcl_NewIntObj((int)pinst->bbox.lowerleft.x);
6022 Tcl_ListObjAppendElement(interp, objPtr, coord);
6023 coord = Tcl_NewIntObj((int)pinst->bbox.lowerleft.y);
6024 Tcl_ListObjAppendElement(interp, objPtr, coord);
6025 coord = Tcl_NewIntObj((int)(pinst->bbox.lowerleft.x +
6026 pinst->bbox.width));
6027 Tcl_ListObjAppendElement(interp, objPtr, coord);
6028 coord = Tcl_NewIntObj((int)(pinst->bbox.lowerleft.y +
6029 pinst->bbox.height));
6030 Tcl_ListObjAppendElement(interp, objPtr, coord);
6031 if (numfound > 0)
6032 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
6033 if ((++numfound) == 1)
6034 listPtr = objPtr;
6037 switch (numfound) {
6038 case 0:
6039 Tcl_SetResult(interp, "Error: no object instances selected", NULL);
6040 return TCL_ERROR;
6041 break;
6042 case 1:
6043 Tcl_SetObjResult(interp, objPtr);
6044 break;
6045 default:
6046 Tcl_SetObjResult(interp, listPtr);
6047 break;
6050 else {
6051 /* e.g., "instance bbox recompute" */
6052 for (i = 0; i < areawin->selects; i++) {
6053 if (SELECTTYPE(areawin->selectlist + i) == OBJINST) {
6054 pinst = SELTOOBJINST(areawin->selectlist + i);
6055 calcbbox(pinst);
6059 break;
6061 return XcTagCallback(interp, objc, objv);
6064 /*----------------------------------------------------------------------*/
6065 /* "element" configures properties of elements. Note that if the */
6066 /* second argument is not an element handle (pointer), then operations */
6067 /* will be applied to all selected elements. If there is no element */
6068 /* handle and no objects are selected, the operation will be applied */
6069 /* to default settings, like the "xcircuit::set" command. */
6070 /*----------------------------------------------------------------------*/
6072 int xctcl_element(ClientData clientData, Tcl_Interp *interp,
6073 int objc, Tcl_Obj *CONST objv[])
6075 int result, nidx, idx, i, flags;
6076 Tcl_Obj *listPtr;
6077 Tcl_Obj **newobjv;
6078 int newobjc;
6079 genericptr egen;
6080 short *newselect, *tempselect, *orderlist;
6082 /* Commands */
6083 static char *subCmds[] = {
6084 "delete", "copy", "flip", "rotate", "edit", "select", "snap", "move",
6085 "color", "parameters", "raise", "lower", "exchange", "hide", "show",
6086 "handle", "deselect", NULL
6088 enum SubIdx {
6089 DeleteIdx, CopyIdx, FlipIdx, RotateIdx, EditIdx, SelectIdx, SnapIdx,
6090 MoveIdx, ColorIdx, ParamIdx, RaiseIdx, LowerIdx, ExchangeIdx,
6091 HideIdx, ShowIdx, HandleIdx, DeselectIdx
6094 static char *etypes[] = {
6095 "Label", "Polygon", "Bezier Curve", "Object Instance", "Path",
6096 "Arc", "Graphic", NULL /* (jdk) */
6099 /* Before doing a standard parse, we need to check for the single case */
6100 /* "element X deselect"; otherwise, calling ParseElementArguements() */
6101 /* is going to destroy the selection list. */
6103 if ((objc == 3) && (!strcmp(Tcl_GetString(objv[2]), "deselect"))) {
6104 result = xctcl_deselect(clientData, interp, objc, objv);
6105 return result;
6108 /* All other commands are dispatched to individual element commands */
6109 /* for the indicated element or for each selected element. */
6111 nidx = 7;
6112 result = ParseElementArguments(interp, objc, objv, &nidx, ALL_TYPES);
6113 if (result != TCL_OK) return result;
6115 if ((objc - nidx) < 1) {
6116 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
6117 return TCL_ERROR;
6120 if (!strcmp(Tcl_GetString(objv[nidx]), "type")) {
6121 /* Return a list of types of the selected elements */
6123 if (areawin->selects > 1)
6124 listPtr = Tcl_NewListObj(0, NULL);
6126 for (i = 0; i < areawin->selects; i++) {
6127 Tcl_Obj *objPtr;
6128 int idx2, type = SELECTTYPE(areawin->selectlist + i);
6129 switch (type) {
6130 case LABEL: idx2 = 0; break;
6131 case POLYGON: idx2 = 1; break;
6132 case SPLINE: idx2 = 2; break;
6133 case OBJINST: idx2 = 3; break;
6134 case PATH: idx2 = 4; break;
6135 case ARC: idx2 = 5; break;
6136 case GRAPHIC: idx2 = 6; break;
6137 default: return TCL_ERROR;
6139 objPtr = Tcl_NewStringObj(etypes[idx2], strlen(etypes[idx2]));
6140 if (areawin->selects == 1) {
6141 Tcl_SetObjResult(interp, objPtr);
6142 return TCL_OK;
6144 else {
6145 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
6147 Tcl_SetObjResult(interp, listPtr);
6149 return XcTagCallback(interp, objc, objv);
6151 else if (!strcmp(Tcl_GetString(objv[nidx]), "handle")) {
6152 /* Return a list of handles of the selected elements */
6154 listPtr = SelectToTclList(interp, areawin->selectlist, areawin->selects);
6155 Tcl_SetObjResult(interp, listPtr);
6156 return XcTagCallback(interp, objc, objv);
6159 if (Tcl_GetIndexFromObj(interp, objv[nidx],
6160 (CONST84 char **)subCmds,
6161 "option", 0, &idx) == TCL_OK) {
6163 newobjv = (Tcl_Obj **)(&objv[nidx]);
6164 newobjc = objc - nidx;
6166 /* Shift the argument list and call the indicated function. */
6168 switch(idx) {
6169 case DeleteIdx:
6170 result = xctcl_delete(clientData, interp, newobjc, newobjv);
6171 break;
6172 case CopyIdx:
6173 result = xctcl_copy(clientData, interp, newobjc, newobjv);
6174 break;
6175 case FlipIdx:
6176 result = xctcl_flip(clientData, interp, newobjc, newobjv);
6177 break;
6178 case RotateIdx:
6179 result = xctcl_rotate(clientData, interp, newobjc, newobjv);
6180 break;
6181 case EditIdx:
6182 result = xctcl_edit(clientData, interp, newobjc, newobjv);
6183 break;
6184 case ParamIdx:
6185 result = xctcl_param(clientData, interp, newobjc, newobjv);
6186 break;
6187 case HideIdx:
6188 for (i = 0; i < areawin->selects; i++) {
6189 newselect = areawin->selectlist + i;
6190 egen = SELTOGENERIC(newselect);
6191 egen->type |= DRAW_HIDE;
6193 refresh(NULL, NULL, NULL);
6194 break;
6195 case ShowIdx:
6196 if (newobjc == 2) {
6197 if (!strcmp(Tcl_GetString(newobjv[1]), "all")) {
6198 for (i = 0; i < topobject->parts; i++) {
6199 egen = *(topobject->plist + i);
6200 egen->type &= (~DRAW_HIDE);
6204 else {
6205 for (i = 0; i < areawin->selects; i++) {
6206 newselect = areawin->selectlist + i;
6207 egen = SELTOGENERIC(newselect);
6208 egen->type &= (~DRAW_HIDE);
6211 refresh(NULL, NULL, NULL);
6212 break;
6213 case SelectIdx:
6214 if (newobjc == 2) {
6215 if (!strncmp(Tcl_GetString(newobjv[1]), "hide", 4)) {
6216 for (i = 0; i < areawin->selects; i++) {
6217 newselect = areawin->selectlist + i;
6218 egen = SELTOGENERIC(newselect);
6219 egen->type |= SELECT_HIDE;
6222 else if (!strncmp(Tcl_GetString(newobjv[1]), "allow", 5)) {
6223 for (i = 0; i < topobject->parts; i++) {
6224 egen = *(topobject->plist + i);
6225 egen->type &= (~SELECT_HIDE);
6228 else {
6229 Tcl_SetResult(interp, "Select options are \"hide\" "
6230 "and \"allow\"", NULL);
6231 return TCL_ERROR;
6234 /* If nidx == 2, then we've already done the selection! */
6235 else if (nidx == 1)
6236 result = xctcl_select(clientData, interp, newobjc, newobjv);
6237 else
6238 result = TCL_OK;
6239 break;
6240 case DeselectIdx:
6241 /* case nidx == 2 was already taken care of. case nidx == 1 */
6242 /* implies "deselect all". */
6243 unselect_all();
6244 result = TCL_OK;
6245 break;
6246 case ColorIdx:
6247 result = xctcl_color(clientData, interp, newobjc, newobjv);
6248 break;
6249 case SnapIdx:
6250 snapelement();
6251 break;
6252 case ExchangeIdx:
6253 exchange();
6254 break;
6255 case LowerIdx:
6257 /* Improved method thanks to Dimitri Princen */
6259 /* First move the selected parts to the bottom. This sets */
6260 /* all the values pointed by (selectlist + i) to zero, and */
6261 /* inverts the order between the selected elements. */
6262 /* Finally *tempselect += i inverts the original numbering, */
6263 /* so the second loop inverts the placing again, regaining */
6264 /* the correct order (and writes it so). */
6265 /* */
6266 /* RaiseIdx works similar but starts from the top. */
6268 if (newobjc == 2) {
6269 if (!strcmp(Tcl_GetString(newobjv[1]), "all")) {
6270 orderlist = (short *)malloc(topobject->parts * sizeof(short));
6271 for (i = 0; i < topobject->parts; i++) *(orderlist + i) = i;
6273 for (i = 0; i < areawin->selects; i++) {
6274 tempselect = areawin->selectlist + i;
6275 xc_bottom(tempselect, orderlist);
6276 *tempselect += i;
6278 for (i = 0; i < areawin->selects; i++) {
6279 tempselect = areawin->selectlist + i;
6280 xc_bottom(tempselect, orderlist);
6281 *tempselect += (areawin->selects - 1 - i);
6283 register_for_undo(XCF_Reorder, UNDO_MORE, areawin->topinstance,
6284 orderlist, topobject->parts);
6287 else {
6288 xc_lower();
6290 break;
6292 case RaiseIdx:
6294 /* Improved method thanks to Dimitri Princen */
6296 if (newobjc == 2) {
6297 if (!strcmp(Tcl_GetString(newobjv[1]), "all")) {
6298 orderlist = (short *)malloc(topobject->parts * sizeof(short));
6299 for (i = 0; i < topobject->parts; i++) *(orderlist + i) = i;
6301 for (i = areawin->selects - 1; i >= 0 ; i--) {
6302 tempselect = areawin->selectlist + i;
6303 xc_top(tempselect, orderlist);
6304 *tempselect -= (areawin->selects - 1 - i);
6306 for (i = areawin->selects - 1; i >= 0 ; i--) {
6307 tempselect = areawin->selectlist + i;
6308 xc_top(tempselect, orderlist);
6309 *tempselect -= i;
6311 register_for_undo(XCF_Reorder, UNDO_MORE, areawin->topinstance,
6312 orderlist, topobject->parts);
6315 else {
6316 xc_raise();
6318 break;
6320 case MoveIdx:
6321 result = xctcl_move(clientData, interp, newobjc, newobjv);
6322 break;
6324 return result;
6327 /* Call each individual element function. */
6328 /* Each function is responsible for filtering the select list to */
6329 /* choose only the appropriate elements. However, we first check */
6330 /* if at least one of that type exists in the list, so the function */
6331 /* won't return an error. */
6333 Tcl_ResetResult(interp);
6335 newobjv = (Tcl_Obj **)(&objv[nidx - 1]);
6336 newobjc = objc - nidx + 1;
6338 flags = 0;
6339 for (i = 0; i < areawin->selects; i++)
6340 flags |= SELECTTYPE(areawin->selectlist + i);
6342 if (flags & LABEL) {
6343 result = xctcl_label(clientData, interp, newobjc, newobjv);
6344 if (result != TCL_OK) return result;
6346 if (flags & POLYGON) {
6347 result = xctcl_polygon(clientData, interp, newobjc, newobjv);
6348 if (result != TCL_OK) return result;
6350 if (flags & OBJINST) {
6351 result = xctcl_instance(clientData, interp, newobjc, newobjv);
6352 if (result != TCL_OK) return result;
6354 if (flags & SPLINE) {
6355 result = xctcl_spline(clientData, interp, newobjc, newobjv);
6356 if (result != TCL_OK) return result;
6358 if (flags & PATH) {
6359 result = xctcl_path(clientData, interp, newobjc, newobjv);
6360 if (result != TCL_OK) return result;
6362 if (flags & ARC) {
6363 result = xctcl_arc(clientData, interp, newobjc, newobjv);
6365 if (flags & GRAPHIC) {
6366 result = xctcl_graphic(clientData, interp, newobjc, newobjv);
6368 return result;
6371 /*----------------------------------------------------------------------*/
6372 /* "config" manipulates a whole bunch of option settings. */
6373 /*----------------------------------------------------------------------*/
6375 int xctcl_config(ClientData clientData, Tcl_Interp *interp,
6376 int objc, Tcl_Obj *CONST objv[])
6378 int tmpint, i;
6379 int result, idx;
6380 char *tmpstr, buffer[30], **sptr;
6381 Pagedata *curpage;
6383 static char *boxsubCmds[] = {"manhattan", "rhomboidx", "rhomboidy",
6384 "rhomboida", "normal", NULL};
6385 static char *pathsubCmds[] = {"tangents", "normal", NULL};
6386 static char *coordsubCmds[] = {"decimal inches", "fractional inches",
6387 "centimeters", "internal units", NULL};
6388 static char *filterTypes[] = {"instances", "labels", "polygons", "arcs",
6389 "splines", "paths", "graphics", NULL};
6390 static char *searchOpts[] = {"files", "lib", "libs", "library", "libraries", NULL};
6392 static char *subCmds[] = {
6393 "axis", "axes", "grid", "snap", "bbox", "editinplace",
6394 "pinpositions", "pinattach", "clipmasks", "boxedit", "pathedit", "linewidth",
6395 "colorscheme", "coordstyle", "drawingscale", "manhattan", "centering",
6396 "filter", "buschar", "backup", "search", "focus", "init",
6397 "delete", "windownames", "hold", "database", "suspend",
6398 "technologies", "fontnames", "debug", NULL
6400 enum SubIdx {
6401 AxisIdx, AxesIdx, GridIdx, SnapIdx, BBoxIdx, EditInPlaceIdx,
6402 PinPosIdx, PinAttachIdx, ShowClipIdx, BoxEditIdx, PathEditIdx, LineWidthIdx,
6403 ColorSchemeIdx, CoordStyleIdx, ScaleIdx, ManhattanIdx, CenteringIdx,
6404 FilterIdx, BusCharIdx, BackupIdx, SearchIdx, FocusIdx,
6405 InitIdx, DeleteIdx, WindowNamesIdx, HoldIdx, DatabaseIdx,
6406 SuspendIdx, TechnologysIdx, FontNamesIdx, DebugIdx
6409 if ((objc == 1) || (objc > 5)) {
6410 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
6411 return TCL_ERROR;
6413 if (Tcl_GetIndexFromObj(interp, objv[1],
6414 (CONST84 char **)subCmds,
6415 "option", 0, &idx) != TCL_OK) {
6416 return TCL_ERROR;
6419 /* Set curpage for those routines that need it */
6421 switch(idx) {
6422 case GridIdx:
6423 case SnapIdx:
6424 case LineWidthIdx:
6425 case CoordStyleIdx:
6426 case ScaleIdx:
6427 if (areawin == NULL) {
6428 Tcl_SetResult(interp, "No current window set, assuming default\n",
6429 NULL);
6430 curpage = xobjs.pagelist[0];
6431 if (curpage == NULL) return TCL_ERROR;
6433 else
6434 curpage = xobjs.pagelist[areawin->page];
6435 break;
6438 /* Check number of arguments wholesale (to be done) */
6440 switch(idx) {
6441 case SuspendIdx:
6442 if (objc == 2) {
6443 switch (xobjs.suspend) {
6444 case -1:
6445 Tcl_SetResult(interp, "normal drawing", NULL);
6446 break;
6447 case 0:
6448 Tcl_SetResult(interp, "drawing suspended", NULL);
6449 break;
6450 case 1:
6451 Tcl_SetResult(interp, "refresh pending", NULL);
6452 break;
6453 case 2:
6454 Tcl_SetResult(interp, "drawing locked", NULL);
6455 break;
6458 else {
6459 result = Tcl_GetBooleanFromObj(interp, objv[2], &tmpint);
6460 if (result != TCL_OK) return result;
6461 if (tmpint == 0) {
6463 /* Pending drawing */
6465 if (xobjs.suspend == 1) {
6466 xobjs.suspend = -1;
6467 refresh(NULL, NULL, NULL);
6469 else
6470 xobjs.suspend = -1;
6472 else {
6473 /* Calling "config suspend true" twice effectively */
6474 /* locks the graphics in a state that can only be */
6475 /* removed by a call to "config suspend false". */
6476 if (xobjs.suspend >= 0)
6477 xobjs.suspend = 2;
6478 else
6479 xobjs.suspend = 0;
6482 break;
6484 case DatabaseIdx:
6485 /* Regenerate the database of colors, fonts, etc. from Tk options */
6486 if (objc == 3) {
6487 Tk_Window tkwind, tktop;
6489 tktop = Tk_MainWindow(interp);
6490 tkwind = Tk_NameToWindow(interp, Tcl_GetString(objv[2]), tktop);
6491 build_app_database(tkwind);
6492 setcolorscheme(!areawin->invert);
6494 break;
6496 case FontNamesIdx:
6497 /* To do: Return a list of known font names. The Tk wrapper uses */
6498 /* this list to regenerate the font menu for each new window. */
6499 break;
6501 case WindowNamesIdx:
6502 /* Generate and return a list of existing window names */
6504 if (objc == 2) {
6505 XCWindowData *winptr;
6506 for (winptr = xobjs.windowlist; winptr != NULL; winptr = winptr->next)
6507 Tcl_AppendElement(interp, Tk_PathName(winptr->area));
6509 break;
6511 case DeleteIdx:
6512 if (objc == 3) {
6513 XCWindowData *winptr;
6514 Tk_Window tkwind, tktop;
6516 tktop = Tk_MainWindow(interp);
6517 tkwind = Tk_NameToWindow(interp, Tcl_GetString(objv[2]), tktop);
6518 for (winptr = xobjs.windowlist; winptr != NULL; winptr = winptr->next) {
6519 if (winptr->area == tkwind) {
6520 delete_window(winptr);
6521 break;
6524 if (winptr == NULL) {
6525 Tcl_SetResult(interp, "No such window\n", NULL);
6526 return TCL_ERROR;
6529 break;
6531 case DebugIdx:
6532 #ifdef ASG
6533 if (objc == 3) {
6534 result = Tcl_GetIntFromObj(interp, objv[2], &tmpint);
6535 if (result != TCL_OK) return result;
6536 SetDebugLevel(&tmpint);
6538 else {
6539 Tcl_SetObjResult(interp, Tcl_NewIntObj(SetDebugLevel(NULL)));
6541 #endif
6542 break;
6545 case InitIdx:
6546 /* Create a data structure for a new drawing window. */
6547 /* Give it the same page number and view as the current window */
6549 if (objc == 3) {
6550 XCWindowData *newwin, *savewin;
6551 savewin = areawin; // In case focus callback overwrites areawin.
6552 newwin = GUI_init(objc - 2, objv + 2);
6553 if (newwin != NULL) {
6554 newwin->page = savewin->page;
6555 newwin->vscale = savewin->vscale;
6556 newwin->pcorner = savewin->pcorner;
6557 newwin->topinstance = savewin->topinstance;
6559 else {
6560 Tcl_SetResult(interp, "Unable to create new window structure\n", NULL);
6561 return TCL_ERROR;
6564 break;
6566 case FocusIdx:
6567 if (objc == 2) {
6568 Tcl_SetResult(interp, Tk_PathName(areawin->area), NULL);
6570 else if (objc == 3) {
6571 Tk_Window tkwind, tktop;
6572 XCWindowData *winptr;
6573 XPoint locsave;
6575 tktop = Tk_MainWindow(interp);
6576 tkwind = Tk_NameToWindow(interp, Tcl_GetString(objv[2]), tktop);
6577 /* (Diagnostic) */
6578 /* printf("Focusing: %s\n", Tcl_GetString(objv[2])); */
6579 for (winptr = xobjs.windowlist; winptr != NULL; winptr = winptr->next) {
6580 if (winptr->area == tkwind) {
6581 int savemode;
6582 objectptr savestack;
6584 if (areawin == winptr) break;
6585 else if (areawin == NULL) {
6586 areawin = winptr;
6587 break;
6589 if ((eventmode == MOVE_MODE || eventmode == COPY_MODE) &&
6590 winptr->editstack->parts == 0) {
6591 locsave = areawin->save;
6592 delete_for_xfer(NORMAL, areawin->selectlist, areawin->selects);
6593 /* Swap editstacks */
6594 savestack = winptr->editstack;
6595 winptr->editstack = areawin->editstack;
6596 areawin->editstack = savestack;
6597 savemode = eventmode;
6598 eventmode = NORMAL_MODE;
6600 /* Change event handlers */
6601 xcRemoveEventHandler(areawin->area, PointerMotionMask, False,
6602 (xcEventHandler)xctk_drag, NULL);
6603 drawarea(areawin->area, NULL, NULL);
6604 Tk_CreateEventHandler(winptr->area, PointerMotionMask,
6605 (Tk_EventProc *)xctk_drag, NULL);
6607 /* Set new window */
6608 areawin = winptr;
6609 eventmode = savemode;
6610 areawin->save = locsave;
6611 transferselects();
6612 drawarea(areawin->area, NULL, NULL);
6614 else
6615 areawin = winptr;
6616 break;
6619 if (winptr == NULL) {
6620 Tcl_SetResult(interp, "No such xcircuit drawing window\n", NULL);
6621 return TCL_ERROR;
6624 else {
6625 Tcl_WrongNumArgs(interp, 2, objv, "[window]");
6626 return TCL_ERROR;
6628 break;
6630 case AxisIdx: case AxesIdx:
6631 if (objc == 2) {
6632 Tcl_SetResult(interp, (areawin->axeson) ? "true" : "false", NULL);
6633 break;
6635 else {
6636 result = Tcl_GetBooleanFromObj(interp, objv[2], &tmpint);
6637 if (result != TCL_OK) return result;
6638 areawin->axeson = (Boolean) tmpint;
6640 break;
6642 case GridIdx:
6643 if (objc == 2) {
6644 Tcl_SetResult(interp, (areawin->gridon) ? "true" : "false", NULL);
6645 break;
6647 else {
6648 if (!strncmp("spac", Tcl_GetString(objv[2]), 4)) {
6649 if (objc == 3) {
6650 measurestr((float)curpage->gridspace, buffer);
6651 Tcl_SetObjResult(interp, Tcl_NewStringObj(buffer, strlen(buffer)));
6652 break;
6654 else {
6655 strcpy(_STR2, Tcl_GetString(objv[3]));
6656 setgrid(NULL, &(curpage->gridspace));
6659 else {
6660 result = Tcl_GetBooleanFromObj(interp, objv[2], &tmpint);
6661 if (result != TCL_OK) return result;
6662 areawin->gridon = (Boolean) tmpint;
6665 break;
6667 case SnapIdx:
6668 if (objc == 2) {
6669 Tcl_SetResult(interp, (areawin->snapto) ? "true" : "false", NULL);
6671 else {
6672 if (!strncmp("spac", Tcl_GetString(objv[2]), 4)) {
6673 if (objc == 3) {
6674 measurestr((float)curpage->snapspace, buffer);
6675 Tcl_SetObjResult(interp, Tcl_NewStringObj(buffer, strlen(buffer)));
6676 break;
6678 else {
6679 strcpy(_STR2, Tcl_GetString(objv[3]));
6680 setgrid(NULL, &(curpage->snapspace));
6683 else {
6684 result = Tcl_GetBooleanFromObj(interp, objv[2], &tmpint);
6685 if (result != TCL_OK) return result;
6686 areawin->snapto = (Boolean) tmpint;
6689 break;
6691 case BoxEditIdx:
6692 if (objc == 2) {
6693 switch (areawin->boxedit) {
6694 case MANHATTAN: idx = 0; break;
6695 case RHOMBOIDX: idx = 1; break;
6696 case RHOMBOIDY: idx = 2; break;
6697 case RHOMBOIDA: idx = 3; break;
6698 case NORMAL: idx = 4; break;
6700 Tcl_SetObjResult(interp, Tcl_NewStringObj(boxsubCmds[idx],
6701 strlen(boxsubCmds[idx])));
6703 else if (objc != 3) {
6704 Tcl_WrongNumArgs(interp, 2, objv, "boxedit ?arg ...?");
6705 return TCL_ERROR;
6707 else {
6708 if (Tcl_GetIndexFromObj(interp, objv[2],
6709 (CONST84 char **)boxsubCmds,
6710 "option", 0, &idx) != TCL_OK) {
6711 return TCL_ERROR;
6713 switch (idx) {
6714 case 0: tmpint = MANHATTAN; break;
6715 case 1: tmpint = RHOMBOIDX; break;
6716 case 2: tmpint = RHOMBOIDY; break;
6717 case 3: tmpint = RHOMBOIDA; break;
6718 case 4: tmpint = NORMAL; break;
6720 areawin->boxedit = tmpint;
6722 break;
6724 case PathEditIdx:
6725 if (objc == 2) {
6726 switch (areawin->pathedit) {
6727 case TANGENTS: idx = 0; break;
6728 case NORMAL: idx = 1; break;
6730 Tcl_SetObjResult(interp, Tcl_NewStringObj(pathsubCmds[idx],
6731 strlen(pathsubCmds[idx])));
6733 else if (objc != 3) {
6734 Tcl_WrongNumArgs(interp, 2, objv, "pathedit ?arg ...?");
6735 return TCL_ERROR;
6737 else {
6738 if (Tcl_GetIndexFromObj(interp, objv[2],
6739 (CONST84 char **)pathsubCmds,
6740 "option", 0, &idx) != TCL_OK) {
6741 return TCL_ERROR;
6743 switch (idx) {
6744 case 0: tmpint = TANGENTS; break;
6745 case 1: tmpint = NORMAL; break;
6747 areawin->pathedit = tmpint;
6749 break;
6751 case LineWidthIdx:
6752 if (objc == 2) {
6753 Tcl_SetObjResult(interp,
6754 Tcl_NewDoubleObj((double)curpage->wirewidth / 2.0));
6756 else if (objc != 3) {
6757 Tcl_WrongNumArgs(interp, 3, objv, "linewidth");
6758 return TCL_ERROR;
6760 else {
6761 strcpy(_STR2, Tcl_GetString(objv[2]));
6762 setwidth(NULL, &(curpage->wirewidth));
6764 break;
6766 case BBoxIdx:
6767 if (objc == 2) {
6768 Tcl_SetResult(interp, (areawin->bboxon) ? "visible" : "invisible", NULL);
6770 else {
6771 tmpstr = Tcl_GetString(objv[2]);
6772 if (strstr(tmpstr, "visible"))
6773 tmpint = (tmpstr[0] == 'i') ? False : True;
6774 else {
6775 result = Tcl_GetBooleanFromObj(interp, objv[2], &tmpint);
6776 if (result != TCL_OK) return result;
6778 areawin->bboxon = (Boolean) tmpint;
6780 break;
6782 case HoldIdx:
6783 if (objc == 2) {
6784 Tcl_SetResult(interp, (xobjs.hold) ? "true" : "false", NULL);
6786 else {
6787 result = Tcl_GetBooleanFromObj(interp, objv[2], &tmpint);
6788 if (result != TCL_OK) return result;
6789 xobjs.hold = (Boolean) tmpint;
6791 break;
6793 case EditInPlaceIdx:
6794 if (objc == 2) {
6795 Tcl_SetResult(interp, (areawin->editinplace) ? "true" : "false", NULL);
6797 else {
6798 result = Tcl_GetBooleanFromObj(interp, objv[2], &tmpint);
6799 if (result != TCL_OK) return result;
6800 areawin->editinplace = (Boolean) tmpint;
6802 break;
6804 case ShowClipIdx:
6805 if (objc == 2) {
6806 Tcl_SetResult(interp, (areawin->showclipmasks) ? "show" : "hide", NULL);
6808 else {
6809 tmpstr = Tcl_GetString(objv[2]);
6810 if (!strcmp(tmpstr, "show"))
6811 tmpint = True;
6812 else if (!strcmp(tmpstr, "hide"))
6813 tmpint = False;
6814 else {
6815 result = Tcl_GetBooleanFromObj(interp, objv[2], &tmpint);
6816 if (result != TCL_OK) return result;
6818 areawin->showclipmasks = (Boolean) tmpint;
6820 break;
6822 case PinPosIdx:
6823 if (objc == 2) {
6824 Tcl_SetResult(interp, (areawin->pinpointon) ? "visible" : "invisible", NULL);
6826 else {
6827 tmpstr = Tcl_GetString(objv[2]);
6828 if (strstr(tmpstr, "visible"))
6829 tmpint = (tmpstr[0] == 'i') ? False : True;
6830 else {
6831 result = Tcl_GetBooleanFromObj(interp, objv[2], &tmpint);
6832 if (result != TCL_OK) return result;
6834 areawin->pinpointon = (Boolean) tmpint;
6836 break;
6838 case PinAttachIdx:
6839 if (objc == 2) {
6840 Tcl_SetResult(interp, (areawin->pinattach) ? "true" : "false", NULL);
6842 else {
6843 result = Tcl_GetBooleanFromObj(interp, objv[2], &tmpint);
6844 if (result != TCL_OK) return result;
6845 areawin->pinattach = (Boolean) tmpint;
6847 break;
6849 case ColorSchemeIdx:
6850 if (objc == 2) {
6851 Tcl_SetResult(interp, (areawin->invert) ? "inverse" : "normal", NULL);
6853 else {
6854 tmpstr = Tcl_GetString(objv[2]);
6855 if (!strcmp(tmpstr, "normal") || !strcmp(tmpstr, "standard"))
6856 tmpint = False;
6857 else if (!strcmp(tmpstr, "inverse") || !strcmp(tmpstr, "alternate"))
6858 tmpint = True;
6859 else {
6860 result = Tcl_GetBooleanFromObj(interp, objv[2], &tmpint);
6861 if (result != TCL_OK) return result;
6863 areawin->invert = (Boolean) tmpint;
6864 setcolorscheme(!areawin->invert);
6866 break;
6868 case CoordStyleIdx:
6869 if (objc == 2) {
6870 switch (curpage->coordstyle) {
6871 case DEC_INCH: idx = 0; break;
6872 case FRAC_INCH: idx = 1; break;
6873 case CM: idx = 2; break;
6874 case INTERNAL: idx = 3; break;
6876 Tcl_SetObjResult(interp, Tcl_NewStringObj(coordsubCmds[idx],
6877 strlen(coordsubCmds[idx])));
6879 else if (objc != 3) {
6880 Tcl_WrongNumArgs(interp, 2, objv, "coordstyle ?arg ...?");
6881 return TCL_ERROR;
6883 else {
6884 if (Tcl_GetIndexFromObj(interp, objv[2],
6885 (CONST84 char **)coordsubCmds,
6886 "option", 0, &idx) != TCL_OK) {
6887 return TCL_ERROR;
6889 switch (idx) {
6890 case 0: tmpint = DEC_INCH; break;
6891 case 1: tmpint = FRAC_INCH; break;
6892 case 2: tmpint = CM; break;
6893 case 3: tmpint = INTERNAL; break;
6895 getgridtype(NULL, tmpint, NULL);
6897 break;
6899 case ScaleIdx:
6900 if (objc == 2) {
6901 Tcl_Obj *objPtr = Tcl_NewListObj(0, NULL);
6902 Tcl_ListObjAppendElement(interp, objPtr,
6903 Tcl_NewIntObj((int)curpage->drawingscale.x));
6904 Tcl_ListObjAppendElement(interp, objPtr,
6905 Tcl_NewStringObj(":", 1));
6906 Tcl_ListObjAppendElement(interp, objPtr,
6907 Tcl_NewIntObj((int)curpage->drawingscale.y));
6908 Tcl_SetObjResult(interp, objPtr);
6910 else if (objc == 3) {
6911 strcpy(_STR2, Tcl_GetString(objv[2]));
6912 setdscale(NULL, &(curpage->drawingscale));
6914 else {
6915 Tcl_WrongNumArgs(interp, 2, objv, "drawingscale ?arg ...?");
6916 return TCL_ERROR;
6918 break;
6920 case TechnologysIdx:
6921 if (objc == 2) {
6922 Tcl_SetResult(interp, (xobjs.showtech) ? "true" : "false", NULL);
6924 else {
6925 short libnum;
6927 result = Tcl_GetBooleanFromObj(interp, objv[2], &tmpint);
6928 if (result != TCL_OK) return result;
6929 if (xobjs.showtech != (Boolean) tmpint) {
6930 xobjs.showtech = (Boolean) tmpint;
6932 /* When namespaces are included, the length of the printed */
6933 /* name may cause names to overlap, so recompose each */
6934 /* library when the showtech flag is changed. */
6935 for (libnum = 0; libnum < xobjs.numlibs; libnum++)
6936 composelib(LIBRARY + libnum);
6938 if (eventmode == CATALOG_MODE) refresh(NULL, NULL, NULL);
6941 break;
6943 case ManhattanIdx:
6944 if (objc == 2) {
6945 Tcl_SetResult(interp, (areawin->manhatn) ? "true" : "false", NULL);
6947 else {
6948 result = Tcl_GetBooleanFromObj(interp, objv[2], &tmpint);
6949 if (result != TCL_OK) return result;
6950 areawin->manhatn = (Boolean) tmpint;
6952 break;
6954 case CenteringIdx:
6955 if (objc == 2) {
6956 Tcl_SetResult(interp, (areawin->center) ? "true" : "false", NULL);
6958 else {
6959 result = Tcl_GetBooleanFromObj(interp, objv[2], &tmpint);
6960 if (result != TCL_OK) return result;
6961 areawin->center = (Boolean) tmpint;
6963 break;
6965 case FilterIdx:
6966 if (objc == 2) {
6967 for (i = 0; i < 6; i++) {
6968 tmpint = 1 << i;
6969 if (areawin->filter & tmpint) {
6970 Tcl_AppendElement(interp, filterTypes[i]);
6974 else if (objc >= 3) {
6975 if (Tcl_GetIndexFromObj(interp, objv[2],
6976 (CONST84 char **)filterTypes,
6977 "filter_type", 0, &tmpint) != TCL_OK) {
6978 return TCL_ERROR;
6980 if (objc == 3) {
6981 if (areawin->filter & (1 << tmpint))
6982 Tcl_SetResult(interp, "true", NULL);
6983 else
6984 Tcl_SetResult(interp, "false", NULL);
6986 else {
6987 int ftype = 1 << tmpint;
6988 if (!strcmp(Tcl_GetString(objv[3]), "true"))
6989 areawin->filter |= ftype;
6990 else
6991 areawin->filter &= (~ftype);
6994 break;
6996 case BusCharIdx:
6997 if (objc == 2) {
6998 buffer[0] = '\\';
6999 buffer[1] = areawin->buschar;
7000 buffer[2] = '\0';
7001 Tcl_SetResult(interp, buffer, TCL_VOLATILE);
7003 else if (objc == 3) {
7004 tmpstr = Tcl_GetString(objv[2]);
7005 areawin->buschar = (tmpstr[0] == '\\') ? tmpstr[1] : tmpstr[0];
7007 break;
7009 case BackupIdx:
7010 if (objc == 2) {
7011 Tcl_SetResult(interp, (xobjs.retain_backup) ? "true" : "false", NULL);
7013 else {
7014 result = Tcl_GetBooleanFromObj(interp, objv[2], &tmpint);
7015 if (result != TCL_OK) return result;
7016 xobjs.retain_backup = (Boolean) tmpint;
7018 break;
7020 case SearchIdx:
7021 if (objc < 3) {
7022 Tcl_WrongNumArgs(interp, 2, objv, "search files|libraries ?arg ...?");
7023 return TCL_ERROR;
7025 if (Tcl_GetIndexFromObj(interp, objv[2],
7026 (CONST84 char **)searchOpts, "options", 0, &idx) != TCL_OK) {
7027 return TCL_ERROR;
7029 sptr = (idx == 0) ? &xobjs.filesearchpath : &xobjs.libsearchpath;
7030 if (objc == 3) {
7031 if (*sptr != NULL) Tcl_SetResult(interp, *sptr, TCL_VOLATILE);
7033 else {
7034 if (*sptr != NULL) free(*sptr);
7035 *sptr = NULL;
7036 tmpstr = Tcl_GetString(objv[3]);
7037 if (strlen(tmpstr) > 0)
7038 *sptr = strdup(Tcl_GetString(objv[3]));
7040 break;
7042 return XcTagCallback(interp, objc, objv);
7045 /*----------------------------------------------------------------------*/
7047 int xctcl_promptsavepage(ClientData clientData, Tcl_Interp *interp,
7048 int objc, Tcl_Obj *CONST objv[])
7050 int page = areawin->page;
7051 int result;
7052 Pagedata *curpage;
7053 objectptr pageobj;
7054 struct stat statbuf;
7056 /* save page popup */
7058 if (objc > 2) {
7059 Tcl_WrongNumArgs(interp, 1, objv, "[page_number]");
7060 return TCL_ERROR;
7062 else if (objc == 2) {
7063 result = Tcl_GetIntFromObj(interp, objv[1], &page);
7064 if (result != TCL_OK) return result;
7066 else page = areawin->page;
7068 curpage = xobjs.pagelist[page];
7069 if (curpage->pageinst == NULL) {
7070 Tcl_SetResult(interp, "Page does not exist. . . cannot save.", NULL);
7071 return TCL_ERROR;
7073 pageobj = curpage->pageinst->thisobject;
7075 /* recompute bounding box and auto-scale, if set */
7077 calcbbox(xobjs.pagelist[page]->pageinst);
7078 if (curpage->pmode & 2) autoscale(page);
7080 /* get file information, if filename is set */
7082 if (curpage->filename != NULL) {
7083 if (strstr(curpage->filename, ".") == NULL)
7084 sprintf(_STR2, "%s.ps", curpage->filename);
7085 else sprintf(_STR2, "%s", curpage->filename);
7086 if (stat(_STR2, &statbuf) == 0) {
7087 Wprintf(" Warning: File exists");
7089 else {
7090 if (errno == ENOTDIR)
7091 Wprintf("Error: Incorrect pathname");
7092 else if (errno == EACCES)
7093 Wprintf("Error: Path not readable");
7094 else
7095 W3printf(" ");
7098 Tcl_SetObjResult(interp, Tcl_NewIntObj((int)page));
7100 return XcTagCallback(interp, objc, objv);
7103 /*----------------------------------------------------------------------*/
7105 int xctcl_quit(ClientData clientData, Tcl_Interp *interp,
7106 int objc, Tcl_Obj *CONST objv[])
7108 Boolean is_intr = False;
7110 /* quit, without checks */
7111 if (objc != 1) {
7112 if (strncasecmp(Tcl_GetString(objv[0]), "intr", 4))
7113 is_intr = True;
7114 else {
7115 Tcl_WrongNumArgs(interp, 1, objv, "(no arguments)");
7116 return TCL_ERROR;
7119 quit(areawin->area, NULL);
7121 if (consoleinterp == interp)
7122 Tcl_Exit(XcTagCallback(interp, objc, objv));
7123 else {
7124 /* Ham-fisted, but prevents hanging on Ctrl-C kill */
7125 if (is_intr) exit(1);
7126 Tcl_Eval(interp, "catch {tkcon eval exit}\n");
7129 return TCL_OK; /* Not reached */
7132 /*----------------------------------------------------------------------*/
7134 int xctcl_promptquit(ClientData clientData, Tcl_Interp *interp,
7135 int objc, Tcl_Obj *CONST objv[])
7137 int result;
7139 /* quit, with checks */
7140 if (objc != 1) {
7141 Tcl_WrongNumArgs(interp, 1, objv, "(no arguments)");
7142 return TCL_ERROR;
7144 if (areawin != NULL) {
7145 result = quitcheck(areawin->area, NULL, NULL);
7146 if (result == 1) {
7147 /* Immediate exit */
7148 if (consoleinterp == interp)
7149 Tcl_Exit(XcTagCallback(interp, objc, objv));
7150 else
7151 Tcl_Eval(interp, "catch {tkcon eval exit}\n");
7154 return XcTagCallback(interp, objc, objv);
7157 /*----------------------------------------------------------------------*/
7159 int xctcl_refresh(ClientData clientData, Tcl_Interp *interp,
7160 int objc, Tcl_Obj *CONST objv[])
7162 /* refresh */
7163 if (objc != 1) {
7164 Tcl_WrongNumArgs(interp, 1, objv, "(no arguments)");
7165 return TCL_ERROR;
7167 areawin->redraw_needed = True;
7168 drawarea(areawin->area, (caddr_t)clientData, (caddr_t)NULL);
7169 if (areawin->scrollbarh)
7170 drawhbar(areawin->scrollbarh, NULL, NULL);
7171 if (areawin->scrollbarv)
7172 drawvbar(areawin->scrollbarv, NULL, NULL);
7173 printname(topobject);
7174 return XcTagCallback(interp, objc, objv);
7177 /*----------------------------------------------------------------------*/
7178 /* Load a schematic that belongs to a symbol referenced by the current */
7179 /* schematic by loading the file pointed to by the "link" parameter */
7180 /* in the symbol. */
7181 /* */
7182 /* Return 1 on success, 0 if the link has already been loaded, and -1 */
7183 /* on failure to find, open, or read the link's schematic. */
7184 /*----------------------------------------------------------------------*/
7186 int loadlinkfile(objinstptr tinst, char *filename, int target, Boolean do_load)
7188 int j, savepage;
7189 FILE *ps;
7190 char file_return[150];
7191 int result;
7192 Boolean fgood;
7194 /* Shorthand: "%n" can be used to indicate that the link filename is */
7195 /* the same as the name of the object, minus technology prefix. */
7196 /* While unlikely to be used, "%N" includes the technology prefix. */
7198 if (!strcmp(filename, "%n")) {
7199 char *suffix = strstr(tinst->thisobject->name, "::");
7200 if (suffix == NULL)
7201 suffix = tinst->thisobject->name;
7202 else
7203 suffix += 2;
7204 strcpy(_STR, suffix);
7206 else if (!strcmp(filename, "%N"))
7207 strcpy(_STR, tinst->thisobject->name);
7208 else
7209 strcpy(_STR, filename);
7211 /* When loading links, we want to avoid */
7212 /* loading the same file more than once, so */
7213 /* compare filename against all existing */
7214 /* page filenames. Also compare links; any */
7215 /* page with a link to the same object is a */
7216 /* duplicate. */
7218 ps = fileopen(_STR, ".ps", file_return, 149);
7219 if (ps != NULL) {
7220 fgood = TRUE;
7221 fclose(ps);
7223 else
7224 fgood = FALSE;
7226 for (j = 0; j < xobjs.pages; j++) {
7227 if (xobjs.pagelist[j]->filename == NULL)
7228 continue;
7229 else if (!strcmp(file_return, xobjs.pagelist[j]->filename))
7230 break;
7231 else if ((strlen(xobjs.pagelist[j]->filename) > 0) &&
7232 !strcmp(file_return + strlen(file_return) - 3, ".ps")
7233 && !strncmp(xobjs.pagelist[j]->filename, file_return,
7234 strlen(file_return) - 3))
7235 break;
7236 else if ((xobjs.pagelist[j]->pageinst != NULL) && (tinst->thisobject ==
7237 xobjs.pagelist[j]->pageinst->thisobject->symschem))
7238 break;
7240 if (j < xobjs.pages) {
7242 /* Duplicate page. Don't load it, but make sure that an association */
7243 /* exists between the symbol and schematic. */
7245 if (tinst->thisobject->symschem == NULL) {
7246 tinst->thisobject->symschem =
7247 xobjs.pagelist[j]->pageinst->thisobject;
7248 if (xobjs.pagelist[j]->pageinst->thisobject->symschem == NULL)
7249 xobjs.pagelist[j]->pageinst->thisobject->symschem = tinst->thisobject;
7251 return 0;
7254 if (fgood == FALSE) {
7255 Fprintf(stderr, "Failed to open dependency \"%s\"\n", _STR);
7256 return -1;
7259 /* Report that a pending link exists, but do not load it. */
7260 if (!do_load) return 1;
7262 savepage = areawin->page;
7263 while (areawin->page < xobjs.pages &&
7264 xobjs.pagelist[areawin->page]->pageinst != NULL &&
7265 xobjs.pagelist[areawin->page]->pageinst->thisobject->parts > 0)
7266 areawin->page++;
7268 changepage(areawin->page);
7269 result = (loadfile(0, (target >= 0) ? target + LIBRARY : -1) == TRUE) ? 1 : -1;
7271 /* Make symschem link if not done by loadfile() */
7273 if (tinst->thisobject->symschem == NULL) {
7274 tinst->thisobject->symschem =
7275 xobjs.pagelist[areawin->page]->pageinst->thisobject;
7277 /* Many symbols may link to one schematic, but a schematic can */
7278 /* only link to one symbol (the first one associated). */
7280 if (xobjs.pagelist[areawin->page]->pageinst->thisobject->symschem == NULL)
7281 xobjs.pagelist[areawin->page]->pageinst->thisobject->symschem
7282 = tinst->thisobject;
7284 changepage(savepage);
7285 return result;
7288 /*----------------------------------------------------------------------*/
7290 int xctcl_page(ClientData clientData, Tcl_Interp *interp,
7291 int objc, Tcl_Obj *CONST objv[])
7293 int result, idx, nidx, aval, i, locidx;
7294 int cpage, multi, savepage, pageno = -1, linktype, importtype;
7295 char *filename, *froot, *astr;
7296 Tcl_Obj *objPtr;
7297 double newheight, newwidth, newscale;
7298 float oldscale;
7299 int newrot, newmode;
7300 objectptr pageobj;
7301 oparamptr ops;
7302 char *oldstr, *newstr, *key, *argv;
7303 Pagedata *curpage, *lpage;
7304 short *pagelist;
7305 u_short changes;
7306 int target = -1;
7307 Boolean forcepage = FALSE;
7309 char *subCmds[] = {
7310 "load", "list", "import", "save", "saveonly", "make", "directory",
7311 "reset", "links", "fit", "filename", "label", "scale", "width",
7312 "height", "size", "margins", "bbox", "goto", "orientation",
7313 "encapsulation", "handle", "update", "changes", NULL
7315 enum SubIdx {
7316 LoadIdx, ListIdx, ImportIdx, SaveIdx, SaveOnlyIdx, MakeIdx, DirIdx,
7317 ResetIdx, LinksIdx, FitIdx, FileIdx, LabelIdx, ScaleIdx,
7318 WidthIdx, HeightIdx, SizeIdx, MarginsIdx, BBoxIdx, GoToIdx,
7319 OrientIdx, EPSIdx, HandleIdx, UpdateIdx, ChangesIdx
7322 char *importTypes[] = {"xcircuit", "postscript", "background", "spice", NULL};
7323 enum ImportTypes {
7324 XCircuitIdx, PostScriptIdx, BackGroundIdx, SPICEIdx
7327 char *linkTypes[] = {"independent", "dependent", "total", "linked",
7328 "pagedependent", "all", "pending", "sheet", "load", NULL};
7329 enum LinkTypes {
7330 IndepIdx, DepIdx, TotalIdx, LinkedIdx, PageDepIdx, AllIdx, PendingIdx,
7331 SheetIdx, LinkLoadIdx
7333 char *psTypes[] = {"eps", "full", NULL};
7335 if (areawin == NULL) {
7336 Tcl_SetResult(interp, "No database!", NULL);
7337 return TCL_ERROR;
7339 savepage = areawin->page;
7341 /* Check for option "-force" (create page if it doesn't exist) */
7342 if (!strncmp(Tcl_GetString(objv[objc - 1]), "-forc", 5)) {
7343 forcepage = TRUE;
7344 objc--;
7347 result = ParsePageArguments(interp, objc, objv, &nidx, &pageno);
7348 if ((result != TCL_OK) || (nidx < 0)) {
7349 if (forcepage && (pageno == xobjs.pages)) {
7350 /* For now, allow a page to be created only if the page number */
7351 /* is one higher than the current last page. */
7352 Tcl_ResetResult(interp);
7353 idx = MakeIdx;
7354 nidx = 0;
7355 pageno = areawin->page; /* so we don't get a segfault */
7357 else
7358 return result;
7360 else if (nidx == 1 && objc == 2) {
7361 idx = GoToIdx;
7363 else if (Tcl_GetIndexFromObj(interp, objv[1 + nidx],
7364 (CONST84 char **)subCmds, "option", 0, &idx) != TCL_OK) {
7365 return result;
7368 result = TCL_OK;
7370 curpage = xobjs.pagelist[pageno];
7372 if (curpage->pageinst != NULL)
7373 pageobj = curpage->pageinst->thisobject;
7374 else {
7375 if (idx != LoadIdx && idx != MakeIdx && idx != DirIdx && idx != GoToIdx) {
7376 Tcl_SetResult(interp, "Cannot do function on non-initialized page.", NULL);
7377 return TCL_ERROR;
7381 switch (idx) {
7382 case HandleIdx:
7383 /* return handle of page instance */
7384 objPtr = Tcl_NewHandleObj(curpage->pageinst);
7385 Tcl_SetObjResult(interp, objPtr);
7386 break;
7388 case ResetIdx:
7389 /* clear page */
7390 resetbutton(NULL, (pointertype)(pageno + 1), NULL);
7391 break;
7393 case ListIdx:
7394 /* return a list of all non-empty pages */
7395 objPtr = Tcl_NewListObj(0, NULL);
7396 for (i = 0; i < xobjs.pages; i++) {
7397 lpage = xobjs.pagelist[i];
7398 if ((lpage != NULL) && (lpage->pageinst != NULL)) {
7399 Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewIntObj(i + 1));
7402 Tcl_SetObjResult(interp, objPtr);
7403 break;
7405 case LoadIdx:
7406 TechReplaceSave();
7407 sprintf(_STR2, Tcl_GetString(objv[2 + nidx]));
7408 for (i = 3 + nidx; i < objc; i++) {
7409 argv = Tcl_GetString(objv[i]);
7410 if ((*argv == '-') && !strncmp(argv, "-repl", 5)) {
7411 if (i < objc - 1) {
7412 char *techstr = Tcl_GetString(objv[i + 1]);
7413 if (!strcmp(techstr, "all") || !strcmp(techstr, "any"))
7414 TechReplaceAll();
7415 else if (!strcmp(techstr, "none")) TechReplaceNone();
7416 else {
7417 TechPtr nsptr = LookupTechnology(techstr);
7418 if (nsptr != NULL) nsptr->flags |= TECH_REPLACE;
7420 i++;
7422 else
7423 TechReplaceAll(); /* replace ALL */
7425 else if ((*argv == '-') && !strncmp(argv, "-targ", 5)) {
7426 if (i < objc - 1) {
7427 ParseLibArguments(interp, 2, &objv[i], NULL, &target);
7428 i++;
7431 else {
7432 strcat(_STR2, ",");
7433 strcat(_STR2, argv);
7437 if (savepage != pageno) newpage(pageno);
7438 startloadfile((target >= 0) ? target + LIBRARY : -1);
7439 if (savepage != pageno) newpage(savepage);
7440 TechReplaceRestore();
7441 break;
7443 case ImportIdx:
7444 if ((objc - nidx) < 3) {
7445 Tcl_WrongNumArgs(interp, 2, objv, "option");
7446 return TCL_ERROR;
7449 if (Tcl_GetIndexFromObj(interp, objv[2 + nidx],
7450 (CONST84 char **)importTypes, "file type",
7451 0, &importtype) != TCL_OK)
7452 return TCL_ERROR;
7454 /* First check the number of arguments, which varies by option. */
7456 switch (importtype) {
7458 /* Xcircuit imports may specify any number of files > 1. */
7460 case XCircuitIdx:
7461 if ((objc - nidx) == 3) {
7462 Tcl_SetResult(interp, "Must specify a filename to import!", NULL);
7463 return TCL_ERROR;
7465 break;
7467 /* Postscript imports may specify 1 or 0 files. 0 causes */
7468 /* the function to report back what file is the background. */
7470 case PostScriptIdx:
7471 case BackGroundIdx:
7472 if ((objc - nidx) != 3 && (objc - nidx) != 4) {
7473 Tcl_SetResult(interp, "Can only specify one filename "
7474 "for background", NULL);
7475 return TCL_ERROR;
7478 /* All other import types must specify exactly one filename. */
7480 default:
7481 if ((objc - nidx) != 4) {
7482 Tcl_SetResult(interp, "Must specify one filename "
7483 "for import", NULL);
7484 return TCL_ERROR;
7486 break;
7489 /* Now process the option */
7491 switch (importtype) {
7492 case XCircuitIdx:
7493 sprintf(_STR2, Tcl_GetString(objv[3 + nidx]));
7494 for (i = 4; i < objc; i++) {
7495 strcat(_STR2, ",");
7496 strcat(_STR2, Tcl_GetString(objv[i + nidx]));
7498 if (savepage != pageno) newpage(pageno);
7499 importfile();
7500 if (savepage != pageno) newpage(savepage);
7501 break;
7502 case PostScriptIdx: /* replaces "background" */
7503 case BackGroundIdx:
7504 if (objc - nidx == 2) {
7505 objPtr = Tcl_NewStringObj(curpage->background.name,
7506 strlen(curpage->background.name));
7507 Tcl_SetObjResult(interp, objPtr);
7508 return XcTagCallback(interp, objc, objv);
7510 sprintf(_STR2, Tcl_GetString(objv[3 + nidx]));
7511 if (savepage != pageno) newpage(pageno);
7512 loadbackground();
7513 if (savepage != pageno) newpage(savepage);
7514 break;
7516 case SPICEIdx:
7517 #ifdef ASG
7518 /* Make sure that the ASG library is present */
7520 if (NameToLibrary(ASG_SPICE_LIB) < 0) {
7521 short ilib;
7523 strcpy(_STR, ASG_SPICE_LIB);
7524 ilib = createlibrary(FALSE);
7525 if (loadlibrary(ilib) == FALSE) {
7526 Tcl_SetResult(interp, "Error loading library.\n", NULL);
7527 return TCL_ERROR;
7532 sprintf(_STR2, Tcl_GetString(objv[3 + nidx]));
7533 if (savepage != pageno) newpage(pageno);
7534 importspice();
7535 if (savepage != pageno) newpage(savepage);
7536 #else
7537 Tcl_SetResult(interp, "ASG not compiled in; "
7538 "function is unavailable.\n", NULL);
7539 return TCL_ERROR;
7540 #endif
7541 break;
7544 /* Redraw */
7545 drawarea(areawin->area, NULL, NULL);
7546 break;
7548 case MakeIdx:
7549 if (nidx == 1) {
7550 Tcl_SetResult(interp, "syntax is: \"page make [<name>]\"", NULL);
7551 return TCL_ERROR;
7553 if (objc != 2 && objc != 3) {
7554 Tcl_WrongNumArgs(interp, 2, objv, "make [<name>]");
7555 return TCL_ERROR;
7557 newpage((short)255);
7558 if (objc == 3) {
7559 curpage = xobjs.pagelist[areawin->page];
7560 strcpy(curpage->pageinst->thisobject->name,
7561 Tcl_GetString(objv[2]));
7563 updatepagelib(PAGELIB, areawin->page);
7564 printname(topobject);
7565 break;
7566 case SaveOnlyIdx:
7567 case SaveIdx:
7568 if (objc - nidx > 3) {
7569 Tcl_WrongNumArgs(interp, 2, objv, "[filename]");
7570 return TCL_ERROR;
7572 else if (objc - nidx == 3) {
7573 filename = Tcl_GetString(objv[nidx + 2]);
7574 if (strcmp(filename, curpage->filename)) {
7575 Wprintf("Warning: Filename is \"%s\" but will be "
7576 "saved as \"%s\"\n", curpage->filename, filename);
7579 else if (curpage->filename == NULL) {
7580 Fprintf(stderr, "Warning: Filename created to match object name\n");
7581 filename = curpage->pageinst->thisobject->name;
7583 else
7584 filename = curpage->filename;
7586 if (savepage != pageno) newpage(pageno);
7587 if (!strncmp(Tcl_GetString(objv[nidx + 1]), "saveo", 5))
7588 setfile(filename, NO_SUBCIRCUITS);
7589 else
7590 setfile(filename, CURRENT_PAGE);
7591 if (savepage != pageno) newpage(savepage);
7592 break;
7594 case LinksIdx:
7595 if ((objc - nidx) < 2 && (objc - nidx) > 6) {
7596 Tcl_WrongNumArgs(interp, 1, objv, "links");
7597 return TCL_ERROR;
7599 if ((objc - nidx) == 2)
7600 linktype = TOTAL_PAGES;
7601 else {
7602 if (Tcl_GetIndexFromObj(interp, objv[2 + nidx],
7603 (CONST84 char **)linkTypes,
7604 "link type", 0, &linktype) != TCL_OK)
7605 return TCL_ERROR;
7607 multi = 0;
7608 pagelist = pagetotals(pageno, (linktype >= PendingIdx) ?
7609 LINKED_PAGES : linktype);
7610 TechReplaceSave();
7611 switch (linktype) {
7613 /* Load any pending links, that is, objects that have a */
7614 /* "link" parameter containing a string indicating a file */
7615 /* defining the schematic for that symbol. Allow the use */
7616 /* of the same "-replace" flag used by "page load". */
7618 case LinkLoadIdx:
7619 locidx = objc - 1;
7620 argv = Tcl_GetString(objv[locidx]);
7621 if (*argv != '-') argv = Tcl_GetString(objv[--locidx]);
7622 if ((*argv == '-') && !strncmp(argv, "-repl", 5)) {
7623 if (locidx < objc - 1) {
7624 char *techstr = Tcl_GetString(objv[locidx + 1]);
7625 if (!strcmp(techstr, "all")) TechReplaceAll();
7626 else if (!strcmp(techstr, "none")) TechReplaceNone();
7627 else {
7628 TechPtr nsptr = LookupTechnology(techstr);
7629 if (nsptr != NULL)
7630 nsptr->flags |= TECH_REPLACE;
7632 objc--;
7634 else
7635 TechReplaceAll(); /* replace ALL */
7636 objc--;
7638 if ((*argv == '-') && !strncmp(argv, "-targ", 5)) {
7639 if (locidx < objc - 1) {
7640 ParseLibArguments(interp, 2, &objv[locidx], NULL, &target);
7641 objc--;
7643 objc--;
7645 /* drop through */
7647 case PendingIdx:
7648 key = ((objc - nidx) == 4) ? Tcl_GetString(objv[3 + nidx]) : "link";
7649 for (i = 0; i < xobjs.pages; i++) {
7650 if (pagelist[i] > 0) {
7651 objinstptr tinst;
7652 objectptr tpage = xobjs.pagelist[i]->pageinst->thisobject;
7653 genericptr *tgen;
7655 for (tgen = tpage->plist; tgen < tpage->plist
7656 + tpage->parts; tgen++) {
7657 if ((*tgen)->type == OBJINST) {
7658 tinst = TOOBJINST(tgen);
7659 /* Corrected 8/31/07: Instance value of "link" has */
7660 /* priority over any default value in the object! */
7661 ops = find_param(tinst, key);
7662 if ((ops != NULL) && (ops->type == XC_STRING)) {
7663 filename = textprint(ops->parameter.string, tinst);
7664 if (strlen(filename) > 0) {
7665 if ((result = loadlinkfile(tinst, filename, target,
7666 (linktype == LinkLoadIdx))) > 0) {
7667 multi++;
7668 setsymschem(); /* Update GUI */
7669 result = TCL_OK;
7671 else if (result < 0) {
7672 Tcl_SetResult(interp, "Cannot load link", NULL);
7673 result = TCL_ERROR;
7675 else result = TCL_OK;
7677 free(filename);
7683 break;
7684 default:
7685 for (i = 0; i < xobjs.pages; i++) {
7686 if (pagelist[i] > 0) {
7687 multi++;
7688 if ((linktype == SheetIdx) && (i == pageno) && (pagelist[i] > 0))
7689 break;
7692 break;
7694 TechReplaceRestore();
7695 free((char *)pagelist);
7696 if (result == TCL_ERROR) return result;
7697 Tcl_SetObjResult(interp, Tcl_NewIntObj(multi));
7698 break;
7700 case DirIdx:
7701 startcatalog(NULL, PAGELIB, NULL);
7702 break;
7704 case GoToIdx:
7705 newpage((short)pageno);
7706 break;
7708 case UpdateIdx:
7709 calcbbox(curpage->pageinst);
7710 if (curpage->pmode & 2) autoscale(pageno);
7711 break;
7713 case BBoxIdx:
7714 if (((objc - nidx) == 2) || ((objc - nidx) == 3)) {
7715 Tcl_Obj *tuple;
7716 BBox *bbox, *sbbox;
7717 int value;
7719 bbox = &curpage->pageinst->bbox;
7720 if (bbox == NULL)
7721 bbox = &curpage->pageinst->thisobject->bbox;
7722 sbbox = bbox;
7724 if ((objc - nidx) == 3) {
7725 sbbox = curpage->pageinst->schembbox;
7726 if (sbbox == NULL) sbbox = bbox;
7729 objPtr = Tcl_NewListObj(0, NULL);
7731 tuple = Tcl_NewListObj(0, NULL);
7732 value = min(sbbox->lowerleft.x, bbox->lowerleft.x);
7733 Tcl_ListObjAppendElement(interp, tuple, Tcl_NewIntObj(value));
7734 value = min(sbbox->lowerleft.y, bbox->lowerleft.y);
7735 Tcl_ListObjAppendElement(interp, tuple, Tcl_NewIntObj(value));
7736 Tcl_ListObjAppendElement(interp, objPtr, tuple);
7738 tuple = Tcl_NewListObj(0, NULL);
7739 value = max(sbbox->lowerleft.x + sbbox->width,
7740 bbox->lowerleft.x + bbox->width);
7741 Tcl_ListObjAppendElement(interp, tuple, Tcl_NewIntObj(value));
7742 value = max(sbbox->lowerleft.y + sbbox->height,
7743 bbox->lowerleft.y + bbox->height);
7744 Tcl_ListObjAppendElement(interp, tuple, Tcl_NewIntObj(value));
7745 Tcl_ListObjAppendElement(interp, objPtr, tuple);
7747 Tcl_SetObjResult(interp, objPtr);
7748 return XcTagCallback(interp, objc, objv);
7750 else {
7751 Tcl_WrongNumArgs(interp, 1, objv, "bbox [all]");
7752 return TCL_ERROR;
7754 break;
7756 case SizeIdx:
7757 if ((objc - nidx) != 2 && (objc - nidx) != 3) {
7758 Tcl_WrongNumArgs(interp, 1, objv, "size ?\"width x height\"?");
7759 return TCL_ERROR;
7761 if ((objc - nidx) == 2) {
7762 float xsize, ysize, cfact;
7764 objPtr = Tcl_NewListObj(0, NULL);
7766 cfact = (curpage->coordstyle == CM) ? IN_CM_CONVERT
7767 : 72.0;
7768 xsize = (float)curpage->pagesize.x / cfact;
7769 ysize = (float)curpage->pagesize.y / cfact;
7771 Tcl_ListObjAppendElement(interp, objPtr,
7772 Tcl_NewDoubleObj((double)xsize));
7773 Tcl_ListObjAppendElement(interp, objPtr,
7774 Tcl_NewStringObj("x", 1));
7775 Tcl_ListObjAppendElement(interp, objPtr,
7776 Tcl_NewDoubleObj((double)ysize));
7777 Tcl_ListObjAppendElement(interp, objPtr,
7778 Tcl_NewStringObj(((curpage->coordstyle == CM) ?
7779 "cm" : "in"), 2));
7780 Tcl_SetObjResult(interp, objPtr);
7782 return XcTagCallback(interp, objc, objv);
7785 strcpy(_STR2, Tcl_GetString(objv[2 + nidx]));
7786 setoutputpagesize(&curpage->pagesize);
7788 /* Only need to recompute values and refresh if autoscaling is enabled */
7789 if (curpage->pmode & 2) autoscale(pageno);
7790 break;
7792 case MarginsIdx:
7793 if ((objc - nidx) < 2 && (objc - nidx) > 4) {
7794 Tcl_WrongNumArgs(interp, 1, objv, "margins ?x y?");
7795 return TCL_ERROR;
7797 if ((objc - nidx) == 2) {
7798 newwidth = (double)curpage->margins.x / 72.0;
7799 newheight = (double)curpage->margins.y / 72.0;
7800 objPtr = Tcl_NewListObj(0, NULL);
7801 Tcl_ListObjAppendElement(interp, objPtr,
7802 Tcl_NewDoubleObj(newwidth));
7803 Tcl_ListObjAppendElement(interp, objPtr,
7804 Tcl_NewDoubleObj(newheight));
7805 Tcl_SetObjResult(interp, objPtr);
7806 return XcTagCallback(interp, objc, objv);
7808 newwidth = (double)parseunits(Tcl_GetString(objv[2 + nidx]));
7809 if ((objc - nidx) == 4)
7810 newheight = (double)parseunits(Tcl_GetString(objv[3 + nidx]));
7811 else
7812 newheight = newwidth;
7814 newheight *= 72.0;
7815 newwidth *= 72.0;
7816 curpage->margins.x = (int)newwidth;
7817 curpage->margins.y = (int)newheight;
7818 break;
7820 case HeightIdx:
7821 if ((objc - nidx) != 2 && (objc - nidx) != 3) {
7822 Tcl_WrongNumArgs(interp, 1, objv, "height ?output_height?");
7823 return TCL_ERROR;
7825 if ((objc - nidx) == 2) {
7826 newheight = toplevelheight(curpage->pageinst, NULL);
7827 newheight *= getpsscale(curpage->outscale, pageno);
7828 newheight /= (curpage->coordstyle == CM) ? IN_CM_CONVERT : 72.0;
7829 objPtr = Tcl_NewDoubleObj((double)newheight);
7830 Tcl_SetObjResult(interp, objPtr);
7831 return XcTagCallback(interp, objc, objv);
7833 newheight = (double)parseunits(Tcl_GetString(objv[2 + nidx]));
7834 if (newheight <= 0 || topobject->bbox.height == 0) {
7835 Tcl_SetResult(interp, "Illegal height value", NULL);
7836 return TCL_ERROR;
7838 newheight = (newheight * ((curpage->coordstyle == CM) ?
7839 IN_CM_CONVERT : 72.0)) / topobject->bbox.height;
7840 newheight /= getpsscale(1.0, pageno);
7841 curpage->outscale = (float)newheight;
7843 if (curpage->pmode & 2) autoscale(pageno);
7844 break;
7846 case WidthIdx:
7847 if ((objc - nidx) != 2 && (objc - nidx) != 3) {
7848 Tcl_WrongNumArgs(interp, 1, objv, "output_width");
7849 return TCL_ERROR;
7851 if ((objc - nidx) == 2) {
7852 newwidth = toplevelwidth(curpage->pageinst, NULL);
7853 newwidth *= getpsscale(curpage->outscale, pageno);
7854 newwidth /= (curpage->coordstyle == CM) ? IN_CM_CONVERT : 72.0;
7855 objPtr = Tcl_NewDoubleObj((double)newwidth);
7856 Tcl_SetObjResult(interp, objPtr);
7857 return XcTagCallback(interp, objc, objv);
7859 newwidth = (double)parseunits(Tcl_GetString(objv[2 + nidx]));
7860 if (newwidth <= 0 || topobject->bbox.width == 0) {
7861 Tcl_SetResult(interp, "Illegal width value", NULL);
7862 return TCL_ERROR;
7865 newwidth = (newwidth * ((curpage->coordstyle == CM) ?
7866 IN_CM_CONVERT : 72.0)) / topobject->bbox.width;
7867 newwidth /= getpsscale(1.0, pageno);
7868 curpage->outscale = (float)newwidth;
7870 if (curpage->pmode & 2) autoscale(pageno);
7871 break;
7873 case ScaleIdx:
7874 if ((objc - nidx) != 2 && (objc - nidx) != 3) {
7875 Tcl_WrongNumArgs(interp, 1, objv, "output_scale");
7876 return TCL_ERROR;
7878 if ((objc - nidx) == 2) {
7879 objPtr = Tcl_NewDoubleObj((double)curpage->outscale);
7880 Tcl_SetObjResult(interp, objPtr);
7881 return XcTagCallback(interp, objc, objv);
7883 result = Tcl_GetDoubleFromObj(interp, objv[2 + nidx], &newscale);
7884 if (result != TCL_OK) return result;
7886 oldscale = curpage->outscale;
7888 if (oldscale == (float)newscale) return TCL_OK; /* nothing to do */
7889 else curpage->outscale = (float)newscale;
7891 if (curpage->pmode & 2) autoscale(pageno);
7892 break;
7894 case OrientIdx:
7895 if ((objc - nidx) != 2 && (objc - nidx) != 3) {
7896 Tcl_WrongNumArgs(interp, 1, objv, "orientation");
7897 return TCL_ERROR;
7899 if ((objc - nidx) == 2) {
7900 objPtr = Tcl_NewIntObj((int)curpage->orient);
7901 Tcl_SetObjResult(interp, objPtr);
7902 return XcTagCallback(interp, objc, objv);
7904 result = Tcl_GetIntFromObj(interp, objv[2 + nidx], &newrot);
7905 if (result != TCL_OK) return result;
7906 curpage->orient = (short)newrot;
7908 /* rescale after rotation if "auto-scale" is set */
7909 if (curpage->pmode & 2) autoscale(pageno);
7910 break;
7912 case EPSIdx:
7913 if ((objc - nidx) != 2 && (objc - nidx) != 3) {
7914 Tcl_WrongNumArgs(interp, 1, objv, "encapsulation");
7915 return TCL_ERROR;
7917 if ((objc - nidx) == 2) {
7918 newstr = psTypes[curpage->pmode & 1];
7919 Tcl_SetResult(interp, newstr, NULL);
7920 return XcTagCallback(interp, objc, objv);
7922 newstr = Tcl_GetString(objv[2 + nidx]);
7923 if (Tcl_GetIndexFromObj(interp, objv[2 + nidx],
7924 (CONST84 char **)psTypes,
7925 "encapsulation", 0, &newmode) != TCL_OK) {
7926 return result;
7928 curpage->pmode &= 0x2; /* preserve auto-fit flag */
7929 curpage->pmode |= (short)newmode;
7930 break;
7932 case LabelIdx:
7933 if ((objc - nidx) != 2 && (objc - nidx) != 3) {
7934 Tcl_WrongNumArgs(interp, 1, objv, "label ?name?");
7935 return TCL_ERROR;
7937 if ((objc - nidx) == 2) {
7938 objPtr = Tcl_NewStringObj(pageobj->name, strlen(pageobj->name));
7939 Tcl_SetObjResult(interp, objPtr);
7940 return XcTagCallback(interp, objc, objv);
7943 /* Whitespace and non-printing characters not allowed */
7945 strcpy(_STR2, Tcl_GetString(objv[2 + nidx]));
7946 for (i = 0; i < strlen(_STR2); i++) {
7947 if ((!isprint(_STR2[i])) || (isspace(_STR2[i]))) {
7948 _STR2[i] = '_';
7949 Wprintf("Replaced illegal whitespace in name with underscore");
7953 if (!strcmp(pageobj->name, _STR2)) return TCL_OK; /* no change in string */
7954 if (strlen(_STR2) == 0)
7955 sprintf(pageobj->name, "Page %d", areawin->page + 1);
7956 else
7957 sprintf(pageobj->name, "%.79s", _STR2);
7959 /* For schematics, all pages with associations to symbols must have */
7960 /* unique names. */
7961 if (pageobj->symschem != NULL) checkpagename(pageobj);
7963 if (pageobj == topobject) printname(pageobj);
7964 renamepage(pageno);
7965 break;
7967 case FileIdx:
7969 if ((objc - nidx) != 2 && (objc - nidx) != 3) {
7970 Tcl_WrongNumArgs(interp, 1, objv, "filename ?name?");
7971 return TCL_ERROR;
7974 oldstr = curpage->filename;
7976 if ((objc - nidx) == 2) {
7977 if (oldstr)
7978 objPtr = Tcl_NewStringObj(oldstr, strlen(oldstr));
7979 else
7980 objPtr = Tcl_NewListObj(0, NULL); /* NULL list */
7981 Tcl_SetObjResult(interp, objPtr);
7982 return XcTagCallback(interp, objc, objv);
7985 newstr = Tcl_GetString(objv[2 + nidx]);
7986 if (strlen(newstr) > 0) {
7987 froot = strrchr(newstr, '/');
7988 if (froot == NULL) froot = newstr;
7989 if (strchr(froot, '.') == NULL) {
7990 astr = malloc(strlen(newstr) + 4);
7991 sprintf(astr, "%s.ps", newstr);
7992 newstr = astr;
7996 if (oldstr && (!strcmp(oldstr, newstr))) { /* no change in string */
7997 if (newstr == astr) free(astr);
7998 return XcTagCallback(interp, objc, objv);
8001 if (strlen(newstr) == 0) { /* empty string */
8002 Tcl_SetResult(interp, "Warning: No filename!", NULL);
8003 multi = 1;
8005 else {
8006 multi = pagelinks(pageno); /* Are there multiple pages? */
8009 /* Make the change to the current page */
8010 curpage->filename = strdup(newstr);
8011 if (newstr == astr) free(astr);
8013 /* All existing filenames which match the old string should */
8014 /* also be changed unless the filename has been set to the */
8015 /* null string, which unlinks the page. */
8017 if ((strlen(curpage->filename) > 0) && (multi > 1)) {
8018 for (cpage = 0; cpage < xobjs.pages; cpage++) {
8019 lpage = xobjs.pagelist[cpage];
8020 if ((lpage->pageinst != NULL) && (cpage != pageno)) {
8021 if (lpage->filename && (!filecmp(lpage->filename, oldstr))) {
8022 free(lpage->filename);
8023 lpage->filename = strdup(newstr);
8028 free(oldstr);
8029 autoscale(pageno);
8031 /* Run pagelinks again; this checks if a page has been attached */
8032 /* to existing schematics by being renamed to match. */
8034 if ((strlen(curpage->filename) > 0) && (multi <= 1)) {
8035 for (cpage = 0; cpage < xobjs.pages; cpage++) {
8036 lpage = xobjs.pagelist[cpage];
8037 if ((lpage->pageinst != NULL) && (cpage != pageno)) {
8038 if (lpage->filename && (!filecmp(lpage->filename,
8039 curpage->filename))) {
8040 free(curpage->filename);
8041 curpage->filename = strdup(lpage->filename);
8042 break;
8047 break;
8049 case FitIdx:
8050 if ((objc - nidx) > 3) {
8051 Tcl_WrongNumArgs(interp, 1, objv, "fit ?true|false?");
8052 return TCL_ERROR;
8054 else if ((objc - nidx) == 3) {
8055 result = Tcl_GetBooleanFromObj(interp, objv[2 + nidx], &aval);
8056 if (result != TCL_OK) return result;
8057 if (aval)
8058 curpage->pmode |= 2;
8059 else
8060 curpage->pmode &= 1;
8062 else
8063 Tcl_SetResult(interp, ((curpage->pmode & 2) > 0) ? "true" : "false", NULL);
8065 /* Refresh values (does autoscale if specified) */
8066 autoscale(pageno);
8067 break;
8069 case ChangesIdx:
8070 if ((objc - nidx) != 2 && (objc - nidx) != 3) {
8071 Tcl_WrongNumArgs(interp, 1, objv, "changes");
8072 return TCL_ERROR;
8074 /* Allow changes to be set, so that a page can be forced to be */
8075 /* recognized as either modified or unmodified. */
8077 if ((objc - nidx) == 3) {
8078 int value;
8079 Tcl_GetIntFromObj(interp, objv[2 + nidx], &value);
8080 curpage->pageinst->thisobject->changes = (u_short)value;
8082 changes = getchanges(curpage->pageinst->thisobject);
8083 objPtr = Tcl_NewIntObj((double)changes);
8084 Tcl_SetObjResult(interp, objPtr);
8085 return XcTagCallback(interp, objc, objv);
8086 break;
8088 return XcTagCallback(interp, objc, objv);
8091 /*----------------------------------------------------------------------*/
8092 /* The "technology" command deals with library *technologies*, where */
8093 /* they differ from files or pages (see the "library" command */
8094 /* xctcl_library, below). Specifically, "library load" loads a file */
8095 /* (containing object defintions in a specific technology) onto a page, */
8096 /* whereas "technology save" writes back the object definitions that */
8097 /* came from the specified file. Although one would typically have one */
8098 /* library page per technology, this is not necessarily the case. */
8099 /* */
8100 /* Only one technology is defined by a library file, but the library */
8101 /* may contain (copies of) dependent objects from another technology. */
8102 /*----------------------------------------------------------------------*/
8104 int xctcl_tech(ClientData clientData, Tcl_Interp *interp,
8105 int objc, Tcl_Obj *CONST objv[])
8107 char *technology, *filename, *libobjname;
8108 short *pagelist;
8109 int idx, ilib, j, pageno, nidx, result;
8110 TechPtr nsptr = NULL;
8111 Tcl_Obj *olist;
8112 objectptr libobj;
8113 Boolean usertech = FALSE;
8114 FILE *chklib;
8116 char *subCmds[] = {
8117 "save", "list", "objects", "filename", "changed", "used", "prefer",
8118 "writable", "writeable", NULL
8120 enum SubIdx {
8121 SaveIdx, ListIdx, ObjectsIdx, FileNameIdx, ChangedIdx, UsedIdx,
8122 PreferIdx, WritableIdx, WriteableIdx
8125 if (objc < 2) {
8126 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
8127 return TCL_ERROR;
8129 if (Tcl_GetIndexFromObj(interp, objv[1],
8130 (CONST84 char **)subCmds, "option", 0, &idx) != TCL_OK) {
8131 return TCL_ERROR;
8134 /* All options except "list" and "used" expect a technology argument */
8135 if (idx != ListIdx && idx != UsedIdx) {
8136 if (objc > 2) {
8137 technology = Tcl_GetString(objv[2]);
8138 nsptr = LookupTechnology(technology);
8139 if (nsptr == NULL) {
8141 /* If the command is "objects" and has one or more */
8142 /* additional arguments, then a NULL nsptr is okay (new */
8143 /* technology will be created and added to the list). */
8145 if (idx != ObjectsIdx || objc <= 3) {
8147 /* If nsptr is NULL, then the technology should be */
8148 /* "none", "user", or "default". */
8150 if ((strstr(technology, "none") == NULL) &&
8151 (strstr(technology, "user") == NULL) &&
8152 (strstr(technology, "default") == NULL)) {
8153 Tcl_SetResult(interp, "Error: Unknown technology name!", NULL);
8154 return TCL_ERROR;
8156 usertech = TRUE;
8160 /* And if the user technology has been saved to a file, the technology */
8161 /* will have a NULL string. Also check for technology name "(user)", */
8162 /* although that is not supposed to happen. */
8164 else if (*nsptr->technology == '\0')
8165 usertech = TRUE;
8167 else if (!strcmp(nsptr->technology, "(user)"))
8168 usertech = TRUE;
8170 else {
8171 Tcl_WrongNumArgs(interp, 1, objv, "<option> technology ?args ...?");
8172 return TCL_ERROR;
8176 switch (idx) {
8177 case ListIdx:
8178 /* List all of the known technologies */
8179 olist = Tcl_NewListObj(0, NULL);
8180 for (nsptr = xobjs.technologies; nsptr != NULL; nsptr = nsptr->next) {
8181 Tcl_ListObjAppendElement(interp, olist,
8182 Tcl_NewStringObj(nsptr->technology,
8183 strlen(nsptr->technology)));
8185 Tcl_SetObjResult(interp, olist);
8186 break;
8188 case UsedIdx:
8189 /* List all of the technologies used by the schematic of the */
8190 /* indicated (or current) page. That is, enumerate all */
8191 /* in the hierarchy of the schematic, and list all unique */
8192 /* technology prefixes. */
8194 result = ParsePageArguments(interp, objc - 1, objv + 1, &nidx, &pageno);
8195 if (result != TCL_OK) return result;
8196 olist = Tcl_NewListObj(0, NULL);
8198 pagelist = pagetotals(pageno, TOTAL_PAGES);
8199 for (j = 0; j < xobjs.pages; j++) {
8200 if (pagelist[j] > 0) {
8201 objinstptr tinst;
8202 objectptr tpage = xobjs.pagelist[j]->pageinst->thisobject;
8203 genericptr *tgen;
8205 for (tgen = tpage->plist; tgen < tpage->plist + tpage->parts; tgen++) {
8206 if ((*tgen)->type == OBJINST) {
8207 tinst = TOOBJINST(tgen);
8208 nsptr = GetObjectTechnology(tinst->thisobject);
8209 if (nsptr != NULL) {
8210 if ((nsptr->technology == NULL) ||
8211 (strlen(nsptr->technology) == 0)) continue;
8212 if (!(nsptr->flags & TECH_USED)) {
8213 Tcl_ListObjAppendElement(interp, olist,
8214 Tcl_NewStringObj(nsptr->technology,
8215 strlen(nsptr->technology)));
8216 nsptr->flags |= TECH_USED;
8223 Tcl_SetObjResult(interp, olist);
8224 for (nsptr = xobjs.technologies; nsptr != NULL; nsptr = nsptr->next)
8225 nsptr->flags &= ~TECH_USED;
8226 free((char *)pagelist);
8227 break;
8229 case ObjectsIdx:
8231 if (objc > 3) {
8232 int numobjs, objnamelen, technamelen;
8233 Tcl_Obj *tobj;
8234 char *cptr;
8235 TechPtr otech;
8237 /* Check that 4th argument is a list of objects or that */
8238 /* 4th and higher arguments are all names of objects, and */
8239 /* that these objects are valid existing objects. */
8241 if (objc == 4) {
8242 result = Tcl_ListObjLength(interp, objv[3], &numobjs);
8243 if (result != TCL_OK) return result;
8244 for (j = 0; j < numobjs; j++) {
8245 result = Tcl_ListObjIndex(interp, objv[3], j, &tobj);
8246 if (result != TCL_OK) return result;
8247 libobj = NameToObject(Tcl_GetString(tobj), NULL, FALSE);
8248 if (libobj == NULL) {
8249 Tcl_SetResult(interp, "No such object name", NULL);
8250 return TCL_ERROR;
8254 else {
8255 for (j = 0; j < objc - 4; j++) {
8256 libobj = NameToObject(Tcl_GetString(objv[3 + j]), NULL, FALSE);
8257 if (libobj == NULL) {
8258 Tcl_SetResult(interp, "No such object name", NULL);
8259 return TCL_ERROR;
8264 /* Create a new technology if needed */
8265 technology = Tcl_GetString(objv[2]);
8266 if ((nsptr == NULL) && !usertech)
8267 AddNewTechnology(technology, NULL);
8269 nsptr = LookupTechnology(technology);
8270 technamelen = (usertech) ? 0 : strlen(technology);
8273 /* Change the technology prefix of all the objects listed */
8275 if (objc == 4) {
8276 result = Tcl_ListObjLength(interp, objv[3], &numobjs);
8277 if (result != TCL_OK) return result;
8278 for (j = 0; j < numobjs; j++) {
8279 result = Tcl_ListObjIndex(interp, objv[3], j, &tobj);
8280 if (result != TCL_OK) return result;
8281 libobj = NameToObject(Tcl_GetString(tobj), NULL, FALSE);
8282 cptr = strstr(libobj->name, "::");
8283 if (cptr == NULL) {
8284 objnamelen = strlen(libobj->name);
8285 memmove(libobj->name + technamelen + 2,
8286 libobj->name, (size_t)strlen(libobj->name));
8288 else {
8289 otech = GetObjectTechnology(libobj);
8290 otech->flags |= TECH_CHANGED;
8291 objnamelen = strlen(cptr + 2);
8292 memmove(libobj->name + technamelen + 2,
8293 cptr + 2, (size_t)strlen(cptr + 2));
8296 if (!usertech) strcpy(libobj->name, technology);
8297 *(libobj->name + technamelen) = ':';
8298 *(libobj->name + technamelen + 1) = ':';
8299 *(libobj->name + technamelen + 2 + objnamelen) = '\0';
8302 else {
8303 for (j = 0; j < objc - 4; j++) {
8304 libobj = NameToObject(Tcl_GetString(objv[3 + j]), NULL, FALSE);
8305 cptr = strstr(libobj->name, "::");
8306 if (cptr == NULL) {
8307 objnamelen = strlen(libobj->name);
8308 memmove(libobj->name + technamelen + 2,
8309 libobj->name, (size_t)strlen(libobj->name));
8311 else {
8312 otech = GetObjectTechnology(libobj);
8313 otech->flags |= TECH_CHANGED;
8314 objnamelen = strlen(cptr + 2);
8315 memmove(libobj->name + technamelen + 2,
8316 cptr + 2, (size_t)strlen(cptr + 2));
8319 if (!usertech) strcpy(libobj->name, technology);
8320 *(libobj->name + technamelen) = ':';
8321 *(libobj->name + technamelen + 1) = ':';
8322 *(libobj->name + technamelen + 2 + objnamelen) = '\0';
8325 if (nsptr != NULL) nsptr->flags |= TECH_CHANGED;
8326 break;
8329 /* List all objects having this technology */
8331 olist = Tcl_NewListObj(0, NULL);
8332 for (ilib = 0; ilib < xobjs.numlibs; ilib++) {
8333 for (j = 0; j < xobjs.userlibs[ilib].number; j++) {
8334 libobj = *(xobjs.userlibs[ilib].library + j);
8335 if (GetObjectTechnology(libobj) == nsptr) {
8336 libobjname = strstr(libobj->name, "::");
8337 if (libobjname == NULL)
8338 libobjname = libobj->name;
8339 else
8340 libobjname += 2;
8341 Tcl_ListObjAppendElement(interp, olist,
8342 Tcl_NewStringObj(libobjname, strlen(libobjname)));
8346 Tcl_SetObjResult(interp, olist);
8347 break;
8349 case FileNameIdx:
8350 if (nsptr != NULL) {
8351 if (objc == 3) {
8352 if (nsptr->filename == NULL)
8353 Tcl_SetResult(interp, "(no associated file)", NULL);
8354 else
8355 Tcl_SetResult(interp, nsptr->filename, NULL);
8357 else {
8358 if (nsptr->filename != NULL) free(nsptr->filename);
8359 nsptr->filename = strdup(Tcl_GetString(objv[3]));
8362 else {
8363 Tcl_SetResult(interp, "Valid technology is required", NULL);
8364 return TCL_ERROR;
8366 break;
8368 case ChangedIdx:
8369 if (objc == 4) {
8370 int bval;
8371 if (Tcl_GetBooleanFromObj(interp, objv[3], &bval) != TCL_OK)
8372 return TCL_ERROR;
8373 else if (bval == 1)
8374 nsptr->flags |= TECH_CHANGED;
8375 else
8376 nsptr->flags &= ~TECH_CHANGED;
8378 else {
8379 tech_set_changes(nsptr); /* Ensure change flags are updated */
8380 Tcl_SetObjResult(interp,
8381 Tcl_NewBooleanObj(((nsptr->flags & TECH_CHANGED)
8382 == 0) ? FALSE : TRUE));
8384 break;
8386 case PreferIdx:
8387 if (nsptr) {
8388 if (objc == 3) {
8389 Tcl_SetObjResult(interp,
8390 Tcl_NewBooleanObj(((nsptr->flags & TECH_PREFER) == 0)
8391 ? TRUE : FALSE));
8393 else if (objc == 4) {
8394 int bval;
8396 Tcl_GetBooleanFromObj(interp, objv[3], &bval);
8397 if (bval == 0)
8398 nsptr->flags |= TECH_PREFER;
8399 else
8400 nsptr->flags &= (~TECH_PREFER);
8403 else {
8404 Tcl_SetResult(interp, "Valid technology is required", NULL);
8405 return TCL_ERROR;
8407 break;
8409 case WritableIdx:
8410 case WriteableIdx:
8411 if (nsptr) {
8412 if (objc == 3) {
8413 Tcl_SetObjResult(interp,
8414 Tcl_NewBooleanObj(((nsptr->flags & TECH_READONLY) == 0)
8415 ? TRUE : FALSE));
8417 else if (objc == 4) {
8418 int bval;
8420 Tcl_GetBooleanFromObj(interp, objv[3], &bval);
8421 if (bval == 0)
8422 nsptr->flags |= TECH_READONLY;
8423 else
8424 nsptr->flags &= (~TECH_READONLY);
8427 else {
8428 Tcl_SetResult(interp, "Valid technology is required", NULL);
8429 return TCL_ERROR;
8431 break;
8433 case SaveIdx:
8435 /* technology save [filename] */
8436 if ((objc == 3) && ((nsptr == NULL) || (nsptr->filename == NULL))) {
8437 Tcl_SetResult(interp, "Error: Filename is required.", NULL);
8438 return TCL_ERROR;
8440 else if ((nsptr != NULL) && (objc == 4)) {
8441 /* Technology being saved under a different filename. */
8442 filename = Tcl_GetString(objv[3]);
8444 /* Re-check read-only status of the file */
8445 nsptr->flags &= ~(TECH_READONLY);
8446 chklib = fopen(filename, "a");
8447 if (chklib == NULL)
8448 nsptr->flags |= TECH_READONLY;
8449 else
8450 fclose(chklib);
8452 else if (objc == 4) {
8453 filename = Tcl_GetString(objv[3]);
8454 if (!usertech) AddNewTechnology(technology, filename);
8456 else
8457 filename = nsptr->filename;
8459 savetechnology((usertech) ? NULL : technology, filename);
8460 break;
8462 return XcTagCallback(interp, objc, objv);
8465 /*----------------------------------------------------------------------*/
8466 /* The "library" command deals with library *pages* */
8467 /*----------------------------------------------------------------------*/
8469 int xctcl_library(ClientData clientData, Tcl_Interp *interp,
8470 int objc, Tcl_Obj *CONST objv[])
8472 char *filename = NULL, *objname, *argv;
8473 int j = 0, libnum = -1;
8474 int idx, nidx, result, res;
8475 Tcl_Obj *olist;
8476 Tcl_Obj **newobjv;
8477 int newobjc, hidmode;
8478 objectptr libobj;
8479 liblistptr spec;
8480 char *subCmds[] = {
8481 "load", "make", "directory", "next", "goto", "override",
8482 "handle", "import", "list", "compose", NULL
8484 enum SubIdx {
8485 LoadIdx, MakeIdx, DirIdx, NextIdx, GoToIdx, OverrideIdx,
8486 HandleIdx, ImportIdx, ListIdx, ComposeIdx
8489 result = ParseLibArguments(interp, objc, objv, &nidx, &libnum);
8490 if ((result != TCL_OK) || (nidx < 0)) return result;
8491 else if ((objc - nidx) > 5) {
8492 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
8493 return TCL_ERROR;
8495 else if (objc <= (1 + nidx)) { /* No subcommand */
8497 /* return index if name given; return name if index given. */
8498 /* return index if neither is given (current library) */
8500 if (objc > 1) {
8501 int lnum; /* unused; only checks if argument is integer */
8502 char *lname;
8503 result = Tcl_GetIntFromObj(interp, objv[1], &lnum);
8504 if (result == TCL_OK) {
8505 lname = xobjs.libtop[libnum + LIBRARY]->thisobject->name;
8506 Tcl_SetObjResult(interp, Tcl_NewStringObj(lname, strlen(lname)));
8508 else {
8509 result = TCL_OK;
8510 Tcl_SetObjResult(interp, Tcl_NewIntObj(libnum + 1));
8513 else
8514 Tcl_SetObjResult(interp, Tcl_NewIntObj(libnum + 1));
8515 idx = -1;
8517 else if (Tcl_GetIndexFromObj(interp, objv[1 + nidx],
8518 (CONST84 char **)subCmds, "option", 0, &idx) != TCL_OK) {
8520 /* Backwards compatibility: "library filename [number]" is */
8521 /* the same as "library [number] load filename" */
8523 Tcl_ResetResult(interp);
8524 newobjv = (Tcl_Obj **)(&objv[1]);
8525 newobjc = objc - 1;
8527 result = ParseLibArguments(interp, newobjc, newobjv, &nidx, &libnum);
8528 if (result != TCL_OK) return result;
8530 idx = LoadIdx;
8531 filename = Tcl_GetString(newobjv[0]);
8534 /* libnum = -1 is equivalent to "USER LIBRARY" */
8535 if (libnum < 0) libnum = xobjs.numlibs - 1;
8537 switch (idx) {
8538 case LoadIdx:
8539 TechReplaceSave();
8541 /* library [<name>|<number>] load <filename> [-replace [library]] */
8542 if (objc < (3 + nidx)) {
8543 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
8544 return TCL_ERROR;
8546 if (filename == NULL) filename = Tcl_GetString(objv[2 + nidx]);
8548 /* if loading of default libraries is not overridden, load them first */
8550 if (!(flags & (LIBOVERRIDE | LIBLOADED))) {
8551 result = defaultscript();
8552 flags |= LIBLOADED;
8555 /* If library number is out of range, create a new library */
8556 /* libnum = -1 is equivalent to the user library page. */
8558 if (libnum > (xobjs.numlibs - 1))
8559 libnum = createlibrary(FALSE);
8560 else if (libnum < 0)
8561 libnum = USERLIB;
8562 else
8563 libnum += LIBRARY;
8565 if (objc > (3 + nidx)) {
8566 argv = Tcl_GetString(objv[3 + nidx]);
8567 if ((*argv == '-') && !strncmp(argv, "-repl", 5)) {
8568 if (objc > (4 + nidx)) {
8569 char *techstr = Tcl_GetString(objv[3 + nidx]);
8570 if (!strcmp(techstr, "all")) TechReplaceAll();
8571 else if (!strcmp(techstr, "none")) TechReplaceNone();
8572 else {
8573 TechPtr nsptr = LookupTechnology(techstr);
8574 if (nsptr != NULL)
8575 nsptr->flags |= TECH_REPLACE;
8578 else
8579 TechReplaceAll(); /* replace ALL */
8583 strcpy(_STR, filename);
8584 res = loadlibrary(libnum);
8585 if (res == False) {
8586 res = loadfile(2, libnum);
8587 TechReplaceRestore();
8588 if (res == False) {
8589 Tcl_SetResult(interp, "Error loading library.\n", NULL);
8590 return TCL_ERROR;
8593 TechReplaceRestore();
8594 break;
8596 case ImportIdx:
8597 /* library [<name>|<number>] import <filename> <objectname> */
8598 if (objc != (4 + nidx)) {
8599 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
8600 return TCL_ERROR;
8602 if (filename == NULL) filename = Tcl_GetString(objv[2 + nidx]);
8604 /* if loading of default libraries is not overridden, load them first */
8606 if (!(flags & (LIBOVERRIDE | LIBLOADED))) {
8607 defaultscript();
8608 flags |= LIBLOADED;
8611 if ((libnum >= xobjs.numlibs) || (libnum < 0))
8612 libnum = createlibrary(FALSE);
8613 else
8614 libnum += LIBRARY;
8616 objname = Tcl_GetString(objv[3 + nidx]);
8617 importfromlibrary(libnum, filename, objname);
8618 break;
8620 case ListIdx:
8622 if (!strncmp(Tcl_GetString(objv[objc - 1]), "-vis", 4))
8623 hidmode = 1; /* list visible objects only */
8624 else if (!strncmp(Tcl_GetString(objv[objc - 1]), "-hid", 4))
8625 hidmode = 2; /* list hidden objects only */
8626 else
8627 hidmode = 3; /* list everything */
8629 /* library [name|number] list [-visible|-hidden] */
8630 olist = Tcl_NewListObj(0, NULL);
8631 for (j = 0; j < xobjs.userlibs[libnum].number; j++) {
8632 libobj = *(xobjs.userlibs[libnum].library + j);
8633 if (((libobj->hidden) && (hidmode & 2)) ||
8634 ((!libobj->hidden) && (hidmode & 1)))
8635 Tcl_ListObjAppendElement(interp, olist,
8636 Tcl_NewStringObj(libobj->name, strlen(libobj->name)));
8638 Tcl_SetObjResult(interp, olist);
8639 break;
8641 case HandleIdx:
8643 if (objc == (3 + nidx)) {
8644 /* library [name|number] handle <object name> */
8646 olist = Tcl_NewListObj(0, NULL);
8647 for (spec = xobjs.userlibs[libnum].instlist; spec != NULL;
8648 spec = spec->next) {
8649 libobj = spec->thisinst->thisobject;
8650 if (!strcmp(libobj->name, Tcl_GetString(objv[objc - 1])))
8651 Tcl_ListObjAppendElement(interp, olist,
8652 Tcl_NewHandleObj((genericptr)spec->thisinst));
8654 Tcl_SetObjResult(interp, olist);
8656 else if (objc == (2 + nidx)) {
8657 /* library [name|number] handle */
8659 olist = Tcl_NewListObj(0, NULL);
8660 for (spec = xobjs.userlibs[libnum].instlist; spec != NULL;
8661 spec = spec->next) {
8662 Tcl_ListObjAppendElement(interp, olist,
8663 Tcl_NewHandleObj((genericptr)spec->thisinst));
8665 Tcl_SetObjResult(interp, olist);
8667 else {
8668 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
8669 return TCL_ERROR;
8671 break;
8673 case ComposeIdx:
8674 composelib(libnum + LIBRARY);
8675 centerview(xobjs.libtop[libnum + LIBRARY]);
8676 break;
8678 case MakeIdx:
8679 /* library make [name] */
8680 if (nidx == 1) {
8681 Tcl_SetResult(interp, "syntax is: library make [<name>]", NULL);
8682 return TCL_ERROR;
8685 /* If the (named or numbered) library exists, don't create it. */
8686 /* ParseLibArguments() returns the library number for the User */
8687 /* Library. The User Library always exists and cannot be */
8688 /* created or destroyed, so it's okay to use it as a check for */
8689 /* "no library found". */
8691 if (libnum == xobjs.numlibs - 1)
8692 libnum = createlibrary(TRUE);
8694 if (objc == 3) {
8695 strcpy(xobjs.libtop[libnum]->thisobject->name, Tcl_GetString(objv[2]));
8696 renamelib(libnum);
8697 composelib(LIBLIB);
8699 /* Don't go to the library page---use "library goto" instead */
8700 /* startcatalog((Tk_Window)clientData, libnum, NULL); */
8701 break;
8703 case DirIdx:
8704 /* library directory */
8705 if ((nidx == 0) && (objc == 2)) {
8706 startcatalog(NULL, LIBLIB, NULL);
8708 else if ((nidx == 0) && (objc == 3) &&
8709 !strcmp(Tcl_GetString(objv[2]), "list")) {
8710 olist = Tcl_NewListObj(0, NULL);
8711 for (j = 0; j < xobjs.numlibs; j++) {
8712 libobj = xobjs.libtop[j + LIBRARY]->thisobject;
8713 Tcl_ListObjAppendElement(interp, olist,
8714 Tcl_NewStringObj(libobj->name, strlen(libobj->name)));
8716 Tcl_SetObjResult(interp, olist);
8718 else {
8719 Tcl_SetResult(interp, "syntax is: library directory [list]", NULL);
8720 return TCL_ERROR;
8722 break;
8724 case NextIdx:
8725 libnum = is_library(topobject);
8726 if (++libnum >= xobjs.numlibs) libnum = 0; /* fall through */
8728 case GoToIdx:
8729 /* library go */
8730 startcatalog(NULL, LIBRARY + libnum, NULL);
8731 break;
8732 case OverrideIdx:
8733 flags |= LIBOVERRIDE;
8734 return TCL_OK; /* no tag callback */
8735 break;
8737 return (result == TCL_OK) ? XcTagCallback(interp, objc, objv) : result;
8740 /*----------------------------------------------------------------------*/
8741 /* "bindkey" command --- this is a direct implementation of the same */
8742 /* key binding found in the "ad-hoc" and Python interfaces; it is */
8743 /* preferable to make use of the Tk "bind" command directly, and work */
8744 /* from the event handler. */
8745 /*----------------------------------------------------------------------*/
8747 int xctcl_bind(ClientData clientData, Tcl_Interp *interp,
8748 int objc, Tcl_Obj *CONST objv[])
8750 Tk_Window window = (Tk_Window)NULL;
8751 XCWindowDataPtr searchwin;
8752 char *keyname, *commandname, *binding;
8753 int keywstate, func = -1, value = -1;
8754 int result;
8755 Boolean compat = FALSE;
8757 if (objc == 2) {
8758 keyname = Tcl_GetString(objv[1]);
8759 if (!strcmp(keyname, "override")) {
8760 flags |= KEYOVERRIDE;
8761 return TCL_OK; /* no tag callback */
8765 if (!(flags & KEYOVERRIDE)) {
8766 default_keybindings();
8767 flags |= KEYOVERRIDE;
8770 if (objc == 1) {
8771 Tcl_Obj *list;
8772 int i;
8774 list = Tcl_NewListObj(0, NULL);
8775 for (i = 0; i < NUM_FUNCTIONS; i++) {
8776 commandname = func_to_string(i);
8777 Tcl_ListObjAppendElement(interp, list,
8778 Tcl_NewStringObj(commandname, strlen(commandname)));
8780 Tcl_SetObjResult(interp, list);
8781 return TCL_OK;
8783 else if (objc > 5) {
8784 Tcl_WrongNumArgs(interp, 1, objv,
8785 "[<key> [<window>] [<command> [<value>|forget]]]");
8786 return TCL_ERROR;
8789 /* If 1st argument matches a window name, create a window-specific */
8790 /* binding. Otherwise, create a binding for all windows. */
8792 if (objc > 1) {
8793 window = Tk_NameToWindow(interp, Tcl_GetString(objv[1]), Tk_MainWindow(interp));
8794 if (window == (Tk_Window)NULL)
8795 Tcl_ResetResult(interp);
8796 else {
8797 for (searchwin = xobjs.windowlist; searchwin != NULL; searchwin =
8798 searchwin->next)
8799 if (searchwin->area == window)
8800 break;
8801 if (searchwin != NULL) {
8802 /* Shift arguments */
8803 objc--;
8804 objv++;
8806 else
8807 window = (xcWidget)NULL;
8811 /* 1st argument can be option "-compatible" */
8812 if ((objc > 1) && !strncmp(Tcl_GetString(objv[1]), "-comp", 5)) {
8813 objc--;
8814 objv++;
8815 compat = TRUE;
8818 keyname = Tcl_GetString(objv[1]);
8819 keywstate = string_to_key(keyname);
8821 /* 1st arg may be a function, not a key, if we want the binding returned */
8822 if ((objc == 3) && !strncmp(keyname, "-func", 5)) {
8823 keywstate = -1;
8824 func = string_to_func(Tcl_GetString(objv[2]), NULL);
8825 objc = 2;
8826 if (func == -1) {
8827 Tcl_SetResult(interp, "Invalid function name\n", NULL);
8828 return TCL_ERROR;
8831 else if ((objc == 2) && (keywstate == 0)) {
8832 keywstate = -1;
8833 func = string_to_func(keyname, NULL);
8836 if ((keywstate == -1 || keywstate == 0) && func == -1) {
8837 Tcl_SetResult(interp, "Invalid key name ", NULL);
8838 Tcl_AppendElement(interp, keyname);
8839 return TCL_ERROR;
8842 if (objc == 2) {
8843 if (keywstate == -1)
8844 binding = function_binding_to_string(window, func);
8845 else if (compat)
8846 binding = compat_key_to_string(window, keywstate);
8847 else
8848 binding = key_binding_to_string(window, keywstate);
8849 Tcl_SetResult(interp, binding, TCL_VOLATILE);
8850 free(binding);
8851 return TCL_OK;
8854 if (objc < 3) {
8855 Tcl_SetResult(interp, "Usage: bindkey <key> [<function>]\n", NULL);
8856 return TCL_ERROR;
8859 commandname = Tcl_GetString(objv[2]);
8860 if (strlen(commandname) == 0)
8861 func = -1;
8862 else
8863 func = string_to_func(commandname, NULL);
8865 if (objc == 4) {
8866 result = Tcl_GetIntFromObj(interp, objv[3], &value);
8867 if (result != TCL_OK)
8869 if (strcmp(Tcl_GetString(objv[3]), "forget"))
8870 return (result);
8871 else {
8872 /* Unbind command */
8873 Tcl_ResetResult(interp);
8874 result = remove_binding(window, keywstate, func);
8875 if (result == 0)
8876 return TCL_OK;
8877 else {
8878 Tcl_SetResult(interp, "Key/Function pair not found "
8879 "in binding list.\n", NULL);
8880 return TCL_ERROR;
8885 result = add_vbinding(window, keywstate, func, value);
8886 if (result == 1) {
8887 Tcl_SetResult(interp, "Key is already bound to a command.\n", NULL);
8888 return (result);
8890 return (result == TCL_OK) ? XcTagCallback(interp, objc, objv) : result;
8893 /*----------------------------------------------------------------------*/
8895 int xctcl_font(ClientData clientData, Tcl_Interp *interp,
8896 int objc, Tcl_Obj *CONST objv[])
8898 char *fontname;
8899 int result;
8901 /* font name */
8902 if (objc != 2) {
8903 Tcl_WrongNumArgs(interp, 1, objv, "fontname");
8904 return TCL_ERROR;
8906 fontname = Tcl_GetString(objv[1]);
8908 /* Allow overrides of the default font loading mechanism */
8909 if (!strcmp(fontname, "override")) {
8910 flags |= FONTOVERRIDE;
8911 return TCL_OK;
8914 /* If we need to load the default font "Helvetica" because no fonts */
8915 /* have been loaded yet, then we call this function twice, so that */
8916 /* the command tag callback gets applied both times. */
8918 if (!(flags & FONTOVERRIDE)) {
8919 flags |= FONTOVERRIDE;
8920 xctcl_font(clientData, interp, objc, objv);
8921 loadfontfile("Helvetica");
8923 result = loadfontfile((char *)fontname);
8924 if (result >= 1) {
8925 Tcl_SetObjResult(interp, Tcl_NewStringObj(fonts[fontcount - 1].family,
8926 strlen(fonts[fontcount - 1].family)));
8928 switch (result) {
8929 case 1:
8930 return XcTagCallback(interp, objc, objv);
8931 case 0:
8932 return TCL_OK;
8933 case -1:
8934 return TCL_ERROR;
8936 return TCL_ERROR; /* (jdk) */
8939 /*----------------------------------------------------------------------*/
8940 /* Set the X11 cursor to one of those defined in the XCircuit cursor */
8941 /* set (cursors.h) */
8942 /*----------------------------------------------------------------------*/
8944 int xctcl_cursor(ClientData clientData, Tcl_Interp *interp,
8945 int objc, Tcl_Obj *CONST objv[])
8947 int idx, result;
8949 static char *cursNames[] = {
8950 "arrow", "cross", "scissors", "copy", "rotate", "edit",
8951 "text", "circle", "question", "wait", "hand", NULL
8954 if (!areawin) return TCL_ERROR;
8956 /* cursor name */
8957 if (objc != 2) {
8958 Tcl_WrongNumArgs(interp, 1, objv, "cursor name");
8959 return TCL_ERROR;
8961 if ((result = Tcl_GetIndexFromObj(interp, objv[1],
8962 (CONST84 char **)cursNames,
8963 "cursor name", 0, &idx)) != TCL_OK)
8964 return result;
8966 XDefineCursor(dpy, areawin->window, appcursors[idx]);
8967 areawin->defaultcursor = &appcursors[idx];
8968 return XcTagCallback(interp, objc, objv);
8971 /*----------------------------------------------------------------------*/
8973 int xctcl_filerecover(ClientData clientData, Tcl_Interp *interp,
8974 int objc, Tcl_Obj *CONST objv[])
8976 if (objc != 1) {
8977 Tcl_WrongNumArgs(interp, 1, objv, "(no arguments)");
8978 return TCL_ERROR;
8980 crashrecover();
8981 return XcTagCallback(interp, objc, objv);
8984 /*----------------------------------------------------------------------*/
8985 /* Replace the functions of the simple rcfile.c interpreter. */
8986 /*----------------------------------------------------------------------*/
8988 /*----------------------------------------------------------------------*/
8989 /* Execute a single command from a script or from the command line */
8990 /*----------------------------------------------------------------------*/
8992 short execcommand(short pflags, char *cmdptr)
8994 flags = pflags;
8995 Tcl_Eval(xcinterp, cmdptr);
8996 refresh(NULL, NULL, NULL);
8997 return flags;
9000 /*----------------------------------------------------------------------*/
9001 /* Load the default script (like execscript() but don't allow recursive */
9002 /* loading of the startup script) */
9003 /*----------------------------------------------------------------------*/
9005 int defaultscript()
9007 FILE *fd;
9008 char *tmp_s = getenv((const char *)"XCIRCUIT_SRC_DIR");
9009 int result;
9011 flags = LIBOVERRIDE | LIBLOADED | FONTOVERRIDE;
9013 if (!tmp_s) tmp_s = SCRIPTS_DIR;
9014 sprintf(_STR2, "%s/%s", tmp_s, STARTUP_FILE);
9016 if ((fd = fopen(_STR2, "r")) == NULL) {
9017 sprintf(_STR2, "%s/%s", SCRIPTS_DIR, STARTUP_FILE);
9018 if ((fd = fopen(_STR2, "r")) == NULL) {
9019 sprintf(_STR2, "%s/tcl/%s", SCRIPTS_DIR, STARTUP_FILE);
9020 if ((fd = fopen(_STR2, "r")) == NULL) {
9021 Wprintf("Failed to open startup script \"%s\"\n", STARTUP_FILE);
9022 return TCL_ERROR;
9026 fclose(fd);
9027 result = Tcl_EvalFile(xcinterp, _STR2);
9028 return result;
9031 /*----------------------------------------------------------------------*/
9032 /* Execute a script */
9033 /*----------------------------------------------------------------------*/
9035 void execscript()
9037 FILE *fd;
9039 flags = 0;
9041 xc_tilde_expand(_STR2, 249);
9042 if ((fd = fopen(_STR2, "r")) != NULL) {
9043 fclose(fd);
9044 Tcl_EvalFile(xcinterp, _STR2);
9045 refresh(NULL, NULL, NULL);
9047 else {
9048 Wprintf("Failed to open script file \"%s\"\n", _STR2);
9052 /*----------------------------------------------------------------------*/
9053 /* Evaluate an expression from a parameter and return the result as a */
9054 /* Tcl object. The actual return value (TCL_OK, TCL_ERROR) is stored */
9055 /* in pointer "eval_status", if it is non-NULL. */
9056 /*----------------------------------------------------------------------*/
9058 Tcl_Obj *evaluate_raw(objectptr thisobj, oparamptr ops, objinstptr pinst,
9059 int *eval_status)
9061 Tcl_SavedResult state;
9062 Tcl_Obj *robj;
9063 int status;
9064 char *exprptr, *pptr, *pkey, *pnext;
9066 /* Sanity check */
9067 if (ops->type != XC_EXPR) return NULL;
9068 exprptr = ops->parameter.expr;
9069 pnext = exprptr;
9070 if (pnext == NULL) return NULL;
9072 /* Check for "@<parameter>" notation and substitute parameter values */
9073 while ((pptr = strchr(pnext, '@')) != NULL)
9075 oparam temps;
9076 oparamptr ips;
9077 char psave, *promoted, *newexpr;
9079 pptr++;
9080 for (pkey = pptr; *pkey && !isspace(*pkey); pkey++)
9081 if (*pkey == '{' || *pkey == '}' || *pkey == '[' || *pkey == ']' ||
9082 *pkey == '(' || *pkey == ')' || *pkey == ',')
9083 break;
9085 if (pkey > pptr) {
9086 psave = *pkey;
9087 *pkey = '\0';
9088 if (pinst)
9089 ips = find_param(pinst, pptr);
9090 else
9091 ips = match_param(thisobj, pptr);
9092 if (ips == ops) {
9093 /* Avoid infinite recursion by treating a reference */
9094 /* to oneself as plain text. */
9095 ips = NULL;
9097 if ((ips == NULL) && !strncmp(pptr, "p_", 2)) {
9098 ips = &temps;
9099 if (!strcmp(pptr + 2, "rotation")) {
9100 temps.type = XC_FLOAT;
9101 temps.parameter.fvalue = pinst ? pinst->rotation : 0;
9103 else if (!strcmp(pptr + 2, "xposition")) {
9104 temps.type = XC_INT;
9105 temps.parameter.ivalue = pinst ? pinst->position.x : 0;
9107 else if (!strcmp(pptr + 2, "yposition")) {
9108 temps.type = XC_INT;
9109 temps.parameter.ivalue = pinst ? pinst->position.y : 0;
9111 else if (!strcmp(pptr + 2, "scale")) {
9112 temps.type = XC_FLOAT;
9113 temps.parameter.fvalue = pinst ? pinst->scale : 1.0;
9115 else if (!strcmp(pptr + 2, "color")) {
9116 temps.type = XC_INT;
9117 temps.parameter.ivalue = pinst ? pinst->color : DEFAULTCOLOR;
9119 else if (!strcmp(pptr + 2, "top_xposition")) {
9120 temps.type = XC_INT;
9121 UTopDrawingOffset(&temps.parameter.ivalue, NULL);
9123 else if (!strcmp(pptr + 2, "top_yposition")) {
9124 temps.type = XC_INT;
9125 UTopDrawingOffset(NULL, &temps.parameter.ivalue);
9127 else if (!strcmp(pptr + 2, "top_rotation")) {
9128 temps.type = XC_FLOAT;
9129 temps.parameter.fvalue = UTopRotation();
9131 else if (!strcmp(pptr + 2, "top_scale")) {
9132 temps.type = XC_FLOAT;
9133 temps.parameter.fvalue = UTopDrawingScale();
9135 else
9136 ips = NULL;
9138 *pkey = psave;
9139 if (ips != NULL) {
9140 switch (ips->type) {
9141 case XC_INT:
9142 promoted = malloc(12);
9143 snprintf(promoted, 12, "%d", ips->parameter.ivalue);
9144 break;
9145 case XC_FLOAT:
9146 promoted = malloc(12);
9147 snprintf(promoted, 12, "%g", ips->parameter.fvalue);
9148 break;
9149 case XC_STRING:
9150 promoted = textprint(ips->parameter.string, pinst);
9151 break;
9152 case XC_EXPR:
9153 /* We really ought to prevent infinite loops here. . .*/
9154 promoted = evaluate_expr(thisobj, ips, pinst);
9155 break;
9157 if (promoted == NULL) break;
9158 newexpr = (char *)malloc(1 + strlen(exprptr) +
9159 (max(strlen(promoted), strlen(pkey))));
9160 *(pptr - 1) = '\0';
9161 strcpy(newexpr, exprptr);
9162 *(pptr - 1) = '@';
9163 strcat(newexpr, promoted);
9164 pnext = newexpr + strlen(newexpr); /* For next search of '@' escape */
9165 strcat(newexpr, pkey);
9166 free(promoted);
9167 if (exprptr != ops->parameter.expr) free(exprptr);
9168 exprptr = newexpr;
9170 else {
9171 /* Ignore the keyword and move to the end */
9172 pnext = pkey;
9177 /* Evaluate the expression in TCL */
9179 Tcl_SaveResult(xcinterp, &state);
9180 status = Tcl_Eval(xcinterp, exprptr);
9181 robj = Tcl_GetObjResult(xcinterp);
9182 Tcl_IncrRefCount(robj);
9183 Tcl_RestoreResult(xcinterp, &state);
9184 if (eval_status) *eval_status = status;
9185 if (exprptr != ops->parameter.expr) free(exprptr);
9186 return robj;
9189 /*----------------------------------------------------------------------*/
9190 /* Evaluate an expression from a parameter and return the result as an */
9191 /* allocated string. */
9192 /*----------------------------------------------------------------------*/
9194 char *evaluate_expr(objectptr thisobj, oparamptr ops, objinstptr pinst)
9196 Tcl_Obj *robj;
9197 char *rexpr = NULL;
9198 int status, ip = 0;
9199 float fp = 0.0;
9200 stringpart *tmpptr, *promote = NULL;
9201 oparamptr ips = (pinst == NULL) ? NULL : match_instance_param(pinst, ops->key);
9203 robj = evaluate_raw(thisobj, ops, pinst, &status);
9204 if (robj != NULL) {
9205 rexpr = strdup(Tcl_GetString(robj));
9206 Tcl_DecrRefCount(robj);
9209 if ((status == TCL_ERROR) && (ips != NULL)) {
9210 switch(ips->type) {
9211 case XC_STRING:
9212 rexpr = textprint(ips->parameter.string, pinst);
9213 break;
9214 case XC_FLOAT:
9215 fp = ips->parameter.fvalue;
9216 break;
9220 /* If a TCL expression contains a three digit octal value \ooo */
9221 /* then the string returned by TclEval() can contain a */
9222 /* multi-byte UTF-8 character. */
9223 /* */
9224 /* This multi-byte character needs to be converted back to a */
9225 /* character that can be displayed. */
9226 /* */
9227 /* The following fix assumes that at most two bytes will */
9228 /* represent any converted character. In this case, the most */
9229 /* significant digit (octal) of the first byte will be 3, and */
9230 /* the most significant digit of the second byte will be 2. */
9231 /* */
9232 /* See: https://en.wikipedia.org/wiki/UTF-8 */
9234 if ((rexpr != NULL) && ((status == TCL_RETURN) || (status == TCL_OK))) {
9235 u_char *strptr1 = rexpr;
9236 u_char *strptr2 = rexpr;
9237 while (*strptr1 != '\0') {
9238 if (*strptr1 >= 0300 && *(strptr1 + 1) >= 0200) {
9239 *strptr2 = ((*strptr1 & ~0300) << 6) | (*(strptr1 + 1) & 0077);
9240 strptr1 += 2;
9241 } else {
9242 *strptr2 = *strptr1;
9243 strptr1++;
9245 strptr2++;
9247 if (*strptr1 == '\0')
9248 *strptr2 = *strptr1;
9251 /* If an instance redefines an expression, don't preserve */
9252 /* the result. It is necessary in this case that the */
9253 /* expression does not reference objects during redisplay, */
9254 /* or else the correct result will not be written to the */
9255 /* output. */
9257 if ((ips != NULL) && (ips->type == XC_EXPR))
9258 return rexpr;
9260 /* Preserve the result in the object instance; this will be */
9261 /* used when writing the output or when the result cannot */
9262 /* be evaluated (see above). */
9264 if ((rexpr != NULL) && (status == TCL_OK) && (pinst != NULL)) {
9265 switch (ops->which) {
9266 case P_SUBSTRING: case P_EXPRESSION:
9267 if (ips == NULL) {
9268 ips = make_new_parameter(ops->key);
9269 ips->which = ops->which;
9270 ips->type = XC_STRING;
9271 ips->next = pinst->params;
9272 pinst->params = ips;
9274 else {
9275 free(ips->parameter.string);
9277 /* Promote the expression result to an XCircuit string type */
9278 tmpptr = makesegment(&promote, NULL);
9279 tmpptr->type = TEXT_STRING;
9280 tmpptr = makesegment(&promote, NULL);
9281 tmpptr->type = PARAM_END;
9282 promote->data.string = strdup(rexpr);
9283 ips->parameter.string = promote;
9284 break;
9286 case P_COLOR: /* must be integer, exact to 32 bits */
9287 if (ips == NULL) {
9288 ips = make_new_parameter(ops->key);
9289 ips->which = ops->which;
9290 ips->next = pinst->params;
9291 pinst->params = ips;
9293 /* Promote the expression result to type float */
9294 if (rexpr != NULL) {
9295 if (sscanf(rexpr, "%i", &ip) == 1)
9296 ips->parameter.ivalue = ip;
9297 else
9298 ips->parameter.ivalue = 0;
9300 else
9301 ips->parameter.ivalue = ip;
9302 ips->type = XC_INT;
9303 break;
9305 default: /* all others convert to type float */
9306 if (ips == NULL) {
9307 ips = make_new_parameter(ops->key);
9308 ips->which = ops->which;
9309 ips->next = pinst->params;
9310 pinst->params = ips;
9312 /* Promote the expression result to type float */
9313 if (rexpr != NULL) {
9314 if (sscanf(rexpr, "%g", &fp) == 1)
9315 ips->parameter.fvalue = fp;
9316 else
9317 ips->parameter.fvalue = 0.0;
9319 else
9320 ips->parameter.fvalue = fp;
9321 ips->type = XC_FLOAT;
9322 break;
9325 return rexpr;
9328 /*----------------------------------------------------------------------*/
9329 /* Execute the .xcircuitrc startup script */
9330 /*----------------------------------------------------------------------*/
9332 int loadrcfile()
9334 char *userdir = getenv((const char *)"HOME");
9335 FILE *fd;
9336 short i;
9337 int result = TCL_OK, result1 = TCL_OK;
9339 /* Initialize flags */
9341 flags = 0;
9343 /* Try first in current directory, then look in user's home directory */
9344 /* First try looking for a file .xcircuitrc followed by a dash and */
9345 /* the program version; this allows backward compatibility of the rc */
9346 /* file in cases where a new version (e.g., 3 vs. 2) introduces */
9347 /* incompatible syntax. Thanks to Romano Giannetti for this */
9348 /* suggestion plus provided code. */
9350 /* (names USER_RC_FILE and PROG_VERSION imported from Makefile) */
9352 sprintf(_STR2, "%s-%s", USER_RC_FILE, PROG_VERSION);
9353 xc_tilde_expand(_STR2, 249);
9354 if ((fd = fopen(_STR2, "r")) == NULL) {
9355 /* Not found; check for the same in $HOME directory */
9356 if (userdir != NULL) {
9357 sprintf(_STR2, "%s/%s-%s", userdir, USER_RC_FILE, PROG_VERSION);
9358 if ((fd = fopen(_STR2, "r")) == NULL) {
9359 /* Not found again; check for rc file w/o version # in CWD */
9360 sprintf(_STR2, "%s", USER_RC_FILE);
9361 xc_tilde_expand(_STR2, 249);
9362 if ((fd = fopen(_STR2, "r")) == NULL) {
9363 /* last try: plain USER_RC_FILE in $HOME */
9364 sprintf(_STR2, "%s/%s", userdir, USER_RC_FILE);
9365 fd = fopen(_STR2, "r");
9370 if (fd != NULL) {
9371 fclose(fd);
9372 result = Tcl_EvalFile(xcinterp, _STR2);
9373 if (result != TCL_OK) {
9374 Fprintf(stderr, "Encountered error in startup file.");
9375 Fprintf(stderr, "%s\n", Tcl_GetStringResult(xcinterp));
9376 Fprintf(stderr, "Running default startup script instead.\n");
9380 /* Add the default font if not loaded already */
9382 if (!(flags & FONTOVERRIDE)) {
9383 loadfontfile("Helvetica");
9384 if (areawin->psfont == -1)
9385 for (i = 0; i < fontcount; i++)
9386 if (!strcmp(fonts[i].psname, "Helvetica")) {
9387 areawin->psfont = i;
9388 break;
9391 if (areawin->psfont == -1) areawin->psfont = 0;
9393 setdefaultfontmarks();
9395 /* arrange the loaded libraries */
9397 if ((result != TCL_OK) || !(flags & (LIBOVERRIDE | LIBLOADED))) {
9398 result1 = defaultscript();
9401 /* Add the default colors */
9403 if (!(flags & COLOROVERRIDE)) {
9404 addnewcolorentry(xc_alloccolor("Gray40"));
9405 addnewcolorentry(xc_alloccolor("Gray60"));
9406 addnewcolorentry(xc_alloccolor("Gray80"));
9407 addnewcolorentry(xc_alloccolor("Gray90"));
9408 addnewcolorentry(xc_alloccolor("Red"));
9409 addnewcolorentry(xc_alloccolor("Blue"));
9410 addnewcolorentry(xc_alloccolor("Green2"));
9411 addnewcolorentry(xc_alloccolor("Yellow"));
9412 addnewcolorentry(xc_alloccolor("Purple"));
9413 addnewcolorentry(xc_alloccolor("SteelBlue2"));
9414 addnewcolorentry(xc_alloccolor("Red3"));
9415 addnewcolorentry(xc_alloccolor("Tan"));
9416 addnewcolorentry(xc_alloccolor("Brown"));
9417 addnewcolorentry(xc_alloccolor("#d20adc"));
9418 addnewcolorentry(xc_alloccolor("Pink"));
9421 if ((result != TCL_OK) || !(flags & KEYOVERRIDE)) {
9422 default_keybindings();
9424 return (result1 != TCL_OK) ? result1 : result;
9427 /*----------------------------------------------------------------------*/
9428 /* Alternative button handler for use with Tk "bind" */
9429 /*----------------------------------------------------------------------*/
9431 int xctcl_standardaction(ClientData clientData,
9432 Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
9434 int idx, result, knum, kstate;
9435 XKeyEvent kevent;
9436 static char *updown[] = {"up", "down", NULL};
9438 if ((objc != 3) && (objc != 4)) goto badargs;
9440 if ((result = Tcl_GetIntFromObj(interp, objv[1], &knum)) != TCL_OK)
9441 goto badargs;
9443 if ((result = Tcl_GetIndexFromObj(interp, objv[2],
9444 (CONST84 char **)updown, "direction", 0, &idx)) != TCL_OK)
9445 goto badargs;
9447 if (objc == 4) {
9448 if ((result = Tcl_GetIntFromObj(interp, objv[3], &kstate)) != TCL_OK)
9449 goto badargs;
9451 else
9452 kstate = 0;
9454 make_new_event(&kevent);
9455 kevent.state = kstate;
9456 kevent.keycode = 0;
9458 if (idx == 0)
9459 kevent.type = KeyRelease;
9460 else
9461 kevent.type = KeyPress;
9463 switch (knum) {
9464 case 1:
9465 kevent.state |= Button1Mask;
9466 break;
9467 case 2:
9468 kevent.state |= Button2Mask;
9469 break;
9470 case 3:
9471 kevent.state |= Button3Mask;
9472 break;
9473 case 4:
9474 kevent.state |= Button4Mask;
9475 break;
9476 case 5:
9477 kevent.state |= Button5Mask;
9478 break;
9479 default:
9480 kevent.keycode = knum;
9481 break;
9483 #ifdef _MSC_VER
9484 if (kevent.state & Mod1Mask) {
9485 kevent.state &= ~Mod1Mask;
9487 if (kevent.state & (AnyModifier<<2)) {
9488 kevent.state &= ~(AnyModifier<<2);
9489 kevent.state |= Mod1Mask;
9491 #endif
9492 keyhandler((xcWidget)NULL, (caddr_t)NULL, &kevent);
9493 return TCL_OK;
9495 badargs:
9496 Tcl_SetResult(interp, "Usage: standardaction <button_num> up|down [<keystate>]\n"
9497 "or standardaction <keycode> up|down [<keystate>]\n", NULL);
9498 return TCL_ERROR;
9501 /*----------------------------------------------------------------------*/
9502 /* Action handler for use with Tk "bind" */
9503 /* This dispatches events based on specific named actions that xcircuit */
9504 /* knows about, rather than by named key. This bypasses xcircuit's */
9505 /* key bindings. */
9506 /*----------------------------------------------------------------------*/
9508 int xctcl_action(ClientData clientData,
9509 Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
9511 short value = 0;
9512 int function, result, ival;
9513 XPoint newpos, wpoint;
9515 if (objc >= 2 && objc <= 4) {
9516 function = string_to_func(Tcl_GetString(objv[1]), &value);
9517 if (objc >= 3) {
9518 result = (short)Tcl_GetIntFromObj(interp, objv[2], &ival);
9519 if (result == TCL_ERROR) return TCL_ERROR;
9520 value = (short)ival;
9523 newpos = UGetCursorPos();
9524 user_to_window(newpos, &wpoint);
9526 result = compatible_function(function);
9527 if (result == -1)
9528 Tcl_SetResult(interp, "Action not allowed\n", NULL);
9530 result = functiondispatch(function, value, wpoint.x, wpoint.y);
9531 if (result == -1)
9532 Tcl_SetResult(interp, "Action not handled\n", NULL);
9534 else {
9535 Tcl_SetResult(interp, "Usage: action <action_name> [<value>]\n", NULL);
9536 return TCL_ERROR;
9538 return XcTagCallback(interp, objc, objv);
9542 /*----------------------------------------------------------------------*/
9543 /* Argument-converting wrappers from Tk callback to Xt callback format */
9544 /*----------------------------------------------------------------------*/
9546 void xctk_drawarea(ClientData clientData, XEvent *eventPtr)
9548 Tcl_ServiceAll();
9549 if (areawin->topinstance != NULL)
9550 drawarea(areawin->area, (caddr_t)clientData, (caddr_t)NULL);
9553 /*----------------------------------------------------------------------*/
9555 void xctk_resizearea(ClientData clientData, XEvent *eventPtr)
9557 resizearea(areawin->area, (caddr_t)clientData, (caddr_t)NULL);
9558 /* Callback to function "arrangetoolbar" */
9559 Tcl_Eval(xcinterp, "catch {xcircuit::arrangetoolbar $XCOps(focus)}");
9562 /*----------------------------------------------------------------------*/
9563 /* Because Tk doesn't filter MotionEvent events based on context, we */
9564 /* have to filter the context here. */
9565 /*----------------------------------------------------------------------*/
9567 void xctk_panhbar(ClientData clientData, XEvent *eventPtr)
9569 XMotionEvent *mevent = (XMotionEvent *)eventPtr;
9570 u_int state = mevent->state;
9571 if (state & (Button1Mask | Button2Mask))
9572 panhbar(areawin->scrollbarh, (caddr_t)clientData, (XButtonEvent *)eventPtr);
9575 /*----------------------------------------------------------------------*/
9577 void xctk_panvbar(ClientData clientData, XEvent *eventPtr)
9579 XMotionEvent *mevent = (XMotionEvent *)eventPtr;
9580 u_int state = mevent->state;
9581 if (state & (Button1Mask | Button2Mask))
9582 panvbar(areawin->scrollbarv, (caddr_t)clientData, (XButtonEvent *)eventPtr);
9585 /*----------------------------------------------------------------------*/
9587 void xctk_drawhbar(ClientData clientData, XEvent *eventPtr)
9589 if (areawin->topinstance)
9590 drawhbar(areawin->scrollbarh, (caddr_t)clientData, (caddr_t)NULL);
9593 /*----------------------------------------------------------------------*/
9595 void xctk_drawvbar(ClientData clientData, XEvent *eventPtr)
9597 if (areawin->topinstance)
9598 drawvbar(areawin->scrollbarv, (caddr_t)clientData, (caddr_t)NULL);
9601 /*----------------------------------------------------------------------*/
9603 void xctk_endhbar(ClientData clientData, XEvent *eventPtr)
9605 if (areawin->topinstance)
9606 endhbar(areawin->scrollbarh, (caddr_t)clientData, (XButtonEvent *)eventPtr);
9609 /*----------------------------------------------------------------------*/
9611 void xctk_endvbar(ClientData clientData, XEvent *eventPtr)
9613 if (areawin->topinstance)
9614 endvbar(areawin->scrollbarv, (caddr_t)clientData, (XButtonEvent *)eventPtr);
9617 /*----------------------------------------------------------------------*/
9619 void xctk_zoomview(ClientData clientData, XEvent *eventPtr)
9621 zoomview((xcWidget)NULL, (caddr_t)clientData, (caddr_t)NULL);
9624 /*----------------------------------------------------------------------*/
9626 void xctk_swapschem(ClientData clientData, XEvent *eventPtr)
9628 swapschem((int)((pointertype)clientData), -1, NULL);
9631 /*----------------------------------------------------------------------*/
9633 void xctk_drag(ClientData clientData, XEvent *eventPtr)
9635 XButtonEvent *b_event = (XButtonEvent *)eventPtr;
9637 drag((int)b_event->x, (int)b_event->y);
9638 flusharea();
9639 #ifdef HAVE_CAIRO
9640 if (areawin->redraw_needed)
9641 drawarea(NULL, NULL, NULL);
9642 #endif /* HAVE_CAIRO */
9645 /*----------------------------------------------------------------------*/
9646 /* This really should be set up so that the "okay" button command tcl */
9647 /* procedure does the job of lookdirectory(). */
9648 /*----------------------------------------------------------------------*/
9650 void xctk_fileselect(ClientData clientData, XEvent *eventPtr)
9652 XButtonEvent *beventPtr = (XButtonEvent *)eventPtr;
9653 popupstruct *listp = (popupstruct *)clientData;
9654 char curentry[150];
9656 if (beventPtr->button == Button2) {
9657 Tcl_Eval(xcinterp, ".filelist.textent.txt get");
9658 sprintf(curentry, "%.149s", (char *)Tcl_GetStringResult(xcinterp));
9660 if (strlen(curentry) > 0) {
9661 if (lookdirectory(curentry, 149))
9662 newfilelist(listp->filew, listp);
9663 else
9664 Tcl_Eval(xcinterp, ".filelist.bbar.okay invoke");
9667 else if (beventPtr->button == Button4) { /* scroll wheel binding */
9668 flstart--;
9669 showlscroll(listp->scroll, NULL, NULL);
9670 listfiles(listp->filew, listp, NULL);
9672 else if (beventPtr->button == Button5) { /* scroll wheel binding */
9673 flstart++;
9674 showlscroll(listp->scroll, NULL, NULL);
9675 listfiles(listp->filew, listp, NULL);
9677 else
9678 fileselect(listp->filew, listp, beventPtr);
9681 /*----------------------------------------------------------------------*/
9683 void xctk_listfiles(ClientData clientData, XEvent *eventPtr)
9685 popupstruct *listp = (popupstruct *)clientData;
9686 char *filter;
9688 Tcl_Eval(xcinterp, ".filelist.listwin.win cget -data");
9689 filter = (char *)Tcl_GetStringResult(xcinterp);
9691 if (filter != NULL) {
9692 if ((listp->filter == NULL) || (strcmp(filter, listp->filter))) {
9693 if (listp->filter != NULL)
9694 free(listp->filter);
9695 listp->filter = strdup(filter);
9696 newfilelist(listp->filew, listp);
9698 else
9699 listfiles(listp->filew, listp, NULL);
9701 else {
9702 if (listp->filter != NULL) {
9703 free(listp->filter);
9704 listp->filter = NULL;
9706 listfiles(listp->filew, listp, NULL);
9710 /*----------------------------------------------------------------------*/
9712 void xctk_startfiletrack(ClientData clientData, XEvent *eventPtr)
9714 startfiletrack((Tk_Window)clientData, NULL, (XCrossingEvent *)eventPtr);
9717 /*----------------------------------------------------------------------*/
9719 void xctk_endfiletrack(ClientData clientData, XEvent *eventPtr)
9721 endfiletrack((Tk_Window)clientData, NULL, (XCrossingEvent *)eventPtr);
9724 /*----------------------------------------------------------------------*/
9726 void xctk_dragfilebox(ClientData clientData, XEvent *eventPtr)
9728 dragfilebox((Tk_Window)clientData, NULL, (XMotionEvent *)eventPtr);
9731 /*----------------------------------------------------------------------*/
9733 void xctk_draglscroll(ClientData clientData, XEvent *eventPtr)
9735 popupstruct *listp = (popupstruct *)clientData;
9736 XMotionEvent *mevent = (XMotionEvent *)eventPtr;
9737 u_int state = mevent->state;
9739 if (state & (Button1Mask | Button2Mask))
9740 draglscroll(listp->scroll, listp, (XButtonEvent *)eventPtr);
9743 /*----------------------------------------------------------------------*/
9745 void xctk_showlscroll(ClientData clientData, XEvent *eventPtr)
9747 showlscroll((Tk_Window)clientData, NULL, NULL);
9750 /*----------------------------------------------------------------------*/
9751 /* Build or rebuild the database of colors, fonts, and other settings */
9752 /* from the Tk option settings. */
9753 /*----------------------------------------------------------------------*/
9755 void build_app_database(Tk_Window tkwind)
9757 Tk_Uid xcuid;
9759 /*--------------------------*/
9760 /* Build the color database */
9761 /*--------------------------*/
9763 if ((xcuid = Tk_GetOption(tkwind, "globalpincolor", "Color")) == NULL)
9764 xcuid = "Orange2";
9765 appdata.globalcolor = xc_alloccolor((char *)xcuid);
9766 if ((xcuid = Tk_GetOption(tkwind, "localpincolor", "Color")) == NULL)
9767 xcuid = "Red";
9768 appdata.localcolor = xc_alloccolor((char *)xcuid);
9769 if ((xcuid = Tk_GetOption(tkwind, "infolabelcolor", "Color")) == NULL)
9770 xcuid = "SeaGreen";
9771 appdata.infocolor = xc_alloccolor((char *)xcuid);
9772 if ((xcuid = Tk_GetOption(tkwind, "ratsnestcolor", "Color")) == NULL)
9773 xcuid = "tan4";
9774 appdata.ratsnestcolor = xc_alloccolor((char *)xcuid);
9776 if ((xcuid = Tk_GetOption(tkwind, "bboxcolor", "Color")) == NULL)
9777 xcuid = "greenyellow";
9778 appdata.bboxpix = xc_alloccolor((char *)xcuid);
9780 if ((xcuid = Tk_GetOption(tkwind, "fixedbboxcolor", "Color")) == NULL)
9781 xcuid = "pink";
9782 appdata.fixedbboxpix = xc_alloccolor((char *)xcuid);
9784 if ((xcuid = Tk_GetOption(tkwind, "clipcolor", "Color")) == NULL)
9785 xcuid = "powderblue";
9786 appdata.clipcolor = xc_alloccolor((char *)xcuid);
9788 if ((xcuid = Tk_GetOption(tkwind, "paramcolor", "Color")) == NULL)
9789 xcuid = "Plum3";
9790 appdata.parampix = xc_alloccolor((char *)xcuid);
9791 if ((xcuid = Tk_GetOption(tkwind, "auxiliarycolor", "Color")) == NULL)
9792 xcuid = "Green3";
9793 appdata.auxpix = xc_alloccolor((char *)xcuid);
9794 if ((xcuid = Tk_GetOption(tkwind, "axescolor", "Color")) == NULL)
9795 xcuid = "Antique White";
9796 appdata.axespix = xc_alloccolor((char *)xcuid);
9797 if ((xcuid = Tk_GetOption(tkwind, "filtercolor", "Color")) == NULL)
9798 xcuid = "SteelBlue3";
9799 appdata.filterpix = xc_alloccolor((char *)xcuid);
9800 if ((xcuid = Tk_GetOption(tkwind, "selectcolor", "Color")) == NULL)
9801 xcuid = "Gold3";
9802 appdata.selectpix = xc_alloccolor((char *)xcuid);
9803 if ((xcuid = Tk_GetOption(tkwind, "snapcolor", "Color")) == NULL)
9804 xcuid = "Red";
9805 appdata.snappix = xc_alloccolor((char *)xcuid);
9806 if ((xcuid = Tk_GetOption(tkwind, "gridcolor", "Color")) == NULL)
9807 xcuid = "Gray95";
9808 appdata.gridpix = xc_alloccolor((char *)xcuid);
9809 if ((xcuid = Tk_GetOption(tkwind, "pagebackground", "Color")) == NULL)
9810 xcuid = "White";
9811 appdata.bg = xc_alloccolor((char *)xcuid);
9812 if ((xcuid = Tk_GetOption(tkwind, "pageforeground", "Color")) == NULL)
9813 xcuid = "Black";
9814 appdata.fg = xc_alloccolor((char *)xcuid);
9816 if ((xcuid = Tk_GetOption(tkwind, "paramcolor2", "Color")) == NULL)
9817 xcuid = "Plum3";
9818 appdata.parampix2 = xc_alloccolor((char *)xcuid);
9819 if ((xcuid = Tk_GetOption(tkwind, "auxiliarycolor2", "Color")) == NULL)
9820 xcuid = "Green";
9821 appdata.auxpix2 = xc_alloccolor((char *)xcuid);
9822 if ((xcuid = Tk_GetOption(tkwind, "selectcolor2", "Color")) == NULL)
9823 xcuid = "Gold";
9824 appdata.selectpix2 = xc_alloccolor((char *)xcuid);
9825 if ((xcuid = Tk_GetOption(tkwind, "filtercolor2", "Color")) == NULL)
9826 xcuid = "SteelBlue1";
9827 appdata.gridpix2 = xc_alloccolor((char *)xcuid);
9828 if ((xcuid = Tk_GetOption(tkwind, "snapcolor2", "Color")) == NULL)
9829 xcuid = "Red";
9830 appdata.snappix2 = xc_alloccolor((char *)xcuid);
9831 if ((xcuid = Tk_GetOption(tkwind, "axescolor2", "Color")) == NULL)
9832 xcuid = "NavajoWhite4";
9833 appdata.axespix2 = xc_alloccolor((char *)xcuid);
9834 if ((xcuid = Tk_GetOption(tkwind, "background2", "Color")) == NULL)
9835 xcuid = "DarkSlateGray";
9836 appdata.bg2 = xc_alloccolor((char *)xcuid);
9837 if ((xcuid = Tk_GetOption(tkwind, "foreground2", "Color")) == NULL)
9838 xcuid = "White";
9839 appdata.fg2 = xc_alloccolor((char *)xcuid);
9840 if ((xcuid = Tk_GetOption(tkwind, "barcolor", "Color")) == NULL)
9841 xcuid = "Tan";
9842 appdata.barpix = xc_alloccolor((char *)xcuid);
9844 /* These are GUI colors---unused by Tcl */
9845 appdata.buttonpix = xc_alloccolor("Gray85");
9846 appdata.buttonpix2 = xc_alloccolor("Gray50");
9848 /* Get some default fonts (Should be using Tk calls here. . . ) */
9850 if ((xcuid = Tk_GetOption(tkwind, "filelistfont", "Font")) == NULL)
9851 xcuid = "-*-helvetica-medium-r-normal--14-*";
9852 appdata.filefont = XLoadQueryFont(dpy, (char *)xcuid);
9854 if (appdata.filefont == NULL)
9856 appdata.filefont = XLoadQueryFont(dpy, "-*-*-medium-r-normal--14-*");
9857 if (appdata.filefont == NULL)
9858 appdata.filefont = XLoadQueryFont(dpy, "-*-*-*-*-*--*-*");
9859 if (appdata.filefont == NULL)
9860 appdata.filefont = XLoadQueryFont(dpy, "*");
9861 if (appdata.filefont == NULL) {
9862 Fprintf(stderr, "Fatal error: No X11 fonts found.\n");
9866 /* Other defaults */
9868 if ((xcuid = Tk_GetOption(tkwind, "timeout", "TimeOut")) == NULL)
9869 xcuid = "10";
9870 appdata.timeout = atoi((char *)xcuid);
9873 /*--------------------------------------------------------------*/
9874 /* GUI Initialization under Tk */
9875 /* First argument is the Tk path name of the drawing window. */
9876 /* This function should be called for each new window created. */
9877 /*--------------------------------------------------------------*/
9879 XCWindowData *GUI_init(int objc, Tcl_Obj *CONST objv[])
9881 Tk_Window tkwind, tktop, tkdraw, tksb;
9882 Tk_Window wsymb, wschema, corner;
9883 int i, locobjc, done = 1, first;
9884 XGCValues values;
9885 Window win;
9886 popupstruct *fileliststruct;
9887 char *xctopwin, *xcdrawwin;
9888 char winpath[512];
9889 XCWindowData *newwin;
9891 tktop = Tk_MainWindow(xcinterp);
9892 if (tktop == (Tk_Window)NULL) {
9893 Fprintf(stderr, "No Top-Level Tk window available. . .\n");
9895 /* No top level window, assuming batch mode. To get */
9896 /* access to font information requires that cairo be set */
9897 /* up with a surface, even if it is not an xlib target. */
9899 newwin = create_new_window();
9900 newwin->area = NULL;
9901 newwin->scrollbarv = NULL;
9902 newwin->scrollbarh = NULL;
9903 newwin->width = 100;
9904 newwin->height = 100;
9906 #ifdef HAVE_CAIRO
9907 newwin->surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
9908 newwin->width, newwin->height);
9909 newwin->cr = cairo_create(newwin->surface);
9910 #endif /* !HAVE_CAIRO */
9912 number_colors = NUMBER_OF_COLORS;
9913 colorlist = (colorindex *)malloc(NUMBER_OF_COLORS * sizeof(colorindex));
9915 return newwin;
9918 /* Check if any parameter is a Tk window name */
9920 locobjc = objc;
9921 while (locobjc > 0) {
9922 xctopwin = Tcl_GetString(objv[locobjc - 1]);
9923 tkwind = Tk_NameToWindow(xcinterp, xctopwin, tktop);
9924 if (tkwind != (Tk_Window)NULL)
9925 break;
9926 locobjc--;
9929 if (locobjc == 0) {
9930 /* Okay to have no GUI wrapper. However, if this is the case, */
9931 /* then the variable "XCOps(window)" must be set to the Tk path */
9932 /* name of the drawing window. */
9934 xcdrawwin = (char *)Tcl_GetVar2(xcinterp, "XCOps", "window", 0);
9935 if (xcdrawwin == NULL) {
9936 Fprintf(stderr, "The Tk window hierarchy must be rooted at"
9937 " .xcircuit, or XCOps(top)");
9938 Fprintf(stderr, " must point to the hierarchy. If XCOps(top)"
9939 " is NULL, then XCOps(window) must");
9940 Fprintf(stderr, " point to the drawing window.\n");
9941 return NULL;
9943 tkwind = Tk_NameToWindow(xcinterp, xcdrawwin, tktop);
9944 if (tkwind == NULL) {
9945 Fprintf(stderr, "Error: XCOps(window) is set but does not point to"
9946 " a valid Tk window.\n");
9947 return NULL;
9950 /* Create new window data structure */
9951 newwin = create_new_window();
9952 newwin->area = tkwind;
9954 /* No GUI---GUI widget pointers need to be NULL'd */
9955 newwin->scrollbarv = NULL;
9956 newwin->scrollbarh = NULL;
9958 else {
9960 /* Expect a top-level window name passed as the first argument. */
9961 /* Having a fixed hierarchy is a total kludge and needs to be */
9962 /* rewritten. . . */
9964 if (tkwind == NULL) {
9965 Fprintf(stderr, "Error: config init given a bad window name!\n");
9966 return NULL;
9968 else {
9969 /* Make sure that this window does not already exist */
9970 XCWindowDataPtr searchwin;
9971 sprintf(winpath, "%s.mainframe.mainarea.drawing", xctopwin);
9972 tkdraw = Tk_NameToWindow(xcinterp, winpath, tktop);
9973 for (searchwin = xobjs.windowlist; searchwin != NULL; searchwin =
9974 searchwin->next) {
9975 if (searchwin->area == tkdraw) {
9976 Fprintf(stderr, "Error: window already exists!\n");
9977 return NULL;
9982 /* Create new window data structure and */
9983 /* fill in global variables from the Tk window values */
9985 newwin = create_new_window();
9986 sprintf(winpath, "%s.mainframe.mainarea.sbleft", xctopwin);
9987 newwin->scrollbarv = Tk_NameToWindow(xcinterp, winpath, tktop);
9988 sprintf(winpath, "%s.mainframe.mainarea.sbbottom", xctopwin);
9989 newwin->scrollbarh = Tk_NameToWindow(xcinterp, winpath, tktop);
9990 sprintf(winpath, "%s.mainframe.mainarea.drawing", xctopwin);
9991 newwin->area = Tk_NameToWindow(xcinterp, winpath, tktop);
9993 sprintf(winpath, "%s.mainframe.mainarea.corner", xctopwin);
9994 corner = Tk_NameToWindow(xcinterp, winpath, tktop);
9996 sprintf(winpath, "%s.infobar.symb", xctopwin);
9997 wsymb = Tk_NameToWindow(xcinterp, winpath, tktop);
9999 sprintf(winpath, "%s.infobar.schem", xctopwin);
10000 wschema = Tk_NameToWindow(xcinterp, winpath, tktop);
10002 Tk_CreateEventHandler(newwin->scrollbarh, ButtonMotionMask,
10003 (Tk_EventProc *)xctk_panhbar, NULL);
10004 Tk_CreateEventHandler(newwin->scrollbarv, ButtonMotionMask,
10005 (Tk_EventProc *)xctk_panvbar, NULL);
10006 Tk_CreateEventHandler(newwin->scrollbarh, StructureNotifyMask | ExposureMask,
10007 (Tk_EventProc *)xctk_drawhbar, NULL);
10008 Tk_CreateEventHandler(newwin->scrollbarv, StructureNotifyMask | ExposureMask,
10009 (Tk_EventProc *)xctk_drawvbar, NULL);
10010 Tk_CreateEventHandler(newwin->scrollbarh, ButtonReleaseMask,
10011 (Tk_EventProc *)xctk_endhbar, NULL);
10012 Tk_CreateEventHandler(newwin->scrollbarv, ButtonReleaseMask,
10013 (Tk_EventProc *)xctk_endvbar, NULL);
10015 Tk_CreateEventHandler(corner, ButtonPressMask,
10016 (Tk_EventProc *)xctk_zoomview, Number(1));
10017 Tk_CreateEventHandler(wsymb, ButtonPressMask,
10018 (Tk_EventProc *)xctk_swapschem, Number(0));
10019 Tk_CreateEventHandler(wschema, ButtonPressMask,
10020 (Tk_EventProc *)xctk_swapschem, Number(0));
10022 /* Setup event handlers for the drawing area and scrollbars */
10023 /* There are purposely no callback functions for these windows---they are */
10024 /* defined as type "simple" to keep down the cruft, as I will define my */
10025 /* own event handlers. */
10027 Tk_CreateEventHandler(newwin->area, StructureNotifyMask,
10028 (Tk_EventProc *)xctk_resizearea, NULL);
10029 Tk_CreateEventHandler(newwin->area, ExposureMask,
10030 (Tk_EventProc *)xctk_drawarea, NULL);
10033 if ((locobjc > 0) || !Tk_IsMapped(newwin->area)) {
10035 /* This code copied from code for the "tkwait" command */
10037 Tk_CreateEventHandler(newwin->area,
10038 VisibilityChangeMask|StructureNotifyMask,
10039 WaitVisibilityProc, (ClientData) &done);
10040 done = 0;
10043 /* Make sure the window is mapped */
10045 Tk_MapWindow(tkwind);
10046 win = Tk_WindowId(tkwind);
10047 Tk_MapWindow(newwin->area);
10049 first = (dpy == NULL) ? 1 : 0;
10050 dpy = Tk_Display(tkwind);
10052 if (!done) {
10053 while (!done) Tcl_DoOneEvent(0);
10054 Tk_DeleteEventHandler(newwin->area,
10055 VisibilityChangeMask|StructureNotifyMask,
10056 WaitVisibilityProc, (ClientData) &done);
10059 newwin->window = Tk_WindowId(newwin->area);
10060 newwin->width = Tk_Width(newwin->area);
10061 newwin->height = Tk_Height(newwin->area);
10063 /* Things to set once only */
10065 if (first == 1) {
10066 dpy = Tk_Display(tkwind);
10067 cmap = Tk_Colormap(tkwind);
10068 // (The following may be required on some systems where
10069 // Tk will not report a valid colormap after Tk_MapWindow())
10070 // cmap = DefaultColormap(dpy, DefaultScreen(dpy));
10072 /*------------------------------------------------------*/
10073 /* Handle different screen resolutions in a sane manner */
10074 /*------------------------------------------------------*/
10076 screenDPI = getscreenDPI();
10078 /*-------------------------*/
10079 /* Create stipple patterns */
10080 /*-------------------------*/
10082 for (i = 0; i < STIPPLES; i++)
10083 STIPPLE[i] = XCreateBitmapFromData(dpy, win, STIPDATA[i], 4, 4);
10085 /*----------------------------------------*/
10086 /* Allocate space for the basic color map */
10087 /*----------------------------------------*/
10089 number_colors = NUMBER_OF_COLORS;
10090 colorlist = (colorindex *)malloc(NUMBER_OF_COLORS * sizeof(colorindex));
10091 areawin = newwin;
10092 build_app_database(tkwind);
10093 areawin = NULL;
10095 /* Create the filelist window and its event handlers */
10097 tksb = Tk_NameToWindow(xcinterp, ".filelist.listwin.sb", tktop);
10098 tkdraw = Tk_NameToWindow(xcinterp, ".filelist.listwin.win", tktop);
10100 fileliststruct = (popupstruct *) malloc(sizeof(popupstruct));
10101 fileliststruct->popup = Tk_NameToWindow(xcinterp, ".filelist", tktop);
10102 fileliststruct->textw = Tk_NameToWindow(xcinterp, ".filelist.textent",
10103 fileliststruct->popup);
10104 fileliststruct->filew = tkdraw;
10105 fileliststruct->scroll = tksb;
10106 fileliststruct->setvalue = NULL;
10107 fileliststruct->filter = NULL;
10109 if (tksb != NULL) {
10110 Tk_CreateEventHandler(tksb, ButtonMotionMask,
10111 (Tk_EventProc *)xctk_draglscroll, (ClientData)fileliststruct);
10112 Tk_CreateEventHandler(tksb, ExposureMask,
10113 (Tk_EventProc *)xctk_showlscroll, (ClientData)tksb);
10115 if (tkdraw != NULL) {
10116 Tk_CreateEventHandler(tkdraw, ButtonPressMask,
10117 (Tk_EventProc *)xctk_fileselect, (ClientData)fileliststruct);
10118 Tk_CreateEventHandler(tkdraw, ExposureMask,
10119 (Tk_EventProc *)xctk_listfiles, (ClientData)fileliststruct);
10120 Tk_CreateEventHandler(tkdraw, EnterWindowMask,
10121 (Tk_EventProc *)xctk_startfiletrack, (ClientData)tkdraw);
10122 Tk_CreateEventHandler(tkdraw, LeaveWindowMask,
10123 (Tk_EventProc *)xctk_endfiletrack, (ClientData)tkdraw);
10127 /*-------------------------------------------------------------------*/
10128 /* Generate the GC */
10129 /* Set "graphics_exposures" to False. Every XCopyArea function */
10130 /* copies from virtual memory (dbuf pixmap), which can never be */
10131 /* obscured. Otherwise, the server gets flooded with useless */
10132 /* NoExpose events. */
10133 /*-------------------------------------------------------------------*/
10135 values.foreground = BlackPixel(dpy, DefaultScreen(dpy));
10136 values.background = WhitePixel(dpy, DefaultScreen(dpy));
10137 values.graphics_exposures = False;
10138 newwin->gc = XCreateGC(dpy, win, GCForeground | GCBackground
10139 | GCGraphicsExposures, &values);
10141 #ifdef HAVE_CAIRO
10142 newwin->surface = cairo_xlib_surface_create(dpy, newwin->window,
10143 DefaultVisual(dpy, 0), newwin->width, newwin->height);
10144 newwin->cr = cairo_create(newwin->surface);
10145 #else /* HAVE_CAIRO */
10146 newwin->clipmask = XCreatePixmap(dpy, win, newwin->width,
10147 newwin->height, 1);
10149 values.foreground = 0;
10150 values.background = 0;
10151 newwin->cmgc = XCreateGC(dpy, newwin->clipmask, GCForeground
10152 | GCBackground, &values);
10153 #endif /* HAVE_CAIRO */
10155 XDefineCursor (dpy, win, *newwin->defaultcursor);
10156 return newwin;
10159 /*--------------------------------------*/
10160 /* Inline the main wrapper prodedure */
10161 /*--------------------------------------*/
10163 int xctcl_start(ClientData clientData, Tcl_Interp *interp,
10164 int objc, Tcl_Obj *CONST objv[])
10166 int result = TCL_OK;
10167 Boolean rcoverride = False;
10168 char *filearg = NULL;
10169 Tcl_Obj *cmdname = objv[0];
10171 Fprintf(stdout, "Starting xcircuit under Tcl interpreter\n");
10173 /* xcircuit initialization routines --- these assume that the */
10174 /* GUI has been created by the startup script; otherwise bad */
10175 /* things will probably occur. */
10177 pre_initialize();
10178 areawin = GUI_init(--objc, ++objv);
10179 if (areawin == NULL) {
10180 /* Create new window data structure */
10181 areawin = create_new_window();
10182 areawin->area = NULL;
10183 areawin->scrollbarv = NULL;
10184 areawin->scrollbarh = NULL;
10186 Tcl_SetResult(interp, "Invalid or missing top-level windowname"
10187 " given to start command, assuming batch mode.\n", NULL);
10189 post_initialize();
10191 ghostinit();
10193 /* The Tcl version accepts some command-line arguments. Due */
10194 /* to the way ".wishrc" is processed, all arguments are */
10195 /* glommed into one Tcl (list) object, objv[1]. */
10197 filearg = (char *)malloc(sizeof(char));
10198 *filearg = '\0';
10200 if (objc == 2) {
10201 char **argv;
10202 int argc;
10204 Tcl_SplitList(interp, Tcl_GetString(objv[1]), &argc,
10205 (CONST84 char ***)&argv);
10206 while (argc) {
10207 if (**argv == '-') {
10208 if (!strncmp(*argv, "-exec", 5)) {
10209 if (--argc > 0) {
10210 argv++;
10211 result = Tcl_EvalFile(interp, *argv);
10212 if (result != TCL_OK) {
10213 free(filearg);
10214 return result;
10216 else
10217 rcoverride = True;
10219 else {
10220 Tcl_SetResult(interp, "No filename given to exec argument.", NULL);
10221 free(filearg);
10222 return TCL_ERROR;
10225 else if (!strncmp(*argv, "-2", 2)) {
10226 /* 2-button mouse bindings option */
10227 pressmode = 1;
10230 else if (strcmp(*argv, ".xcircuit")) {
10231 filearg = (char *)realloc(filearg, sizeof(char) *
10232 (strlen(filearg) + strlen(*argv) + 2));
10233 strcat(filearg, ",");
10234 strcat(filearg, *argv);
10236 argv++;
10237 argc--;
10240 else {
10241 /* Except---this appears to be no longer true. When did it change? */
10242 int argc = objc;
10243 char *argv;
10245 for (argc = 0; argc < objc; argc++) {
10246 argv = Tcl_GetString(objv[argc]);
10247 if (*argv == '-') {
10248 if (!strncmp(argv, "-exec", 5)) {
10249 if (++argc < objc) {
10250 argv = Tcl_GetString(objv[argc]);
10251 result = Tcl_EvalFile(interp, argv);
10252 if (result != TCL_OK) {
10253 free(filearg);
10254 return result;
10256 else
10257 rcoverride = True;
10259 else {
10260 Tcl_SetResult(interp, "No filename given to exec argument.", NULL);
10261 free(filearg);
10262 return TCL_ERROR;
10265 else if (!strncmp(argv, "-2", 2)) {
10266 /* 2-button mouse bindings option */
10267 pressmode = 1;
10270 else if (strcmp(argv, ".xcircuit")) {
10271 filearg = (char *)realloc(filearg, sizeof(char) *
10272 (strlen(filearg) + strlen(argv) + 2));
10273 strcat(filearg, ",");
10274 strcat(filearg, argv);
10279 if (!rcoverride)
10280 result = loadrcfile();
10282 composelib(PAGELIB); /* make sure we have a valid page list */
10283 composelib(LIBLIB); /* and library directory */
10284 if ((objc >= 2) && (*filearg != '\0')) {
10285 char *libname;
10286 int target = -1;
10288 strcpy(_STR2, filearg);
10289 libname = (char *)Tcl_GetVar2(xcinterp, "XCOps", "library", 0);
10290 if (libname != NULL) {
10291 target = NameToLibrary(libname);
10293 startloadfile((target >= 0) ? target + LIBRARY : -1);
10295 else {
10296 findcrashfiles();
10298 pressmode = 0; /* Done using this to track 2-button bindings */
10300 /* Note that because the setup has the windows generated and */
10301 /* mapped prior to calling the xcircuit routines, nothing */
10302 /* gets CreateNotify, MapNotify, or other definitive events. */
10303 /* So, we have to do all the drawing once. */
10305 xobjs.suspend = -1; /* Release from suspend mode */
10306 if (areawin->scrollbarv)
10307 drawvbar(areawin->scrollbarv, NULL, NULL);
10308 if (areawin->scrollbarh)
10309 drawhbar(areawin->scrollbarh, NULL, NULL);
10310 drawarea(areawin->area, NULL, NULL);
10312 /* Return back to the interpreter; Tk is handling the GUI */
10313 free(filearg);
10314 return (result == TCL_OK) ? XcTagCallback(interp, 1, &cmdname) : result;
10317 /*--------------------------------------------------------------*/
10318 /* Message printing procedures for the Tcl version */
10319 /* */
10320 /* Evaluate the variable-length argument, and make a call to */
10321 /* the routine xcircuit::print, which should be defined. */
10322 /*--------------------------------------------------------------*/
10324 void W0vprintf(char *window, const char *format, va_list args_in)
10326 char tstr[128], *bigstr = NULL, *strptr;
10327 int n, size;
10328 va_list args;
10330 if (window != NULL) {
10331 sprintf(tstr, "catch {xcircuit::print %s {", window);
10332 size = strlen(tstr);
10334 va_copy(args, args_in);
10335 n = vsnprintf(tstr + size, 128 - size, format, args);
10336 va_end(args);
10338 if (n <= -1 || n > 125 - size) {
10339 bigstr = malloc(n + size + 4);
10340 strncpy(bigstr, tstr, size);
10341 va_copy(args, args_in);
10342 vsnprintf(bigstr + size, n + 1, format, args);
10343 va_end(args);
10344 strptr = bigstr;
10345 strcat(bigstr, "}}");
10347 else {
10348 strptr = tstr;
10349 strcat(tstr, "}}");
10351 Tcl_Eval(xcinterp, strptr);
10352 if (bigstr != NULL) free(bigstr);
10356 /* Prints to pagename window */
10358 void W1printf(char *format, ...)
10360 va_list args;
10361 va_start(args, format);
10362 W0vprintf("coord", format, args);
10363 va_end(args);
10366 /* Prints to coordinate window */
10368 void W2printf(char *format, ...)
10370 va_list args;
10371 va_start(args, format);
10372 W0vprintf("page", format, args);
10373 va_end(args);
10376 /* Prints to status window but does not tee output to the console. */
10378 void W3printf(char *format, ...)
10380 va_list args;
10381 va_start(args, format);
10382 W0vprintf("stat", format, args);
10383 va_end(args);
10386 /* Prints to status window and duplicates the output to stdout. */
10388 void Wprintf(char *format, ...)
10390 va_list args;
10391 va_start(args, format);
10392 W0vprintf("stat", format, args);
10393 if (strlen(format) > 0) {
10394 if (strstr(format, "Error")) {
10395 tcl_vprintf(stderr, format, args);
10396 tcl_printf(stderr, "\n");
10398 else {
10399 tcl_vprintf(stdout, format, args);
10400 tcl_printf(stdout, "\n");
10403 va_end(args);
10406 /*------------------------------------------------------*/
10408 #endif /* defined(TCL_WRAPPER) && !defined(HAVE_PYTHON) */