Merge branch 'master' into xcircuit-3.10
[xcircuit.git] / schema.c
blob5869478f29b7801b0ae6379110c540fd09e2c8c8
1 /*-------------------------------------------------------------------------*/
2 /* schema.c --- xcircuit routines specific to the schematic capture system */
3 /* Copyright (c) 2002 Tim Edwards, Johns Hopkins University */
4 /*-------------------------------------------------------------------------*/
6 /*-------------------------------------------------------------------------*/
7 /* written by Tim Edwards, 10/13/97 */
8 /*-------------------------------------------------------------------------*/
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
14 #ifndef XC_WIN32
15 #include <X11/Intrinsic.h>
16 #include <X11/StringDefs.h>
17 #endif
19 #ifdef TCL_WRAPPER
20 #include <tk.h>
21 #else
22 #ifndef XC_WIN32
23 #include "Xw/Xw.h"
24 #include "Xw/MenuBtn.h"
25 #endif
26 #endif
28 /*-------------------------------------------------------------------------*/
29 /* Local includes */
30 /*-------------------------------------------------------------------------*/
32 #include "xcircuit.h"
33 #include "colordefs.h"
34 #include "menudep.h"
36 /*----------------------------------------------------------------------*/
37 /* Function prototype declarations */
38 /*----------------------------------------------------------------------*/
39 #include "prototypes.h"
41 /*------------------------------------------------------------------------*/
42 /* External Variable definitions */
43 /*------------------------------------------------------------------------*/
45 #ifdef TCL_WRAPPER
46 extern Tcl_Interp *xcinterp;
47 #endif
49 extern Globaldata xobjs;
50 extern XCWindowData *areawin;
51 extern xcWidget menuwidgets[];
52 extern xcWidget netbutton;
53 extern char _STR[150];
54 extern char _STR2[250];
55 extern colorindex *colorlist;
57 /*-------------------------------------------------------------------------*/
59 extern xcWidget wsymb, wschema;
61 /*--------------------------------------------------------*/
62 /* Menu calls (procedure wrappers) */
63 /*--------------------------------------------------------*/
65 void callwritenet(xcWidget w, pointertype mode, caddr_t calldata)
67 switch(mode) {
68 case 0:
69 writenet(topobject, "spice", "spc");
70 break;
71 case 1:
72 writenet(topobject, "flatsim", "sim");
73 break;
74 case 2:
75 writenet(topobject, "pcb", "pcbnet");
76 break;
77 case 3:
78 writenet(topobject, "flatspice", "fspc");
79 break;
80 case 4:
81 /* Auto-numbering: no output generated */
82 writenet(topobject, "indexpcb", "");
83 break;
87 /*----------------------------------------------------------------------*/
88 /* Find the page object and instance with the indicated name. */
89 /*----------------------------------------------------------------------*/
91 objectptr NameToPageObject(char *objname, objinstptr *ret_inst, int *ret_page)
93 int i;
95 for (i = 0; i < xobjs.pages; i++) {
96 if (xobjs.pagelist[i]->pageinst == NULL) continue;
97 if (!strcmp(objname, xobjs.pagelist[i]->pageinst->thisobject->name)) {
98 if (ret_inst) *ret_inst = xobjs.pagelist[i]->pageinst;
99 if (ret_page) *ret_page = i;
100 return xobjs.pagelist[i]->pageinst->thisobject;
103 return NULL;
106 /*--------------------------------------------------------------*/
107 /* Get the canonical name of an object (the part without the */
108 /* technology prefix, if any). */
109 /*--------------------------------------------------------------*/
111 char *GetCanonicalName(char *fullname)
113 char *canonname = strstr(fullname, "::");
114 if (canonname == NULL) return fullname;
115 return canonname + 2;
118 /*----------------------------------------------------------------------*/
119 /* Find the object with the indicated name. */
120 /* */
121 /* WARNING: If no library technology is given, XCircuit will return */
122 /* the first object with the name "objname". If there are multiple */
123 /* objects of the same name in different technologies, then the one */
124 /* returned may not be the one expected! */
125 /* */
126 /* UPDATE (4/17/2020): The "technology prefer" option can be used to */
127 /* set a precedence of one library over another in case of a naming */
128 /* conflict. This should be used appropriately, as ambiguous object */
129 /* names in two technologies both marked "preferred" cannot be */
130 /* disambiguated. */
131 /*----------------------------------------------------------------------*/
133 objectptr NameToObject(char *objname, objinstptr *ret_inst, Boolean dopages)
135 int i;
136 liblistptr spec;
137 Boolean notech = FALSE;
138 Boolean preferred = FALSE;
139 char *techptr;
140 objectptr retobj = NULL;
141 TechPtr nsp;
143 if (strstr(objname, "::") == NULL) notech = TRUE;
145 for (i = 0; i < xobjs.numlibs; i++) {
146 for (spec = xobjs.userlibs[i].instlist; spec != NULL; spec = spec->next) {
147 techptr = spec->thisinst->thisobject->name;
148 if (notech)
149 techptr = GetCanonicalName(spec->thisinst->thisobject->name);
150 if (!strcmp(objname, techptr)) {
151 if ((retobj == NULL) || ((retobj != NULL) && (preferred == FALSE))) {
152 if (ret_inst) *ret_inst = spec->thisinst;
153 retobj = spec->thisinst->thisobject;
154 nsp = GetObjectTechnology(retobj);
155 preferred = (nsp && (nsp->flags & TECH_PREFER)) ? TRUE : FALSE;
161 if (retobj != NULL) return retobj;
163 if (dopages)
164 return NameToPageObject(objname, ret_inst, NULL);
165 else
166 return NULL;
169 /*------------------------------------------------------------------------*/
170 /* Ensure that a page name is unique (required for schematic association) */
171 /*------------------------------------------------------------------------*/
173 int checkpagename(objectptr thispageobj)
175 int p, thispage;
176 /* char *objname = thispageobj->name; (jdk) */
177 Boolean changed;
178 Boolean update = False;
179 char *clnptr = NULL;
180 int n;
182 /* Check for ":n" suffix and prepare for possible update */
183 clnptr = strrchr(thispageobj->name, ':');
184 if (clnptr != NULL)
185 if (sscanf(clnptr + 1, "%d", &n) != 1)
186 clnptr = NULL;
188 /* Find the page number of this page object */
189 for (p = 0; p < xobjs.pages; p++) {
190 if (xobjs.pagelist[p]->pageinst != NULL) {
191 if (xobjs.pagelist[p]->pageinst->thisobject == thispageobj) {
192 thispage = p;
193 break;
197 if (p == xobjs.pages) {
198 Fprintf(stderr, "Error: Object is not a page object!\n");
199 return 0;
202 /* Look for any other pages with the same name */
203 do {
204 changed = False;
205 for (p = 0; p < xobjs.pages; p++) {
206 if (p == thispage) continue;
207 if (xobjs.pagelist[p]->pageinst != NULL) {
208 if (!filecmp(xobjs.pagelist[p]->pageinst->thisobject->name,
209 thispageobj->name)) {
210 /* append ":2" to name or update suffix to ensure uniqueness */
211 if (clnptr == NULL)
212 sprintf(thispageobj->name, "%s:2", thispageobj->name);
213 else
214 sprintf(clnptr + 1, "%d", n + 1);
215 changed = True;
216 update = True;
217 break;
221 } while (changed);
222 if (update) {
223 renamepage(thispage);
224 return -1;
226 return 0;
229 /*--------------------------------------*/
230 /* Find connectivity of a selection */
231 /*--------------------------------------*/
233 void startconnect(xcWidget button, caddr_t clientdata, caddr_t calldata)
235 if (areawin->selects > 0)
236 connectivity(button, clientdata, calldata);
239 /*----------------------------------------------------------------------*/
240 /* connectivity(): Find electrical connections into a node. */
241 /* (does recursive search on object heirarchy) */
242 /*----------------------------------------------------------------------*/
244 void connectivity(xcWidget button, caddr_t clientdata, caddr_t calldata)
246 short *gsel = NULL;
247 selection *rselect = NULL, *nextselect;
248 genericptr ggen = NULL;
249 Genericlist *netlist = NULL;
250 int depth, lbus, netid, subnetid;
251 buslist *sbus;
252 pushlistptr seltop, nextptr;
253 objectptr nettop, pschem; /* thisobj, (jdk) */
254 char *snew;
255 stringpart *ppin;
257 /* erase any existing highlighted network */
258 highlightnetlist(topobject, areawin->topinstance, 0);
260 seltop = (pushlistptr)malloc(sizeof(pushlist));
261 seltop->thisinst = areawin->topinstance;
262 seltop->next = NULL;
264 /* pick the first selection that looks like a valid network part */
266 if (areawin->selects > 0) {
267 for (gsel = areawin->selectlist; gsel < areawin->selectlist +
268 areawin->selects; gsel++) {
269 ggen = *(topobject->plist + *gsel);
270 if (SELECTTYPE(gsel) == LABEL) {
271 labelptr glab = SELTOLABEL(gsel);
272 if (glab->pin == LOCAL || glab->pin == GLOBAL) break;
274 else if (SELECTTYPE(gsel) == POLYGON) {
275 polyptr gpoly = SELTOPOLY(gsel);
276 if (!nonnetwork(gpoly)) break;
280 if ((areawin->selects == 0) || (gsel == areawin->selectlist
281 + areawin->selects)) {
282 rselect = recurselect(POLYGON | LABEL | OBJINST, MODE_CONNECT, &seltop);
283 /* Should look at the top scorer, when scoring has been implemented */
284 if (rselect && (rselect->selects > 0)) {
285 for (nextselect = rselect; (nextselect->next != NULL) &&
286 (nextselect->selects > 0); nextselect = nextselect->next);
287 ggen = *(nextselect->thisinst->thisobject->plist + *(nextselect->selectlist));
288 while (rselect != NULL) {
289 nextselect = rselect->next;
290 free(rselect->selectlist);
291 free(rselect);
292 rselect = nextselect;
297 /* Determine the net and the topmost object in which that */
298 /* net appears. Then build the transformation matrix down */
299 /* to that object. highlightnet() should end by popping the */
300 /* entire matrix stack. */
302 if (ggen != NULL) {
303 if (checkvalid(topobject) == -1) {
304 destroynets(topobject);
305 createnets(areawin->topinstance, FALSE);
307 if ((netlist = is_resolved(&ggen, seltop, &nettop)) != NULL) {
308 depth = pushnetwork(seltop, nettop);
309 /* Fprintf(stdout, ">> Pushed network %d levels deep\n", depth); */
310 nextptr = seltop;
311 while (nextptr->thisinst->thisobject != nettop)
312 nextptr = nextptr->next;
314 nextptr->thisinst->thisobject->highlight.netlist = netlist;
315 nextptr->thisinst->thisobject->highlight.thisinst = nextptr->thisinst;
316 highlightnetlist(nettop, nextptr->thisinst, 1);
318 /* pop the matrix stack */
319 while (depth-- > 0)
320 UPopCTM();
322 /* get the primary schematic */
323 pschem = (nettop->schemtype == SECONDARY) ? nettop->symschem : nettop;
325 /* print the net name to the message window */
327 if (netlist->subnets == 0) {
328 ppin = nettopin(netlist->net.id, pschem, NULL);
329 snew = textprint(ppin, areawin->topinstance);
330 sprintf(_STR2, "Network is \"%s\" in %s", snew, nettop->name);
331 free(snew);
333 else {
334 char *sptr;
335 sprintf(_STR2, "Network(s): ");
336 sptr = _STR2 + strlen(_STR2);
337 for (lbus = 0; lbus < netlist->subnets; lbus++) {
338 sbus = netlist->net.list + lbus;
339 netid = sbus->netid;
340 subnetid = sbus->subnetid;
341 ppin = nettopin(netid, pschem, NULL);
342 snew = textprintsubnet(ppin, areawin->topinstance, subnetid);
343 sprintf(sptr, "%s ", snew);
344 sptr += strlen(snew) + 1;
345 free(snew);
347 sprintf(sptr, "in %s", nettop->name);
349 Wprintf("%s", _STR2);
351 #ifdef TCL_WRAPPER
352 Tcl_SetObjResult(xcinterp, Tcl_NewStringObj(snew, strlen(snew)));
353 #endif
355 else
356 Wprintf("Selected element is not part of a valid network.");
358 else {
359 Wprintf("No networks found near the cursor position");
360 netid = 0;
363 /* free up linked list */
365 while (seltop != NULL) {
366 nextptr = seltop->next;
367 free(seltop);
368 seltop = nextptr;
372 /*--------------------------------------------------------------*/
373 /* Set object type to FUNDAMENTAL if it contains one or more */
374 /* info labels and is not associated with a schematic/symbol. */
375 /* */
376 /* Return TRUE if the object has electrically relevant */
377 /* networks, FALSE if not. */
378 /*--------------------------------------------------------------*/
380 Boolean setobjecttype(objectptr cschem)
382 genericptr *cgen;
383 labelptr clab;
385 /* If networks are specifically prohibited. . . */
386 if (cschem->schemtype == NONETWORK) return False;
388 /* Apply only to schematic objects */
390 if ((cschem->schemtype != PRIMARY) && (cschem->schemtype != SECONDARY)) {
391 if (cschem->schemtype == FUNDAMENTAL)
392 cschem->schemtype = SYMBOL;
393 if (cschem->symschem == NULL) {
394 for (cgen = cschem->plist; cgen < cschem->plist + cschem->parts; cgen++) {
395 if (IS_LABEL(*cgen)) {
396 clab = TOLABEL(cgen);
397 if (clab->pin == INFO) {
398 cschem->schemtype = FUNDAMENTAL;
399 break;
406 if ((cschem->symschem != NULL) && (cschem->schemtype == SYMBOL))
407 return False;
408 else if ((cschem->schemtype == TRIVIAL) || (cschem->schemtype == FUNDAMENTAL))
409 return False;
411 return True;
414 /*------------------------------------------------------*/
415 /* Pin conversion subroutine for dopintype() */
416 /*------------------------------------------------------*/
418 void pinconvert(labelptr thislab, pointertype mode)
420 thislab->pin = mode;
421 switch (mode) {
422 case NORMAL:
423 thislab->color = DEFAULTCOLOR; /* nominally black */
424 break;
425 case GLOBAL:
426 thislab->color = GLOBALPINCOLOR; /* orange */
427 break;
428 case LOCAL:
429 thislab->color = LOCALPINCOLOR; /* red */
430 break;
431 case INFO:
432 thislab->color = INFOLABELCOLOR; /* green */
433 break;
437 /*---------------------------------------------------------*/
438 /* Change a label's type to NORMAL, GLOBAL, INFO, or LOCAL */
439 /*---------------------------------------------------------*/
441 void dopintype(xcWidget w, pointertype mode, caddr_t calldata)
443 short *gsel;
444 char typestr[40];
445 short savetype = -1;
447 if (areawin->selects == 0) {
448 Wprintf("Must first select a label to change type");
449 return;
452 strcpy(typestr, "Changed label to ");
453 switch(mode) {
454 case NORMAL:
455 strcat(typestr, "normal label");
456 break;
457 case GLOBAL:
458 strcat(typestr, "global pin");
459 break;
460 case LOCAL:
461 strcat(typestr, "local pin");
462 break;
463 case INFO:
464 strcat(typestr, "info-label");
465 break;
468 for (gsel = areawin->selectlist; gsel < areawin->selectlist +
469 areawin->selects; gsel++)
470 if (SELECTTYPE(gsel) == LABEL) {
471 labelptr glab = SELTOLABEL(gsel);
472 savetype = glab->pin;
473 pinconvert(glab, mode);
474 setobjecttype(topobject);
477 if (savetype >= 0) {
478 unselect_all();
479 drawarea(NULL, NULL, NULL);
480 Wprintf("%s", typestr);
482 else {
483 Wprintf("No labels selected.");
487 /*----------------------------------------------------------*/
488 /* Set colors on the symbol/schematic buttons appropriately */
489 /*----------------------------------------------------------*/
491 #ifdef TCL_WRAPPER
493 void setsymschem()
495 XcInternalTagCall(xcinterp, 1, "schematic");
498 #else
500 void setsymschem()
502 Arg aargs[2], bargs[2];
504 /* Set menu items appropriately for this object */
506 if (topobject->symschem != NULL) {
507 if (topobject->schemtype == PRIMARY || topobject->schemtype == SECONDARY) {
508 XtSetArg(aargs[0], XtNlabel, "Go To Symbol");
509 XtSetArg(bargs[0], XtNlabel, "Disassociate Symbol");
511 else {
512 XtSetArg(aargs[0], XtNlabel, "Go To Schematic");
513 XtSetArg(bargs[0], XtNlabel, "Disassociate Schematic");
516 else {
517 if (topobject->schemtype == PRIMARY || topobject->schemtype == SECONDARY) {
518 XtSetArg(aargs[0], XtNlabel, "Make Matching Symbol");
519 XtSetArg(bargs[0], XtNlabel, "Associate with Symbol");
521 else {
522 XtSetArg(aargs[0], XtNlabel, "Make Matching Schematic");
523 XtSetArg(bargs[0], XtNlabel, "Associate with Schematic");
526 XtSetValues(NetlistMakeMatchingSymbolButton, aargs, 1);
527 XtSetValues(NetlistAssociatewithSymbolButton, bargs, 1);
529 /* Set colors on the symbol and schematic buttons */
531 if (topobject->schemtype == PRIMARY || topobject->schemtype == SECONDARY) {
532 if (topobject->symschem == NULL) {
533 XtSetArg(aargs[0], XtNbackground, colorlist[OFFBUTTONCOLOR].color.pixel);
534 XtSetArg(aargs[1], XtNforeground, colorlist[OFFBUTTONCOLOR].color.pixel);
536 else {
537 XtSetArg(aargs[0], XtNbackground, colorlist[BACKGROUND].color.pixel);
538 XtSetArg(aargs[1], XtNforeground, colorlist[FOREGROUND].color.pixel);
541 XtSetArg(bargs[1], XtNforeground, colorlist[FOREGROUND].color.pixel);
542 XtSetArg(bargs[0], XtNbackground, colorlist[SNAPCOLOR].color.pixel);
544 else {
545 if (topobject->symschem != NULL) {
546 XtSetArg(bargs[0], XtNbackground, colorlist[BACKGROUND].color.pixel);
547 XtSetArg(bargs[1], XtNforeground, colorlist[FOREGROUND].color.pixel);
549 else {
550 XtSetArg(bargs[0], XtNbackground, colorlist[OFFBUTTONCOLOR].color.pixel);
551 XtSetArg(bargs[1], XtNforeground, colorlist[OFFBUTTONCOLOR].color.pixel);
554 XtSetArg(aargs[1], XtNforeground, colorlist[FOREGROUND].color.pixel);
555 if (topobject->schemtype == FUNDAMENTAL)
556 XtSetArg(aargs[0], XtNbackground, colorlist[AUXCOLOR].color.pixel);
557 else if (topobject->schemtype == TRIVIAL || topobject->symschem != NULL)
558 XtSetArg(aargs[0], XtNbackground, colorlist[SNAPCOLOR].color.pixel);
559 else {
560 XtSetArg(aargs[0], XtNbackground, colorlist[OFFBUTTONCOLOR].color.pixel);
561 XtSetArg(aargs[1], XtNforeground, colorlist[OFFBUTTONCOLOR].color.pixel);
562 XtSetArg(bargs[0], XtNbackground, colorlist[BBOXCOLOR].color.pixel);
563 XtSetArg(bargs[1], XtNforeground, colorlist[FOREGROUND].color.pixel);
567 XtSetValues(wsymb, aargs, 2);
568 XtSetValues(wschema, bargs, 2);
571 #endif
573 /*--------------------------------------------------------*/
574 /* Find the page number for an object */
575 /*--------------------------------------------------------*/
577 int findpageobj(objectptr pobj)
579 int tpage;
581 for (tpage = 0; tpage < xobjs.pages; tpage++)
582 if (xobjs.pagelist[tpage]->pageinst != NULL)
583 if (xobjs.pagelist[tpage]->pageinst->thisobject == pobj)
584 return tpage;
586 return -1;
589 /*------------------------------------------------------*/
590 /* Enumerate all of the pages which are subschematics */
591 /* of "toppage" and return the result in the list */
592 /* passed as a pointer. The list is assumed to be */
593 /* already allocated, and equal to the total number of */
594 /* pages (xobjs.pages). */
595 /* */
596 /* Avoid possible recursion problems by limiting the */
597 /* number of recursion levels. Presumably no circuit */
598 /* would have more than several hundred hierarchical */
599 /* levels. */
600 /* */
601 /* If "dolinks" is TRUE, ennumerate and follow pages */
602 /* that are descendents of the top level when the */
603 /* calling symbol instance has a "link" parameter and */
604 /* that parameter points to the same filename as the */
605 /* page. This indicates a multiple-file project; the */
606 /* pages keep separate records of changes, and are */
607 /* saved independently, and loaded through the link */
608 /* dependency method. */
609 /*------------------------------------------------------*/
611 int findsubschems(int toppage, objectptr cschem, int level, short *pagelist,
612 Boolean dolinks)
614 genericptr *cgen;
616 if (level == HIERARCHY_LIMIT) return -1; /* sanity check */
618 for (cgen = cschem->plist; cgen < cschem->plist + cschem->parts; cgen++) {
619 if (IS_OBJINST(*cgen)) {
620 objinstptr cinst = TOOBJINST(cgen);
621 objectptr cobj = cinst->thisobject;
623 if (cobj->symschem != NULL) {
624 int pageno = findpageobj(cobj->symschem);
626 if ((pageno >= 0) && (pageno < xobjs.pages)) {
628 /* Look for a "link" parameter */
629 if (dolinks == FALSE) {
630 oparamptr ops;
631 ops = find_param(cinst, "link");
632 if ((ops != NULL) && (ops->type == XC_STRING)) {
633 char *filename = textprint(ops->parameter.string, cinst);
634 if (!strcmp(filename, "%n") || !strcmp(filename, "%N") ||
635 !strcmp(filename, xobjs.pagelist[pageno]->filename)) {
636 free(filename);
637 continue;
639 free(filename);
643 /* Add this page to the list */
644 pagelist[pageno]++;
647 /* A symbol on its own schematic page is allowed for clarity */
648 /* of the schematic, but this cannot be a functional part of */
649 /* the schematic circuit! */
651 if (cobj->symschem != cschem) {
652 if (findsubschems(toppage, cobj->symschem,
653 level + 1, pagelist, dolinks) == -1)
654 return -1;
657 else if (cobj->schemtype != FUNDAMENTAL && cobj->schemtype != TRIVIAL) {
658 /* Check symbols acting as their own schematics */
659 if (findsubschems(toppage, cobj, level + 1, pagelist, dolinks) == -1)
660 return -1;
664 return 0;
667 /*------------------------------------------------------*/
668 /* Recursively find all sub-circuits associated with */
669 /* the top-level circuit. For each schematic that does */
670 /* not have a valid filename, set the filename equal to */
671 /* the filename of the "toppage" schematic. */
672 /*------------------------------------------------------*/
674 void collectsubschems(int toppage)
676 int loctop;
677 objectptr cschem;
678 short *pagelist, pageno;
679 Pagedata *curpage;
681 loctop = toppage;
682 curpage = xobjs.pagelist[loctop];
683 if (curpage->pageinst == NULL) return;
684 cschem = curpage->pageinst->thisobject;
685 if (cschem->schemtype == SECONDARY) {
686 cschem = cschem->symschem;
687 loctop = is_page(cschem);
688 if (loctop < 0) return;
689 curpage = xobjs.pagelist[loctop];
692 pagelist = (short *)malloc(xobjs.pages * sizeof(short));
694 for (pageno = 0; pageno < xobjs.pages; pageno++)
695 pagelist[pageno] = 0;
697 findsubschems(loctop, cschem, 0, pagelist, FALSE);
699 for (pageno = 0; pageno < xobjs.pages; pageno++) {
700 if (pageno == loctop) continue;
701 if (pagelist[pageno] > 0) {
702 if (xobjs.pagelist[pageno]->filename != NULL)
703 free(xobjs.pagelist[pageno]->filename);
704 xobjs.pagelist[pageno]->filename =
705 strdup(xobjs.pagelist[loctop]->filename);
708 free((char *)pagelist);
711 /*------------------------------------------------------*/
712 /* compare_qualified(str1, str2) --- */
713 /* */
714 /* String comparison for technology-qualified */
715 /* names. str2" must be a fully-qualified name. */
716 /* "str1" may be qualified or unqualified. If */
717 /* unqualified, compare_qualified will return TRUE */
718 /* for any "str2" that matches the name part of */
719 /* the technology::name format. */
720 /* */
721 /* Return value: TRUE if match, FALSE if not. */
722 /*------------------------------------------------------*/
724 Boolean compare_qualified(char *str1, char *str2)
726 char *sptr1, *sptr2;
727 Boolean qual1, qual2;
729 sptr2 = strstr(str2, "::");
730 qual2 = (sptr2 == NULL) ? FALSE : TRUE;
732 if (!qual2) return (!strcmp(str1, str2));
734 sptr1 = strstr(str1, "::");
735 qual1 = (sptr1 == NULL) ? FALSE : TRUE;
737 if (qual1) return (!strcmp(str1, str2));
739 sptr2 += 2;
740 return (!strcmp(str1, sptr2));
743 /*-------------------------------------------------------*/
744 /* Check if top-level page is the same name as a library */
745 /* object; if so, connect it. */
746 /* Note that it is not an error not to find the matching */
747 /* symbol/schematic. "is_schematic" and "is_symbol" */
748 /* comments in the .ps file are complementary, so the */
749 /* first one encountered will always fail, and the other */
750 /* will succeed. */
751 /*-------------------------------------------------------*/
753 int checkschem(objectptr thisobj, char *cname)
755 objectptr *tlib;
756 short i, j;
758 if (thisobj->symschem != NULL) return 0;
760 for (i = 0; i < xobjs.numlibs; i++) {
761 for (j = 0; j < xobjs.userlibs[i].number; j++) {
762 tlib = xobjs.userlibs[i].library + j;
764 if (compare_qualified(cname, (*tlib)->name)) {
765 thisobj->symschem = (*tlib);
766 thisobj->schemtype = PRIMARY;
767 (*tlib)->symschem = thisobj;
768 (*tlib)->schemtype = SYMBOL;
769 return 1;
773 return 0;
776 /*--------------------------------------------------------*/
777 /* Complement to the above routine: If a library object */
778 /* name is the same as a top-level page, connect them. */
779 /*--------------------------------------------------------*/
781 int checksym(objectptr symobj, char *cname)
783 short cpage;
784 objectptr checkpage;
786 if (symobj->symschem != NULL) return 0;
788 for (cpage = 0; cpage < xobjs.pages; cpage++) {
789 if (xobjs.pagelist[cpage]->pageinst != NULL) {
790 checkpage = xobjs.pagelist[cpage]->pageinst->thisobject;
791 if (compare_qualified(cname, checkpage->name)) {
792 symobj->symschem = checkpage;
793 symobj->schemtype = SYMBOL;
794 checkpage->symschem = symobj;
795 checkpage->schemtype = PRIMARY;
796 return 1;
800 return 0;
803 /*----------------------------------------------------------------------*/
804 /* Find location of corresponding pin in symbol/schematic and change it */
805 /* to the text of the indicated label. */
806 /* */
807 /* Return the number of other pins changed. zero indicates that no */
808 /* corresponding pins were found, and therefore nothing was changed. */
809 /*----------------------------------------------------------------------*/
811 int changeotherpins(labelptr newlabel, stringpart *oldstring)
813 objectptr other = topobject->symschem;
814 genericptr *tgen;
815 labelptr tlab;
816 int rval = 0;
818 if (other == NULL) return rval;
820 for (tgen = other->plist; tgen < other->plist + other->parts; tgen++) {
821 if (IS_LABEL(*tgen)) {
822 tlab = TOLABEL(tgen);
823 if (tlab->pin != LOCAL) continue;
824 if (!stringcomp(tlab->string, oldstring)) {
825 if (newlabel != NULL) {
826 free(tlab->string);
827 tlab->string = stringcopy(newlabel->string);
828 rval++;
833 return rval;
836 /*----------------------------------------------------------------------*/
837 /* Xt wrapper for swapschem() */
838 /*----------------------------------------------------------------------*/
840 void xlib_swapschem(xcWidget w, pointertype mode, caddr_t calldata)
842 swapschem((int)mode, -1, NULL);
845 /*----------------------------------------------------------------------*/
846 /* Swap object schematic and symbol pages. */
847 /* allow_create = 0 disallows creation of a new schematic or symbol; */
848 /* i.e., if there is no corresponding schematic/symbol, nothing */
849 /* happens. */
850 /*----------------------------------------------------------------------*/
852 void swapschem(int allow_create, int libnum, char *fullname)
854 objectptr savepage = topobject;
855 labelptr *pinlab;
856 genericptr *plab;
857 Boolean lflag;
858 pushlistptr stacktop;
859 short loclibnum = libnum;
860 char *canonname;
862 if (libnum == -1) loclibnum = USERLIB - LIBRARY;
864 /* Create symbol or schematic, if allowed by allow_create */
866 if ((topobject->symschem == NULL) && (allow_create != 0)
867 && (topobject->schemtype != SECONDARY)) {
869 if (topobject->schemtype != PRIMARY) {
870 int tpage;
872 /* create a new page for the new schematic */
874 for (tpage = 0; tpage < xobjs.pages; tpage++)
875 if (xobjs.pagelist[tpage]->pageinst == NULL) break;
877 /* Push the current instance onto the push stack */
878 /* Change the page without destroying the pushlist */
880 push_stack(&areawin->stack, areawin->topinstance, NULL);
881 stacktop = areawin->stack;
882 areawin->stack = NULL;
883 changepage(tpage);
884 areawin->stack = stacktop;
886 else {
887 objectptr *newobject;
889 /* create a new library object for the new symbol */
891 xobjs.userlibs[loclibnum].library = (objectptr *)
892 realloc(xobjs.userlibs[loclibnum].library,
893 ++xobjs.userlibs[loclibnum].number * sizeof(objectptr));
894 newobject = xobjs.userlibs[loclibnum].library
895 + xobjs.userlibs[loclibnum].number - 1;
896 *newobject = (objectptr) malloc(sizeof(object));
897 initmem(*newobject);
898 (*newobject)->schemtype = SYMBOL;
899 (*newobject)->hidden = False;
901 incr_changes(*newobject);
903 if (eventmode == MOVE_MODE || eventmode == COPY_MODE)
904 standard_element_delete(ERASE);
905 else
906 unselect_all();
908 /* Generate a library instance for this object and set the */
909 /* top instance to point to it. */
911 topobject->viewscale = areawin->vscale;
912 topobject->pcorner = areawin->pcorner;
913 push_stack(&areawin->stack, areawin->topinstance, NULL);
914 areawin->topinstance = addtoinstlist(loclibnum, *newobject, FALSE);
916 /* Generate the default bounding box for a size-zero object */
917 calcbbox(areawin->topinstance);
920 /* set links between the two objects */
922 savepage->symschem = topobject;
923 topobject->symschem = savepage;
925 /* Make the name of the new object equal to that of the old, */
926 /* except that symbols get the full name while schematics */
927 /* get the canonical name (without the technology prefix) */
929 if (fullname == NULL)
930 canonname = GetCanonicalName(savepage->name);
931 else {
932 canonname = strstr(fullname, "::");
933 if ((canonname == NULL) || (topobject->schemtype != PRIMARY))
934 canonname = fullname;
935 else
936 canonname += 2;
938 strcpy(topobject->name, canonname);
939 checkname(topobject);
941 /* copy all pin labels into the new object */
943 for (plab = savepage->plist; plab < savepage->plist + savepage->parts;
944 plab++) {
945 if (IS_LABEL(*plab)) {
946 genericptr *tgen;
947 labelptr tlab, lpin = (labelptr)*plab;
949 if (lpin->pin == LOCAL) {
951 /* Only make one copy of each pin name */
953 lflag = False;
954 for (tgen = topobject->plist; tgen <
955 topobject->plist + topobject->parts; tgen++) {
956 if (IS_LABEL(*tgen)) {
957 tlab = TOLABEL(tgen);
958 if (!stringcomp(tlab->string, lpin->string)) lflag = True;
961 if (lflag == True) continue;
963 NEW_LABEL(pinlab, topobject);
964 (*pinlab)->pin = lpin->pin;
965 (*pinlab)->color = lpin->color;
966 (*pinlab)->rotation = 0.0;
967 (*pinlab)->scale = 1.0;
968 (*pinlab)->anchor = areawin->anchor;
969 (*pinlab)->position.x = 0;
970 (*pinlab)->position.y = topobject->parts * (TEXTHEIGHT + 10);
971 (*pinlab)->passed = NULL;
972 (*pinlab)->cycle = NULL;
973 u2u_snap(&((*pinlab)->position));
974 (*pinlab)->string = stringcopy(lpin->string);
975 incr_changes(topobject);
979 calcbbox(areawin->topinstance);
981 /* Recreate the user library with the new symbol */
982 if (savepage->schemtype != SYMBOL) composelib(loclibnum + LIBRARY);
984 else if (topobject->symschem != NULL) {
986 /* If symschem matches the last entry on the push stack, then we */
987 /* pop; otherwise, we push. */
989 if (areawin->stack && areawin->stack->thisinst->thisobject
990 == topobject->symschem) {
991 topobject->viewscale = areawin->vscale;
992 topobject->pcorner = areawin->pcorner;
993 areawin->topinstance = areawin->stack->thisinst;
994 pop_stack(&areawin->stack);
996 else {
997 int p;
998 objinstptr syminst = NULL;
999 liblistptr symlist;
1001 /* If symschem is a schematic, find the appropriate page */
1003 for (p = 0; p < xobjs.pages; p++) {
1004 syminst = xobjs.pagelist[p]->pageinst;
1005 if (syminst != NULL)
1006 if (syminst->thisobject == topobject->symschem)
1007 break;
1009 if (p == xobjs.pages) {
1011 /* If symschem is a symbol, and it wasn't on the push stack, */
1012 /* get the library default symbol and go there. */
1014 for (p = 0; p < xobjs.numlibs; p++) {
1015 for (symlist = xobjs.userlibs[p].instlist; symlist != NULL;
1016 symlist = symlist->next) {
1017 syminst = symlist->thisinst;
1018 if (syminst->thisobject == topobject->symschem &&
1019 symlist->virtual == FALSE)
1020 break;
1022 if (symlist != NULL) break;
1024 if (p == xobjs.numlibs) {
1025 Fprintf(stderr, "swapschem(): BAD SYMSCHEM\n");
1026 return;
1030 if (eventmode == MOVE_MODE || eventmode == COPY_MODE)
1031 delete_for_xfer(NORMAL, areawin->selectlist, areawin->selects);
1033 topobject->viewscale = areawin->vscale;
1034 topobject->pcorner = areawin->pcorner;
1035 push_stack(&areawin->stack, areawin->topinstance, NULL);
1036 areawin->topinstance = syminst;
1040 /* If there was no action, then there is nothing more to do. */
1042 if (topobject == savepage) return;
1044 setpage(TRUE);
1045 transferselects();
1046 refresh(NULL, NULL, NULL);
1047 setsymschem();
1050 #ifndef TCL_WRAPPER
1052 /*----------------------------------------------------------------------*/
1053 /* Wrapper for swapschem() when generating a new symbol. */
1054 /*----------------------------------------------------------------------*/
1056 void makesymbol(xcWidget w, caddr_t calldata)
1058 /* copy name from popup prompt buffer and check */
1060 swapschem(1, -1, _STR2);
1063 /*----------------------------------------------------------------------*/
1064 /* Check name before doing a swap: If name begins with "Page", prompt */
1065 /* for the object name, as you would when doing selectsave(). */
1066 /*----------------------------------------------------------------------*/
1068 void dobeforeswap(xcWidget w, caddr_t clientdata, caddr_t calldata)
1070 buttonsave *popdata = (buttonsave *)malloc(sizeof(buttonsave));
1072 /* Check for requirement to change the name before creating the symbol */
1074 if ((topobject->symschem == NULL) && (topobject->schemtype == PRIMARY)
1075 && (strstr(topobject->name, "Page ") != NULL)) {
1077 /* Get a name for the new object */
1079 eventmode = NORMAL_MODE;
1080 popdata->dataptr = NULL;
1081 popdata->button = NULL; /* indicates that no button is assc'd w/ the popup */
1082 popupprompt(w, "Enter name for new object:", "\0", makesymbol, popdata, NULL);
1084 else
1085 swapschem(1, -1, NULL);
1088 #endif /* !TCL_WRAPPER */
1090 /*------------------------------------------*/
1091 /* Disassociate a symbol from its schematic */
1092 /*------------------------------------------*/
1094 void schemdisassoc()
1096 if (eventmode != NORMAL) {
1097 Wprintf("Cannot disassociate schematics in this mode");
1099 else {
1100 topobject->symschem->symschem = NULL;
1101 topobject->symschem = NULL;
1102 setsymschem();
1103 Wprintf("Schematic and symbol are now unlinked.");
1107 /*--------------------------------------------------------------*/
1108 /* Schematic<-->symbol association. Determine whether action */
1109 /* acts on a symbol or a schematic from context. */
1110 /* mode == 0 associate only. */
1111 /* mode == 1 determine action (associate or disassociate) from */
1112 /* context (toggle) */
1113 /*--------------------------------------------------------------*/
1115 void startschemassoc(xcWidget w, pointertype mode, caddr_t calldata)
1117 if ((topobject->symschem != NULL) && (mode == 1))
1118 schemdisassoc();
1119 else if ((topobject->symschem != NULL) && (mode == 0)) {
1120 Wprintf("Refusing to undo current association.");
1122 else if (topobject->schemtype == SECONDARY) {
1123 Wprintf("Cannot attach symbol to a secondary schematic page.");
1125 else {
1126 eventmode = ASSOC_MODE;
1127 if (topobject->schemtype == PRIMARY) {
1128 /* Find a symbol to associate */
1129 startcatalog(w, LIBLIB, NULL);
1130 Wprintf("Select library page, then symbol to associate.");
1132 else {
1133 /* Find a schematic (page) to associate */
1134 startcatalog(w, PAGELIB, NULL);
1135 Wprintf("Select schematic page to associate.");
1140 /*--------------------------------------------------------------*/
1141 /* Callback procedures on the schematic/symbol buttons. */
1142 /*--------------------------------------------------------------*/
1144 Boolean schemassoc(objectptr schemobj, objectptr symbolobj)
1146 if (schemobj->symschem != NULL || symbolobj->symschem != NULL) {
1147 Wprintf("Both objects must be disassociated first.");
1148 #if TCL_WRAPPER
1149 Tcl_SetResult(xcinterp, "Both objects must be disassociated first.", NULL);
1150 #endif
1151 return False;
1153 else {
1154 schemobj->symschem = symbolobj;
1155 symbolobj->symschem = schemobj;
1156 if (symbolobj->schemtype == TRIVIAL)
1157 symbolobj->schemtype = SYMBOL;
1159 /* Schematic takes the name of its associated symbol, by default */
1160 /* Don't copy any technology prefix. */
1161 strcpy(schemobj->name, GetCanonicalName(symbolobj->name));
1163 /* Ensure that schematic (page) name is unique */
1164 while (checkpagename(schemobj) < 0);
1165 setsymschem(); /* Set buttons and menu items appropriately */
1167 return True;
1170 /*-------------------------------------------------------------------------*/