1 /*-------------------------------------------------------------------------*/
2 /* schema.c --- xcircuit routines specific to the schematic capture system */
3 /* Copyright (c) 2002 Tim Edwards, Johns Hopkins University */
4 /*-------------------------------------------------------------------------*/
5
6 /*-------------------------------------------------------------------------*/
7 /* written by Tim Edwards, 10/13/97 */
8 /*-------------------------------------------------------------------------*/
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13
14 #ifndef XC_WIN32
15 #include <X11/Intrinsic.h>
16 #include <X11/StringDefs.h>
17 #endif
18
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
27
28 /*-------------------------------------------------------------------------*/
29 /* Local includes */
30 /*-------------------------------------------------------------------------*/
31
32 #include "xcircuit.h"
33 #include "colordefs.h"
34 #include "menudep.h"
35
36 /*----------------------------------------------------------------------*/
37 /* Function prototype declarations */
38 /*----------------------------------------------------------------------*/
39 #include "prototypes.h"
40
41 /*------------------------------------------------------------------------*/
42 /* External Variable definitions */
43 /*------------------------------------------------------------------------*/
44
45 #ifdef TCL_WRAPPER
46 extern Tcl_Interp *xcinterp;
47 #endif
48
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;
56
57 /*-------------------------------------------------------------------------*/
58
59 extern xcWidget wsymb, wschema;
60
61 /*--------------------------------------------------------*/
62 /* Menu calls (procedure wrappers) */
63 /*--------------------------------------------------------*/
64
callwritenet(xcWidget w,pointertype mode,caddr_t calldata)65 void callwritenet(xcWidget w, pointertype mode, caddr_t calldata)
66 {
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;
84 }
85 }
86
87 /*----------------------------------------------------------------------*/
88 /* Find the page object and instance with the indicated name. */
89 /*----------------------------------------------------------------------*/
90
NameToPageObject(char * objname,objinstptr * ret_inst,int * ret_page)91 objectptr NameToPageObject(char *objname, objinstptr *ret_inst, int *ret_page)
92 {
93 int i;
94
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;
101 }
102 }
103 return NULL;
104 }
105
106 /*--------------------------------------------------------------*/
107 /* Get the canonical name of an object (the part without the */
108 /* technology prefix, if any). */
109 /*--------------------------------------------------------------*/
110
GetCanonicalName(char * fullname)111 char *GetCanonicalName(char *fullname)
112 {
113 char *canonname = strstr(fullname, "::");
114 if (canonname == NULL) return fullname;
115 return canonname + 2;
116 }
117
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 /*----------------------------------------------------------------------*/
132
NameToObject(char * objname,objinstptr * ret_inst,Boolean dopages)133 objectptr NameToObject(char *objname, objinstptr *ret_inst, Boolean dopages)
134 {
135 int i;
136 liblistptr spec;
137 Boolean notech = FALSE;
138 Boolean preferred = FALSE;
139 char *techptr;
140 objectptr retobj = NULL;
141 TechPtr nsp;
142
143 if (strstr(objname, "::") == NULL) notech = TRUE;
144
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;
156 }
157 }
158 }
159 }
160
161 if (retobj != NULL) return retobj;
162
163 if (dopages)
164 return NameToPageObject(objname, ret_inst, NULL);
165 else
166 return NULL;
167 }
168
169 /*------------------------------------------------------------------------*/
170 /* Ensure that a page name is unique (required for schematic association) */
171 /*------------------------------------------------------------------------*/
172
checkpagename(objectptr thispageobj)173 int checkpagename(objectptr thispageobj)
174 {
175 int p, thispage;
176 /* char *objname = thispageobj->name; (jdk) */
177 Boolean changed;
178 Boolean update = False;
179 char *clnptr = NULL;
180 int n;
181
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;
187
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;
194 }
195 }
196 }
197 if (p == xobjs.pages) {
198 Fprintf(stderr, "Error: Object is not a page object!\n");
199 return 0;
200 }
201
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;
218 }
219 }
220 }
221 } while (changed);
222 if (update) {
223 renamepage(thispage);
224 return -1;
225 }
226 return 0;
227 }
228
229 /*--------------------------------------*/
230 /* Find connectivity of a selection */
231 /*--------------------------------------*/
232
startconnect(xcWidget button,caddr_t clientdata,caddr_t calldata)233 void startconnect(xcWidget button, caddr_t clientdata, caddr_t calldata)
234 {
235 if (areawin->selects > 0)
236 connectivity(button, clientdata, calldata);
237 }
238
239 /*----------------------------------------------------------------------*/
240 /* connectivity(): Find electrical connections into a node. */
241 /* (does recursive search on object heirarchy) */
242 /*----------------------------------------------------------------------*/
243
connectivity(xcWidget button,caddr_t clientdata,caddr_t calldata)244 void connectivity(xcWidget button, caddr_t clientdata, caddr_t calldata)
245 {
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;
256
257 /* erase any existing highlighted network */
258 highlightnetlist(topobject, areawin->topinstance, 0);
259
260 seltop = (pushlistptr)malloc(sizeof(pushlist));
261 seltop->thisinst = areawin->topinstance;
262 seltop->next = NULL;
263
264 /* pick the first selection that looks like a valid network part */
265
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;
273 }
274 else if (SELECTTYPE(gsel) == POLYGON) {
275 polyptr gpoly = SELTOPOLY(gsel);
276 if (!nonnetwork(gpoly)) break;
277 }
278 }
279 }
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;
293 }
294 }
295 }
296
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. */
301
302 if (ggen != NULL) {
303 if (checkvalid(topobject) == -1) {
304 destroynets(topobject);
305 createnets(areawin->topinstance, FALSE);
306 }
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;
313
314 nextptr->thisinst->thisobject->highlight.netlist = netlist;
315 nextptr->thisinst->thisobject->highlight.thisinst = nextptr->thisinst;
316 highlightnetlist(nettop, nextptr->thisinst, 1);
317
318 /* pop the matrix stack */
319 while (depth-- > 0)
320 UPopCTM();
321
322 /* get the primary schematic */
323 pschem = (nettop->schemtype == SECONDARY) ? nettop->symschem : nettop;
324
325 /* print the net name to the message window */
326
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);
332 }
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);
346 }
347 sprintf(sptr, "in %s", nettop->name);
348 }
349 Wprintf("%s", _STR2);
350
351 #ifdef TCL_WRAPPER
352 Tcl_SetObjResult(xcinterp, Tcl_NewStringObj(snew, strlen(snew)));
353 #endif
354 }
355 else
356 Wprintf("Selected element is not part of a valid network.");
357 }
358 else {
359 Wprintf("No networks found near the cursor position");
360 netid = 0;
361 }
362
363 /* free up linked list */
364
365 while (seltop != NULL) {
366 nextptr = seltop->next;
367 free(seltop);
368 seltop = nextptr;
369 }
370 }
371
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 /*--------------------------------------------------------------*/
379
setobjecttype(objectptr cschem)380 Boolean setobjecttype(objectptr cschem)
381 {
382 genericptr *cgen;
383 labelptr clab;
384
385 /* If networks are specifically prohibited. . . */
386 if (cschem->schemtype == NONETWORK) return False;
387
388 /* Apply only to schematic objects */
389
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;
400 }
401 }
402 }
403 }
404 }
405
406 if ((cschem->symschem != NULL) && (cschem->schemtype == SYMBOL))
407 return False;
408 else if ((cschem->schemtype == TRIVIAL) || (cschem->schemtype == FUNDAMENTAL))
409 return False;
410
411 return True;
412 }
413
414 /*------------------------------------------------------*/
415 /* Pin conversion subroutine for dopintype() */
416 /*------------------------------------------------------*/
417
pinconvert(labelptr thislab,pointertype mode)418 void pinconvert(labelptr thislab, pointertype mode)
419 {
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;
434 }
435 }
436
437 /*---------------------------------------------------------*/
438 /* Change a label's type to NORMAL, GLOBAL, INFO, or LOCAL */
439 /*---------------------------------------------------------*/
440
dopintype(xcWidget w,pointertype mode,caddr_t calldata)441 void dopintype(xcWidget w, pointertype mode, caddr_t calldata)
442 {
443 short *gsel;
444 char typestr[40];
445 short savetype = -1;
446
447 if (areawin->selects == 0) {
448 Wprintf("Must first select a label to change type");
449 return;
450 }
451
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;
466 }
467
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);
475 }
476
477 if (savetype >= 0) {
478 unselect_all();
479 drawarea(NULL, NULL, NULL);
480 Wprintf("%s", typestr);
481 }
482 else {
483 Wprintf("No labels selected.");
484 }
485 }
486
487 /*----------------------------------------------------------*/
488 /* Set colors on the symbol/schematic buttons appropriately */
489 /*----------------------------------------------------------*/
490
491 #ifdef TCL_WRAPPER
492
setsymschem()493 void setsymschem()
494 {
495 XcInternalTagCall(xcinterp, 1, "schematic");
496 }
497
498 #else
499
setsymschem()500 void setsymschem()
501 {
502 Arg aargs[2], bargs[2];
503
504 /* Set menu items appropriately for this object */
505
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");
510 }
511 else {
512 XtSetArg(aargs[0], XtNlabel, "Go To Schematic");
513 XtSetArg(bargs[0], XtNlabel, "Disassociate Schematic");
514 }
515 }
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");
520 }
521 else {
522 XtSetArg(aargs[0], XtNlabel, "Make Matching Schematic");
523 XtSetArg(bargs[0], XtNlabel, "Associate with Schematic");
524 }
525 }
526 XtSetValues(NetlistMakeMatchingSymbolButton, aargs, 1);
527 XtSetValues(NetlistAssociatewithSymbolButton, bargs, 1);
528
529 /* Set colors on the symbol and schematic buttons */
530
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);
535 }
536 else {
537 XtSetArg(aargs[0], XtNbackground, colorlist[BACKGROUND].color.pixel);
538 XtSetArg(aargs[1], XtNforeground, colorlist[FOREGROUND].color.pixel);
539 }
540
541 XtSetArg(bargs[1], XtNforeground, colorlist[FOREGROUND].color.pixel);
542 XtSetArg(bargs[0], XtNbackground, colorlist[SNAPCOLOR].color.pixel);
543 }
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);
548 }
549 else {
550 XtSetArg(bargs[0], XtNbackground, colorlist[OFFBUTTONCOLOR].color.pixel);
551 XtSetArg(bargs[1], XtNforeground, colorlist[OFFBUTTONCOLOR].color.pixel);
552 }
553
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);
564 }
565 }
566
567 XtSetValues(wsymb, aargs, 2);
568 XtSetValues(wschema, bargs, 2);
569 }
570
571 #endif
572
573 /*--------------------------------------------------------*/
574 /* Find the page number for an object */
575 /*--------------------------------------------------------*/
576
findpageobj(objectptr pobj)577 int findpageobj(objectptr pobj)
578 {
579 int tpage;
580
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;
585
586 return -1;
587 }
588
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 /*------------------------------------------------------*/
610
findsubschems(int toppage,objectptr cschem,int level,short * pagelist,Boolean dolinks)611 int findsubschems(int toppage, objectptr cschem, int level, short *pagelist,
612 Boolean dolinks)
613 {
614 genericptr *cgen;
615
616 if (level == HIERARCHY_LIMIT) return -1; /* sanity check */
617
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;
622
623 if (cobj->symschem != NULL) {
624 int pageno = findpageobj(cobj->symschem);
625
626 if ((pageno >= 0) && (pageno < xobjs.pages)) {
627
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;
638 }
639 free(filename);
640 }
641 }
642
643 /* Add this page to the list */
644 pagelist[pageno]++;
645 }
646
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! */
650
651 if (cobj->symschem != cschem) {
652 if (findsubschems(toppage, cobj->symschem,
653 level + 1, pagelist, dolinks) == -1)
654 return -1;
655 }
656 }
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;
661 }
662 }
663 }
664 return 0;
665 }
666
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 /*------------------------------------------------------*/
673
collectsubschems(int toppage)674 void collectsubschems(int toppage)
675 {
676 int loctop;
677 objectptr cschem;
678 short *pagelist, pageno;
679 Pagedata *curpage;
680
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];
690 }
691
692 pagelist = (short *)malloc(xobjs.pages * sizeof(short));
693
694 for (pageno = 0; pageno < xobjs.pages; pageno++)
695 pagelist[pageno] = 0;
696
697 findsubschems(loctop, cschem, 0, pagelist, FALSE);
698
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);
706 }
707 }
708 free((char *)pagelist);
709 }
710
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 /*------------------------------------------------------*/
723
compare_qualified(char * str1,char * str2)724 Boolean compare_qualified(char *str1, char *str2)
725 {
726 char *sptr1, *sptr2;
727 Boolean qual1, qual2;
728
729 sptr2 = strstr(str2, "::");
730 qual2 = (sptr2 == NULL) ? FALSE : TRUE;
731
732 if (!qual2) return (!strcmp(str1, str2));
733
734 sptr1 = strstr(str1, "::");
735 qual1 = (sptr1 == NULL) ? FALSE : TRUE;
736
737 if (qual1) return (!strcmp(str1, str2));
738
739 sptr2 += 2;
740 return (!strcmp(str1, sptr2));
741 }
742
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 /*-------------------------------------------------------*/
752
checkschem(objectptr thisobj,char * cname)753 int checkschem(objectptr thisobj, char *cname)
754 {
755 objectptr *tlib;
756 short i, j;
757
758 if (thisobj->symschem != NULL) return 0;
759
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;
763
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;
770 }
771 }
772 }
773 return 0;
774 }
775
776 /*--------------------------------------------------------*/
777 /* Complement to the above routine: If a library object */
778 /* name is the same as a top-level page, connect them. */
779 /*--------------------------------------------------------*/
780
checksym(objectptr symobj,char * cname)781 int checksym(objectptr symobj, char *cname)
782 {
783 short cpage;
784 objectptr checkpage;
785
786 if (symobj->symschem != NULL) return 0;
787
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;
797 }
798 }
799 }
800 return 0;
801 }
802
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 /*----------------------------------------------------------------------*/
810
changeotherpins(labelptr newlabel,stringpart * oldstring)811 int changeotherpins(labelptr newlabel, stringpart *oldstring)
812 {
813 objectptr other = topobject->symschem;
814 genericptr *tgen;
815 labelptr tlab;
816 int rval = 0;
817
818 if (other == NULL) return rval;
819
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++;
829 }
830 }
831 }
832 }
833 return rval;
834 }
835
836 /*----------------------------------------------------------------------*/
837 /* Xt wrapper for swapschem() */
838 /*----------------------------------------------------------------------*/
839
xlib_swapschem(xcWidget w,pointertype mode,caddr_t calldata)840 void xlib_swapschem(xcWidget w, pointertype mode, caddr_t calldata)
841 {
842 swapschem((int)mode, -1, NULL);
843 }
844
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 /*----------------------------------------------------------------------*/
851
swapschem(int allow_create,int libnum,char * fullname)852 void swapschem(int allow_create, int libnum, char *fullname)
853 {
854 objectptr savepage = topobject;
855 labelptr *pinlab;
856 genericptr *plab;
857 Boolean lflag;
858 pushlistptr stacktop;
859 short loclibnum = libnum;
860 char *canonname;
861
862 if (libnum == -1) loclibnum = USERLIB - LIBRARY;
863
864 /* Create symbol or schematic, if allowed by allow_create */
865
866 if ((topobject->symschem == NULL) && (allow_create != 0)
867 && (topobject->schemtype != SECONDARY)) {
868
869 if (topobject->schemtype != PRIMARY) {
870 int tpage;
871
872 /* create a new page for the new schematic */
873
874 for (tpage = 0; tpage < xobjs.pages; tpage++)
875 if (xobjs.pagelist[tpage]->pageinst == NULL) break;
876
877 /* Push the current instance onto the push stack */
878 /* Change the page without destroying the pushlist */
879
880 push_stack(&areawin->stack, areawin->topinstance, NULL);
881 stacktop = areawin->stack;
882 areawin->stack = NULL;
883 changepage(tpage);
884 areawin->stack = stacktop;
885 }
886 else {
887 objectptr *newobject;
888
889 /* create a new library object for the new symbol */
890
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;
900
901 incr_changes(*newobject);
902
903 if (eventmode == MOVE_MODE || eventmode == COPY_MODE)
904 standard_element_delete(ERASE);
905 else
906 unselect_all();
907
908 /* Generate a library instance for this object and set the */
909 /* top instance to point to it. */
910
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);
915
916 /* Generate the default bounding box for a size-zero object */
917 calcbbox(areawin->topinstance);
918 }
919
920 /* set links between the two objects */
921
922 savepage->symschem = topobject;
923 topobject->symschem = savepage;
924
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) */
928
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;
937 }
938 strcpy(topobject->name, canonname);
939 checkname(topobject);
940
941 /* copy all pin labels into the new object */
942
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;
948
949 if (lpin->pin == LOCAL) {
950
951 /* Only make one copy of each pin name */
952
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;
959 }
960 }
961 if (lflag == True) continue;
962
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);
976 }
977 }
978 }
979 calcbbox(areawin->topinstance);
980
981 /* Recreate the user library with the new symbol */
982 if (savepage->schemtype != SYMBOL) composelib(loclibnum + LIBRARY);
983 }
984 else if (topobject->symschem != NULL) {
985
986 /* If symschem matches the last entry on the push stack, then we */
987 /* pop; otherwise, we push. */
988
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);
995 }
996 else {
997 int p;
998 objinstptr syminst = NULL;
999 liblistptr symlist;
1000
1001 /* If symschem is a schematic, find the appropriate page */
1002
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;
1008 }
1009 if (p == xobjs.pages) {
1010
1011 /* If symschem is a symbol, and it wasn't on the push stack, */
1012 /* get the library default symbol and go there. */
1013
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;
1021 }
1022 if (symlist != NULL) break;
1023 }
1024 if (p == xobjs.numlibs) {
1025 Fprintf(stderr, "swapschem(): BAD SYMSCHEM\n");
1026 return;
1027 }
1028 }
1029
1030 if (eventmode == MOVE_MODE || eventmode == COPY_MODE)
1031 delete_for_xfer(NORMAL, areawin->selectlist, areawin->selects);
1032
1033 topobject->viewscale = areawin->vscale;
1034 topobject->pcorner = areawin->pcorner;
1035 push_stack(&areawin->stack, areawin->topinstance, NULL);
1036 areawin->topinstance = syminst;
1037 }
1038 }
1039
1040 /* If there was no action, then there is nothing more to do. */
1041
1042 if (topobject == savepage) return;
1043
1044 setpage(TRUE);
1045 transferselects();
1046 refresh(NULL, NULL, NULL);
1047 setsymschem();
1048 }
1049
1050 #ifndef TCL_WRAPPER
1051
1052 /*----------------------------------------------------------------------*/
1053 /* Wrapper for swapschem() when generating a new symbol. */
1054 /*----------------------------------------------------------------------*/
1055
makesymbol(xcWidget w,caddr_t calldata)1056 void makesymbol(xcWidget w, caddr_t calldata)
1057 {
1058 /* copy name from popup prompt buffer and check */
1059
1060 swapschem(1, -1, _STR2);
1061 }
1062
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 /*----------------------------------------------------------------------*/
1067
dobeforeswap(xcWidget w,caddr_t clientdata,caddr_t calldata)1068 void dobeforeswap(xcWidget w, caddr_t clientdata, caddr_t calldata)
1069 {
1070 buttonsave *popdata = (buttonsave *)malloc(sizeof(buttonsave));
1071
1072 /* Check for requirement to change the name before creating the symbol */
1073
1074 if ((topobject->symschem == NULL) && (topobject->schemtype == PRIMARY)
1075 && (strstr(topobject->name, "Page ") != NULL)) {
1076
1077 /* Get a name for the new object */
1078
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);
1083 }
1084 else
1085 swapschem(1, -1, NULL);
1086 }
1087
1088 #endif /* !TCL_WRAPPER */
1089
1090 /*------------------------------------------*/
1091 /* Disassociate a symbol from its schematic */
1092 /*------------------------------------------*/
1093
schemdisassoc()1094 void schemdisassoc()
1095 {
1096 if (eventmode != NORMAL) {
1097 Wprintf("Cannot disassociate schematics in this mode");
1098 }
1099 else {
1100 topobject->symschem->symschem = NULL;
1101 topobject->symschem = NULL;
1102 setsymschem();
1103 Wprintf("Schematic and symbol are now unlinked.");
1104 }
1105 }
1106
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 /*--------------------------------------------------------------*/
1114
startschemassoc(xcWidget w,pointertype mode,caddr_t calldata)1115 void startschemassoc(xcWidget w, pointertype mode, caddr_t calldata)
1116 {
1117 if ((topobject->symschem != NULL) && (mode == 1))
1118 schemdisassoc();
1119 else if ((topobject->symschem != NULL) && (mode == 0)) {
1120 Wprintf("Refusing to undo current association.");
1121 }
1122 else if (topobject->schemtype == SECONDARY) {
1123 Wprintf("Cannot attach symbol to a secondary schematic page.");
1124 }
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.");
1131 }
1132 else {
1133 /* Find a schematic (page) to associate */
1134 startcatalog(w, PAGELIB, NULL);
1135 Wprintf("Select schematic page to associate.");
1136 }
1137 }
1138 }
1139
1140 /*--------------------------------------------------------------*/
1141 /* Callback procedures on the schematic/symbol buttons. */
1142 /*--------------------------------------------------------------*/
1143
schemassoc(objectptr schemobj,objectptr symbolobj)1144 Boolean schemassoc(objectptr schemobj, objectptr symbolobj)
1145 {
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;
1152 }
1153 else {
1154 schemobj->symschem = symbolobj;
1155 symbolobj->symschem = schemobj;
1156 if (symbolobj->schemtype == TRIVIAL)
1157 symbolobj->schemtype = SYMBOL;
1158
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));
1162
1163 /* Ensure that schematic (page) name is unique */
1164 while (checkpagename(schemobj) < 0);
1165 setsymschem(); /* Set buttons and menu items appropriately */
1166 }
1167 return True;
1168 }
1169
1170 /*-------------------------------------------------------------------------*/
1171