1 /* -*- tab-width: 4 -*-
2 *
3 * Electric(tm) VLSI Design System
4 *
5 * File: usrnet.c
6 * User interface tool: network manipulation routines
7 * Written by: Steven M. Rubin, Static Free Software
8 *
9 * Copyright (c) 2000 Static Free Software.
10 *
11 * Electric(tm) is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * Electric(tm) is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with Electric(tm); see the file COPYING. If not, write to
23 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
24 * Boston, Mass 02111-1307, USA.
25 *
26 * Static Free Software
27 * 4119 Alpine Road
28 * Portola Valley, California 94028
29 * info@staticfreesoft.com
30 */
31
32 #include "global.h"
33 #include "egraphics.h"
34 #include "usr.h"
35 #include "usrtrack.h"
36 #include "usrdiacom.h"
37 #include "database.h"
38 #include "conlay.h"
39 #include "efunction.h"
40 #include "edialogs.h"
41 #include "tech.h"
42 #include "tecgen.h"
43 #include "tecart.h"
44 #include "tecschem.h"
45 #include "sim.h"
46 #include <math.h>
47
48 #define EXACTSELECTDISTANCE 5 /* number of pixels for selection */
49 #define MAXCELLEXPLHSCROLL 200 /* maximum horizontal scroll */
50
51 /* working memory for "us_pickhigherinstance()" */
52 static INTBIG us_pickinstlistsize = 0;
53 static NODEPROTO **us_pickinstcelllist;
54 static INTBIG *us_pickinstinstcount;
55 static NODEINST **us_pickinstinstlist;
56
57 /* working memory for "us_makenewportproto()" */
58 static INTBIG us_netportlimit = 0, *us_netportlocation;
59
60 /* working memory for "us_addexplorernode()" */
61 static INTBIG us_explorertempstringtotal = 0;
62 static CHAR *us_explorertempstring;
63
64 /* for constructing implant coverage polygons */
65 #define NOCOVERRECT ((COVERRECT *)-1)
66
67 typedef struct Icoverrect
68 {
69 INTBIG lx, hx, ly, hy;
70 INTBIG contributions;
71 INTBIG layer;
72 TECHNOLOGY *tech;
73 struct Icoverrect *nextcoverrect;
74 } COVERRECT;
75
76 /* for queuing of exports */
77 typedef struct
78 {
79 NODEINST *ni;
80 PORTPROTO *pp;
81 PORTPROTO *origpp;
82 } QUEUEDEXPORT;
83
84 static QUEUEDEXPORT *us_queuedexport;
85 static INTBIG us_queuedexportcount;
86 static INTBIG us_queuedexporttotal = 0;
87
88 /* for "explorer" window */
89 #define NOEXPLORERNODE ((EXPLORERNODE *)-1)
90
91 /* the meaning of EXPLORERNODE->flags */
92 #define EXNODEOPEN 1
93 #define EXNODESHOWN 2
94 #define EXNODEPLACEHOLDER 4
95
96 /* the meaning of EXPLORERNODE->enodetype */
97 #define EXNODETYPELIBRARY 0
98 #define EXNODETYPECELL 1
99 #define EXNODETYPENODE 2
100 #define EXNODETYPEARC 3
101 #define EXNODETYPENETWORK 4
102 #define EXNODETYPEEXPORT 5
103 #define EXNODETYPEERROR 6
104 #define EXNODETYPEERRORGEOM 7
105 #define EXNODETYPESIMBRANCH 8
106 #define EXNODETYPESIMLEAF 9
107 #define EXNODETYPELABEL 10
108
109 /* the meaning of EXPLORERNODE->addr when the "enodetype" is EXNODETYPELABEL */
110 #define EXLABELTYPEHIVIEW 0 /* hierarchical view */
111 #define EXLABELTYPECONVIEW 1 /* contents view */
112 #define EXLABELTYPEERRVIEW 2 /* error view */
113 #define EXLABELTYPESIMVIEW 3 /* simulation view */
114 #define EXLABELTYPENODELIST 4 /* node list in contents view */
115 #define EXLABELTYPEARCLIST 5 /* arc list in contents view */
116 #define EXLABELTYPEEXPORTLIST 6 /* export list in contents view */
117 #define EXLABELTYPENETLIST 7 /* network list in contents view */
118 #define EXLABELTYPELAYERCELLS 8 /* layer cells in contents view */
119 #define EXLABELTYPEARCCELLS 9 /* arc cells in contents view */
120 #define EXLABELTYPENODECELLS 10 /* node cells in contents view */
121 #define EXLABELTYPEMISCCELLS 11 /* miscellaneous cells in contents view */
122
123 typedef struct Iexplorernode
124 {
125 INTBIG addr;
126 INTBIG enodetype;
127 INTBIG flags;
128 INTBIG count;
129 INTBIG x, y;
130 INTBIG textwidth;
131 BOOLEAN allocated;
132 struct Iexplorernode *subexplorernode;
133 struct Iexplorernode *nextsubexplorernode;
134 struct Iexplorernode *nextexplorernode;
135 struct Iexplorernode *parent;
136 } EXPLORERNODE;
137
138 #define NOEXPWINDOW ((EXPWINDOW *)-1)
139
140 typedef struct Iexplorerwindow
141 {
142 EXPLORERNODE *firstnode;
143 EXPLORERNODE *nodehierarchy; /* the top of the hierarchy tree */
144 EXPLORERNODE *nodecontents; /* the top of the contents tree */
145 EXPLORERNODE *nodeerrors; /* the top of the errors tree */
146 EXPLORERNODE *nodesimulation; /* the top of the simulation tree */
147 EXPLORERNODE *nodeselected; /* the selected explorer node */
148 EXPLORERNODE *nodenowselected; /* the newly selected explorer node */
149 EXPLORERNODE *nodeused;
150 INTBIG firstline; /* first visible line in explorer window */
151 INTBIG firstchar; /* first visible character in explorer window */
152 INTBIG totallines;
153 INTBIG depth; /* size of explorer depth */
154 INTBIG stacksize; /* space allocated for node stacks */
155 EXPLORERNODE **stack; /* stack of explorer nodes */
156 BOOLEAN *stackdone; /* stack of explorer nodes that are done */
157 INTBIG sliderpart; /* 0:down arrow 1:below thumb 2:thumb 3:above thumb 4:up arrow */
158 INTBIG deltasofar; /* for thumb tracking */
159 INTBIG initialthumb; /* for thumb tracking */
160 } EXPWINDOW;
161
162 static WINDOWPART *us_explorertrackwindow; /* current window when arrows are clicked */
163 static EXPLORERNODE *us_explorernodefree = NOEXPLORERNODE;
164
165 static INTBIG us_exploretextsize;
166 static INTBIG us_exploretextheight;
167 static INTBIG us_exploretextwidth;
168
169 /* prototypes for local routines */
170 static void us_recursivelysearch(RTNODE*, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG, HIGHLIGHT*, HIGHLIGHT*, HIGHLIGHT*, HIGHLIGHT*, HIGHLIGHT*, INTBIG*, INTBIG*, INTBIG, INTBIG, WINDOWPART*, INTBIG);
171 static void us_checkoutobject(GEOM*, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG, HIGHLIGHT*, HIGHLIGHT*, HIGHLIGHT*, HIGHLIGHT*, HIGHLIGHT*, INTBIG*, INTBIG*, INTBIG, INTBIG, WINDOWPART*);
172 static void us_fartextsearch(NODEPROTO*, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG, HIGHLIGHT*, HIGHLIGHT *, HIGHLIGHT *, HIGHLIGHT*, HIGHLIGHT*, INTBIG*, INTBIG*, INTBIG, INTBIG, WINDOWPART*, INTBIG);
173 static void us_initarctext(ARCINST*, INTBIG, WINDOWPART*);
174 static BOOLEAN us_getarctext(ARCINST*, WINDOWPART*, POLYGON*, VARIABLE**, VARIABLE**);
175 static INTBIG us_findnewplace(INTBIG*, INTBIG, INTBIG, INTBIG);
176 static void us_setbestsnappoint(HIGHLIGHT *best, INTBIG wantx, INTBIG wanty, INTBIG newx, INTBIG newy, BOOLEAN tan, BOOLEAN perp);
177 static void us_selectsnappoly(HIGHLIGHT *best, POLYGON *poly, INTBIG wantx, INTBIG wanty);
178 static BOOLEAN us_pointonarc(INTBIG x, INTBIG y, POLYGON *poly);
179 static void us_intersectsnapline(HIGHLIGHT *best, INTBIG x1, INTBIG y1, INTBIG x2, INTBIG y2,
180 POLYGON *interpoly, INTBIG wantx, INTBIG wanty);
181 static void us_intersectsnappoly(HIGHLIGHT *best, POLYGON *poly, POLYGON *interpoly, INTBIG wantx, INTBIG wanty);
182 static void us_adjustonetangent(HIGHLIGHT *high, POLYGON *poly, INTBIG x, INTBIG y);
183 static void us_adjustoneperpendicular(HIGHLIGHT *high, POLYGON *poly, INTBIG x, INTBIG y);
184 static void us_xformpointtonode(INTBIG x, INTBIG y, NODEINST *ni, INTBIG *xo, INTBIG *yo);
185 static INTBIG us_alignvalueround(INTBIG value);
186 static INTBIG us_disttoobject(INTBIG x, INTBIG y, GEOM *geom);
187 static void us_addtocoverlist(POLYGON *poly, COVERRECT **crlist);
188 static void us_choparc(ARCINST *ai, INTBIG keepend, INTBIG ix, INTBIG iy);
189 static void us_createhierarchicalexplorertree(EXPWINDOW *ew, EXPLORERNODE *en, NODEPROTO *cell);
190 static EXPLORERNODE *us_allocexplorernode(EXPWINDOW *ew);
191 static void us_freeexplorernode(EXPLORERNODE *en);
192 static void us_addexplorernode(EXPLORERNODE *en, EXPLORERNODE **head, EXPLORERNODE *parent);
193 static BOOLEAN us_expandexplorerdepth(EXPWINDOW *ew);
194 static void us_buildexplorerstruct(WINDOWPART *w, EXPWINDOW *ew);
195 static INTBIG us_scannewexplorerstruct(EXPWINDOW *ew, EXPLORERNODE *firsten, EXPLORERNODE *oldfirsten, INTBIG line);
196 static void us_highlightexplorernode(WINDOWPART *w);
197 static INTBIG us_iconposition(PORTPROTO *pp, INTBIG style);
198 static BOOLEAN us_explorerarrowdown(INTBIG x, INTBIG y);
199 static BOOLEAN us_explorerarrowleft(INTBIG x, INTBIG y);
200 static void us_filltextpoly(CHAR *str, WINDOWPART *win, INTBIG xc, INTBIG yc, XARRAY trans,
201 TECHNOLOGY *tech, UINTBIG *descript, GEOM *geom, POLYGON *poly);
202 static BOOLEAN us_indestlib(NODEPROTO *np, LIBRARY *lib);
203 static void us_ehthumbtrackingtextcallback(INTBIG delta);
204 static void us_evthumbtrackingtextcallback(INTBIG delta);
205 static BOOLEAN us_alreadycellcenter(NODEPROTO *np);
206 static void us_manhattantravel(NODEINST*, BOOLEAN);
207 static int us_queuedexportnameascending(const void *e1, const void *e2);
208 static ARCINST *us_pastarctoarc(ARCINST *destarc, ARCINST *srcarc);
209 static NODEINST *us_pastnodetonode(NODEINST *destnode, NODEINST *srcnode);
210 static BOOLEAN us_linethroughbox(INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty,
211 INTBIG lx, INTBIG hx, INTBIG ly, INTBIG hy, INTBIG *intx, INTBIG *inty);
212 static void us_makecontactstack(ARCINST *ai, INTBIG end, ARCPROTO *ap,
213 NODEINST **conni, PORTPROTO **conpp);
214 static INTBIG us_findpathtoarc(PORTPROTO *pp, ARCPROTO *ap, INTBIG depth);
215 static BOOLEAN us_makeiconexport(PORTPROTO *pp, INTBIG style, INTBIG index,
216 INTBIG xpos, INTBIG ypos, INTBIG xbbpos, INTBIG ybbpos, NODEPROTO *np);
217 static void us_buildexplorersturcthierarchical(EXPWINDOW *ew, EXPLORERNODE *whichtree);
218 static void us_buildexplorersturctcontents(EXPWINDOW *ew, EXPLORERNODE *whichtree);
219 static void us_buildexplorernodecontents(EXPWINDOW *ew, NODEPROTO *np, EXPLORERNODE *whichtree);
220 static CHAR *us_describeexplorernode(EXPLORERNODE *en, BOOLEAN purename);
221 static void us_clearexplorererrors(void);
222 static void us_clearexplorersimulation(void);
223 static void us_buildexplorersturcterrors(EXPWINDOW *ew, EXPLORERNODE *whichtree);
224 static void us_buildexplorersturctsimulation(WINDOWPART *w, EXPWINDOW *ew, EXPLORERNODE *whichtree);
225 static void us_explorrecurseexpand(WINDOWPART *w, EXPWINDOW *ew, EXPLORERNODE *en, BOOLEAN expand);
226 static void us_addtoexplorerwindowcopy(void *infstr, INTBIG depth, EXPLORERNODE *en);
227 static void us_explorerdofunction(WINDOWPART *win, EXPLORERNODE *en, INTBIG funct);
228 static void us_explorertoggleopenness(WINDOWPART *w, EXPLORERNODE *en);
229 static void us_explorerdelete(EXPLORERNODE *en);
230 static void us_explorerrename(EXPLORERNODE *en, WINDOWPART *w);
231 static EXPWINDOW *us_getcurrentexplorerwindow(void);
232
233 /*
234 * Routine to free all memory associated with this module.
235 */
us_freenetmemory(void)236 void us_freenetmemory(void)
237 {
238 REGISTER EXPLORERNODE *en;
239
240 if (us_pickinstlistsize != 0)
241 {
242 efree((CHAR *)us_pickinstcelllist);
243 efree((CHAR *)us_pickinstinstlist);
244 efree((CHAR *)us_pickinstinstcount);
245 }
246 if (us_netportlimit > 0) efree((CHAR *)us_netportlocation);
247 if (us_queuedexporttotal > 0) efree((CHAR *)us_queuedexport);
248 if (us_explorertempstringtotal > 0) efree(us_explorertempstring);
249
250 while (us_explorernodefree != NOEXPLORERNODE)
251 {
252 en = us_explorernodefree;
253 us_explorernodefree = en->nextexplorernode;
254 if (en->allocated) efree((CHAR *)en);
255 }
256 }
257
258 /*********************************** CELL SUPPORT ***********************************/
259
260 /*
261 * routine to recursively expand the cell "ni" by "amount" levels.
262 * "sofar" is the number of levels that this has already been expanded.
263 */
us_doexpand(NODEINST * ni,INTBIG amount,INTBIG sofar)264 void us_doexpand(NODEINST *ni, INTBIG amount, INTBIG sofar)
265 {
266 REGISTER NODEPROTO *np;
267 REGISTER NODEINST *ono;
268
269 if ((ni->userbits & NEXPAND) == 0)
270 {
271 /* expanded the cell */
272 ni->userbits |= NEXPAND;
273 ni->parent->lib->userbits |= LIBCHANGEDMINOR;
274
275 /* if depth limit reached, quit */
276 if (++sofar >= amount) return;
277 }
278
279 /* explore insides of this one */
280 np = ni->proto;
281 for(ono = np->firstnodeinst; ono != NONODEINST; ono = ono->nextnodeinst)
282 {
283 if (ono->proto->primindex != 0) continue;
284
285 /* ignore recursive references (showing icon in contents) */
286 if (isiconof(ono->proto, np)) continue;
287 us_doexpand(ono, amount, sofar);
288 }
289 }
290
us_dounexpand(NODEINST * ni)291 void us_dounexpand(NODEINST *ni)
292 {
293 REGISTER NODEPROTO *np;
294 REGISTER NODEINST *ono;
295
296 if ((ni->userbits & NEXPAND) == 0) return;
297
298 np = ni->proto;
299 for(ono = np->firstnodeinst; ono != NONODEINST; ono = ono->nextnodeinst)
300 {
301 if (ono->proto->primindex != 0) continue;
302
303 /* ignore recursive references (showing icon in contents) */
304 if (isiconof(ono->proto, np)) continue;
305 if ((ono->userbits & NEXPAND) != 0) us_dounexpand(ono);
306 }
307
308 /* expanded the cell */
309 if ((ni->userbits & WANTEXP) != 0)
310 {
311 ni->userbits &= ~NEXPAND;
312 ni->parent->lib->userbits |= LIBCHANGEDMINOR;
313 }
314 }
315
us_setunexpand(NODEINST * ni,INTBIG amount)316 INTBIG us_setunexpand(NODEINST *ni, INTBIG amount)
317 {
318 REGISTER INTBIG depth;
319 REGISTER NODEINST *ono;
320 REGISTER NODEPROTO *np;
321
322 ni->userbits &= ~WANTEXP;
323 if ((ni->userbits & NEXPAND) == 0) return(0);
324 np = ni->proto;
325 depth = 0;
326 for(ono = np->firstnodeinst; ono != NONODEINST; ono = ono->nextnodeinst)
327 {
328 if (ono->proto->primindex != 0) continue;
329
330 /* ignore recursive references (showing icon in contents) */
331 if (isiconof(ono->proto, np)) continue;
332 if ((ono->userbits & NEXPAND) != 0)
333 depth = maxi(depth, us_setunexpand(ono, amount));
334 }
335 if (depth < amount) ni->userbits |= WANTEXP;
336 return(depth+1);
337 }
338
339 /*
340 * Routine to recursively walk the hierarchy from "np", marking all cells below it.
341 */
us_recursivemark(NODEPROTO * np)342 void us_recursivemark(NODEPROTO *np)
343 {
344 REGISTER NODEINST *ni;
345 REGISTER NODEPROTO *cnp;
346
347 if (np->temp1 != 0) return;
348 np->temp1 = 1;
349 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
350 {
351 if (ni->proto->primindex != 0) continue;
352 us_recursivemark(ni->proto);
353 cnp = contentsview(ni->proto);
354 if (cnp != NONODEPROTO) us_recursivemark(cnp);
355 }
356 }
357
358 /*
359 * Routine to adjust selected objects in the cell to even grid units.
360 */
us_regridselected(void)361 void us_regridselected(void)
362 {
363 REGISTER NODEINST *ni;
364 REGISTER ARCINST *ai;
365 REGISTER GEOM **list;
366 REGISTER PORTPROTO *pp;
367 REGISTER INTBIG bodyxoffset, bodyyoffset, portxoffset, portyoffset, pxo, pyo, i, j, tot,
368 adjustednodes, adjustedarcs, end1xoff, end1yoff, end2xoff, end2yoff, ang;
369 INTBIG lx, hx, ly, hy;
370 REGISTER BOOLEAN mixedportpos;
371 REGISTER PORTARCINST *pi;
372 INTBIG px, py, cx, cy;
373 XARRAY transr;
374 static POLYGON *poly = NOPOLYGON;
375
376 /* get polygon */
377 (void)needstaticpolygon(&poly, 4, us_tool->cluster);
378
379 if (us_alignment_ratio <= 0)
380 {
381 us_abortcommand(_("No alignment given: set Alignment Options first"));
382 return;
383 }
384 list = us_gethighlighted(WANTNODEINST|WANTARCINST, 0, 0);
385 if (list[0] == NOGEOM)
386 {
387 us_abortcommand(_("Must select something before aligning it to the grid"));
388 return;
389 }
390
391 /* save and remove highlighting */
392 us_pushhighlight();
393 us_clearhighlightcount();
394
395 adjustednodes = adjustedarcs = 0;
396 for(i=0; list[i] != NOGEOM; i++)
397 {
398 if (!list[i]->entryisnode) continue;
399 ni = list[i]->entryaddr.ni;
400
401 /* ignore pins */
402 if (((ni->proto->userbits&NFUNCTION) >> NFUNCTIONSH) == NPPIN) continue;
403 us_getnodedisplayposition(ni, &cx, &cy);
404 bodyxoffset = us_alignvalueround(cx) - cx;
405 bodyyoffset = us_alignvalueround(cy) - cy;
406
407 portxoffset = bodyxoffset;
408 portyoffset = bodyyoffset;
409 mixedportpos = FALSE;
410 for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
411 {
412 portposition(ni, pp, &px, &py);
413 pxo = us_alignvalueround(px) - px;
414 pyo = us_alignvalueround(py) - py;
415 if (pp == ni->proto->firstportproto)
416 {
417 portxoffset = pxo; portyoffset = pyo;
418 } else
419 {
420 if (portxoffset != pxo || portyoffset != pyo) mixedportpos = TRUE;
421 }
422 }
423 if (!mixedportpos)
424 {
425 bodyxoffset = portxoffset; bodyyoffset = portyoffset;
426 }
427
428 /* if a primitive has an offset, see if the node edges are aligned */
429 if (bodyxoffset != 0 || bodyyoffset != 0)
430 {
431 if (ni->proto->primindex != 0)
432 {
433 makerot(ni, transr);
434 tot = nodepolys(ni, 0, NOWINDOWPART);
435 for(j=0; j<tot; j++)
436 {
437 shapenodepoly(ni, j, poly);
438 xformpoly(poly, transr);
439 if (!isbox(poly, &lx, &hx, &ly, &hy)) continue;
440 if (us_alignvalueround(hx) == hx &&
441 us_alignvalueround(lx) == lx) bodyxoffset = 0;
442 if (us_alignvalueround(hy) == hy &&
443 us_alignvalueround(ly) == ly) bodyyoffset = 0;
444 if (bodyxoffset == 0 && bodyyoffset == 0) break;
445 }
446 }
447 }
448
449 /* move the node */
450 if (bodyxoffset != 0 || bodyyoffset != 0)
451 {
452 /* turn off all constraints on arcs */
453 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
454 {
455 ai = pi->conarcinst;
456 ai->temp1 = ai->userbits;
457 ai->userbits &= ~(FIXED|FIXANG);
458 }
459 startobjectchange((INTBIG)ni, VNODEINST);
460 modifynodeinst(ni, bodyxoffset, bodyyoffset, bodyxoffset, bodyyoffset, 0, 0);
461 endobjectchange((INTBIG)ni, VNODEINST);
462 adjustednodes++;
463
464 /* restore arc constraints */
465 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
466 {
467 ai = pi->conarcinst;
468 ai->userbits = ai->temp1;
469 }
470 }
471 }
472 for(i=0; list[i] != NOGEOM; i++)
473 {
474 if (list[i]->entryisnode) continue;
475 ai = list[i]->entryaddr.ai;
476 end1xoff = us_alignvalueround(ai->end[0].xpos) - ai->end[0].xpos;
477 end1yoff = us_alignvalueround(ai->end[0].ypos) - ai->end[0].ypos;
478 end2xoff = us_alignvalueround(ai->end[1].xpos) - ai->end[1].xpos;
479 end2yoff = us_alignvalueround(ai->end[1].ypos) - ai->end[1].ypos;
480 if (end1xoff == 0 && end2xoff == 0 && end1yoff == 0 && end2yoff == 0) continue;
481
482 if (!db_stillinport(ai, 0, ai->end[0].xpos+end1xoff, ai->end[0].ypos+end1yoff))
483 {
484 if (!db_stillinport(ai, 0, ai->end[0].xpos, ai->end[0].ypos)) continue;
485 end1xoff = end1yoff = 0;
486 }
487 if (!db_stillinport(ai, 1, ai->end[1].xpos+end2xoff, ai->end[1].ypos+end2yoff))
488 {
489 if (!db_stillinport(ai, 1, ai->end[1].xpos, ai->end[1].ypos)) continue;
490 end2xoff = end2yoff = 0;
491 }
492
493 /* make sure an arc does not change angle */
494 ang = (ai->userbits&AANGLE) >> AANGLESH;
495 if (ang == 0 || ang == 180)
496 {
497 /* horizontal arc: both DY values must be the same */
498 if (end1yoff != end2yoff) end1yoff = end2yoff = 0;
499 } else if (ang == 90 || ang == 270)
500 {
501 /* vertical arc: both DX values must be the same */
502 if (end1xoff != end2xoff) end1xoff = end2xoff = 0;
503 }
504 if (end1xoff != 0 || end2xoff != 0 || end1yoff != 0 || end2yoff != 0)
505 {
506 ai->temp1 = ai->userbits;
507 ai->userbits &= ~(FIXED|FIXANG);
508 startobjectchange((INTBIG)ai, VARCINST);
509 modifyarcinst(ai, 0, end1xoff, end1yoff, end2xoff, end2yoff);
510 endobjectchange((INTBIG)ai, VARCINST);
511 adjustedarcs++;
512 ai->userbits = ai->temp1;
513 }
514 }
515
516 /* restore highlighting and show results */
517 us_pophighlight(TRUE);
518 if (adjustednodes == 0 && adjustedarcs == 0) ttyputmsg(_("No adjustments necessary")); else
519 ttyputmsg(_("Adjusted %ld %s and %ld %s"),
520 adjustednodes, makeplural(_("node"), adjustednodes),
521 adjustedarcs, makeplural(_("arc"), adjustedarcs));
522 }
523
524 /*
525 * Routine to create or modify implant (well/substrate) nodes that cover their components
526 * cleanly.
527 */
us_coverimplant(void)528 void us_coverimplant(void)
529 {
530 COVERRECT *crlist;
531 REGISTER COVERRECT *cr, *ocr, *lastcr, *nextcr;
532 REGISTER NODEPROTO *np, *cell;
533 REGISTER INTBIG i, total, domerge;
534 REGISTER INTBIG fun, count;
535 REGISTER NODEINST *ni, *nextni;
536 REGISTER ARCINST *ai;
537 HIGHLIGHT high;
538 XARRAY trans;
539 static POLYGON *poly = NOPOLYGON;
540
541 /* get polygon */
542 (void)needstaticpolygon(&poly, 4, us_tool->cluster);
543
544 /* generate an initial coverage list from all nodes and arcs in the cell */
545 crlist = NOCOVERRECT;
546 cell = us_needcell();
547 if (cell == NONODEPROTO) return;
548 for(ni = cell->firstnodeinst; ni != NONODEINST; ni = nextni)
549 {
550 nextni = ni->nextnodeinst;
551 if (ni->proto->primindex == 0) continue;
552 fun = nodefunction(ni);
553 if (fun == NPNODE)
554 {
555 startobjectchange((INTBIG)ni, VNODEINST);
556 (void)killnodeinst(ni);
557 continue;
558 }
559 makerot(ni, trans);
560 total = nodepolys(ni, 0, NOWINDOWPART);
561 for(i=0; i<total; i++)
562 {
563 shapenodepoly(ni, i, poly);
564 fun = layerfunction(ni->proto->tech, poly->layer) & LFTYPE;
565 if (fun != LFIMPLANT && fun != LFSUBSTRATE && fun != LFWELL) continue;
566 xformpoly(poly, trans);
567 us_addtocoverlist(poly, &crlist);
568 }
569 }
570 for(ai = cell->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
571 {
572 total = arcpolys(ai, NOWINDOWPART);
573 for(i=0; i<total; i++)
574 {
575 shapearcpoly(ai, i, poly);
576 fun = layerfunction(ai->proto->tech, poly->layer) & LFTYPE;
577 if (fun != LFIMPLANT && fun != LFSUBSTRATE && fun != LFWELL) continue;
578 us_addtocoverlist(poly, &crlist);
579 }
580 }
581
582 /* merge the coverage rectangles that touch */
583 for(cr = crlist; cr != NOCOVERRECT; cr = cr->nextcoverrect)
584 {
585 lastcr = NOCOVERRECT;
586 for(ocr = cr->nextcoverrect; ocr != NOCOVERRECT; ocr = nextcr)
587 {
588 nextcr = ocr->nextcoverrect;
589 domerge = 0;
590 if (cr->layer == ocr->layer && cr->tech == ocr->tech)
591 {
592 if (cr->hx >= ocr->lx && cr->lx <= ocr->hx &&
593 cr->hy >= ocr->ly && cr->ly <= ocr->hy) domerge = 1;
594 }
595 if (domerge != 0)
596 {
597 /* merge them */
598 if (ocr->lx < cr->lx) cr->lx = ocr->lx;
599 if (ocr->hx > cr->hx) cr->hx = ocr->hx;
600 if (ocr->ly < cr->ly) cr->ly = ocr->ly;
601 if (ocr->hy > cr->hy) cr->hy = ocr->hy;
602 cr->contributions += ocr->contributions;
603
604 if (lastcr == NOCOVERRECT) cr->nextcoverrect = ocr->nextcoverrect; else
605 lastcr->nextcoverrect = ocr->nextcoverrect;
606 efree((CHAR *)ocr);
607 continue;
608 }
609 lastcr = ocr;
610 }
611 }
612 #if 0 /* merge coverage rectangles across open space */
613 for(cr = crlist; cr != NOCOVERRECT; cr = cr->nextcoverrect)
614 {
615 lastcr = NOCOVERRECT;
616 for(ocr = cr->nextcoverrect; ocr != NOCOVERRECT; ocr = nextcr)
617 {
618 nextcr = ocr->nextcoverrect;
619 domerge = 0;
620 if (cr->layer == ocr->layer && cr->tech == ocr->tech)
621 {
622 REGISTER COVERRECT *icr;
623 REGISTER INTBIG mlx, mhx, mly, mhy;
624
625 mlx = mini(cr->lx, ocr->lx);
626 mhx = maxi(cr->hx, ocr->hx);
627 mly = mini(cr->ly, ocr->ly);
628 mhy = maxi(cr->hy, ocr->hy);
629 for(icr = crlist; icr != NOCOVERRECT; icr = icr->nextcoverrect)
630 {
631 if (icr == cr || icr == ocr) continue;
632 if (icr->layer == cr->layer && icr->tech == cr->tech) continue;
633 if (icr->lx < mhx && icr->hx > mlx && icr->ly < mhy && icr->hy > mly)
634 break;
635 }
636 if (icr == NOCOVERRECT) domerge = 1;
637 }
638 if (domerge != 0)
639 {
640 /* merge them */
641 if (ocr->lx < cr->lx) cr->lx = ocr->lx;
642 if (ocr->hx > cr->hx) cr->hx = ocr->hx;
643 if (ocr->ly < cr->ly) cr->ly = ocr->ly;
644 if (ocr->hy > cr->hy) cr->hy = ocr->hy;
645 cr->contributions += ocr->contributions;
646
647 if (lastcr == NOCOVERRECT) cr->nextcoverrect = ocr->nextcoverrect; else
648 lastcr->nextcoverrect = ocr->nextcoverrect;
649 efree((CHAR *)ocr);
650 continue;
651 }
652 lastcr = ocr;
653 }
654 }
655 #endif
656
657 count = 0;
658 while (crlist != NOCOVERRECT)
659 {
660 cr = crlist;
661 crlist = crlist->nextcoverrect;
662
663 if (cr->contributions > 1)
664 {
665 np = getpurelayernode(cr->tech, cr->layer, 0);
666 ni = newnodeinst(np, cr->lx, cr->hx, cr->ly, cr->hy, 0, 0, cell);
667 if (ni == NONODEINST) continue;
668 ni->userbits |= HARDSELECTN;
669 endobjectchange((INTBIG)ni, VNODEINST);
670 if (count == 0) us_clearhighlightcount();
671 high.status = HIGHFROM;
672 high.fromgeom = ni->geom;
673 high.fromport = NOPORTPROTO;
674 high.cell = cell;
675 us_addhighlight(&high);
676 count++;
677 }
678 efree((CHAR *)cr);
679 }
680 if (count == 0) ttyputmsg(_("No implant areas added")); else
681 ttyputmsg(_("Added %ld implant %s"), count, makeplural(_("area"), count));
682 }
683
us_addtocoverlist(POLYGON * poly,COVERRECT ** crlist)684 void us_addtocoverlist(POLYGON *poly, COVERRECT **crlist)
685 {
686 REGISTER COVERRECT *cr;
687 INTBIG lx, hx, ly, hy;
688
689 getbbox(poly, &lx, &hx, &ly, &hy);
690 for(cr = *crlist; cr != NOCOVERRECT; cr = cr->nextcoverrect)
691 {
692 if (cr->layer != poly->layer) continue;
693 if (cr->tech != poly->tech) continue;
694 if (hx < cr->lx || lx > cr->hx || hy < cr->ly || ly > cr->hy) continue;
695
696 /* this polygon touches another: merge into it */
697 if (lx < cr->lx) cr->lx = lx;
698 if (hx > cr->hx) cr->hx = hx;
699 if (ly < cr->ly) cr->ly = ly;
700 if (hy > cr->hy) cr->hy = hy;
701 cr->contributions++;
702 return;
703 }
704
705 /* nothing in this area: create a new one */
706 cr = (COVERRECT *)emalloc(sizeof (COVERRECT), el_tempcluster);
707 if (cr == 0) return;
708 cr->lx = lx; cr->hx = hx;
709 cr->ly = ly; cr->hy = hy;
710 cr->layer = poly->layer;
711 cr->tech = poly->tech;
712 cr->contributions = 1;
713 cr->nextcoverrect = *crlist;
714 *crlist = cr;
715 }
716
717 /*
718 * Routine to round "value" to the nearest "us_alignment_ratio" units.
719 */
us_alignvalueround(INTBIG value)720 INTBIG us_alignvalueround(INTBIG value)
721 {
722 REGISTER INTBIG alignment;
723
724 alignment = muldiv(us_alignment_ratio, el_curlib->lambda[el_curtech->techindex], WHOLE);
725 if (alignment == 0) return(value);
726 if (value > 0)
727 return((value + alignment/2) / alignment * alignment);
728 return((value - alignment/2) / alignment * alignment);
729 }
730
731 /*
732 * Routine to determine whether cell "np" has a cell center in it. If so,
733 * an error is displayed and the routine returns true.
734 */
us_alreadycellcenter(NODEPROTO * np)735 BOOLEAN us_alreadycellcenter(NODEPROTO *np)
736 {
737 REGISTER NODEINST *ni;
738
739 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
740 if (ni->proto == gen_cellcenterprim)
741 {
742 us_abortcommand(_("This cell already has a cell-center"));
743 return(TRUE);
744 }
745 return(FALSE);
746 }
747
748 /*
749 * Routine to return true if cell "cell" belongs in technology "tech".
750 */
us_cellfromtech(NODEPROTO * cell,TECHNOLOGY * tech)751 BOOLEAN us_cellfromtech(NODEPROTO *cell, TECHNOLOGY *tech)
752 {
753 if (tech == sch_tech)
754 {
755 /* schematic: accept {ic}, {sch}, and {pN} */
756 if (cell->cellview == el_iconview) return(TRUE);
757 if (cell->cellview == el_schematicview) return(TRUE);
758 if ((cell->cellview->viewstate&MULTIPAGEVIEW) != 0) return(TRUE);
759 } else
760 {
761 /* not schematic: accept unknown, {lay} */
762 if (cell->cellview == el_unknownview) return(TRUE);
763 if (cell->cellview == el_layoutview) return(TRUE);
764 }
765 return(FALSE);
766 }
767
768 /*
769 * Routine to return the type of node to create, given that it will be put
770 * in "cell" and that the icon should be used if "getcontents" is true.
771 * Gives an error and returns NONODEPROTO on error.
772 */
us_nodetocreate(BOOLEAN getcontents,NODEPROTO * cell)773 NODEPROTO *us_nodetocreate(BOOLEAN getcontents, NODEPROTO *cell)
774 {
775 REGISTER NODEPROTO *np, *rnp;
776 CHAR *par[3];
777 REGISTER void *infstr;
778
779 np = us_curnodeproto;
780 if (np == NONODEPROTO)
781 {
782 us_abortcommand(_("No selected node proto to create"));
783 return(NONODEPROTO);
784 }
785
786 /* disallow placement of text cells */
787 if (np->primindex == 0)
788 {
789 if ((np->cellview->viewstate&TEXTVIEW) != 0)
790 {
791 us_abortcommand(_("Cannot place an instance of a text-only cell"));
792 return(NONODEPROTO);
793 }
794 }
795
796 /* use the icon cell if one exists and contents wasn't requested */
797 if (!getcontents)
798 {
799 if (np->primindex == 0 && (np->cellview == el_schematicview ||
800 (np->cellview->viewstate&MULTIPAGEVIEW) != 0))
801 {
802 rnp = iconview(np);
803 if (rnp != NONODEPROTO)
804 {
805 /* there is an icon: see if the user wants to use it */
806 infstr = initinfstr();
807 formatinfstr(infstr, _("You requested creation of schematic cell: %s. "),
808 describenodeproto(np));
809 formatinfstr(infstr, _("Don't you really want to place its icon?"));
810 if (us_yesnodlog(returninfstr(infstr), par) == 0) return(NONODEPROTO);
811 if (namesame(par[0], x_("yes")) == 0) np = rnp;
812 }
813 }
814 }
815
816 /* create a node instance: check for recursion first */
817 if (isachildof(cell, np))
818 {
819 us_abortcommand(_("Cannot create the node: it would be recursive"));
820 return(NONODEPROTO);
821 }
822
823 /* disallow creation of second cell center */
824 if (np == gen_cellcenterprim && us_alreadycellcenter(cell)) return(NONODEPROTO);
825 return(np);
826 }
827
828 /*
829 * recursive helper routine for "us_copycell" which copies cell "fromnp"
830 * to a new cell called "toname" in library "tolib" with the new view type
831 * "toview". All needed subcells are copied (unless "nosubcells" is true).
832 * All shared view cells referenced by variables are copied too
833 * (unless "norelatedviews" is true). If "useexisting" is TRUE, any subcells
834 * that already exist in the destination library will be used there instead of
835 * creating a cross-library reference.
836 * If "move" is nonzero, delete the original after copying, and update all
837 * references to point to the new cell. If "subdescript" is empty, the operation
838 * is a top-level request. Otherwise, this is for a subcell, so only create a
839 * new cell if one with the same name and date doesn't already exists.
840 */
us_copyrecursively(NODEPROTO * fromnp,CHAR * toname,LIBRARY * tolib,VIEW * toview,BOOLEAN verbose,BOOLEAN move,CHAR * subdescript,BOOLEAN norelatedviews,BOOLEAN nosubcells,BOOLEAN useexisting)841 NODEPROTO *us_copyrecursively(NODEPROTO *fromnp, CHAR *toname, LIBRARY *tolib,
842 VIEW *toview, BOOLEAN verbose, BOOLEAN move, CHAR *subdescript, BOOLEAN norelatedviews,
843 BOOLEAN nosubcells, BOOLEAN useexisting)
844 {
845 REGISTER NODEPROTO *np, *onp, *newfromnp, *beforenewfromnp, *fromnpwalk;
846 REGISTER NODEINST *ni;
847 REGISTER GEOM **list;
848 REGISTER INTBIG i;
849 REGISTER LIBRARY *lib;
850 REGISTER BOOLEAN found;
851 REGISTER CHAR *newname;
852 REGISTER void *infstr;
853 UINTBIG fromnp_creationdate, fromnp_revisiondate;
854
855 fromnp_creationdate = fromnp->creationdate;
856 fromnp_revisiondate = fromnp->revisiondate;
857
858 /* see if the cell is already there */
859 for(newfromnp = tolib->firstnodeproto; newfromnp != NONODEPROTO;
860 newfromnp = newfromnp->nextnodeproto)
861 {
862 if (namesame(newfromnp->protoname, toname) != 0) continue;
863 if (newfromnp->cellview != toview) continue;
864 if (newfromnp->creationdate != fromnp->creationdate) continue;
865 if (newfromnp->revisiondate != fromnp->revisiondate) continue;
866 if (*subdescript != 0) return(newfromnp);
867 break;
868 }
869
870 /* copy subcells */
871 if (!nosubcells)
872 {
873 found = TRUE;
874 while (found)
875 {
876 found = FALSE;
877 for(ni = fromnp->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
878 {
879 np = ni->proto;
880 if (np->primindex != 0) continue;
881
882 /* allow cross-library references to stay */
883 if (np->lib != fromnp->lib) continue;
884 if (np->lib == tolib) continue;
885
886 /* see if the cell is already there */
887 if (us_indestlib(np, tolib)) continue;
888
889 /* copy subcell if not already there */
890 onp = us_copyrecursively(np, np->protoname, tolib, np->cellview,
891 verbose, move, x_("subcell "), norelatedviews, nosubcells, useexisting);
892 if (onp == NONODEPROTO)
893 {
894 newname = describenodeproto(np);
895 if (move) ttyputerr(_("Move of subcell %s failed"), newname); else
896 ttyputerr(_("Copy of subcell %s failed"), newname);
897 return(NONODEPROTO);
898 }
899 found = TRUE;
900 break;
901 }
902 }
903 }
904
905 /* also copy equivalent views */
906 if (!norelatedviews)
907 {
908 /* first copy the icons */
909 found = TRUE;
910 fromnpwalk = fromnp;
911 while (found)
912 {
913 found = FALSE;
914 FOR_CELLGROUP(np, fromnpwalk)
915 {
916 if (np->cellview != el_iconview) continue;
917
918 /* see if the cell is already there */
919 if (us_indestlib(np, tolib)) continue;
920
921 /* copy equivalent view if not already there */
922 if (move) fromnpwalk = np->nextcellgrp; /* if np is moved (i.e. deleted), circular linked list is broken */
923 onp = us_copyrecursively(np, np->protoname, tolib, np->cellview,
924 verbose, move, _("alternate view "), TRUE, nosubcells, useexisting);
925 if (onp == NONODEPROTO)
926 {
927 if (move) ttyputerr(_("Move of alternate view %s failed"), describenodeproto(np)); else
928 ttyputerr(_("Copy of alternate view %s failed"), describenodeproto(np));
929 return(NONODEPROTO);
930 }
931 found = TRUE;
932 break;
933 }
934 }
935
936 /* now copy the rest */
937 found = TRUE;
938 while (found)
939 {
940 found = FALSE;
941 FOR_CELLGROUP(np, fromnpwalk)
942 {
943 if (np->cellview == el_iconview) continue;
944
945 /* see if the cell is already there */
946 if (us_indestlib(np, tolib)) continue;
947
948 /* copy equivalent view if not already there */
949 if (move) fromnpwalk = np->nextcellgrp; /* if np is moved (i.e. deleted), circular linked list is broken */
950 onp = us_copyrecursively(np, np->protoname, tolib, np->cellview,
951 verbose, move, _("alternate view "), TRUE, nosubcells, useexisting);
952 if (onp == NONODEPROTO)
953 {
954 if (move) ttyputerr(_("Move of alternate view %s failed"), describenodeproto(np)); else
955 ttyputerr(_("Copy of alternate view %s failed"), describenodeproto(np));
956 return(NONODEPROTO);
957 }
958 found = TRUE;
959 break;
960 }
961 }
962 }
963
964 /* see if the cell is NOW there */
965 beforenewfromnp = NONODEPROTO;
966 for(newfromnp = tolib->firstnodeproto; newfromnp != NONODEPROTO;
967 newfromnp = newfromnp->nextnodeproto)
968 {
969 if (namesame(newfromnp->protoname, toname) != 0) continue;
970 if (newfromnp->cellview != toview) continue;
971 if (newfromnp->creationdate != fromnp_creationdate) continue;
972 if (!move && newfromnp->revisiondate != fromnp_revisiondate) continue; /* moving icon of schematic changes schematic's revision date */
973 if (*subdescript != 0) return(newfromnp);
974 break;
975 }
976 if (beforenewfromnp == newfromnp || newfromnp == NONODEPROTO)
977 {
978 /* copy the cell */
979 if (*toview->sviewname != 0)
980 {
981 infstr = initinfstr();
982 addstringtoinfstr(infstr, toname);
983 addtoinfstr(infstr, '{');
984 addstringtoinfstr(infstr, toview->sviewname);
985 addtoinfstr(infstr, '}');
986 newname = returninfstr(infstr);
987 } else newname = toname;
988 newfromnp = copynodeproto(fromnp, tolib, newname, useexisting);
989 if (newfromnp == NONODEPROTO)
990 {
991 if (move)
992 {
993 ttyputerr(_("Move of %s%s failed"), subdescript,
994 describenodeproto(fromnp));
995 } else
996 {
997 ttyputerr(_("Copy of %s%s failed"), subdescript,
998 describenodeproto(fromnp));
999 }
1000 return(NONODEPROTO);
1001 }
1002
1003 /* ensure that the copied cell is the right size */
1004 (*el_curconstraint->solve)(newfromnp);
1005
1006 /* if moving, adjust pointers and kill original cell */
1007 if (move)
1008 {
1009 /* ensure that the copied cell is the right size */
1010 (*el_curconstraint->solve)(newfromnp);
1011
1012 /* clear highlighting if the current node is being replaced */
1013 list = us_gethighlighted(WANTNODEINST, 0, 0);
1014 for(i=0; list[i] != NOGEOM; i++)
1015 {
1016 if (!list[i]->entryisnode) continue;
1017 ni = list[i]->entryaddr.ni;
1018 if (ni->proto == fromnp) break;
1019 }
1020 if (list[i] != NOGEOM) us_clearhighlightcount();
1021
1022 /* now replace old instances with the moved one */
1023 for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
1024 {
1025 for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
1026 {
1027 found = TRUE;
1028 while (found)
1029 {
1030 found = FALSE;
1031 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1032 {
1033 if (ni->proto == fromnp)
1034 {
1035 if (replacenodeinst(ni, newfromnp, FALSE, FALSE) == NONODEINST)
1036 ttyputerr(_("Error moving node %s in cell %s"),
1037 describenodeinst(ni), describenodeproto(np));
1038 found = TRUE;
1039 break;
1040 }
1041 }
1042 }
1043 }
1044 }
1045 toolturnoff(net_tool, FALSE);
1046 us_dokillcell(fromnp);
1047 toolturnon(net_tool);
1048 fromnp = NONODEPROTO;
1049 }
1050
1051 if (verbose)
1052 {
1053 if (el_curlib != tolib)
1054 {
1055 infstr = initinfstr();
1056 if (move)
1057 {
1058 formatinfstr(infstr, _("Moved %s%s:%s to library %s"), subdescript,
1059 el_curlib->libname, nldescribenodeproto(newfromnp), tolib->libname);
1060 } else
1061 {
1062 formatinfstr(infstr, _("Copied %s%s:%s to library %s"), subdescript,
1063 el_curlib->libname, nldescribenodeproto(newfromnp), tolib->libname);
1064 }
1065 ttyputmsg(x_("%s"), returninfstr(infstr));
1066 } else
1067 {
1068 ttyputmsg(_("Copied %s%s"), subdescript, describenodeproto(newfromnp));
1069 }
1070 }
1071 }
1072 return(newfromnp);
1073 }
1074
1075 /*
1076 * Routine to return true if a cell like "np" exists in library "lib".
1077 */
us_indestlib(NODEPROTO * np,LIBRARY * lib)1078 BOOLEAN us_indestlib(NODEPROTO *np, LIBRARY *lib)
1079 {
1080 REGISTER NODEPROTO *onp;
1081
1082 for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
1083 {
1084 if (namesame(onp->protoname, np->protoname) != 0) continue;
1085 if (onp->cellview != np->cellview) continue;
1086 if (onp->creationdate != np->creationdate) continue;
1087 if (onp->revisiondate != np->revisiondate) continue;
1088 return(TRUE);
1089 }
1090 return(FALSE);
1091 }
1092
1093 /*
1094 * Routine to determine whether the line from (fx,fy) to (tx,ty) intersects the
1095 * box defined from "lx<=X<=hx" and "ly<=Y<=hy". If there is an intersection,
1096 * the center is placed in (intx,inty) and the routine returns true.
1097 */
us_linethroughbox(INTBIG fx,INTBIG fy,INTBIG tx,INTBIG ty,INTBIG lx,INTBIG hx,INTBIG ly,INTBIG hy,INTBIG * intx,INTBIG * inty)1098 BOOLEAN us_linethroughbox(INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty,
1099 INTBIG lx, INTBIG hx, INTBIG ly, INTBIG hy, INTBIG *intx, INTBIG *inty)
1100 {
1101 INTBIG xint[4], yint[4], ix, iy;
1102 REGISTER INTBIG intcount;
1103 REGISTER BOOLEAN didin;
1104
1105 intcount = 0;
1106
1107 /* see which side has the intersection point */
1108 didin = segintersect(fx, fy, tx, ty, lx, ly, hx, ly, &ix, &iy);
1109 if (didin)
1110 {
1111 xint[intcount] = ix;
1112 yint[intcount] = iy;
1113 intcount++;
1114 }
1115 didin = segintersect(fx, fy, tx, ty, lx, hy, hx, hy, &ix, &iy);
1116 if (didin)
1117 {
1118 xint[intcount] = ix;
1119 yint[intcount] = iy;
1120 intcount++;
1121 }
1122 didin = segintersect(fx, fy, tx, ty, lx, ly, lx, hy, &ix, &iy);
1123 if (didin)
1124 {
1125 xint[intcount] = ix;
1126 yint[intcount] = iy;
1127 intcount++;
1128 }
1129 didin = segintersect(fx, fy, tx, ty, hx, ly, hx, hy, &ix, &iy);
1130 if (didin)
1131 {
1132 xint[intcount] = ix;
1133 yint[intcount] = iy;
1134 intcount++;
1135 }
1136
1137 /* see if two points hit */
1138 if (intcount == 2)
1139 {
1140 *intx = (xint[0] + xint[1]) / 2;
1141 *inty = (yint[0] + yint[1]) / 2;
1142 return(TRUE);
1143 }
1144 return(FALSE);
1145 }
1146
1147 /*
1148 * Routine to remove geometry in the selected area, shortening arcs that enter
1149 * from outside.
1150 */
us_erasegeometry(NODEPROTO * np)1151 void us_erasegeometry(NODEPROTO *np)
1152 {
1153 INTBIG lx, hx, ly, hy, lx1, hx1, ly1, hy1, ix, iy, sx, sy;
1154 REGISTER INTBIG i, in0, in1, keepend, killend, keepx, keepy, killx, killy,
1155 clx, chx, cly, chy, cx, cy;
1156 REGISTER INTBIG ang, didin;
1157 REGISTER ARCINST *ai, *ai1, *ai2, *nextai;
1158 REGISTER NODEINST *ni, *nextni;
1159 REGISTER NODEPROTO *pin;
1160 static POLYGON *poly = NOPOLYGON;
1161
1162 /* get polygon */
1163 (void)needstaticpolygon(&poly, 4, us_tool->cluster);
1164
1165 /* disallow erasing if lock is on */
1166 if (us_cantedit(np, NONODEINST, TRUE)) return;
1167
1168 np = us_getareabounds(&lx, &hx, &ly, &hy);
1169 if (np == NONODEPROTO)
1170 {
1171 us_abortcommand(_("Outline an area first"));
1172 return;
1173 }
1174
1175 /* grid the area */
1176 gridalign(&lx, &ly, 1, np);
1177 gridalign(&hx, &hy, 1, np);
1178
1179 /* all arcs that cross the area but have no endpoint inside need a pin inserted */
1180 for(ai = np->firstarcinst; ai != NOARCINST; ai = nextai)
1181 {
1182 nextai = ai->nextarcinst;
1183
1184 /* if an end is inside, ignore */
1185 if (ai->end[0].xpos > lx && ai->end[0].xpos < hx &&
1186 ai->end[0].ypos > ly && ai->end[0].ypos < hy) continue;
1187 if (ai->end[1].xpos > lx && ai->end[1].xpos < hx &&
1188 ai->end[1].ypos > ly && ai->end[1].ypos < hy) continue;
1189
1190 /* if length is zero, ignore */
1191 if (ai->end[0].xpos == ai->end[1].xpos &&
1192 ai->end[0].ypos == ai->end[1].ypos) continue;
1193
1194 /* if the arc doesn't intersect the area, ignore */
1195 if (!us_linethroughbox(ai->end[0].xpos, ai->end[0].ypos,
1196 ai->end[1].xpos, ai->end[1].ypos, lx, hx, ly, hy, &ix, &iy))
1197 continue;
1198
1199 /* create a pin at this point */
1200 pin = getpinproto(ai->proto);
1201 defaultnodesize(pin, &sx, &sy);
1202 clx = ix - sx/2;
1203 chx = clx + sx;
1204 cly = iy - sy/2;
1205 chy = cly + sy;
1206 ni = newnodeinst(pin, clx, chx, cly, chy, 0, 0, np);
1207 if (ni == NONODEINST) continue;
1208 endobjectchange((INTBIG)ni, VNODEINST);
1209 ai1 = newarcinst(ai->proto, ai->width, ai->userbits, ai->end[0].nodeinst,
1210 ai->end[0].portarcinst->proto, ai->end[0].xpos, ai->end[0].ypos,
1211 ni, ni->proto->firstportproto, ix, iy, np);
1212 if (ai1 == NOARCINST) continue;
1213 (void)copyvars((INTBIG)ai, VARCINST, (INTBIG)ai1, VARCINST, FALSE);
1214 endobjectchange((INTBIG)ai1, VARCINST);
1215 ai2 = newarcinst(ai->proto, ai->width, ai->userbits, ai->end[1].nodeinst,
1216 ai->end[1].portarcinst->proto, ai->end[1].xpos, ai->end[1].ypos,
1217 ni, ni->proto->firstportproto, ix, iy, np);
1218 if (ai2 == NOARCINST) continue;
1219 (void)copyvars((INTBIG)ai, VARCINST, (INTBIG)ai2, VARCINST, FALSE);
1220 endobjectchange((INTBIG)ai1, VARCINST);
1221 startobjectchange((INTBIG)ai, VARCINST);
1222 (void)killarcinst(ai);
1223 }
1224
1225 /* mark all nodes as "able to be deleted" */
1226 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1227 ni->temp1 = 0;
1228
1229 /* truncate all arcs that cross the area */
1230 for(ai = np->firstarcinst; ai != NOARCINST; ai = nextai)
1231 {
1232 nextai = ai->nextarcinst;
1233
1234 /* get the extent of this arc */
1235 i = ai->width - arcwidthoffset(ai);
1236 if (curvedarcoutline(ai, poly, CLOSED, i))
1237 makearcpoly(ai->length, i, ai, poly, FILLED);
1238 getbbox(poly, &lx1, &hx1, &ly1, &hy1);
1239
1240 /* if the arc is outside of the area, ignore it */
1241 if (lx1 >= hx || hx1 <= lx || ly1 >= hy || hy1 <= ly) continue;
1242
1243 /* if the arc is inside of the area, delete it */
1244 if (lx1 >= lx && hx1 <= hx && ly1 >= ly && hy1 <= hy)
1245 {
1246 /* delete the arc */
1247 startobjectchange((INTBIG)ai, VARCINST);
1248 if (killarcinst(ai)) ttyputerr(_("Error killing arc"));
1249 continue;
1250 }
1251
1252 /* partially inside the area: truncate the arc */
1253 if (ai->end[0].xpos > lx && ai->end[0].xpos < hx &&
1254 ai->end[0].ypos > ly && ai->end[0].ypos < hy) in0 = 1; else
1255 in0 = 0;
1256 if (ai->end[1].xpos > lx && ai->end[1].xpos < hx &&
1257 ai->end[1].ypos > ly && ai->end[1].ypos < hy) in1 = 1; else
1258 in1 = 0;
1259 if (in0 == in1) continue;
1260 if (in0 == 0) keepend = 0; else keepend = 1;
1261 killend = 1 - keepend;
1262 keepx = ai->end[keepend].xpos; keepy = ai->end[keepend].ypos;
1263 killx = ai->end[killend].xpos; killy = ai->end[killend].ypos;
1264 clx = lx - i/2;
1265 chx = hx + i/2;
1266 cly = ly - i/2;
1267 chy = hy + i/2;
1268 ang = figureangle(keepx, keepy, killx, killy);
1269
1270 /* see which side has the intersection point */
1271 didin = intersect(keepx, keepy, ang, lx, hy, 0, &ix, &iy);
1272 if (didin >= 0)
1273 {
1274 if (ix >= lx && ix <= hx &&
1275 ix >= mini(keepx, killx) &&
1276 ix <= maxi(keepx, killx) &&
1277 iy >= mini(keepy, killy) &&
1278 iy <= maxi(keepy, killy))
1279 {
1280 /* intersects the top edge */
1281 (void)intersect(keepx, keepy, ang, clx, chy, 0, &ix, &iy);
1282 us_choparc(ai, keepend, ix, iy);
1283 continue;
1284 }
1285 }
1286 didin = intersect(keepx, keepy, ang, lx, ly, 0, &ix, &iy);
1287 if (didin >= 0)
1288 {
1289 if (ix >= lx && ix <= hx &&
1290 ix >= mini(keepx, killx) &&
1291 ix <= maxi(keepx, killx) &&
1292 iy >= mini(keepy, killy) &&
1293 iy <= maxi(keepy, killy))
1294 {
1295 /* intersects the bottom edge */
1296 (void)intersect(keepx, keepy, ang, clx, cly, 0, &ix, &iy);
1297 us_choparc(ai, keepend, ix, iy);
1298 continue;
1299 }
1300 }
1301 didin = intersect(keepx, keepy, ang, lx, ly, 900, &ix, &iy);
1302 if (didin >= 0)
1303 {
1304 if (iy >= ly && iy <= hy &&
1305 ix >= mini(keepx, killx) &&
1306 ix <= maxi(keepx, killx) &&
1307 iy >= mini(keepy, killy) &&
1308 iy <= maxi(keepy, killy))
1309 {
1310 /* intersects the bottom edge */
1311 (void)intersect(keepx, keepy, ang, clx, cly, 900, &ix, &iy);
1312 us_choparc(ai, keepend, ix, iy);
1313 continue;
1314 }
1315 }
1316 didin = intersect(keepx, keepy, ang, hx, ly, 900, &ix, &iy);
1317 if (didin >= 0)
1318 {
1319 if (iy >= ly && iy <= hy &&
1320 ix >= mini(keepx, killx) &&
1321 ix <= maxi(keepx, killx) &&
1322 iy >= mini(keepy, killy) &&
1323 iy <= maxi(keepy, killy))
1324 {
1325 /* intersects the bottom edge */
1326 (void)intersect(keepx, keepy, ang, chx, cly, 900, &ix, &iy);
1327 us_choparc(ai, keepend, ix, iy);
1328 continue;
1329 }
1330 }
1331 }
1332
1333 /* now remove nodes in the area */
1334 for(ni = np->firstnodeinst; ni != NONODEINST; ni = nextni)
1335 {
1336 nextni = ni->nextnodeinst;
1337 if (ni->temp1 != 0) continue;
1338
1339 /* if the node is outside of the area, ignore it */
1340 cx = (ni->lowx + ni->highx) / 2;
1341 cy = (ni->lowy + ni->highy) / 2;
1342 if (cx > hx || cx < lx || cy > hy || cy < ly) continue;
1343
1344 /* if it cannot be modified, stop */
1345 if (us_cantedit(np, ni, TRUE)) continue;
1346
1347 /* delete the node */
1348 while(ni->firstportexpinst != NOPORTEXPINST)
1349 (void)killportproto(np, ni->firstportexpinst->exportproto);
1350 startobjectchange((INTBIG)ni, VNODEINST);
1351 if (killnodeinst(ni)) ttyputerr(_("Error killing node"));
1352 }
1353 }
1354
us_choparc(ARCINST * ai,INTBIG keepend,INTBIG ix,INTBIG iy)1355 void us_choparc(ARCINST *ai, INTBIG keepend, INTBIG ix, INTBIG iy)
1356 {
1357 REGISTER NODEPROTO *pin;
1358 REGISTER NODEINST *ni, *oldni;
1359 REGISTER ARCINST *newai;
1360 REGISTER PORTPROTO *oldpp;
1361 REGISTER ARCPROTO *ap;
1362 REGISTER INTBIG size, lx, hx, ly, hy, wid, bits, oldx, oldy;
1363
1364 ap = ai->proto;
1365 pin = getpinproto(ap);
1366 if (pin == NONODEPROTO) return;
1367 wid = ai->width;
1368 bits = ai->userbits;
1369 if (pin->tech == sch_tech)
1370 {
1371 size = pin->highx - pin->lowx;
1372 } else
1373 {
1374 size = wid - arcwidthoffset(ai);
1375 }
1376 lx = ix - size/2; hx = lx + size;
1377 ly = iy - size/2; hy = ly + size;
1378 ni = newnodeinst(pin, lx, hx, ly, hy, 0, 0, ai->parent);
1379 if (ni == NONODEINST) return;
1380 ni->temp1 = 1;
1381 endobjectchange((INTBIG)ni, VNODEINST);
1382 oldni = ai->end[keepend].nodeinst;
1383 oldpp = ai->end[keepend].portarcinst->proto;
1384 oldx = ai->end[keepend].xpos;
1385 oldy = ai->end[keepend].ypos;
1386 newai = newarcinst(ap, wid, bits, oldni, oldpp, oldx, oldy, ni, ni->proto->firstportproto,
1387 ix, iy, ai->parent);
1388 if (newai == NOARCINST) return;
1389 (void)copyvars((INTBIG)ai, VARCINST, (INTBIG)newai, VARCINST, FALSE);
1390 endobjectchange((INTBIG)newai, VARCINST);
1391 startobjectchange((INTBIG)ai, VARCINST);
1392 if (killarcinst(ai))
1393 ttyputerr(_("Error killing arc"));
1394 }
1395
1396 /*
1397 * routine to clean-up cell "np" as follows:
1398 * remove stranded pins
1399 * collapse redundant arcs
1400 * highlight zero-size nodes
1401 * removes duplicate arcs
1402 * highlight oversize pins that allow arcs to connect without touching
1403 * move unattached and invisible pins with text in a different location
1404 * resize oversized pins that don't have oversized arcs on them
1405 * Returns TRUE if changes are made.
1406 */
us_cleanupcell(NODEPROTO * np,BOOLEAN justthis)1407 BOOLEAN us_cleanupcell(NODEPROTO *np, BOOLEAN justthis)
1408 {
1409 REGISTER NODEINST *ni, *nextni;
1410 REGISTER PORTARCINST *pi, *opi;
1411 REGISTER INTBIG i, pinsremoved, pinsscaled, zerosize, negsize, sx, sy, oversizearc,
1412 oversize, oversizex, oversizey, oversizepin, textmoved, dlx, dhx, dly, dhy,
1413 otherend, ootherend, duparcs;
1414 ARCINST *ai, *oai, **thearcs;
1415 BOOLEAN spoke;
1416 REGISTER VARIABLE *var;
1417 INTBIG lx, hx, ly, hy, x, y;
1418 HIGHLIGHT newhigh;
1419 static POLYGON *poly = NOPOLYGON, *opoly = NOPOLYGON;
1420 REGISTER void *infstr;
1421
1422 /* get polygon */
1423 (void)needstaticpolygon(&poly, 4, us_tool->cluster);
1424 (void)needstaticpolygon(&opoly, 4, us_tool->cluster);
1425
1426 /* look for unused pins that can be deleted */
1427 pinsremoved = 0;
1428 for(ni = np->firstnodeinst; ni != NONODEINST; ni = nextni)
1429 {
1430 nextni = ni->nextnodeinst;
1431 if ((ni->proto->userbits&NFUNCTION) >> NFUNCTIONSH != NPPIN) continue;
1432
1433 /* if the pin is an export, save it */
1434 if (ni->firstportexpinst != NOPORTEXPINST) continue;
1435
1436 /* if the pin is not connected or displayed, delete it */
1437 if (ni->firstportarcinst == NOPORTARCINST)
1438 {
1439 /* see if the pin has displayable variables on it */
1440 for(i=0; i<ni->numvar; i++)
1441 {
1442 var = &ni->firstvar[i];
1443 if ((var->type&VDISPLAY) != 0) break;
1444 }
1445 if (i >= ni->numvar)
1446 {
1447 /* disallow erasing if lock is on */
1448 if (us_cantedit(np, ni, TRUE)) continue;
1449
1450 /* no displayable variables: delete it */
1451 startobjectchange((INTBIG)ni, VNODEINST);
1452 if (killnodeinst(ni)) ttyputerr(_("Error from killnodeinst"));
1453 pinsremoved++;
1454 }
1455 continue;
1456 }
1457
1458 /* if the pin is connected to two arcs along the same slope, delete it */
1459 if (isinlinepin(ni, &thearcs))
1460 {
1461 /* remove the pin and reconnect the arcs */
1462 if (us_erasepassthru(ni, FALSE, &ai) >= 0)
1463 pinsremoved++;
1464 }
1465 }
1466
1467 /* look for oversized pins that can be reduced in size */
1468 pinsscaled = 0;
1469 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1470 {
1471 if ((ni->proto->userbits&NFUNCTION) >> NFUNCTIONSH != NPPIN) continue;
1472
1473 /* if the pin is standard size, leave it alone */
1474 oversizex = (ni->highx - ni->lowx) - (ni->proto->highx - ni->proto->lowx);
1475 if (oversizex < 0) oversizex = 0;
1476 oversizey = (ni->highy - ni->lowy) - (ni->proto->highy - ni->proto->lowy);
1477 if (oversizey < 0) oversizey = 0;
1478 if (oversizex == 0 && oversizey == 0) continue;
1479
1480 /* all arcs must connect in the pin center */
1481 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1482 {
1483 ai = pi->conarcinst;
1484 if (ai->end[0].nodeinst == ni)
1485 {
1486 if (ai->end[0].xpos != (ni->lowx+ni->highx)/2) break;
1487 if (ai->end[0].ypos != (ni->lowy+ni->highy)/2) break;
1488 }
1489 if (ai->end[1].nodeinst == ni)
1490 {
1491 if (ai->end[1].xpos != (ni->lowx+ni->highx)/2) break;
1492 if (ai->end[1].ypos != (ni->lowy+ni->highy)/2) break;
1493 }
1494 }
1495 if (pi != NOPORTARCINST) continue;
1496
1497 /* look for arcs that are oversized */
1498 oversizearc = 0;
1499 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1500 {
1501 ai = pi->conarcinst;
1502 oversize = ai->width - ai->proto->nominalwidth;
1503 if (oversize < 0) oversize = 0;
1504 if (oversize > oversizearc) oversizearc = oversize;
1505 }
1506
1507 /* if an arc covers the pin, leave the pin */
1508 if (oversizearc >= oversizex && oversizearc >= oversizey) continue;
1509
1510 dlx = dhx = dly = dhy = 0;
1511 if (oversizearc < oversizex)
1512 {
1513 dlx = (oversizex - oversizearc) / 2;
1514 dhx = -(oversizex - oversizearc) / 2;
1515 }
1516 if (oversizearc < oversizey)
1517 {
1518 dly = (oversizey - oversizearc) / 2;
1519 dhy = -(oversizey - oversizearc) / 2;
1520 }
1521 startobjectchange((INTBIG)ni, VNODEINST);
1522 modifynodeinst(ni, dlx, dly, dhx, dhy, 0, 0);
1523 endobjectchange((INTBIG)ni, VNODEINST);
1524
1525 pinsscaled++;
1526 }
1527
1528 /* look for pins that are invisible and have text in different location */
1529 textmoved = 0;
1530 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1531 {
1532 if (invisiblepinwithoffsettext(ni, &x, &y, TRUE))
1533 textmoved++;
1534 }
1535
1536 /* highlight oversize pins that allow arcs to connect without touching */
1537 oversizepin = 0;
1538 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1539 {
1540 if ((ni->proto->userbits&NFUNCTION) >> NFUNCTIONSH != NPPIN) continue;
1541
1542 /* make sure all arcs touch each other */
1543 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1544 {
1545 ai = pi->conarcinst;
1546 i = ai->width - arcwidthoffset(ai);
1547 if (curvedarcoutline(ai, poly, CLOSED, i))
1548 makearcpoly(ai->length, i, ai, poly, FILLED);
1549 for(opi = pi->nextportarcinst; opi != NOPORTARCINST; opi = opi->nextportarcinst)
1550 {
1551 oai = opi->conarcinst;
1552 i = ai->width - arcwidthoffset(oai);
1553 if (curvedarcoutline(oai, opoly, CLOSED, i))
1554 makearcpoly(oai->length, i, oai, opoly, FILLED);
1555 if (polyseparation(poly, opoly) <= 0) continue;
1556 if (justthis)
1557 {
1558 newhigh.status = HIGHFROM;
1559 newhigh.cell = np;
1560 newhigh.fromgeom = ni->geom;
1561 newhigh.fromport = NOPORTPROTO;
1562 newhigh.frompoint = 0;
1563 us_addhighlight(&newhigh);
1564 }
1565 oversizepin++;
1566 }
1567 }
1568 }
1569
1570 /* look for duplicate arcs */
1571 duparcs = 0;
1572 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1573 {
1574 for(;;)
1575 {
1576 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1577 {
1578 ai = pi->conarcinst;
1579 if (ai->end[0].portarcinst == pi) otherend = 1; else otherend = 0;
1580 for(opi = pi->nextportarcinst; opi != NOPORTARCINST; opi = opi->nextportarcinst)
1581 {
1582 if (pi->proto != opi->proto) continue;
1583 oai = opi->conarcinst;
1584 if (ai->proto != oai->proto) continue;
1585 if (oai->end[0].portarcinst == opi) ootherend = 1; else ootherend = 0;
1586 if (ai->end[otherend].nodeinst != oai->end[ootherend].nodeinst) continue;
1587 if (ai->end[otherend].portarcinst->proto != oai->end[ootherend].portarcinst->proto)
1588 continue;
1589
1590 /* this arc is a duplicate */
1591 startobjectchange((INTBIG)oai, VARCINST);
1592 killarcinst(oai);
1593 duparcs++;
1594 break;
1595 }
1596 if (opi != NOPORTARCINST) break;
1597 }
1598 if (pi == NOPORTARCINST) break;
1599 }
1600 }
1601
1602 /* now highlight negative or zero-size nodes */
1603 zerosize = negsize = 0;
1604 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1605 {
1606 if (ni->proto == gen_cellcenterprim ||
1607 ni->proto == gen_invispinprim ||
1608 ni->proto == gen_essentialprim) continue;
1609 nodesizeoffset(ni, &lx, &ly, &hx, &hy);
1610 sx = ni->highx - ni->lowx - lx - hx;
1611 sy = ni->highy - ni->lowy - ly - hy;
1612 if (sx > 0 && sy > 0) continue;
1613 if (justthis)
1614 {
1615 newhigh.status = HIGHFROM;
1616 newhigh.cell = np;
1617 newhigh.fromgeom = ni->geom;
1618 newhigh.fromport = NOPORTPROTO;
1619 newhigh.frompoint = 0;
1620 us_addhighlight(&newhigh);
1621 }
1622 if (sx < 0 || sy < 0) negsize++; else
1623 zerosize++;
1624 }
1625
1626 if (pinsremoved == 0 && pinsscaled == 0 && zerosize == 0 &&
1627 negsize == 0 && textmoved == 0 && oversizepin == 0 && duparcs == 0)
1628 {
1629 if (justthis) ttyputmsg(_("Nothing to clean"));
1630 return(FALSE);
1631 }
1632
1633 /* report what was cleaned */
1634 infstr = initinfstr();
1635 if (!justthis)
1636 {
1637 formatinfstr(infstr, _("Cell %s:"), describenodeproto(np));
1638 }
1639 spoke = FALSE;
1640 if (pinsremoved != 0)
1641 {
1642 formatinfstr(infstr, _("Removed %ld %s"), pinsremoved, makeplural(_("pin"), pinsremoved));
1643 spoke = TRUE;
1644 }
1645 if (duparcs != 0)
1646 {
1647 if (spoke) addstringtoinfstr(infstr, x_("; "));
1648 formatinfstr(infstr, _("Removed %ld duplicate %s"), duparcs, makeplural(_("arc"), duparcs));
1649 spoke = TRUE;
1650 }
1651 if (pinsscaled != 0)
1652 {
1653 if (spoke) addstringtoinfstr(infstr, x_("; "));
1654 formatinfstr(infstr, _("Shrunk %ld %s"), pinsscaled, makeplural(_("pin"), pinsscaled));
1655 spoke = TRUE;
1656 }
1657 if (zerosize != 0)
1658 {
1659 if (spoke) addstringtoinfstr(infstr, x_("; "));
1660 if (justthis)
1661 {
1662 formatinfstr(infstr, _("Highlighted %ld zero-size %s"), zerosize,
1663 makeplural(_("node"), zerosize));
1664 } else
1665 {
1666 formatinfstr(infstr, _("Found %ld zero-size %s"), zerosize,
1667 makeplural(_("node"), zerosize));
1668 }
1669 spoke = TRUE;
1670 }
1671 if (negsize != 0)
1672 {
1673 if (spoke) addstringtoinfstr(infstr, x_("; "));
1674 if (justthis)
1675 {
1676 formatinfstr(infstr, _("Highlighted %ld negative-size %s"), negsize,
1677 makeplural(_("node"), negsize));
1678 } else
1679 {
1680 formatinfstr(infstr, _("Found %ld negative-size %s"), negsize,
1681 makeplural(_("node"), negsize));
1682 }
1683 spoke = TRUE;
1684 }
1685 if (oversizepin != 0)
1686 {
1687 if (spoke) addstringtoinfstr(infstr, x_("; "));
1688 if (justthis)
1689 {
1690 formatinfstr(infstr, _("Highlighted %ld oversize %s with arcs that don't touch"),
1691 oversizepin, makeplural(_("pin"), oversizepin));
1692 } else
1693 {
1694 formatinfstr(infstr, _("Found %ld oversize %s with arcs that don't touch"),
1695 oversizepin, makeplural(_("pin"), oversizepin));
1696 }
1697 spoke = TRUE;
1698 }
1699 if (textmoved != 0)
1700 {
1701 if (spoke) addstringtoinfstr(infstr, x_("; "));
1702 formatinfstr(infstr, _("Moved text on %ld %s with offset text"), textmoved,
1703 makeplural(_("pin"), textmoved));
1704 }
1705 ttyputmsg(x_("%s"), returninfstr(infstr));
1706 return(TRUE);
1707 }
1708
1709 /*
1710 * Routine to skeletonize cell "np" and create a new one called "newname"
1711 * in library "newlib". Proceeds quietly if "quiet" is true. Returns the
1712 * skeleton cell (NONODEPROTO on error).
1713 */
us_skeletonize(NODEPROTO * np,CHAR * newname,LIBRARY * newlib,BOOLEAN quiet)1714 NODEPROTO *us_skeletonize(NODEPROTO *np, CHAR *newname, LIBRARY *newlib, BOOLEAN quiet)
1715 {
1716 REGISTER NODEPROTO *onp;
1717 REGISTER NODEINST *ni, *newni;
1718 REGISTER PORTPROTO *pp, *opp, *rpp, *npp;
1719 REGISTER ARCINST *ai;
1720 REGISTER INTBIG lowx, highx, lowy, highy, xc, yc, i;
1721 INTBIG newx, newy, px, py, opx, opy;
1722 REGISTER INTBIG newang, newtran;
1723 XARRAY trans, localtrans, ntrans;
1724 REGISTER void *infstr;
1725
1726 /* cannot skeletonize text-only views */
1727 if ((np->cellview->viewstate&TEXTVIEW) != 0)
1728 {
1729 us_abortcommand(_("Cannot skeletonize textual views: only layout"));
1730 return(NONODEPROTO);
1731 }
1732
1733 /* warn if skeletonizing nonlayout views */
1734 if (np->cellview != el_unknownview && np->cellview != el_layoutview)
1735 ttyputmsg(_("Warning: skeletonization only makes sense for layout cells, not %s"),
1736 np->cellview->viewname);
1737
1738 onp = np;
1739 np = us_newnodeproto(newname, newlib);
1740 if (np == NONODEPROTO)
1741 {
1742 us_abortcommand(_("Cannot create %s"), newname);
1743 return(NONODEPROTO);
1744 }
1745
1746 /* place all exports in the new cell */
1747 lowx = highx = (onp->lowx + onp->highx) / 2;
1748 lowy = highy = (onp->lowy + onp->highy) / 2;
1749 for(pp = onp->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
1750 {
1751 /* make a transformation matrix for the node that is an export */
1752 pp->temp1 = 0;
1753 ni = pp->subnodeinst;
1754 rpp = pp->subportproto;
1755 newang = ni->rotation;
1756 newtran = ni->transpose;
1757 makerot(ni, trans);
1758 while (ni->proto->primindex == 0)
1759 {
1760 maketrans(ni, localtrans);
1761 transmult(localtrans, trans, ntrans);
1762 ni = rpp->subnodeinst;
1763 rpp = rpp->subportproto;
1764 if (ni->transpose == 0) newang = ni->rotation + newang; else
1765 newang = ni->rotation + 3600 - newang;
1766 newtran = (newtran + ni->transpose) & 1;
1767 makerot(ni, localtrans);
1768 transmult(localtrans, ntrans, trans);
1769 }
1770
1771 /* create this node */
1772 xc = (ni->lowx + ni->highx) / 2; yc = (ni->lowy + ni->highy) / 2;
1773 xform(xc, yc, &newx, &newy, trans);
1774 newx -= (ni->highx - ni->lowx) / 2;
1775 newy -= (ni->highy - ni->lowy) / 2;
1776 newang = newang % 3600; if (newang < 0) newang += 3600;
1777 newni = newnodeinst(ni->proto, newx, newx+ni->highx-ni->lowx,
1778 newy, newy+ni->highy-ni->lowy, newtran, newang, np);
1779 if (newni == NONODEINST)
1780 {
1781 us_abortcommand(_("Cannot create node in this cell"));
1782 return(NONODEPROTO);
1783 }
1784 endobjectchange((INTBIG)newni, VNODEINST);
1785 lowx = mini(lowx, newx);
1786 highx = maxi(highx, newx+ni->highx-ni->lowx);
1787 lowy = mini(lowy, newy);
1788 highy = maxi(highy, newy+ni->highy-ni->lowy);
1789
1790 /* export the port from the node */
1791 npp = newportproto(np, newni, rpp, pp->protoname);
1792 if (npp == NOPORTPROTO)
1793 {
1794 us_abortcommand(_("Could not create port %s"), pp->protoname);
1795 return(NONODEPROTO);
1796 }
1797 npp->userbits = pp->userbits;
1798 TDCOPY(npp->textdescript, pp->textdescript);
1799 if (copyvars((INTBIG)pp, VPORTPROTO, (INTBIG)npp, VPORTPROTO, FALSE))
1800 return(NONODEPROTO);
1801 pp->temp1 = (INTBIG)newni;
1802 pp->temp2 = (INTBIG)rpp;
1803 }
1804
1805 /* connect electrically-equivalent ports */
1806 for(pp = onp->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
1807 {
1808 for(opp = pp->nextportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
1809 {
1810 if (pp->network != opp->network) continue;
1811 if (pp->temp1 == 0 || opp->temp1 == 0) continue;
1812 portposition((NODEINST *)pp->temp1, (PORTPROTO *)pp->temp2, &px, &py);
1813 portposition((NODEINST *)opp->temp1, (PORTPROTO *)opp->temp2, &opx, &opy);
1814 ai = newarcinst(gen_universalarc, defaultarcwidth(gen_universalarc),
1815 us_makearcuserbits(gen_universalarc),
1816 (NODEINST *)pp->temp1, (PORTPROTO *)pp->temp2, px, py,
1817 (NODEINST *)opp->temp1, (PORTPROTO *)opp->temp2, opx, opy, np);
1818 if (ai == NOARCINST)
1819 {
1820 us_abortcommand(_("Cannot create connecting arc in this cell"));
1821 return(NONODEPROTO);
1822 }
1823 endobjectchange((INTBIG)ai, VARCINST);
1824 }
1825 }
1826
1827 /* copy the cell center and essential-bounds nodes if they exist */
1828 for(ni = onp->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1829 {
1830 if (ni->proto != gen_cellcenterprim && ni->proto != gen_essentialprim) continue;
1831 newni = newnodeinst(ni->proto, ni->lowx, ni->highx,
1832 ni->lowy, ni->highy, ni->transpose, ni->rotation, np);
1833 if (newni == NONODEINST)
1834 {
1835 us_abortcommand(_("Cannot create node in this cell"));
1836 return(NONODEPROTO);
1837 }
1838 newni->userbits |= HARDSELECTN;
1839 if (ni->proto == gen_cellcenterprim) newni->userbits |= NVISIBLEINSIDE;
1840 endobjectchange((INTBIG)newni, VNODEINST);
1841 lowx = mini(lowx, ni->lowx);
1842 highx = maxi(highx, ni->highx);
1843 lowy = mini(lowy, ni->lowy);
1844 highy = maxi(highy, ni->highy);
1845 break;
1846 }
1847
1848 /* make sure cell is the same size */
1849 if (lowx > onp->lowx)
1850 {
1851 i = (onp->highy+onp->lowy)/2 - (gen_invispinprim->highy-gen_invispinprim->lowy)/2;
1852 (void)newnodeinst(gen_invispinprim, onp->lowx, onp->lowx+gen_invispinprim->highx-gen_invispinprim->lowx,
1853 i, i+gen_invispinprim->highy-gen_invispinprim->lowy, 0, 0, np);
1854 }
1855 if (highx < onp->highx)
1856 {
1857 i = (onp->highy+onp->lowy)/2 - (gen_invispinprim->highy-gen_invispinprim->lowy)/2;
1858 (void)newnodeinst(gen_invispinprim, onp->highx-(gen_invispinprim->highx-gen_invispinprim->lowx), onp->highx,
1859 i, i+gen_invispinprim->highy-gen_invispinprim->lowy, 0, 0, np);
1860 }
1861 if (lowy > onp->lowy)
1862 {
1863 i = (onp->highx+onp->lowx)/2 - (gen_invispinprim->highx-gen_invispinprim->lowx)/2;
1864 (void)newnodeinst(gen_invispinprim, i, i+gen_invispinprim->highx-gen_invispinprim->lowx,
1865 onp->lowy, onp->lowy+gen_invispinprim->highy-gen_invispinprim->lowy, 0, 0, np);
1866 }
1867 if (highy < onp->highy)
1868 {
1869 i = (onp->highx+onp->lowx)/2 - (gen_invispinprim->highx-gen_invispinprim->lowx)/2;
1870 (void)newnodeinst(gen_invispinprim, i, i+gen_invispinprim->highx-gen_invispinprim->lowx,
1871 onp->highy-(gen_invispinprim->highy-gen_invispinprim->lowy), onp->highy, 0, 0,np);
1872 }
1873
1874 /* place the actual origin of the cell inside */
1875 infstr = initinfstr();
1876 addstringtoinfstr(infstr, onp->lib->libfile);
1877 addtoinfstr(infstr, ':');
1878 addstringtoinfstr(infstr, nldescribenodeproto(onp));
1879 (void)setval((INTBIG)np, VNODEPROTO, x_("FACET_original_facet"),
1880 (INTBIG)returninfstr(infstr), VSTRING);
1881
1882 if (!quiet)
1883 ttyputmsg(_("Cell %s created with a skeletal representation of %s"),
1884 describenodeproto(np), describenodeproto(onp));
1885 return(np);
1886 }
1887
1888 /*
1889 * routine to generate an icon in library "lib" with name "iconname" from the
1890 * port list in "fpp". The icon cell is called "pt". The icon cell is
1891 * returned (NONODEPROTO on error).
1892 */
us_makeiconcell(PORTPROTO * fpp,CHAR * iconname,CHAR * pt,LIBRARY * lib)1893 NODEPROTO *us_makeiconcell(PORTPROTO *fpp, CHAR *iconname, CHAR *pt,
1894 LIBRARY *lib)
1895 {
1896 REGISTER NODEPROTO *np;
1897 REGISTER NODEINST *bbni, *pinni;
1898 REGISTER PORTPROTO *pp, **pplist;
1899 REGISTER LIBRARY *savelib;
1900 REGISTER INTBIG leftside, rightside, bottomside, topside, count, i, total,
1901 leadlength, leadspacing, xsize, ysize, xpos, ypos, xbbpos, ybbpos, spacing,
1902 style, index, lambda;
1903 REGISTER VARIABLE *var;
1904 extern COMCOMP us_noyesp;
1905 CHAR *result[2];
1906 REGISTER void *infstr;
1907
1908 /* see if the icon already exists and issue a warning if so */
1909 savelib = el_curlib; el_curlib = lib;
1910 np = getnodeproto(pt);
1911 el_curlib = savelib;
1912 if (np != NONODEPROTO)
1913 {
1914 infstr = initinfstr();
1915 formatinfstr(infstr, _("Warning: Icon %s already exists. Create a new version?"), pt);
1916 i = ttygetparam(returninfstr(infstr), &us_noyesp, 1, result);
1917 if (i <= 0 || (result[0][0] != 'y' && result[0][0] != 'Y')) return(NONODEPROTO);
1918 }
1919
1920 /* get icon style controls */
1921 var = getval((INTBIG)us_tool, VTOOL, VINTEGER, x_("USER_icon_style"));
1922 if (var != NOVARIABLE) style = var->addr; else style = ICONSTYLEDEFAULT;
1923 var = getval((INTBIG)us_tool, VTOOL, VINTEGER, x_("USER_icon_lead_length"));
1924 if (var != NOVARIABLE) leadlength = var->addr; else leadlength = ICONLEADDEFLEN;
1925 var = getval((INTBIG)us_tool, VTOOL, VINTEGER, x_("USER_icon_lead_spacing"));
1926 if (var != NOVARIABLE) leadspacing = var->addr; else leadspacing = ICONLEADDEFSEP;
1927
1928 /* make a sorted list of exports */
1929 count = 0;
1930 for(pp = fpp; pp != NOPORTPROTO; pp = pp->nextportproto) count++;
1931 if (count != 0)
1932 {
1933 pplist = (PORTPROTO **)emalloc(count * (sizeof (PORTPROTO *)), el_tempcluster);
1934 if (pplist == 0) return(NONODEPROTO);
1935 count = 0;
1936 for(pp = fpp; pp != NOPORTPROTO; pp = pp->nextportproto) pplist[count++] = pp;
1937
1938 /* sort the list by name */
1939 esort(pplist, count, sizeof (PORTPROTO *), sort_exportnameascending);
1940
1941 /* reverse sort order if requested */
1942 if ((style&ICONSTYLEREVEXPORT) != 0)
1943 {
1944 for(i=0; i<count/2; i++)
1945 {
1946 index = count - i - 1;
1947 pp = pplist[i]; pplist[i] = pplist[index]; pplist[index] = pp;
1948 }
1949 }
1950 }
1951
1952 /* get lambda */
1953 lambda = lib->lambda[sch_tech->techindex];
1954
1955 /* create the new icon cell */
1956 np = newnodeproto(pt, lib);
1957 if (np == NONODEPROTO)
1958 {
1959 us_abortcommand(_("Cannot create icon %s"), pt);
1960 return(NONODEPROTO);
1961 }
1962 np->userbits |= WANTNEXPAND;
1963
1964 /* determine number of inputs and outputs */
1965 leftside = rightside = bottomside = topside = 0;
1966 for(i=0; i<count; i++)
1967 {
1968 pp = pplist[i];
1969 if ((pp->userbits&BODYONLY) != 0) continue;
1970 index = us_iconposition(pp, style);
1971 switch (index)
1972 {
1973 case 0: pp->temp1 = leftside++; break;
1974 case 1: pp->temp1 = rightside++; break;
1975 case 2: pp->temp1 = topside++; break;
1976 case 3: pp->temp1 = bottomside++; break;
1977 }
1978 }
1979
1980 /* determine the size of the "black box" core */
1981 ysize = maxi(maxi(leftside, rightside), 5) * leadspacing * lambda;
1982 xsize = maxi(maxi(topside, bottomside), 3) * leadspacing * lambda;
1983
1984 /* create the "black box" */
1985 if ((style&ICONSTYLEDRAWNOBODY) != 0) bbni = NONODEINST; else
1986 {
1987 bbni = newnodeinst(art_boxprim, 0, xsize, 0, ysize, 0, 0, np);
1988 if (bbni == NONODEINST) return(NONODEPROTO);
1989 (void)setvalkey((INTBIG)bbni, VNODEINST, art_colorkey, RED, VINTEGER);
1990
1991 /* put the original cell name on it */
1992 var = setvalkey((INTBIG)bbni, VNODEINST, sch_functionkey, (INTBIG)iconname, VSTRING|VDISPLAY);
1993 if (var != NOVARIABLE) defaulttextdescript(var->textdescript, bbni->geom);
1994 }
1995
1996 /* create the Cell Center instance */
1997 if ((us_useroptions&CELLCENTERALWAYS) != 0)
1998 {
1999 pinni = newnodeinst(gen_cellcenterprim, 0, 0, 0, 0, 0, 0, np);
2000 if (pinni == NONODEINST) return(NONODEPROTO);
2001 pinni->userbits |= HARDSELECTN|NVISIBLEINSIDE;
2002 }
2003
2004 /* place pins around the Black Box */
2005 total = 0;
2006 for(i=0; i<count; i++)
2007 {
2008 pp = pplist[i];
2009 if ((pp->userbits&BODYONLY) != 0) continue;
2010
2011 /* determine location of the port */
2012 index = us_iconposition(pp, style);
2013 spacing = leadspacing * lambda;
2014 switch (index)
2015 {
2016 case 0: /* left side */
2017 xpos = -leadlength * lambda;
2018 xbbpos = 0;
2019 if (leftside*2 < rightside) spacing = leadspacing * 2 * lambda;
2020 ybbpos = ypos = ysize - ((ysize - (leftside-1)*spacing) / 2 + pp->temp1 * spacing);
2021 break;
2022 case 1: /* right side */
2023 xpos = xsize + leadlength * lambda;
2024 xbbpos = xsize;
2025 if (rightside*2 < leftside) spacing = leadspacing * 2 * lambda;
2026 ybbpos = ypos = ysize - ((ysize - (rightside-1)*spacing) / 2 + pp->temp1 * spacing);
2027 break;
2028 case 2: /* top */
2029 if (topside*2 < bottomside) spacing = leadspacing * 2 * lambda;
2030 xbbpos = xpos = xsize - ((xsize - (topside-1)*spacing) / 2 + pp->temp1 * spacing);
2031 ypos = ysize + leadlength * lambda;
2032 ybbpos = ysize;
2033 break;
2034 case 3: /* bottom */
2035 if (bottomside*2 < topside) spacing = leadspacing * 2 * lambda;
2036 xbbpos = xpos = xsize - ((xsize - (bottomside-1)*spacing) / 2 + pp->temp1 * spacing);
2037 ypos = -leadlength * lambda;
2038 ybbpos = 0;
2039 break;
2040 }
2041
2042 if (us_makeiconexport(pp, style, index, xpos, ypos, xbbpos, ybbpos, np))
2043 total++;
2044 }
2045
2046 /* if no body, leads, or cell center is drawn, and there is only 1 export, add more */
2047 if ((style&ICONSTYLEDRAWNOBODY) != 0 &&
2048 (style&ICONSTYLEDRAWNOLEADS) != 0 &&
2049 (us_useroptions&CELLCENTERALWAYS) == 0 &&
2050 total <= 1)
2051 {
2052 bbni = newnodeinst(gen_invispinprim, 0, xsize, 0, ysize, 0, 0, np);
2053 if (bbni == NONODEINST) return(NONODEPROTO);
2054 }
2055
2056 if (count > 0) efree((CHAR *)pplist);
2057 (*el_curconstraint->solve)(np);
2058 return(np);
2059 }
2060
2061 /*
2062 * Helper routine to create an export in icon "np". The export is from original port "pp",
2063 * is on side "index" (0: left, 1: right, 2: top, 3: bottom), is at (xpos,ypos), and
2064 * connects to the central box at (xbbpos,ybbpos). Returns TRUE if the export is created.
2065 * It uses icon style "style".
2066 */
us_makeiconexport(PORTPROTO * pp,INTBIG style,INTBIG index,INTBIG xpos,INTBIG ypos,INTBIG xbbpos,INTBIG ybbpos,NODEPROTO * np)2067 BOOLEAN us_makeiconexport(PORTPROTO *pp, INTBIG style, INTBIG index,
2068 INTBIG xpos, INTBIG ypos, INTBIG xbbpos, INTBIG ybbpos, NODEPROTO *np)
2069 {
2070 REGISTER NODEPROTO *pintype;
2071 REGISTER NODEINST *pinni, *ni;
2072 REGISTER ARCINST *ai;
2073 REGISTER PORTPROTO *port, *bpp;
2074 REGISTER ARCPROTO *wiretype;
2075 REGISTER INTBIG xoffset, yoffset, pinsizex, pinsizey, lambda, wid, hei;
2076 UINTBIG descript[TEXTDESCRIPTSIZE];
2077
2078 lambda = el_curlib->lambda[sch_tech->techindex];
2079
2080 /* determine type of pin and lead */
2081 switch ((style&ICONSTYLETECH) >> ICONSTYLETECHSH)
2082 {
2083 case 0: /* generic */
2084 pintype = gen_invispinprim;
2085 pinsizex = pinsizey = 0;
2086 break;
2087 case 1: /* schematic */
2088 pintype = sch_buspinprim;
2089 pinsizex = pintype->highx - pintype->lowx;
2090 pinsizey = pintype->highy - pintype->lowy;
2091 break;
2092 }
2093 wiretype = sch_wirearc;
2094 if (pp->subnodeinst != NONODEINST)
2095 {
2096 bpp = pp;
2097 while (bpp->subnodeinst->proto->primindex == 0) bpp = bpp->subportproto;
2098 if (bpp->subnodeinst->proto == sch_buspinprim)
2099 wiretype = sch_busarc;
2100 }
2101
2102 /* if the export is on the body (no leads) then move it in */
2103 if ((style&ICONSTYLEDRAWNOLEADS) != 0)
2104 {
2105 xpos = xbbpos; ypos = ybbpos;
2106 style &= ~ICONSTYLEPORTLOC;
2107 }
2108
2109 /* make the pin with the port */
2110 pinni = newnodeinst(pintype, xpos-pinsizex/2, xpos+pinsizex/2,
2111 ypos-pinsizey/2, ypos+pinsizey/2, 0, 0, np);
2112 if (pinni == NONODEINST) return(FALSE);
2113
2114 /* export the port that should be on this pin */
2115 port = newportproto(np, pinni, pintype->firstportproto, pp->protoname);
2116 if (port != NOPORTPROTO)
2117 {
2118 TDCOPY(descript, port->textdescript);
2119 switch ((style&ICONSTYLEPORTSTYLE) >> ICONSTYLEPORTSTYLESH)
2120 {
2121 case 0: /* Centered */
2122 TDSETPOS(descript, VTPOSCENT);
2123 break;
2124 case 1: /* Inward */
2125 switch (index)
2126 {
2127 case 0: TDSETPOS(descript, VTPOSRIGHT); break; /* left */
2128 case 1: TDSETPOS(descript, VTPOSLEFT); break; /* right */
2129 case 2: TDSETPOS(descript, VTPOSDOWN); break; /* top */
2130 case 3: TDSETPOS(descript, VTPOSUP); break; /* bottom */
2131 }
2132 break;
2133 case 2: /* Outward */
2134 switch (index)
2135 {
2136 case 0: TDSETPOS(descript, VTPOSLEFT); break; /* left */
2137 case 1: TDSETPOS(descript, VTPOSRIGHT); break; /* right */
2138 case 2: TDSETPOS(descript, VTPOSUP); break; /* top */
2139 case 3: TDSETPOS(descript, VTPOSDOWN); break; /* bottom */
2140 }
2141 break;
2142 }
2143 switch ((style&ICONSTYLEPORTLOC) >> ICONSTYLEPORTLOCSH)
2144 {
2145 case 0: /* port on body */
2146 xoffset = xbbpos - xpos; yoffset = ybbpos - ypos;
2147 break;
2148 case 1: /* port on lead end */
2149 xoffset = yoffset = 0;
2150 break;
2151 case 2: /* port on lead middle */
2152 xoffset = (xpos+xbbpos) / 2 - xpos;
2153 yoffset = (ypos+ybbpos) / 2 - ypos;
2154 break;
2155 }
2156 TDSETOFF(descript, xoffset * 4 / lambda, yoffset * 4 / lambda);
2157 TDCOPY(port->textdescript, descript);
2158 port->userbits = (port->userbits & ~(STATEBITS|PORTDRAWN)) |
2159 (pp->userbits & (STATEBITS|PORTDRAWN));
2160 if (copyvars((INTBIG)pp, VPORTPROTO, (INTBIG)port, VPORTPROTO, FALSE))
2161 return(TRUE);
2162 }
2163 endobjectchange((INTBIG)pinni, VNODEINST);
2164
2165 /* add lead if requested */
2166 if ((style&ICONSTYLEDRAWNOLEADS) == 0)
2167 {
2168 pintype = getpinproto(wiretype);
2169 wid = pintype->highx - pintype->lowx;
2170 hei = pintype->highy - pintype->lowy;
2171 ni = newnodeinst(pintype, xbbpos-wid/2, xbbpos+wid/2, ybbpos-hei/2, ybbpos+hei/2, 0, 0, np);
2172 if (ni != NONODEINST)
2173 {
2174 endobjectchange((INTBIG)ni, VNODEINST);
2175 ai = newarcinst(wiretype, defaultarcwidth(wiretype), us_makearcuserbits(wiretype),
2176 pinni, pinni->proto->firstportproto, xpos, ypos, ni, pintype->firstportproto,
2177 xbbpos, ybbpos, np);
2178 if (ai != NOARCINST) endobjectchange((INTBIG)ai, VARCINST);
2179 }
2180 }
2181 return(TRUE);
2182 }
2183
2184 /*
2185 * Routine to determine the side of the icon that port "pp" belongs on, given that
2186 * the icon style is "style".
2187 */
us_iconposition(PORTPROTO * pp,INTBIG style)2188 INTBIG us_iconposition(PORTPROTO *pp, INTBIG style)
2189 {
2190 REGISTER INTBIG index;
2191 REGISTER UINTBIG character;
2192
2193 character = pp->userbits & STATEBITS;
2194
2195 /* special detection for power and ground ports */
2196 if (portispower(pp)) character = PWRPORT;
2197 if (portisground(pp)) character = GNDPORT;
2198
2199 /* see which side this type of port sits on */
2200 switch (character)
2201 {
2202 case INPORT: index = (style & ICONSTYLESIDEIN) >> ICONSTYLESIDEINSH; break;
2203 case OUTPORT: index = (style & ICONSTYLESIDEOUT) >> ICONSTYLESIDEOUTSH; break;
2204 case BIDIRPORT: index = (style & ICONSTYLESIDEBIDIR) >> ICONSTYLESIDEBIDIRSH; break;
2205 case PWRPORT: index = (style & ICONSTYLESIDEPOWER) >> ICONSTYLESIDEPOWERSH; break;
2206 case GNDPORT: index = (style & ICONSTYLESIDEGROUND) >> ICONSTYLESIDEGROUNDSH; break;
2207 case CLKPORT:
2208 case C1PORT:
2209 case C2PORT:
2210 case C3PORT:
2211 case C4PORT:
2212 case C5PORT:
2213 case C6PORT:
2214 index = (style & ICONSTYLESIDECLOCK) >> ICONSTYLESIDECLOCKSH;
2215 break;
2216 default:
2217 index = (style & ICONSTYLESIDEIN) >> ICONSTYLESIDEINSH;
2218 break;
2219 }
2220 return(index);
2221 }
2222
2223 /*
2224 * Routine to return the name of the technology that is used in cell "np".
2225 * Distinction is made between analog and digital schematics.
2226 */
us_techname(NODEPROTO * np)2227 CHAR *us_techname(NODEPROTO *np)
2228 {
2229 REGISTER TECHNOLOGY *tech;
2230 REGISTER NODEINST *ni;
2231 REGISTER CHAR *techname;
2232
2233 if ((np->userbits&TECEDITCELL) != 0) return(x_("TECHEDIT"));
2234 tech = np->tech;
2235 if (tech == NOTECHNOLOGY) return(x_(""));
2236 techname = tech->techname;
2237 if (tech == sch_tech)
2238 {
2239 /* see if it is analog or digital */
2240 techname = x_("schematic, analog");
2241 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
2242 {
2243 if (ni->proto == sch_bufprim || ni->proto == sch_andprim ||
2244 ni->proto == sch_orprim || ni->proto == sch_xorprim ||
2245 ni->proto == sch_ffprim || ni->proto == sch_muxprim)
2246 return(x_("schematic, digital"));
2247 }
2248 }
2249 return(techname);
2250 }
2251
2252 /*
2253 * Routine to delete cell "np". Validity checks are assumed to be made (i.e. the
2254 * cell is not used and is not locked).
2255 */
us_dokillcell(NODEPROTO * np)2256 void us_dokillcell(NODEPROTO *np)
2257 {
2258 REGISTER NODEPROTO *curcell, *prevversion;
2259 REGISTER NODEINST *ni;
2260 REGISTER BOOLEAN iscurrent, killresult;
2261 REGISTER WINDOWPART *w, *nextw, *neww;
2262
2263 /* delete random references to this cell */
2264 curcell = getcurcell();
2265 if (np == curcell)
2266 {
2267 (void)setval((INTBIG)el_curlib, VLIBRARY, x_("curnodeproto"), (INTBIG)NONODEPROTO, VNODEPROTO);
2268 us_clearhighlightcount();
2269 }
2270
2271 /* close windows that reference this cell */
2272 for(w = el_topwindowpart; w != NOWINDOWPART; w = nextw)
2273 {
2274 nextw = w->nextwindowpart;
2275 if (w->curnodeproto != np) continue;
2276 if (w == el_curwindowpart) iscurrent = TRUE; else iscurrent = FALSE;
2277 if (iscurrent)
2278 (void)setvalkey((INTBIG)us_tool, VTOOL, us_current_window_key, (INTBIG)NOWINDOWPART,
2279 VWINDOWPART|VDONTSAVE);
2280 startobjectchange((INTBIG)us_tool, VTOOL);
2281 neww = newwindowpart(w->location, w);
2282 if (neww == NOWINDOWPART) return;
2283
2284 /* adjust the new window to account for borders in the old one */
2285 if ((w->state&WINDOWTYPE) != DISPWINDOW)
2286 {
2287 neww->usehx -= DISPLAYSLIDERSIZE;
2288 neww->usely += DISPLAYSLIDERSIZE;
2289 }
2290 if ((w->state&WINDOWTYPE) == WAVEFORMWINDOW)
2291 {
2292 neww->uselx -= DISPLAYSLIDERSIZE;
2293 neww->usely -= DISPLAYSLIDERSIZE;
2294 }
2295 if ((w->state&WINDOWMODE) != 0)
2296 {
2297 neww->uselx -= WINDOWMODEBORDERSIZE; neww->usehx += WINDOWMODEBORDERSIZE;
2298 neww->usely -= WINDOWMODEBORDERSIZE; neww->usehy += WINDOWMODEBORDERSIZE;
2299 }
2300
2301 neww->curnodeproto = NONODEPROTO;
2302 neww->buttonhandler = DEFAULTBUTTONHANDLER;
2303 neww->charhandler = DEFAULTCHARHANDLER;
2304 neww->changehandler = DEFAULTCHANGEHANDLER;
2305 neww->termhandler = DEFAULTTERMHANDLER;
2306 neww->redisphandler = DEFAULTREDISPHANDLER;
2307 neww->state = (neww->state & ~(WINDOWTYPE|WINDOWMODE)) | DISPWINDOW;
2308 killwindowpart(w);
2309 endobjectchange((INTBIG)us_tool, VTOOL);
2310 if (iscurrent)
2311 (void)setvalkey((INTBIG)us_tool, VTOOL, us_current_window_key, (INTBIG)neww,
2312 VWINDOWPART|VDONTSAVE);
2313 }
2314
2315 prevversion = np->prevversion;
2316 toolturnoff(net_tool, FALSE);
2317 killresult = killnodeproto(np);
2318 toolturnon(net_tool);
2319 if (killresult)
2320 {
2321 ttyputerr(_("Error killing cell"));
2322 return;
2323 }
2324
2325 /* see if this was the latest version of a cell */
2326 if (prevversion != NONODEPROTO)
2327 {
2328 /* newest version was deleted: rename next older version */
2329 for(ni = prevversion->firstinst; ni != NONODEINST; ni = ni->nextinst)
2330 {
2331 if ((ni->userbits&NEXPAND) != 0) continue;
2332 startobjectchange((INTBIG)ni, VNODEINST);
2333 endobjectchange((INTBIG)ni, VNODEINST);
2334 }
2335 }
2336
2337 /* update status display if necessary */
2338 if (us_curnodeproto != NONODEPROTO && us_curnodeproto->primindex == 0)
2339 {
2340 if (np == us_curnodeproto)
2341 {
2342 if ((us_state&NONPERSISTENTCURNODE) != 0) us_setnodeproto(NONODEPROTO); else
2343 us_setnodeproto(el_curtech->firstnodeproto);
2344 }
2345 }
2346 }
2347
2348 /*
2349 * Routine to compare the contents of two cells and return true if they are the same.
2350 * If "explain" is positive, tell why they differ.
2351 */
us_samecontents(NODEPROTO * np1,NODEPROTO * np2,INTBIG explain)2352 BOOLEAN us_samecontents(NODEPROTO *np1, NODEPROTO *np2, INTBIG explain)
2353 {
2354 REGISTER NODEINST *ni1, *ni2;
2355 REGISTER GEOM *geom;
2356 REGISTER ARCINST *ai1, *ai2;
2357 REGISTER PORTPROTO *pp1, *pp2;
2358 REGISTER PORTARCINST *pi;
2359 REGISTER PORTEXPINST *pe;
2360 REGISTER INTBIG sea, cx, cy, i, lambda1, lambda2;
2361
2362 /* make sure the nodes are the same */
2363 lambda1 = lambdaofcell(np1);
2364 lambda2 = lambdaofcell(np2);
2365 for(ni2 = np2->firstnodeinst; ni2 != NONODEINST; ni2 = ni2->nextnodeinst)
2366 ni2->temp1 = 0;
2367 for(ni1 = np1->firstnodeinst; ni1 != NONODEINST; ni1 = ni1->nextnodeinst)
2368 {
2369 /* find the node in the other cell */
2370 ni1->temp1 = 0;
2371 cx = (ni1->lowx + ni1->highx) / 2;
2372 cy = (ni1->lowy + ni1->highy) / 2;
2373 sea = initsearch(cx, cx, cy, cy, np2);
2374 for(;;)
2375 {
2376 geom = nextobject(sea);
2377 if (geom == NOGEOM) break;
2378 if (!geom->entryisnode) continue;
2379 ni2 = geom->entryaddr.ni;
2380 if (ni1->lowx != ni2->lowx || ni1->highx != ni2->highx ||
2381 ni1->lowy != ni2->lowy || ni1->highy != ni2->highy) continue;
2382 if (ni1->rotation != ni2->rotation || ni1->transpose != ni2->transpose) continue;
2383 if (ni1->proto->primindex != ni2->proto->primindex) continue;
2384 if (ni1->proto->primindex != 0)
2385 {
2386 /* make sure the two primitives are the same */
2387 if (ni1->proto != ni2->proto) continue;
2388 } else
2389 {
2390 /* make sure the two cells are the same */
2391 if (namesame(ni1->proto->protoname, ni2->proto->protoname) != 0)
2392 continue;
2393 if (ni1->proto->cellview != ni2->proto->cellview) continue;
2394 }
2395
2396 /* the nodes match */
2397 ni1->temp1 = (INTBIG)ni2;
2398 ni2->temp1 = (INTBIG)ni1;
2399 termsearch(sea);
2400 break;
2401 }
2402 if (ni1->temp1 == 0)
2403 {
2404 if (explain > 0)
2405 ttyputmsg(_("No equivalent to node %s at (%s,%s) in cell %s"),
2406 describenodeinst(ni1), latoa((ni1->lowx+ni1->highx)/2, lambda1),
2407 latoa((ni1->lowy+ni1->highy)/2, lambda1), describenodeproto(np1));
2408 return(FALSE);
2409 }
2410 }
2411 for(ni2 = np2->firstnodeinst; ni2 != NONODEINST; ni2 = ni2->nextnodeinst)
2412 {
2413 if (ni2->temp1 != 0) continue;
2414 if (explain > 0)
2415 ttyputmsg(_("No equivalent to node %s at (%s,%s) in cell %s"),
2416 describenodeinst(ni2), latoa((ni2->lowx+ni2->highx)/2, lambda2),
2417 latoa((ni2->lowy+ni2->highy)/2, lambda2), describenodeproto(np2));
2418 return(FALSE);
2419 }
2420
2421 /* all nodes match up, now check the arcs */
2422 for(ai2 = np2->firstarcinst; ai2 != NOARCINST; ai2 = ai2->nextarcinst)
2423 ai2->temp1 = 0;
2424 for(ai1 = np1->firstarcinst; ai1 != NOARCINST; ai1 = ai1->nextarcinst)
2425 {
2426 ai1->temp1 = 0;
2427 ni2 = (NODEINST *)ai1->end[0].nodeinst->temp1;
2428 for(pi = ni2->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
2429 {
2430 ai2 = pi->conarcinst;
2431 if (ai2->proto != ai1->proto) continue;
2432 if (ai2->width != ai1->width) continue;
2433 for(i=0; i<2; i++)
2434 {
2435 if (ai2->end[i].xpos != ai1->end[i].xpos) break;
2436 if (ai2->end[i].ypos != ai1->end[i].ypos) break;
2437 }
2438 if (i >= 2) break;
2439 }
2440 if (pi == NOPORTARCINST)
2441 {
2442 if (explain > 0)
2443 ttyputmsg(_("No equivalent to arc %s from (%s,%s) to (%s,%s) in cell %s"),
2444 describearcinst(ai1), latoa(ai1->end[0].xpos, lambda1), latoa(ai1->end[0].ypos, lambda1),
2445 latoa(ai1->end[1].xpos, lambda1), latoa(ai1->end[1].ypos, lambda1), describenodeproto(np1));
2446 return(FALSE);
2447 }
2448 ai1->temp1 = (INTBIG)ai2;
2449 ai2->temp1 = (INTBIG)ai1;
2450 }
2451 for(ai2 = np2->firstarcinst; ai2 != NOARCINST; ai2 = ai2->nextarcinst)
2452 {
2453 if (ai2->temp1 != 0) continue;
2454 if (explain > 0)
2455 ttyputmsg(_("No equivalent to arc %s from (%s,%s) to (%s,%s) in cell %s"),
2456 describearcinst(ai2), latoa(ai2->end[0].xpos, lambda2), latoa(ai2->end[0].ypos, lambda2),
2457 latoa(ai2->end[1].xpos, lambda2), latoa(ai2->end[1].ypos, lambda2), describenodeproto(np2));
2458 return(FALSE);
2459 }
2460
2461 /* now match the ports */
2462 for(pp2 = np2->firstportproto; pp2 != NOPORTPROTO; pp2 = pp2->nextportproto)
2463 pp2->temp1 = 0;
2464 for(pp1 = np1->firstportproto; pp1 != NOPORTPROTO; pp1 = pp1->nextportproto)
2465 {
2466 pp1->temp1 = 0;
2467 ni2 = (NODEINST *)pp1->subnodeinst->temp1;
2468 for(pe = ni2->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
2469 {
2470 pp2 = pe->exportproto;
2471 if (namesame(pp1->protoname, pp2->protoname) != 0) continue;
2472 break;
2473 }
2474 if (pe == NOPORTEXPINST)
2475 {
2476 if (explain > 0)
2477 ttyputmsg(_("No equivalent to port %s in cell %s"), pp1->protoname,
2478 describenodeproto(np1));
2479 return(FALSE);
2480 }
2481 pp1->temp1 = (INTBIG)pp2;
2482 pp2->temp1 = (INTBIG)pp1;
2483 }
2484 for(pp2 = np2->firstportproto; pp2 != NOPORTPROTO; pp2 = pp2->nextportproto)
2485 {
2486 if (pp2->temp1 != 0) continue;
2487 if (explain > 0)
2488 ttyputmsg(_("No equivalent to port %s in cell %s"), pp2->protoname,
2489 describenodeproto(np2));
2490 return(FALSE);
2491 }
2492
2493 /* cells match! */
2494 return(TRUE);
2495 }
2496
2497 /*
2498 * Routine to create cell "name" in library "lib". Returns NONODEPROTO on error.
2499 * Also makes icons expanded and places cell centers if requested.
2500 */
us_newnodeproto(CHAR * name,LIBRARY * lib)2501 NODEPROTO *us_newnodeproto(CHAR *name, LIBRARY *lib)
2502 {
2503 REGISTER NODEPROTO *np;
2504 REGISTER NODEINST *ni;
2505
2506 np = newnodeproto(name, lib);
2507 if (np == NONODEPROTO) return(NONODEPROTO);
2508
2509 /* icon cells should always be expanded */
2510 if (np->cellview == el_iconview) np->userbits |= WANTNEXPAND;
2511
2512 /* place a cell-center if requested */
2513 if ((us_useroptions&CELLCENTERALWAYS) != 0 &&
2514 (np->cellview->viewstate&TEXTVIEW) == 0)
2515 {
2516 ni = newnodeinst(gen_cellcenterprim, 0, 0, 0, 0, 0, 0, np);
2517 if (ni != NONODEINST)
2518 {
2519 endobjectchange((INTBIG)ni, VNODEINST);
2520 ni->userbits |= HARDSELECTN|NVISIBLEINSIDE;
2521 }
2522 }
2523 return(np);
2524 }
2525
2526 /*********************************** EXPLORER WINDOWS ***********************************/
2527
2528 /*
2529 * Routine to free the former explorer structure and build a new one.
2530 */
us_createexplorerstruct(WINDOWPART * w)2531 void us_createexplorerstruct(WINDOWPART *w)
2532 {
2533 REGISTER EXPWINDOW *ew;
2534 REGISTER WINDOWPART *ow;
2535 REGISTER INTBIG lx, hx, ly, hy;
2536
2537 /* create an explorer-window object */
2538 ew = (EXPWINDOW *)emalloc(sizeof (EXPWINDOW), us_tool->cluster);
2539 if (ew == 0) return;
2540
2541 /* store the explorerwindow structure in the window structure */
2542 w->expwindow = ew;
2543
2544 /* build a new explorer structure */
2545 ew->nodeused = NOEXPLORERNODE;
2546 ew->stacksize = 0;
2547 ew->firstnode = NOEXPLORERNODE;
2548 ew->nodeselected = NOEXPLORERNODE;
2549 ew->firstline = 0;
2550 ew->firstchar = 0;
2551 us_buildexplorerstruct(w, ew);
2552 adviseofchanges(us_clearexplorererrors);
2553
2554 /* set up the window object */
2555 startobjectchange((INTBIG)w, VWINDOWPART);
2556 (void)setval((INTBIG)w, VWINDOWPART, x_("buttonhandler"), (INTBIG)us_explorebuttonhandler,
2557 VADDRESS);
2558 (void)setval((INTBIG)w, VWINDOWPART, x_("charhandler"), (INTBIG)us_explorecharhandler,
2559 VADDRESS);
2560 (void)setval((INTBIG)w, VWINDOWPART, x_("redisphandler"), (INTBIG)us_exploreredisphandler,
2561 VADDRESS);
2562 (void)setval((INTBIG)w, VWINDOWPART, x_("state"),
2563 (w->state & ~(WINDOWTYPE|WINDOWMODE)) | EXPLORERWINDOW, VINTEGER);
2564 endobjectchange((INTBIG)w, VWINDOWPART);
2565
2566 /* redisplay the other window's horizontal slider */
2567 for(ow = el_topwindowpart; ow != NOWINDOWPART; ow = ow->nextwindowpart)
2568 {
2569 if (ow->frame != w->frame) continue;
2570 if ((ow->state&WINDOWTYPE) != DISPWINDOW) continue;
2571
2572 /* prepare a window in which to draw the thumbs */
2573 lx = ow->uselx; hx = ow->usehx;
2574 ly = ow->usely; hy = ow->usehy;
2575 ow->usehx += DISPLAYSLIDERSIZE;
2576 ow->usely -= DISPLAYSLIDERSIZE;
2577 us_drawhorizontalslider(ow, ly, lx, hx, 0);
2578 us_drawhorizontalsliderthumb(ow, ly, lx, hx, ow->thumblx, ow->thumbhx, 0);
2579 ow->usehx -= DISPLAYSLIDERSIZE;
2580 ow->usely += DISPLAYSLIDERSIZE;
2581 }
2582 }
2583
2584 /*
2585 * Routine called to deallocate the explorer window structures in "w".
2586 * Called prior to destruction of the window.
2587 */
us_deleteexplorerstruct(WINDOWPART * w)2588 void us_deleteexplorerstruct(WINDOWPART *w)
2589 {
2590 REGISTER EXPWINDOW *ew;
2591 REGISTER EXPLORERNODE *en;
2592
2593 ew = (EXPWINDOW *)w->expwindow;
2594 if (ew == NOEXPWINDOW) return;
2595
2596 /* free all existing explorer nodes (no longer necessary) */
2597 while (ew->nodeused != NOEXPLORERNODE)
2598 {
2599 en = ew->nodeused;
2600 ew->nodeused = en->nextexplorernode;
2601 us_freeexplorernode(en);
2602 }
2603 }
2604
2605 /*
2606 * Routine to build an explorer structure from the current database.
2607 */
us_buildexplorerstruct(WINDOWPART * w,EXPWINDOW * ew)2608 void us_buildexplorerstruct(WINDOWPART *w, EXPWINDOW *ew)
2609 {
2610 REGISTER WINDOWPART *ww;
2611
2612 /* see if there is a simulation window in the other half */
2613 for(ww = el_topwindowpart; ww != NOWINDOWPART; ww = ww->nextwindowpart)
2614 {
2615 if (ww == w) continue;
2616 if (ww->frame != w->frame) continue;
2617 if ((ww->state&WINDOWTYPE) == WAVEFORMWINDOW) break;
2618 }
2619 if (ww == NOWINDOWPART)
2620 {
2621 /* create an explorer node for the hierarchical view */
2622 ew->nodehierarchy = us_allocexplorernode(ew);
2623 if (ew->nodehierarchy == NOEXPLORERNODE) return;
2624 ew->nodehierarchy->flags = EXNODEPLACEHOLDER;
2625 ew->nodehierarchy->enodetype = EXNODETYPELABEL;
2626 ew->nodehierarchy->addr = EXLABELTYPEHIVIEW;
2627 us_addexplorernode(ew->nodehierarchy, &ew->firstnode, NOEXPLORERNODE);
2628
2629 /* create an explorer node for the contents view */
2630 ew->nodecontents = us_allocexplorernode(ew);
2631 if (ew->nodecontents == NOEXPLORERNODE) return;
2632 ew->nodecontents->flags = EXNODEPLACEHOLDER;
2633 ew->nodecontents->enodetype = EXNODETYPELABEL;
2634 ew->nodecontents->addr = EXLABELTYPECONVIEW;
2635 us_addexplorernode(ew->nodecontents, &ew->firstnode, NOEXPLORERNODE);
2636
2637 /* create an explorer node for the errors view */
2638 ew->nodeerrors = us_allocexplorernode(ew);
2639 if (ew->nodeerrors == NOEXPLORERNODE) return;
2640 ew->nodeerrors->flags = EXNODEPLACEHOLDER;
2641 ew->nodeerrors->enodetype = EXNODETYPELABEL;
2642 ew->nodeerrors->addr = EXLABELTYPEERRVIEW;
2643 us_addexplorernode(ew->nodeerrors, &ew->firstnode, NOEXPLORERNODE);
2644
2645 ew->nodesimulation = 0;
2646 } else
2647 {
2648 /* create an explorer node for the simulation signal view */
2649 ew->nodesimulation = us_allocexplorernode(ew);
2650 if (ew->nodesimulation == NOEXPLORERNODE) return;
2651 ew->nodesimulation->flags = EXNODEPLACEHOLDER;
2652 ew->nodesimulation->enodetype = EXNODETYPELABEL;
2653 ew->nodesimulation->addr = EXLABELTYPESIMVIEW;
2654 us_addexplorernode(ew->nodesimulation, &ew->firstnode, NOEXPLORERNODE);
2655
2656 ew->nodehierarchy = 0;
2657 ew->nodecontents = 0;
2658 ew->nodeerrors = 0;
2659 }
2660 }
2661
2662 /*
2663 * Routine to build an error-explorer structure from the current database.
2664 */
us_buildexplorersturcterrors(EXPWINDOW * ew,EXPLORERNODE * whichtree)2665 void us_buildexplorersturcterrors(EXPWINDOW *ew, EXPLORERNODE *whichtree)
2666 {
2667 REGISTER void *curerror, *eg;
2668 REGISTER INTBIG i, geomcount;
2669 REGISTER EXPLORERNODE *en, *suben;
2670
2671 whichtree->flags &= ~EXNODEPLACEHOLDER;
2672 curerror = 0;
2673 for(;;)
2674 {
2675 curerror = getnexterror(curerror);
2676 if (curerror == 0) break;
2677
2678 en = us_allocexplorernode(ew);
2679 if (en == NOEXPLORERNODE) break;
2680 en->enodetype = EXNODETYPEERROR;
2681 en->addr = (INTBIG)curerror;
2682 us_addexplorernode(en, &whichtree->subexplorernode, whichtree);
2683
2684 /* add any geometry objects */
2685 geomcount = getnumerrorgeom(curerror);
2686 for(i=0; i<geomcount; i++)
2687 {
2688 eg = geterrorgeom(curerror, i);
2689 suben = us_allocexplorernode(ew);
2690 if (suben == NOEXPLORERNODE) return;
2691 suben->enodetype = EXNODETYPEERRORGEOM;
2692 suben->addr = (INTBIG)eg;
2693 us_addexplorernode(suben, &en->subexplorernode, en);
2694 }
2695 }
2696 }
2697
2698 /*
2699 * Routine to clear the error-explorer information (because the error list has changed).
2700 */
us_clearexplorererrors(void)2701 void us_clearexplorererrors(void)
2702 {
2703 REGISTER WINDOWPART *w;
2704 REGISTER EXPLORERNODE *en, *suben, *lasten;
2705 REGISTER EXPWINDOW *ew;
2706
2707 /* see if there is an explorer window */
2708 for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
2709 {
2710 if ((w->state&WINDOWTYPE) != EXPLORERWINDOW) continue;
2711 ew = (EXPWINDOW *)w->expwindow;
2712 if ((ew->nodeerrors->flags&EXNODEPLACEHOLDER) == 0)
2713 {
2714 ew->nodeerrors->flags |= EXNODEPLACEHOLDER;
2715 ew->nodeerrors->flags &= ~EXNODEOPEN;
2716
2717 /* remove all error entries */
2718 lasten = NOEXPLORERNODE;
2719 for(en = ew->nodeused; en != NOEXPLORERNODE; en = en->nextexplorernode)
2720 {
2721 if (en->enodetype == EXNODETYPEERROR || en->enodetype == EXNODETYPEERRORGEOM)
2722 {
2723 if (lasten == NOEXPLORERNODE) ew->nodeused = en->nextexplorernode; else
2724 lasten->nextexplorernode = en->nextexplorernode;
2725 continue;
2726 }
2727 lasten = en;
2728 }
2729 while (ew->nodeerrors->subexplorernode != NOEXPLORERNODE)
2730 {
2731 en = ew->nodeerrors->subexplorernode;
2732 ew->nodeerrors->subexplorernode = en->nextsubexplorernode;
2733 while (en->subexplorernode != NOEXPLORERNODE)
2734 {
2735 suben = en->subexplorernode;
2736 en->subexplorernode = suben->nextsubexplorernode;
2737 us_freeexplorernode(suben);
2738 }
2739 us_freeexplorernode(en);
2740 }
2741 ew->nodeselected = NOEXPLORERNODE;
2742 }
2743 us_exploreredisphandler(w);
2744 }
2745 }
2746
2747 static EXPLORERNODE *us_explorertopsimnode;
2748 static EXPWINDOW *us_explorercursimwindow;
2749
2750
2751 CHAR *us_explorersimnodename(void *ptr);
2752 void *us_explorersimnewbranch(CHAR *branchname, void *parentnode);
2753 void *us_explorersimfindbranch(CHAR *branchname, void *parentnode);
2754 void *us_explorersimnewleaf(CHAR *leafname, void *parentnode);
2755
2756 /*
2757 * Routine to build a simulation-signal-explorer structure from the current database.
2758 */
us_buildexplorersturctsimulation(WINDOWPART * w,EXPWINDOW * ew,EXPLORERNODE * whichtree)2759 void us_buildexplorersturctsimulation(WINDOWPART *w, EXPWINDOW *ew, EXPLORERNODE *whichtree)
2760 {
2761 REGISTER WINDOWPART *ww;
2762
2763 /* see if this explorer window shares a space with a waveform window */
2764 for(ww = el_topwindowpart; ww != NOWINDOWPART; ww = ww->nextwindowpart)
2765 {
2766 if (ww == w) continue;
2767 if (ww->frame != w->frame) continue;
2768 if ((ww->state&WINDOWTYPE) == WAVEFORMWINDOW) break;
2769 }
2770 if (ww == NOWINDOWPART) return;
2771 whichtree->flags &= ~EXNODEPLACEHOLDER;
2772 us_explorertopsimnode = whichtree;
2773 us_explorercursimwindow = ew;
2774 sim_reportsignals(ww, us_explorersimnewbranch, us_explorersimfindbranch,
2775 us_explorersimnewleaf, us_explorersimnodename);
2776 }
2777
2778 /*
2779 * Callback routine (called from "us_buildexplorersturctsimulation()") to return
2780 * the name of simulation-explorer node "ptr".
2781 */
us_explorersimnodename(void * ptr)2782 CHAR *us_explorersimnodename(void *ptr)
2783 {
2784 REGISTER EXPLORERNODE *en;
2785
2786 en = (EXPLORERNODE *)ptr;
2787 return((CHAR *)en->addr);
2788 }
2789
2790 /*
2791 * Callback routine (called from "us_buildexplorersturctsimulation()") to return
2792 * the branch of "parentnode" named "branchname". Returns 0 if not found.
2793 */
us_explorersimfindbranch(CHAR * branchname,void * parentnode)2794 void *us_explorersimfindbranch(CHAR *branchname, void *parentnode)
2795 {
2796 REGISTER EXPLORERNODE *en, *suben;
2797
2798 en = (EXPLORERNODE *)parentnode;
2799 if (en == 0) en = us_explorertopsimnode;
2800 for(suben = en->subexplorernode; suben != NOEXPLORERNODE; suben = suben->nextsubexplorernode)
2801 if (namesame(branchname, (CHAR *)suben->addr) == 0) return(suben);
2802 return(0);
2803 }
2804
2805 /*
2806 * Callback routine (called from "us_buildexplorersturctsimulation()") to create
2807 * a new branch (under "parentnode") called "branchname". Returns the branch node.
2808 */
us_explorersimnewbranch(CHAR * branchname,void * parentnode)2809 void *us_explorersimnewbranch(CHAR *branchname, void *parentnode)
2810 {
2811 REGISTER EXPLORERNODE *en, *suben;
2812 CHAR *pt;
2813
2814 en = (EXPLORERNODE *)parentnode;
2815 if (en == 0) en = us_explorertopsimnode; else
2816 en->enodetype = EXNODETYPESIMBRANCH;
2817
2818 suben = us_allocexplorernode(us_explorercursimwindow);
2819 if (suben == NOEXPLORERNODE) return(0);
2820 suben->enodetype = EXNODETYPESIMBRANCH;
2821 allocstring(&pt, branchname, us_tool->cluster);
2822 suben->addr = (INTBIG)pt;
2823 us_addexplorernode(suben, &en->subexplorernode, en);
2824 return(suben);
2825 }
2826
2827 /*
2828 * Callback routine (called from "us_buildexplorersturctsimulation()") to create
2829 * a new leaf (under "parentnode") called "leafname". Returns the leaf node.
2830 */
us_explorersimnewleaf(CHAR * leafname,void * parentnode)2831 void *us_explorersimnewleaf(CHAR *leafname, void *parentnode)
2832 {
2833 REGISTER EXPLORERNODE *en, *suben;
2834 CHAR *pt;
2835
2836 en = (EXPLORERNODE *)parentnode;
2837 if (en == 0) en = us_explorertopsimnode; else
2838 en->enodetype = EXNODETYPESIMBRANCH;
2839
2840 suben = us_allocexplorernode(us_explorercursimwindow);
2841 if (suben == NOEXPLORERNODE) return(0);
2842 suben->enodetype = EXNODETYPESIMLEAF;
2843 allocstring(&pt, leafname, us_tool->cluster);
2844 suben->addr = (INTBIG)pt;
2845 us_addexplorernode(suben, &en->subexplorernode, en);
2846 return(suben);
2847 }
2848
us_clearexplorersimulation(void)2849 void us_clearexplorersimulation(void)
2850 {
2851 REGISTER WINDOWPART *w;
2852 REGISTER EXPLORERNODE *en, *suben, *lasten;
2853 REGISTER EXPWINDOW *ew;
2854
2855 /* see if there is an explorer window */
2856 for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
2857 {
2858 if ((w->state&WINDOWTYPE) != EXPLORERWINDOW) continue;
2859 ew = (EXPWINDOW *)w->expwindow;
2860 if ((ew->nodesimulation->flags&EXNODEPLACEHOLDER) == 0)
2861 {
2862 ew->nodesimulation->flags |= EXNODEPLACEHOLDER;
2863 ew->nodesimulation->flags &= ~EXNODEOPEN;
2864
2865 /* remove all error entries */
2866 lasten = NOEXPLORERNODE;
2867 for(en = ew->nodeused; en != NOEXPLORERNODE; en = en->nextexplorernode)
2868 {
2869 if (en->enodetype == EXNODETYPESIMBRANCH || en->enodetype == EXNODETYPESIMLEAF)
2870 {
2871 if (lasten == NOEXPLORERNODE) ew->nodeused = en->nextexplorernode; else
2872 lasten->nextexplorernode = en->nextexplorernode;
2873 continue;
2874 }
2875 lasten = en;
2876 }
2877 while (ew->nodesimulation->subexplorernode != NOEXPLORERNODE)
2878 {
2879 en = ew->nodesimulation->subexplorernode;
2880 ew->nodesimulation->subexplorernode = en->nextsubexplorernode;
2881 while (en->subexplorernode != NOEXPLORERNODE)
2882 {
2883 suben = en->subexplorernode;
2884 en->subexplorernode = suben->nextsubexplorernode;
2885 us_freeexplorernode(suben);
2886 }
2887 us_freeexplorernode(en);
2888 }
2889 ew->nodeselected = NOEXPLORERNODE;
2890 }
2891 us_exploreredisphandler(w);
2892 }
2893 }
2894
2895 /*
2896 * Routine to build a hierarchical-explorer structure from the current database.
2897 */
us_buildexplorersturcthierarchical(EXPWINDOW * ew,EXPLORERNODE * whichtree)2898 void us_buildexplorersturcthierarchical(EXPWINDOW *ew, EXPLORERNODE *whichtree)
2899 {
2900 REGISTER EXPLORERNODE *en, *sen;
2901 REGISTER LIBRARY *lib;
2902 REGISTER NODEPROTO *np, *inp, *cnp;
2903
2904 /* scan each library */
2905 whichtree->flags &= ~EXNODEPLACEHOLDER;
2906 for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
2907 {
2908 if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
2909
2910 /* create an explorer node for this library */
2911 en = us_allocexplorernode(ew);
2912 if (en == NOEXPLORERNODE) break;
2913 if (lib == el_curlib) en->flags = EXNODEOPEN;
2914 en->enodetype = EXNODETYPELIBRARY;
2915 en->addr = (INTBIG)lib;
2916 us_addexplorernode(en, &whichtree->subexplorernode, whichtree);
2917
2918 /* find top-level cells in the library */
2919 for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
2920 {
2921 if (np->firstinst != NONODEINST) continue;
2922
2923 /* if any view or version is in use, this cell isn't top-level */
2924 FOR_CELLGROUP(cnp, np)
2925 {
2926 for(inp = cnp; inp != NONODEPROTO; inp = inp->prevversion)
2927 if (inp->firstinst != NONODEINST) break;
2928 if (inp != NONODEPROTO) break;
2929 }
2930 if (cnp != NONODEPROTO) continue;
2931
2932 /* create an explorer node for this cell */
2933 sen = us_allocexplorernode(ew);
2934 if (sen == NOEXPLORERNODE) break;
2935 sen->enodetype = EXNODETYPECELL;
2936 sen->addr = (INTBIG)np;
2937 us_addexplorernode(sen, &en->subexplorernode, en);
2938
2939 /* add explorer nodes for everything under this cell */
2940 us_createhierarchicalexplorertree(ew, sen, np);
2941 }
2942 }
2943 }
2944
2945 /*
2946 * Routine to build a hierarchical explorer structure starting at node "en".
2947 */
us_createhierarchicalexplorertree(EXPWINDOW * ew,EXPLORERNODE * en,NODEPROTO * np)2948 void us_createhierarchicalexplorertree(EXPWINDOW *ew, EXPLORERNODE *en, NODEPROTO *np)
2949 {
2950 REGISTER NODEPROTO *subnp, *cnp, *onp;
2951 REGISTER NODEINST *ni;
2952 REGISTER EXPLORERNODE *sen;
2953
2954 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
2955 {
2956 subnp = ni->proto;
2957 if (subnp->primindex != 0) continue;
2958
2959 /* ignore recursive references (showing icon in contents) */
2960 if (isiconof(subnp, np)) continue;
2961 for(sen = en->subexplorernode; sen != NOEXPLORERNODE; sen = sen->nextsubexplorernode)
2962 {
2963 if (sen->enodetype != EXNODETYPECELL) continue;
2964 if ((NODEPROTO *)sen->addr == subnp) break;
2965 }
2966 if (sen == NOEXPLORERNODE)
2967 {
2968 sen = us_allocexplorernode(ew);
2969 if (sen == NOEXPLORERNODE) break;
2970 sen->enodetype = EXNODETYPECELL;
2971 sen->addr = (INTBIG)subnp;
2972 us_addexplorernode(sen, &en->subexplorernode, en);
2973 us_createhierarchicalexplorertree(ew, sen, subnp);
2974 }
2975 sen->count++;
2976
2977 /* include associated cells here */
2978 FOR_CELLGROUP(cnp, subnp)
2979 {
2980 for(onp = cnp; onp != NONODEPROTO; onp = onp->prevversion)
2981 {
2982 if (onp == subnp) continue;
2983 for(sen = en->subexplorernode; sen != NOEXPLORERNODE; sen = sen->nextsubexplorernode)
2984 {
2985 if (sen->enodetype != EXNODETYPECELL) continue;
2986 if ((NODEPROTO *)sen->addr == onp) break;
2987 }
2988 if (sen == NOEXPLORERNODE)
2989 {
2990 sen = us_allocexplorernode(ew);
2991 if (sen == NOEXPLORERNODE) break;
2992 sen->enodetype = EXNODETYPECELL;
2993 sen->addr = (INTBIG)onp;
2994 us_addexplorernode(sen, &en->subexplorernode, en);
2995 us_createhierarchicalexplorertree(ew, sen, onp);
2996 }
2997 if (onp == contentsview(subnp)) sen->count++;
2998 }
2999 }
3000 }
3001 }
3002
3003 /*
3004 * Routine to build a contents-explorer structure from the current database.
3005 */
us_buildexplorersturctcontents(EXPWINDOW * ew,EXPLORERNODE * whichtree)3006 void us_buildexplorersturctcontents(EXPWINDOW *ew, EXPLORERNODE *whichtree)
3007 {
3008 REGISTER EXPLORERNODE *en, *sen, *ten;
3009 REGISTER INTBIG i;
3010 REGISTER LIBRARY *lib;
3011 REGISTER NODEPROTO *np;
3012
3013 /* scan each library */
3014 whichtree->flags &= ~EXNODEPLACEHOLDER;
3015 for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
3016 {
3017 if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
3018
3019 /* create an explorer node for this library */
3020 en = us_allocexplorernode(ew);
3021 if (en == NOEXPLORERNODE) break;
3022 en->enodetype = EXNODETYPELIBRARY;
3023 en->addr = (INTBIG)lib;
3024 us_addexplorernode(en, &whichtree->subexplorernode, whichtree);
3025
3026 /* see if this library contains technology-edit primitives */
3027 for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
3028 if ((np->userbits&TECEDITCELL) != 0) break;
3029 if (np != NONODEPROTO)
3030 {
3031 /* technology edit library: show it specially */
3032 for(i=0; i<4; i++)
3033 {
3034 ten = us_allocexplorernode(ew);
3035 if (ten == NOEXPLORERNODE) break;
3036 ten->flags = 0;
3037 ten->enodetype = EXNODETYPELABEL;
3038 switch (i)
3039 {
3040 case 0: ten->addr = EXLABELTYPELAYERCELLS; break;
3041 case 1: ten->addr = EXLABELTYPEARCCELLS; break;
3042 case 2: ten->addr = EXLABELTYPENODECELLS; break;
3043 case 3: ten->addr = EXLABELTYPEMISCCELLS; break;
3044 }
3045 us_addexplorernode(ten, &en->subexplorernode, en);
3046 for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
3047 {
3048 /* create an explorer node for this cell */
3049 if (namesamen(np->protoname, x_("layer-"), 6) == 0)
3050 {
3051 if (i != 0) continue;
3052 } else if (namesamen(np->protoname, x_("arc-"), 4) == 0)
3053 {
3054 if (i != 1) continue;
3055 } else if (namesamen(np->protoname, x_("node-"), 5) == 0)
3056 {
3057 if (i != 2) continue;
3058 } else
3059 {
3060 if (i != 3) continue;
3061 }
3062 sen = us_allocexplorernode(ew);
3063 if (sen == NOEXPLORERNODE) break;
3064 sen->flags = EXNODEPLACEHOLDER;
3065 sen->enodetype = EXNODETYPECELL;
3066 sen->addr = (INTBIG)np;
3067 us_addexplorernode(sen, &ten->subexplorernode, ten);
3068 }
3069 }
3070 } else
3071 {
3072 /* normal library: load all cells */
3073 for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
3074 {
3075 /* create an explorer node for this cell */
3076 sen = us_allocexplorernode(ew);
3077 if (sen == NOEXPLORERNODE) break;
3078 sen->flags = EXNODEPLACEHOLDER;
3079 sen->enodetype = EXNODETYPECELL;
3080 sen->addr = (INTBIG)np;
3081 us_addexplorernode(sen, &en->subexplorernode, en);
3082 }
3083 }
3084 }
3085 }
3086
us_buildexplorernodecontents(EXPWINDOW * ew,NODEPROTO * np,EXPLORERNODE * whichtree)3087 void us_buildexplorernodecontents(EXPWINDOW *ew, NODEPROTO *np, EXPLORERNODE *whichtree)
3088 {
3089 REGISTER EXPLORERNODE *len, *ben;
3090 REGISTER PORTPROTO *pp;
3091 REGISTER NODEINST *ni;
3092 REGISTER ARCINST *ai;
3093 REGISTER NETWORK *net;
3094 REGISTER VARIABLE *var;
3095
3096 whichtree->flags &= ~EXNODEPLACEHOLDER;
3097
3098 /* add all nodes in the cell */
3099 len = us_allocexplorernode(ew);
3100 if (len == NOEXPLORERNODE) return;
3101 len->enodetype = EXNODETYPELABEL;
3102 len->addr = EXLABELTYPENODELIST;
3103 us_addexplorernode(len, &whichtree->subexplorernode, whichtree);
3104 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
3105 {
3106 var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
3107 if (var == NOVARIABLE) continue;
3108 ben = us_allocexplorernode(ew);
3109 if (ben == NOEXPLORERNODE) break;
3110 ben->enodetype = EXNODETYPENODE;
3111 ben->addr = (INTBIG)ni;
3112 us_addexplorernode(ben, &len->subexplorernode, len);
3113 }
3114
3115 /* add all arcs in the cell */
3116 len = us_allocexplorernode(ew);
3117 if (len == NOEXPLORERNODE) return;
3118 len->enodetype = EXNODETYPELABEL;
3119 len->addr = EXLABELTYPEARCLIST;
3120 us_addexplorernode(len, &whichtree->subexplorernode, whichtree);
3121 for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
3122 {
3123 var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name_key);
3124 if (var == NOVARIABLE) continue;
3125 ben = us_allocexplorernode(ew);
3126 if (ben == NOEXPLORERNODE) break;
3127 ben->enodetype = EXNODETYPEARC;
3128 ben->addr = (INTBIG)ai;
3129 us_addexplorernode(ben, &len->subexplorernode, len);
3130 }
3131
3132 /* add all exports in the cell */
3133 len = us_allocexplorernode(ew);
3134 if (len == NOEXPLORERNODE) return;
3135 len->enodetype = EXNODETYPELABEL;
3136 len->addr = EXLABELTYPEEXPORTLIST;
3137 us_addexplorernode(len, &whichtree->subexplorernode, whichtree);
3138 for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
3139 {
3140 ben = us_allocexplorernode(ew);
3141 if (ben == NOEXPLORERNODE) break;
3142 ben->enodetype = EXNODETYPEEXPORT;
3143 ben->addr = (INTBIG)pp;
3144 us_addexplorernode(ben, &len->subexplorernode, len);
3145 }
3146
3147 /* add all networks in the cell */
3148 len = us_allocexplorernode(ew);
3149 if (len == NOEXPLORERNODE) return;
3150 len->enodetype = EXNODETYPELABEL;
3151 len->addr = EXLABELTYPENETLIST;
3152 us_addexplorernode(len, &whichtree->subexplorernode, whichtree);
3153 for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
3154 {
3155 ben = us_allocexplorernode(ew);
3156 if (ben == NOEXPLORERNODE) break;
3157 ben->enodetype = EXNODETYPENETWORK;
3158 ben->addr = (INTBIG)net;
3159 us_addexplorernode(ben, &len->subexplorernode, len);
3160 }
3161 }
3162
3163 /*
3164 * Routine to add explorer node "en" to the list headed by "head".
3165 * That tree is under node "parent" (which is NOEXPLORERNODE if at the top).
3166 * The node is inserted alphabetically.
3167 */
us_addexplorernode(EXPLORERNODE * en,EXPLORERNODE ** head,EXPLORERNODE * parent)3168 void us_addexplorernode(EXPLORERNODE *en, EXPLORERNODE **head, EXPLORERNODE *parent)
3169 {
3170 EXPLORERNODE *aen, *lasten;
3171 CHAR *name, *aname;
3172 REGISTER INTBIG len;
3173
3174 lasten = NOEXPLORERNODE;
3175 name = us_describeexplorernode(en, TRUE);
3176 if (name == 0 || en->enodetype == EXNODETYPELABEL)
3177 {
3178 /* no sorting needed: just find the end of the list */
3179 for(aen = *head; aen != NOEXPLORERNODE; aen = aen->nextsubexplorernode)
3180 lasten = aen;
3181 } else
3182 {
3183 /* remember this name in a global string */
3184 len = estrlen(name) + 1;
3185 if (len > us_explorertempstringtotal)
3186 {
3187 if (us_explorertempstringtotal > 0) efree(us_explorertempstring);
3188 us_explorertempstringtotal = 0;
3189 us_explorertempstring = (CHAR *)emalloc(len * SIZEOFCHAR, us_tool->cluster);
3190 if (us_explorertempstring == 0) return;
3191 us_explorertempstringtotal = len;
3192 }
3193 estrcpy(us_explorertempstring, name);
3194
3195 for(aen = *head; aen != NOEXPLORERNODE; aen = aen->nextsubexplorernode)
3196 {
3197 aname = us_describeexplorernode(aen, TRUE);
3198 if (namesame(us_explorertempstring, aname) < 0) break;
3199 lasten = aen;
3200 }
3201 }
3202 if (lasten == NOEXPLORERNODE)
3203 {
3204 en->nextsubexplorernode = *head;
3205 *head = en;
3206 } else
3207 {
3208 en->nextsubexplorernode = lasten->nextsubexplorernode;
3209 lasten->nextsubexplorernode = en;
3210 }
3211 en->parent = parent;
3212 }
3213
3214 /*
3215 * Routine to describe explorer node "en". If "purename" is TRUE, shows only
3216 * the name. Otherwise, gives the text as it will appear in the explorer.
3217 * Returns 0 if it cannot determine a name.
3218 */
us_describeexplorernode(EXPLORERNODE * en,BOOLEAN purename)3219 CHAR *us_describeexplorernode(EXPLORERNODE *en, BOOLEAN purename)
3220 {
3221 REGISTER LIBRARY *lib;
3222 REGISTER NODEPROTO *np;
3223 REGISTER NETWORK *net;
3224 REGISTER NODEINST *ni;
3225 REGISTER ARCINST *ai;
3226 REGISTER VARIABLE *var;
3227 REGISTER void *infstr;
3228 static CHAR genname[50];
3229
3230 switch (en->enodetype)
3231 {
3232 case EXNODETYPELIBRARY:
3233 lib = (LIBRARY *)en->addr;
3234 if (purename) return(lib->libname);
3235 infstr = initinfstr();
3236 if (lib == el_curlib) addstringtoinfstr(infstr, _("CURRENT "));
3237 addstringtoinfstr(infstr, _("LIBRARY: "));
3238 addstringtoinfstr(infstr, lib->libname);
3239 return(returninfstr(infstr));
3240 case EXNODETYPECELL:
3241 np = (NODEPROTO *)en->addr;
3242 if (purename || en->count <= 0) return(nldescribenodeproto(np));
3243 infstr = initinfstr();
3244 formatinfstr(infstr, x_("%s (%ld)"), nldescribenodeproto((NODEPROTO *)en->addr), en->count);
3245 return(returninfstr(infstr));
3246 case EXNODETYPEEXPORT:
3247 return(((PORTPROTO *)en->addr)->protoname);
3248 case EXNODETYPENODE:
3249 ni = (NODEINST *)en->addr;
3250 var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
3251 if (var == NOVARIABLE) return(0);
3252 return((CHAR *)var->addr);
3253 case EXNODETYPEARC:
3254 ai = (ARCINST *)en->addr;
3255 var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name_key);
3256 if (var == NOVARIABLE) return(0);
3257 return((CHAR *)var->addr);
3258 case EXNODETYPENETWORK:
3259 net = (NETWORK *)en->addr;
3260 if (net->namecount != 0) return(networkname(net, 0));
3261 esnprintf(genname, 50, x_("NET%ld"), (INTBIG)net);
3262 return(genname);
3263 case EXNODETYPEERROR:
3264 if (purename) return(0);
3265 return(describeerror((void *)en->addr));
3266 case EXNODETYPEERRORGEOM:
3267 if (purename) return(0);
3268 return(describeerrorgeom((void *)en->addr));
3269 case EXNODETYPESIMBRANCH:
3270 case EXNODETYPESIMLEAF:
3271 if (purename) return(0);
3272 return((CHAR *)en->addr);
3273 case EXNODETYPELABEL:
3274 switch (en->addr)
3275 {
3276 case EXLABELTYPEHIVIEW: return(_("***** HIERARCHICAL VIEW"));
3277 case EXLABELTYPECONVIEW: return(_("***** CONTENTS VIEW"));
3278 case EXLABELTYPEERRVIEW: return(_("***** ERRORS LIST"));
3279 case EXLABELTYPESIMVIEW: return(_("***** SIMULATION SIGNAL LIST"));
3280 case EXLABELTYPENODELIST: return(_("NODE LIST"));
3281 case EXLABELTYPEARCLIST: return(_("ARC LIST"));
3282 case EXLABELTYPEEXPORTLIST: return(_("EXPORT LIST"));
3283 case EXLABELTYPENETLIST: return(_("NETWORK LIST"));
3284 case EXLABELTYPELAYERCELLS: return(_("TECHNOLOGY EDITOR LAYERS"));
3285 case EXLABELTYPEARCCELLS: return(_("TECHNOLOGY EDITOR ARCS"));
3286 case EXLABELTYPENODECELLS: return(_("TECHNOLOGY EDITOR NODES"));
3287 case EXLABELTYPEMISCCELLS: return(_("TECHNOLOGY EDITOR MISCELLANEOUS"));
3288 }
3289 break;
3290 }
3291 return(0);
3292 }
3293
3294 /*
3295 * Routine to increment the stack depth "ew->depth" and to expand the stack
3296 * globals "ew->stack" and "ew->stackdone" if necessary.
3297 */
us_expandexplorerdepth(EXPWINDOW * ew)3298 BOOLEAN us_expandexplorerdepth(EXPWINDOW *ew)
3299 {
3300 REGISTER INTBIG newlimit, i;
3301 REGISTER EXPLORERNODE **stk;
3302 REGISTER BOOLEAN *done;
3303
3304 if (ew->depth >= ew->stacksize)
3305 {
3306 newlimit = ew->depth + 10;
3307 stk = (EXPLORERNODE **)emalloc(newlimit * (sizeof (EXPLORERNODE *)), us_tool->cluster);
3308 if (stk == 0) return(TRUE);
3309 done = (BOOLEAN *)emalloc(newlimit * (sizeof (BOOLEAN)), us_tool->cluster);
3310 if (done == 0) return(TRUE);
3311 for(i=0; i<ew->depth; i++)
3312 {
3313 stk[i] = ew->stack[i];
3314 done[i] = ew->stackdone[i];
3315 }
3316
3317 if (ew->stacksize > 0)
3318 {
3319 efree((CHAR *)ew->stack);
3320 efree((CHAR *)ew->stackdone);
3321 }
3322 ew->stack = stk;
3323 ew->stackdone = done;
3324 ew->stacksize = newlimit;
3325 }
3326 ew->depth++;
3327 return(FALSE);
3328 }
3329
3330 /*
3331 * Routine to figure out which explorer window is the current one. First tries to
3332 * find one in the same frame as the current window. Next looks for any explorer.
3333 * Returns NOEXPWINDOW if none found.
3334 */
us_getcurrentexplorerwindow(void)3335 EXPWINDOW *us_getcurrentexplorerwindow(void)
3336 {
3337 REGISTER WINDOWPART *w;
3338
3339 if (el_curwindowpart != NOWINDOWPART)
3340 {
3341 for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
3342 {
3343 if ((w->state&WINDOWTYPE) != EXPLORERWINDOW) continue;
3344 if (w->frame == el_curwindowpart->frame) return((EXPWINDOW *)w->expwindow);
3345 }
3346 }
3347
3348 /* see if there is any explorer window */
3349 for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
3350 if ((w->state&WINDOWTYPE) == EXPLORERWINDOW) return((EXPWINDOW *)w->expwindow);
3351
3352 return(NOEXPWINDOW);
3353 }
3354
3355 /*
3356 * Routine to copy the current explorer node text to the clipboard.
3357 */
us_copyexplorerwindow(void)3358 void us_copyexplorerwindow(void)
3359 {
3360 REGISTER void *infstr;
3361 REGISTER EXPWINDOW *ew;
3362
3363 ew = us_getcurrentexplorerwindow();
3364 if (ew == NOEXPWINDOW) return;
3365
3366 if (ew->nodeselected == NOEXPLORERNODE)
3367 {
3368 ttyputerr(_("Select a line in the explorer before copying it"));
3369 return;
3370 }
3371 infstr = initinfstr();
3372 us_addtoexplorerwindowcopy(infstr, 0, ew->nodeselected);
3373 setcutbuffer(returninfstr(infstr));
3374 }
3375
3376 /*
3377 * Helper routine for "us_copyexplorerwindow()" to construct a copy of the selected explorer node.
3378 */
us_addtoexplorerwindowcopy(void * infstr,INTBIG depth,EXPLORERNODE * en)3379 void us_addtoexplorerwindowcopy(void *infstr, INTBIG depth, EXPLORERNODE *en)
3380 {
3381 REGISTER CHAR *pt;
3382 REGISTER EXPLORERNODE *sen;
3383 REGISTER INTBIG i;
3384
3385 pt = us_describeexplorernode(en, FALSE);
3386 if (pt == 0) return;
3387 for(i=0; i<depth; i++) addstringtoinfstr(infstr, x_(" "));
3388 formatinfstr(infstr, x_("%s\n"), pt);
3389 if (en->subexplorernode == NOEXPLORERNODE) return;
3390 if ((en->flags&EXNODEOPEN) == 0 || (en->flags&EXNODEPLACEHOLDER) != 0) return;
3391 for(sen = en->subexplorernode; sen != NOEXPLORERNODE; sen = sen->nextsubexplorernode)
3392 {
3393 us_addtoexplorerwindowcopy(infstr, depth+1, sen);
3394 }
3395 }
3396
3397 /*
3398 * Routine called when the hierarchy has changed: rebuilds the explorer
3399 * structure and preserves as much information as possible when
3400 * redisplaying it.
3401 */
us_redoexplorerwindow(void)3402 void us_redoexplorerwindow(void)
3403 {
3404 REGISTER WINDOWPART *w;
3405 REGISTER EXPWINDOW *ew;
3406 REGISTER INTBIG lasttopline, hierarchyflags, contentsflags, errorsflags, simflags;
3407 REGISTER EXPLORERNODE *en, *oldtopnode, *oldusedtop;
3408
3409 /* see if there is an explorer window */
3410 for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
3411 {
3412 if ((w->state&WINDOWTYPE) != EXPLORERWINDOW) continue;
3413 ew = (EXPWINDOW *)w->expwindow;
3414
3415 /* remember the former list of explorer nodes */
3416 if (ew->nodehierarchy == 0) hierarchyflags = EXNODEPLACEHOLDER; else
3417 hierarchyflags = ew->nodehierarchy->flags;
3418 if (ew->nodecontents == 0) contentsflags = EXNODEPLACEHOLDER; else
3419 contentsflags = ew->nodecontents->flags;
3420 if (ew->nodeerrors == 0) errorsflags = EXNODEPLACEHOLDER; else
3421 errorsflags = ew->nodeerrors->flags;
3422 if (ew->nodesimulation == 0) simflags = EXNODEPLACEHOLDER; else
3423 simflags = ew->nodesimulation->flags;
3424 oldusedtop = ew->nodeused;
3425 ew->nodeused = NOEXPLORERNODE;
3426 oldtopnode = ew->firstnode;
3427 ew->firstnode = NOEXPLORERNODE;
3428
3429 /* rebuild the explorer structure */
3430 us_buildexplorerstruct(w, ew);
3431
3432 /* restore state of each section */
3433 if ((hierarchyflags&EXNODEPLACEHOLDER) == 0)
3434 us_buildexplorersturcthierarchical(ew, ew->nodehierarchy);
3435 if ((contentsflags&EXNODEPLACEHOLDER) == 0)
3436 us_buildexplorersturctcontents(ew, ew->nodecontents);
3437 if ((errorsflags&EXNODEPLACEHOLDER) == 0)
3438 us_buildexplorersturcterrors(ew, ew->nodeerrors);
3439 if ((simflags&EXNODEPLACEHOLDER) == 0)
3440 us_buildexplorersturctsimulation(w, ew, ew->nodesimulation);
3441
3442 /* find the place where the former "first line" used to be, copy expansion */
3443 ew->depth = 0;
3444 lasttopline = ew->firstline;
3445 ew->firstline = 0;
3446 ew->nodenowselected = NOEXPLORERNODE;
3447 (void)us_scannewexplorerstruct(ew, ew->firstnode, oldtopnode, 0);
3448 ew->nodeselected = ew->nodenowselected;
3449 if (lasttopline == 0) ew->firstline = 0;
3450 us_exploreredisphandler(w);
3451
3452 /* free the former list of explorer nodes */
3453 while (oldusedtop != NOEXPLORERNODE)
3454 {
3455 en = oldusedtop;
3456 oldusedtop = en->nextexplorernode;
3457 us_freeexplorernode(en);
3458 }
3459 }
3460 }
3461
3462 /*
3463 * Helper routine for "us_redoexplorerwindow" which scans for the proper "top line" in the
3464 * explorer window and copies the "expansion" bits from the old structure. The new
3465 * explorer node being examined is "firsten", and the old one (if there is one) is
3466 * "oldfirsten". The current line number in the explorer window is "line", and the
3467 * routine returns the line number after this node has been scanned.
3468 */
us_scannewexplorerstruct(EXPWINDOW * ew,EXPLORERNODE * firsten,EXPLORERNODE * oldfirsten,INTBIG line)3469 INTBIG us_scannewexplorerstruct(EXPWINDOW *ew, EXPLORERNODE *firsten, EXPLORERNODE *oldfirsten, INTBIG line)
3470 {
3471 REGISTER EXPLORERNODE *en, *oen;
3472 REGISTER INTBIG newline;
3473
3474 /* push the explorer stack */
3475 if (us_expandexplorerdepth(ew)) return(line);
3476
3477 for(en = firsten; en != NOEXPLORERNODE; en = en->nextsubexplorernode)
3478 {
3479 /* record the position in the stack */
3480 ew->stack[ew->depth-1] = en;
3481
3482 line++;
3483
3484 /* copy the expansion bits */
3485 oen = NOEXPLORERNODE;
3486 if (oldfirsten != NOEXPLORERNODE)
3487 {
3488 for(oen = oldfirsten; oen != NOEXPLORERNODE; oen = oen->nextsubexplorernode)
3489 {
3490 if (oen->addr == en->addr) break;
3491 }
3492 if (oen != NOEXPLORERNODE)
3493 {
3494 if (oen == ew->nodeselected)
3495 ew->nodenowselected = en;
3496 en->flags = (en->flags & ~EXNODEOPEN) | (oen->flags & EXNODEOPEN);
3497 }
3498 }
3499
3500 /* now scan children */
3501 if (en->subexplorernode != NOEXPLORERNODE)
3502 {
3503 if (oen != NOEXPLORERNODE) oen = oen->subexplorernode;
3504 newline = us_scannewexplorerstruct(ew, en->subexplorernode, oen, line);
3505 if ((en->flags&EXNODEOPEN) != 0) line = newline;
3506 }
3507 }
3508
3509 /* pop the explorer stack */
3510 ew->depth--;
3511 return(line);
3512 }
3513
3514 /*
3515 * Routine to return the currently selected cell in the explorer window.
3516 * Returns NONODEPROTO if none selected.
3517 */
us_currentexplorernode(void)3518 NODEPROTO *us_currentexplorernode(void)
3519 {
3520 REGISTER EXPWINDOW *ew;
3521
3522 ew = us_getcurrentexplorerwindow();
3523 if (ew == NOEXPWINDOW) return(NONODEPROTO);
3524 if (ew->nodeselected == NOEXPLORERNODE) return(NONODEPROTO);
3525 switch (ew->nodeselected->enodetype)
3526 {
3527 case EXNODETYPECELL:
3528 return((NODEPROTO *)ew->nodeselected->addr);
3529 case EXNODETYPENODE:
3530 return(((NODEINST *)ew->nodeselected->addr)->parent);
3531 case EXNODETYPEARC:
3532 return(((ARCINST *)ew->nodeselected->addr)->parent);
3533 case EXNODETYPENETWORK:
3534 return(((NETWORK *)ew->nodeselected->addr)->parent);
3535 case EXNODETYPEEXPORT:
3536 return(((PORTPROTO *)ew->nodeselected->addr)->parent);
3537 }
3538 return(NONODEPROTO);
3539 }
3540
3541 /*
3542 * Routine to initialize the dumping of the explorer text in window "w".
3543 */
us_explorerreadoutinit(WINDOWPART * w)3544 void us_explorerreadoutinit(WINDOWPART *w)
3545 {
3546 REGISTER EXPWINDOW *ew;
3547
3548 ew = (EXPWINDOW *)w->expwindow;
3549 ew->depth = 0;
3550 (void)us_expandexplorerdepth(ew);
3551 ew->stack[ew->depth-1] = ew->firstnode;
3552 }
3553
3554 /*
3555 * Routine to get the next line of text in the explorer window "w". Returns
3556 * zero at the end. Sets "indent" to the amount of indentation of this line.
3557 * Sets "style" to the style of the line (1 for empty box, 2 for "+" in box,
3558 * and 3 for "-" in box). "donelist" is set to the address of an array of
3559 * BOOLEANs that indicates, for each level of indentation, whether the column is
3560 * done or needs a continuation bar in it.
3561 */
us_explorerreadoutnext(WINDOWPART * w,INTBIG * indent,INTBIG * style,BOOLEAN ** donelist,void ** ven)3562 CHAR *us_explorerreadoutnext(WINDOWPART *w, INTBIG *indent, INTBIG *style, BOOLEAN **donelist,
3563 void **ven)
3564 {
3565 REGISTER CHAR *name;
3566 REGISTER EXPLORERNODE *en;
3567 REGISTER INTBIG i;
3568 REGISTER EXPWINDOW *ew;
3569
3570 ew = (EXPWINDOW *)w->expwindow;
3571
3572 /* pop out if at the end of a depth traversal */
3573 while (ew->stack[ew->depth-1] == NOEXPLORERNODE)
3574 {
3575 ew->depth--;
3576 if (ew->depth == 0) return(0);
3577 }
3578
3579 /* get the current explorer node */
3580 en = ew->stack[ew->depth-1];
3581 ew->stack[ew->depth-1] = en->nextsubexplorernode;
3582
3583 *indent = ew->depth-1;
3584 name = us_describeexplorernode(en, FALSE);
3585 if (name == 0) name = x_("?");
3586 *style = 1;
3587 if (en->subexplorernode != NOEXPLORERNODE || (en->flags&EXNODEPLACEHOLDER) != 0)
3588 {
3589 /* indicate "-" in the box: it has children */
3590 *style = 3;
3591 if ((en->flags&EXNODEOPEN) == 0)
3592 {
3593 /* indicate a "+" because it is not expanded */
3594 *style = 2;
3595 }
3596 }
3597
3598 /* fill in the indication of which levels are at the end */
3599 for(i=0; i<ew->depth; i++)
3600 {
3601 ew->stackdone[i] = FALSE;
3602 if (i < ew->depth-2 && ew->stack[i+1] == NOEXPLORERNODE)
3603 ew->stackdone[i] = TRUE;
3604 }
3605 *donelist = ew->stackdone;
3606
3607 /* now draw children */
3608 if ((en->flags&EXNODEOPEN) != 0 && en->subexplorernode != NOEXPLORERNODE)
3609 {
3610 if (us_expandexplorerdepth(ew)) return(0);
3611 ew->stack[ew->depth-1] = en->subexplorernode;
3612 }
3613
3614 if (ven != 0)
3615 *((EXPLORERNODE **)ven) = en;
3616 return(name);
3617 }
3618
3619 /*
3620 * Redisplay routine for explorer window "w".
3621 */
us_exploreredisphandler(WINDOWPART * w)3622 void us_exploreredisphandler(WINDOWPART *w)
3623 {
3624 INTBIG wid, hei, indent, style;
3625 UINTBIG descript[TEXTDESCRIPTSIZE];
3626 REGISTER INTBIG visiblelines, thumbsize, thumbtop, thumbarea, line, xpos, ypos,
3627 topypos, lastboxxpos, i, l, boxsize, yposbox, dashindent, leftmargin, thumbcenter;
3628 BOOLEAN *done;
3629 REGISTER CHAR *name;
3630 CHAR onechar[2];
3631 EXPLORERNODE *en;
3632 REGISTER VARIABLE *var;
3633 WINDOWPART ww;
3634 extern GRAPHICS us_cellgra;
3635 REGISTER EXPWINDOW *ew;
3636
3637 ew = (EXPWINDOW *)w->expwindow;
3638 if (ew == NOEXPWINDOW) return;
3639 us_erasewindow(w);
3640
3641 /* determine text size */
3642 var = getval((INTBIG)us_tool, VTOOL, VINTEGER, x_("USER_facet_explorer_textsize"));
3643 if (var == NOVARIABLE) us_exploretextsize = DEFAULTEXPLORERTEXTSIZE; else
3644 us_exploretextsize = var->addr;
3645 TDCLEAR(descript);
3646 TDSETSIZE(descript, TXTSETPOINTS(us_exploretextsize));
3647 screensettextinfo(w, NOTECHNOLOGY, descript);
3648 screengettextsize(w, x_("Xy"), &wid, &hei);
3649 us_exploretextheight = hei;
3650 screengettextsize(w, x_("W"), &wid, &hei);
3651 us_exploretextwidth = wid;
3652 visiblelines = (w->usehy - w->usely - DISPLAYSLIDERSIZE) / us_exploretextheight;
3653
3654 /* reset indication of which lines are visible */
3655 for(en = ew->nodeused; en != NOEXPLORERNODE; en = en->nextexplorernode)
3656 en->flags &= ~EXNODESHOWN;
3657
3658 /* redraw the explorer information */
3659 us_explorerreadoutinit(w);
3660 us_cellgra.col = BLACK;
3661 ew->totallines = 0;
3662 leftmargin = w->uselx + 5;
3663 lastboxxpos = -1;
3664 boxsize = us_exploretextheight * 2 / 3;
3665 dashindent = boxsize / 5;
3666 for(line = 0; ; line++)
3667 {
3668 name = us_explorerreadoutnext(w, &indent, &style, &done, (void **)&en);
3669 if (name == 0) break;
3670 ew->totallines++;
3671 if (line < ew->firstline) continue;
3672
3673 xpos = leftmargin + indent * us_exploretextheight - ew->firstchar*us_exploretextwidth;
3674 ypos = w->usehy - (line-ew->firstline+1) * us_exploretextheight;
3675 if (ypos < w->usely+DISPLAYSLIDERSIZE) continue;
3676
3677 /* draw the box */
3678 yposbox = ypos + (us_exploretextheight-boxsize) / 2;
3679 if (xpos >= w->uselx)
3680 {
3681 screendrawline(w, xpos, yposbox, xpos, yposbox+boxsize, &us_cellgra, 0);
3682 screendrawline(w, xpos, yposbox+boxsize, xpos+boxsize, yposbox+boxsize, &us_cellgra, 0);
3683 screendrawline(w, xpos+boxsize, yposbox+boxsize, xpos+boxsize, yposbox, &us_cellgra, 0);
3684 screendrawline(w, xpos+boxsize, yposbox, xpos, yposbox, &us_cellgra, 0);
3685 if (style >= 2)
3686 {
3687 /* draw the "-" in the box: it has children */
3688 screendrawline(w, xpos+dashindent, yposbox+boxsize/2, xpos+boxsize-dashindent, yposbox+boxsize/2,
3689 &us_cellgra, 0);
3690 if (style == 2)
3691 {
3692 /* make the "-" a "+" because it is not expanded */
3693 screendrawline(w, xpos+boxsize/2, yposbox+dashindent, xpos+boxsize/2, yposbox+boxsize-dashindent,
3694 &us_cellgra, 0);
3695 }
3696 }
3697
3698 /* draw the connecting lines */
3699 if (indent > 0)
3700 {
3701 us_cellgra.col = DGRAY;
3702 l = xpos-us_exploretextheight+boxsize/2;
3703 if (l < w->uselx) l = w->uselx;
3704 screendrawline(w, xpos, yposbox+boxsize/2, l, yposbox+boxsize/2, &us_cellgra, 0);
3705
3706 for(i=0; i<indent; i++)
3707 {
3708 if (done[i]) continue;
3709 l = leftmargin + i * us_exploretextheight + boxsize/2 -
3710 ew->firstchar*us_exploretextwidth;
3711 if (l < w->uselx) continue;
3712 if (l == lastboxxpos)
3713 topypos = yposbox+boxsize/2+us_exploretextheight-boxsize/2; else
3714 topypos = yposbox+boxsize/2+us_exploretextheight;
3715 if (topypos > w->usehy) topypos = w->usehy-1;
3716 screendrawline(w, l, yposbox+boxsize/2, l, topypos, &us_cellgra, 0);
3717 }
3718 us_cellgra.col = BLACK;
3719 }
3720 }
3721 lastboxxpos = xpos + boxsize/2;
3722
3723 /* show the text */
3724 l = xpos + boxsize + boxsize/2;
3725 while (l < w->uselx && *name != 0)
3726 {
3727 onechar[0] = *name++;
3728 onechar[1] = 0;
3729 screengettextsize(w, onechar, &wid, &hei);
3730 l += wid;
3731 }
3732 if (*name == 0) wid = hei = 0; else
3733 {
3734 screendrawtext(w, l, ypos, name, &us_cellgra);
3735 screengettextsize(w, name, &wid, &hei);
3736 }
3737 en->textwidth = wid;
3738 en->flags |= EXNODESHOWN;
3739 en->x = xpos+boxsize+boxsize/2; en->y = yposbox;
3740 }
3741
3742 if (ew->nodeselected != NOEXPLORERNODE) us_highlightexplorernode(w);
3743
3744 /* draw the vertical slider on the right */
3745 ww.screenlx = ww.uselx = w->uselx;
3746 ww.screenhx = ww.usehx = w->usehx;
3747 ww.screenly = ww.usely = w->usely;
3748 ww.screenhy = ww.usehy = w->usehy;
3749 ww.frame = w->frame;
3750 ww.state = DISPWINDOW;
3751 computewindowscale(&ww);
3752 us_drawverticalslider(w, w->usehx-DISPLAYSLIDERSIZE, w->usely+DISPLAYSLIDERSIZE, w->usehy, FALSE);
3753
3754 thumbsize = visiblelines * 100 / ew->totallines;
3755 if (thumbsize >= 100) thumbsize = 100;
3756 if (thumbsize != 100 || ew->firstline != 0)
3757 {
3758 thumbtop = ew->firstline * 100 / ew->totallines;
3759 thumbarea = w->usehy - w->usely - DISPLAYSLIDERSIZE*3;
3760
3761 w->thumbhy = w->usehy - DISPLAYSLIDERSIZE - thumbarea * thumbtop / 100;
3762 w->thumbly = w->thumbhy - thumbarea * thumbsize / 100;
3763 if (w->thumbhy > w->usehy-DISPLAYSLIDERSIZE-2) w->thumbhy = w->usehy-DISPLAYSLIDERSIZE-2;
3764 if (w->thumbly < w->usely+DISPLAYSLIDERSIZE*2+2) w->thumbly = w->usely+DISPLAYSLIDERSIZE*2+2;
3765 us_drawverticalsliderthumb(w, w->usehx-DISPLAYSLIDERSIZE, w->usely+DISPLAYSLIDERSIZE,
3766 w->usehy, w->thumbly, w->thumbhy);
3767 }
3768
3769 /* draw the horizontal slider on the bottom */
3770 us_drawhorizontalslider(w, w->usely+DISPLAYSLIDERSIZE, w->uselx, w->usehx-DISPLAYSLIDERSIZE, 1);
3771 thumbsize = (w->usehx - w->uselx - DISPLAYSLIDERSIZE*4) / 20;
3772 thumbarea = w->usehx - w->uselx - DISPLAYSLIDERSIZE*4 - thumbsize;
3773 thumbcenter = w->uselx + DISPLAYSLIDERSIZE*2 + ew->firstchar * thumbarea /
3774 MAXCELLEXPLHSCROLL + thumbsize/2;
3775 w->thumblx = thumbcenter - thumbsize/2;
3776 w->thumbhx = thumbcenter + thumbsize/2;
3777 us_drawhorizontalsliderthumb(w, w->usely+DISPLAYSLIDERSIZE, w->uselx,
3778 w->usehx-DISPLAYSLIDERSIZE, w->thumblx, w->thumbhx, 1);
3779
3780 /* draw the explorer icon */
3781 us_drawexplorericon(w, w->uselx, w->usely);
3782 }
3783
3784 /* coordinate pairs for the dots in the explorer icon (measured from low X/Y) */
3785 static INTBIG us_explorerdots[] = {
3786 1,10, 1,9, 1,8, 1,7, 1,6, 1,5, 1,4, 1,3,
3787 2,10, 2,9, 2,8, 2,7, 2,6, 2,5, 2,4, 2,3,
3788 3,8, 3,7, 3,3, 3,2,
3789 4,8, 4,7, 4,3, 4,2,
3790 6,8, 6,7, 6,3, 6,2,
3791 7,9, 7,6, 7,4, 7,1,
3792 8,9, 8,6, 8,4, 8,1,
3793 9,8, 9,7, 9,3, 9,2,
3794 -1,-1};
3795
us_drawexplorericon(WINDOWPART * w,INTBIG lx,INTBIG ly)3796 void us_drawexplorericon(WINDOWPART *w, INTBIG lx, INTBIG ly)
3797 {
3798 us_drawwindowicon(w, lx, ly, us_explorerdots);
3799 }
3800
us_drawwindowicon(WINDOWPART * w,INTBIG lx,INTBIG ly,INTBIG * dots)3801 void us_drawwindowicon(WINDOWPART *w, INTBIG lx, INTBIG ly, INTBIG *dots)
3802 {
3803 REGISTER INTBIG x, y, i;
3804 extern GRAPHICS us_box;
3805
3806 /* erase the icon area */
3807 us_box.col = WHITE;
3808 gra_drawbox(w, lx, lx+DISPLAYSLIDERSIZE-1, ly, ly+DISPLAYSLIDERSIZE-1, &us_box);
3809
3810 /* draw the top and right edge */
3811 us_box.col = BLACK;
3812 gra_drawbox(w, lx, lx+11, ly+11, ly+11, &us_box);
3813 gra_drawbox(w, lx+11, lx+11, ly, ly+11, &us_box);
3814
3815 /* draw the icon dots */
3816 for(i=0; dots[i] >= 0; i += 2)
3817 {
3818 x = lx + dots[i];
3819 y = ly + dots[i+1];
3820 gra_drawbox(w, x, x, y, y, &us_box);
3821 }
3822 }
3823
us_highlightexplorernode(WINDOWPART * w)3824 void us_highlightexplorernode(WINDOWPART *w)
3825 {
3826 REGISTER INTBIG lowx, highx, lowy, highy;
3827 REGISTER EXPWINDOW *ew;
3828
3829 ew = (EXPWINDOW *)w->expwindow;
3830 if ((ew->nodeselected->flags&EXNODESHOWN) == 0) return;
3831 lowx = ew->nodeselected->x;
3832 highx = lowx + ew->nodeselected->textwidth;
3833 if (lowx < w->uselx) lowx = w->uselx;
3834 if (highx > w->usehx-DISPLAYSLIDERSIZE) highx = w->usehx-DISPLAYSLIDERSIZE;
3835 lowy = ew->nodeselected->y - 2;
3836 highy = lowy + us_exploretextheight;
3837 if (lowy < w->usely+DISPLAYSLIDERSIZE) lowy = w->usely+DISPLAYSLIDERSIZE;
3838 if (highy > w->usehy) highy = w->usehy;
3839 screeninvertbox(w, lowx, highx, lowy, highy);
3840 }
3841
3842 /*
3843 * Routine called when up or down slider arrows are clicked.
3844 */
us_explorerarrowdown(INTBIG x,INTBIG y)3845 BOOLEAN us_explorerarrowdown(INTBIG x, INTBIG y)
3846 {
3847 INTBIG visiblelines;
3848 REGISTER EXPWINDOW *ew;
3849
3850 ew = (EXPWINDOW *)us_explorertrackwindow->expwindow;
3851 if (x < us_explorertrackwindow->usehx - DISPLAYSLIDERSIZE) return(FALSE);
3852 visiblelines = (us_explorertrackwindow->usehy - us_explorertrackwindow->usely - DISPLAYSLIDERSIZE) /
3853 us_exploretextheight;
3854 switch (ew->sliderpart)
3855 {
3856 case 0: /* down arrow clicked */
3857 if (y > us_explorertrackwindow->usely + DISPLAYSLIDERSIZE*2) return(FALSE);
3858 us_explorevpan(us_explorertrackwindow, 1);
3859 break;
3860 case 1: /* clicked below thumb */
3861 if (y > us_explorertrackwindow->thumbly) return(FALSE);
3862 if (ew->totallines - ew->firstline <= visiblelines) return(FALSE);
3863 ew->firstline += visiblelines-1;
3864 if (ew->totallines - ew->firstline < visiblelines)
3865 ew->firstline = ew->totallines - visiblelines;
3866 us_exploreredisphandler(us_explorertrackwindow);
3867 break;
3868 case 2: /* clicked on thumb (not done here) */
3869 break;
3870 case 3: /* clicked above thumb */
3871 if (y < us_explorertrackwindow->thumbhy) return(FALSE);
3872 ew->firstline -= visiblelines-1;
3873 if (ew->firstline < 0) ew->firstline = 0;
3874 us_exploreredisphandler(us_explorertrackwindow);
3875 break;
3876 case 4: /* up arrow clicked */
3877 if (y <= us_explorertrackwindow->usehy - DISPLAYSLIDERSIZE) return(FALSE);
3878 us_explorevpan(us_explorertrackwindow, -1);
3879 break;
3880 }
3881 return(FALSE);
3882 }
3883
us_explorevpan(WINDOWPART * w,INTBIG dy)3884 void us_explorevpan(WINDOWPART *w, INTBIG dy)
3885 {
3886 INTBIG visiblelines;
3887 REGISTER EXPWINDOW *ew;
3888
3889 ew = (EXPWINDOW *)w->expwindow;
3890 visiblelines = (w->usehy - w->usely - DISPLAYSLIDERSIZE) / us_exploretextheight;
3891 if (dy > 0)
3892 {
3893 /* down shift */
3894 if (ew->totallines - ew->firstline <= visiblelines) return;
3895 ew->firstline++;
3896 us_exploreredisphandler(w);
3897 } else if (dy < 0)
3898 {
3899 /* up shift */
3900 if (ew->firstline > 0)
3901 {
3902 ew->firstline--;
3903 us_exploreredisphandler(w);
3904 }
3905 }
3906 }
3907
3908 /*
3909 * Routine called when left or right slider arrows are clicked.
3910 */
us_explorerarrowleft(INTBIG x,INTBIG y)3911 BOOLEAN us_explorerarrowleft(INTBIG x, INTBIG y)
3912 {
3913 INTBIG visiblechars, lx;
3914 REGISTER EXPWINDOW *ew;
3915
3916 ew = (EXPWINDOW *)us_explorertrackwindow->expwindow;
3917 if (y > us_explorertrackwindow->usely + DISPLAYSLIDERSIZE) return(FALSE);
3918 lx = us_explorertrackwindow->uselx + DISPLAYSLIDERSIZE;
3919 visiblechars = (us_explorertrackwindow->usehx - lx - DISPLAYSLIDERSIZE) /
3920 us_exploretextwidth;
3921 switch (ew->sliderpart)
3922 {
3923 case 0: /* left arrow clicked */
3924 if (x > lx + DISPLAYSLIDERSIZE) return(FALSE);
3925 us_explorehpan(us_explorertrackwindow, -1);
3926 break;
3927 case 1: /* clicked to left of thumb */
3928 if (x > us_explorertrackwindow->thumblx) return(FALSE);
3929 ew->firstchar -= visiblechars - 5;
3930 if (ew->firstchar < 0) ew->firstchar = 0;
3931 us_exploreredisphandler(us_explorertrackwindow);
3932 break;
3933 case 2: /* clicked on thumb (not done here) */
3934 break;
3935 case 3: /* clicked to right of thumb */
3936 if (x < us_explorertrackwindow->thumbhx) return(FALSE);
3937 ew->firstchar += visiblechars - 5;
3938 if (ew->firstchar > MAXCELLEXPLHSCROLL)
3939 ew->firstchar = MAXCELLEXPLHSCROLL;
3940 us_exploreredisphandler(us_explorertrackwindow);
3941 break;
3942 case 4: /* right arrow clicked */
3943 if (x < us_explorertrackwindow->usehx - DISPLAYSLIDERSIZE*2) return(FALSE);
3944 us_explorehpan(us_explorertrackwindow, 1);
3945 break;
3946 }
3947 return(FALSE);
3948 }
3949
us_explorehpan(WINDOWPART * w,INTBIG dy)3950 void us_explorehpan(WINDOWPART *w, INTBIG dy)
3951 {
3952 REGISTER EXPWINDOW *ew;
3953
3954 ew = (EXPWINDOW *)w->expwindow;
3955 if (dy > 0)
3956 {
3957 /* left shift */
3958 if (ew->firstchar < MAXCELLEXPLHSCROLL)
3959 {
3960 ew->firstchar++;
3961 us_exploreredisphandler(w);
3962 }
3963 } else if (dy < 0)
3964 {
3965 /* right shift */
3966 if (ew->firstchar > 0)
3967 {
3968 ew->firstchar--;
3969 us_exploreredisphandler(w);
3970 }
3971 }
3972 }
3973
3974 /*
3975 * Button handler for explorer window "w". Button "but" was pushed at (x, y).
3976 */
us_explorebuttonhandler(WINDOWPART * w,INTBIG but,INTBIG x,INTBIG y)3977 void us_explorebuttonhandler(WINDOWPART *w, INTBIG but, INTBIG x, INTBIG y)
3978 {
3979 REGISTER EXPLORERNODE *en;
3980 INTBIG visiblelines, i, boxsize;
3981 REGISTER POPUPMENUITEM *mi;
3982 POPUPMENU *pm, *cpm;
3983 BOOLEAN waitfordown;
3984 REGISTER INTBIG numcontexts;
3985 REGISTER EXPWINDOW *ew;
3986
3987 ew = (EXPWINDOW *)w->expwindow;
3988
3989 /* changes to the mouse-wheel are handled by the user interface */
3990 if (wheelbutton(but))
3991 {
3992 us_buttonhandler(w, but, x, y);
3993 return;
3994 }
3995
3996 if (x >= w->usehx - DISPLAYSLIDERSIZE)
3997 {
3998 /* click in vertical slider */
3999 visiblelines = (w->usehy - w->usely - DISPLAYSLIDERSIZE) / us_exploretextheight;
4000 if (y < w->usely + DISPLAYSLIDERSIZE) return;
4001 if (y <= w->usely + DISPLAYSLIDERSIZE*2)
4002 {
4003 /* down arrow: shift base line (may repeat) */
4004 us_explorertrackwindow = w;
4005 ew->sliderpart = 0;
4006 trackcursor(FALSE, us_nullup, us_nullvoid, us_explorerarrowdown, us_nullchar,
4007 us_nullvoid, TRACKNORMAL);
4008 return;
4009 }
4010 if (y > w->usehy - DISPLAYSLIDERSIZE)
4011 {
4012 /* up arrow: shift base line (may repeat) */
4013 us_explorertrackwindow = w;
4014 ew->sliderpart = 4;
4015 trackcursor(FALSE, us_nullup, us_nullvoid, us_explorerarrowdown, us_nullchar,
4016 us_nullvoid, TRACKNORMAL);
4017 return;
4018 }
4019 if (y < w->thumbly)
4020 {
4021 /* below thumb: shift way down (may repeat) */
4022 us_explorertrackwindow = w;
4023 ew->sliderpart = 1;
4024 trackcursor(FALSE, us_nullup, us_nullvoid, us_explorerarrowdown, us_nullchar,
4025 us_nullvoid, TRACKNORMAL);
4026 return;
4027 }
4028 if (y > w->thumbhy)
4029 {
4030 /* above thumb: shift way up (may repeat) */
4031 us_explorertrackwindow = w;
4032 ew->sliderpart = 3;
4033 trackcursor(FALSE, us_nullup, us_nullvoid, us_explorerarrowdown, us_nullchar,
4034 us_nullvoid, TRACKNORMAL);
4035 return;
4036 }
4037
4038 /* on the thumb: track its motion */
4039 if (visiblelines >= ew->totallines) return;
4040 us_explorertrackwindow = w;
4041 ew->deltasofar = 0;
4042 ew->initialthumb = w->thumbhy;
4043 us_vthumbbegin(y, w, w->usehx-DISPLAYSLIDERSIZE, w->usely+DISPLAYSLIDERSIZE,
4044 w->usehy, FALSE, us_evthumbtrackingtextcallback);
4045 trackcursor(FALSE, us_nullup, us_nullvoid, us_vthumbdown, us_nullchar,
4046 us_vthumbdone, TRACKNORMAL);
4047 return;
4048 }
4049
4050 if (y <= w->usely + DISPLAYSLIDERSIZE)
4051 {
4052 /* click in horizontal slider */
4053 if (x <= w->uselx + DISPLAYSLIDERSIZE)
4054 {
4055 /* explorerwindow icon */
4056 el_curwindowpart = w;
4057 us_killcurrentwindow(TRUE);
4058 return;
4059 }
4060 if (x <= w->uselx + DISPLAYSLIDERSIZE*2)
4061 {
4062 /* left arrow: shift text (may repeat) */
4063 us_explorertrackwindow = w;
4064 ew->sliderpart = 0;
4065 trackcursor(FALSE, us_nullup, us_nullvoid, us_explorerarrowleft, us_nullchar,
4066 us_nullvoid, TRACKNORMAL);
4067 return;
4068 }
4069 if (x >= w->usehx - DISPLAYSLIDERSIZE*2)
4070 {
4071 /* up arrow: shift base line (may repeat) */
4072 us_explorertrackwindow = w;
4073 ew->sliderpart = 4;
4074 trackcursor(FALSE, us_nullup, us_nullvoid, us_explorerarrowleft, us_nullchar,
4075 us_nullvoid, TRACKNORMAL);
4076 return;
4077 }
4078 if (x < w->thumblx)
4079 {
4080 /* to left of thumb: shift way down (may repeat) */
4081 us_explorertrackwindow = w;
4082 ew->sliderpart = 1;
4083 trackcursor(FALSE, us_nullup, us_nullvoid, us_explorerarrowleft, us_nullchar,
4084 us_nullvoid, TRACKNORMAL);
4085 return;
4086 }
4087 if (x > w->thumbhx)
4088 {
4089 /* to right of thumb: shift way up (may repeat) */
4090 us_explorertrackwindow = w;
4091 ew->sliderpart = 3;
4092 trackcursor(FALSE, us_nullup, us_nullvoid, us_explorerarrowleft, us_nullchar,
4093 us_nullvoid, TRACKNORMAL);
4094 return;
4095 }
4096
4097 /* on the thumb: track its motion */
4098 us_explorertrackwindow = w;
4099 ew->deltasofar = 0;
4100 ew->initialthumb = w->thumblx;
4101 us_hthumbbegin(x, w, w->usely+DISPLAYSLIDERSIZE, w->uselx,
4102 w->usehx-DISPLAYSLIDERSIZE, us_ehthumbtrackingtextcallback);
4103 trackcursor(FALSE, us_nullup, us_nullvoid, us_hthumbdown, us_nullchar,
4104 us_hthumbdone, TRACKNORMAL);
4105 return;
4106 }
4107
4108 /* deselect */
4109 if (ew->nodeselected != NOEXPLORERNODE) us_highlightexplorernode(w);
4110 ew->nodeselected = NOEXPLORERNODE;
4111
4112 boxsize = us_exploretextheight * 2 / 3;
4113 for(en = ew->nodeused; en != NOEXPLORERNODE; en = en->nextexplorernode)
4114 {
4115 if ((en->flags&EXNODESHOWN) == 0) continue;
4116 if (y < en->y || y >= en->y + us_exploretextheight) continue;
4117 if (x >= en->x - boxsize - boxsize/2 && x <= en->x - boxsize/2)
4118 {
4119 /* hit the box to the left of the name */
4120 us_explorertoggleopenness(w, en);
4121 return;
4122 }
4123
4124 if (x >= en->x && x <= en->x+en->textwidth)
4125 {
4126 /* hit the name of a cell/library: highlight it */
4127 ew->nodeselected = en;
4128 us_highlightexplorernode(w);
4129
4130 if (doublebutton(but))
4131 {
4132 /* do the first context function */
4133 us_explorerdofunction(w, en, 0);
4134 }
4135
4136 if (contextbutton(but))
4137 {
4138 /* show the context menu */
4139 pm = (POPUPMENU *)emalloc(sizeof(POPUPMENU), el_tempcluster);
4140 if (pm == 0) return;
4141 pm->name = x_("noname");
4142 mi = (POPUPMENUITEM *)emalloc((5 * (sizeof (POPUPMENUITEM))), el_tempcluster);
4143 if (mi == 0) return;
4144 pm->list = mi;
4145 for(i=0; i<5; i++)
4146 {
4147 mi[i].valueparse = NOCOMCOMP;
4148 mi[i].response = NOUSERCOM;
4149 mi[i].value = 0;
4150 mi[i].maxlen = -1;
4151 }
4152 numcontexts = 0;
4153 switch (en->enodetype)
4154 {
4155 case EXNODETYPELIBRARY:
4156 mi[numcontexts++].attribute = _("Make Library Current");
4157 mi[numcontexts++].attribute = _("Delete Library");
4158 mi[numcontexts++].attribute = _("Rename Library");
4159 mi[numcontexts++].attribute = _("Open Entire Tree below Here");
4160 mi[numcontexts++].attribute = _("Close Entire Tree below Here");
4161 break;
4162 case EXNODETYPECELL:
4163 mi[numcontexts++].attribute = _("Edit Cell");
4164 mi[numcontexts++].attribute = _("Delete Cell");
4165 mi[numcontexts++].attribute = _("Rename Cell");
4166 mi[numcontexts++].attribute = _("Open Entire Tree below Here");
4167 mi[numcontexts++].attribute = _("Close Entire Tree below Here");
4168 break;
4169 case EXNODETYPENODE:
4170 mi[numcontexts++].attribute = _("Show Node");
4171 break;
4172 case EXNODETYPEARC:
4173 mi[numcontexts++].attribute = _("Show Arc");
4174 break;
4175 case EXNODETYPENETWORK:
4176 mi[numcontexts++].attribute = _("Show Network");
4177 break;
4178 case EXNODETYPEEXPORT:
4179 mi[numcontexts++].attribute = _("Show Export");
4180 break;
4181 case EXNODETYPEERROR:
4182 mi[numcontexts++].attribute = _("Show Error");
4183 break;
4184 case EXNODETYPEERRORGEOM:
4185 mi[numcontexts++].attribute = _("Show Object");
4186 break;
4187 case EXNODETYPESIMBRANCH:
4188 mi[numcontexts++].attribute = _("Open Entire Tree below Here");
4189 mi[numcontexts++].attribute = _("Close Entire Tree below Here");
4190 break;
4191 case EXNODETYPESIMLEAF:
4192 mi[numcontexts++].attribute = _("Overlay to Waveform Window");
4193 mi[numcontexts++].attribute = _("Add to Waveform Window");
4194 break;
4195 case EXNODETYPELABEL:
4196 mi[numcontexts++].attribute = _("Open/Close");
4197 mi[numcontexts++].attribute = _("Open Entire Tree below Here");
4198 mi[numcontexts++].attribute = _("Close Entire Tree below Here");
4199 break;
4200 }
4201 pm->total = numcontexts;
4202 cpm = pm;
4203 waitfordown = FALSE;
4204 mi = us_popupmenu(&cpm, &waitfordown, FALSE, -1, -1, 0);
4205 if (mi != 0 && mi != NOPOPUPMENUITEM)
4206 {
4207 i = mi - pm->list;
4208 us_explorerdofunction(w, en, i);
4209 }
4210 efree((CHAR *)pm->list);
4211 efree((CHAR *)pm);
4212 return;
4213 }
4214 }
4215 }
4216 }
4217
4218 /*
4219 * Routine to toggle the open/closed state of explorer node "en" in window "w".
4220 */
us_explorertoggleopenness(WINDOWPART * w,EXPLORERNODE * en)4221 void us_explorertoggleopenness(WINDOWPART *w, EXPLORERNODE *en)
4222 {
4223 REGISTER EXPWINDOW *ew;
4224
4225 ew = (EXPWINDOW *)w->expwindow;
4226 if ((en->flags & EXNODEOPEN) != 0) en->flags &= ~EXNODEOPEN; else
4227 {
4228 if ((en->flags&EXNODEPLACEHOLDER) != 0)
4229 {
4230 if (en == ew->nodehierarchy)
4231 {
4232 us_buildexplorersturcthierarchical(ew, en);
4233 } else if (en == ew->nodecontents)
4234 {
4235 us_buildexplorersturctcontents(ew, en);
4236 } else if (en == ew->nodeerrors)
4237 {
4238 us_buildexplorersturcterrors(ew, en);
4239 } else if (en == ew->nodesimulation)
4240 {
4241 us_buildexplorersturctsimulation(w, ew, en);
4242 } else if (en->enodetype == EXNODETYPECELL)
4243 {
4244 us_buildexplorernodecontents(ew, (NODEPROTO *)en->addr, en);
4245 }
4246 }
4247 en->flags |= EXNODEOPEN;
4248 }
4249 us_exploreredisphandler(w);
4250 }
4251
4252 /*
4253 * Routine to perform function "funct" on explorer node "en".
4254 * The function numbers are from the context menu that can be popped-up on that
4255 * node. Double-clicking does the first function.
4256 */
us_explorerdofunction(WINDOWPART * w,EXPLORERNODE * en,INTBIG funct)4257 void us_explorerdofunction(WINDOWPART *w, EXPLORERNODE *en, INTBIG funct)
4258 {
4259 REGISTER NODEPROTO *np;
4260 REGISTER NODEINST *ni;
4261 REGISTER ARCINST *ai;
4262 REGISTER PORTPROTO *pp;
4263 REGISTER NETWORK *net;
4264 REGISTER BOOLEAN found, first, overlay;
4265 REGISTER INTBIG i;
4266 REGISTER void *infstr, *altinfstr;
4267 REGISTER CHAR *pt;
4268 REGISTER WINDOWPART *ow;
4269 INTBIG lx, hx, ly, hy;
4270 CHAR *strname;
4271 REGISTER EXPWINDOW *ew;
4272
4273 ew = (EXPWINDOW *)w->expwindow;
4274 switch (en->enodetype)
4275 {
4276 case EXNODETYPELIBRARY:
4277 switch (funct)
4278 {
4279 case 0: /* make this the current library */
4280 us_switchtolibrary((LIBRARY *)en->addr);
4281 return;
4282 case 1: /* delete the library */
4283 us_explorerdelete(en);
4284 return;
4285 case 2: /* rename the library */
4286 us_explorerrename(en, w);
4287 return;
4288 case 3: /* open entire tree below here */
4289 us_explorrecurseexpand(w, ew, en, TRUE);
4290 us_exploreredisphandler(w);
4291 return;
4292 case 4: /* close entire tree below here */
4293 us_explorrecurseexpand(w, ew, en, FALSE);
4294 us_exploreredisphandler(w);
4295 return;
4296 }
4297 break;
4298 case EXNODETYPECELL:
4299 switch (funct)
4300 {
4301 case 0: /* show this cell */
4302 np = (NODEPROTO *)en->addr;
4303 for(ow = el_topwindowpart; ow != NOWINDOWPART; ow = ow->nextwindowpart)
4304 {
4305 if (ow == w) continue;
4306 if (estrcmp(w->location, x_("entire")) != 0)
4307 if (ow->frame != w->frame) continue;
4308 break;
4309 }
4310 us_fullview(np, &lx, &hx, &ly, &hy);
4311 if (ow == NOWINDOWPART)
4312 {
4313 /* no other window can be found: create one */
4314 us_switchtocell(np, lx, hx, ly, hy, NONODEINST, NOPORTPROTO, TRUE, FALSE, FALSE);
4315 } else
4316 {
4317 us_highlightwindow(ow, FALSE);
4318 us_switchtocell(np, lx, hx, ly, hy, NONODEINST, NOPORTPROTO, FALSE, FALSE, FALSE);
4319 }
4320 return;
4321 case 1: /* delete this cell */
4322 us_explorerdelete(en);
4323 return;
4324 case 2: /* rename the cell */
4325 us_explorerrename(en, w);
4326 return;
4327 case 3: /* open entire tree below here */
4328 us_explorrecurseexpand(w, ew, en, TRUE);
4329 us_exploreredisphandler(w);
4330 return;
4331 case 4: /* close entire tree below here */
4332 us_explorrecurseexpand(w, ew, en, FALSE);
4333 us_exploreredisphandler(w);
4334 return;
4335 }
4336 break;
4337 case EXNODETYPENODE:
4338 switch (funct)
4339 {
4340 case 0: /* show this node */
4341 ni = (NODEINST *)en->addr;
4342 infstr = initinfstr();
4343 formatinfstr(infstr, x_("CELL=%s FROM=0%lo;-1;0"),
4344 describenodeproto(ni->parent), (INTBIG)ni->geom);
4345 us_setmultiplehighlight(returninfstr(infstr), FALSE);
4346 us_showallhighlight();
4347 us_endchanges(NOWINDOWPART);
4348 return;
4349 }
4350 break;
4351 case EXNODETYPEARC:
4352 switch (funct)
4353 {
4354 case 0: /* show this arc */
4355 ai = (ARCINST *)en->addr;
4356 infstr = initinfstr();
4357 formatinfstr(infstr, x_("CELL=%s FROM=0%lo;-1;0"),
4358 describenodeproto(ai->parent), (INTBIG)ai->geom);
4359 us_setmultiplehighlight(returninfstr(infstr), FALSE);
4360 us_showallhighlight();
4361 us_endchanges(NOWINDOWPART);
4362 return;
4363 }
4364 break;
4365 case EXNODETYPENETWORK:
4366 switch (funct)
4367 {
4368 case 0: /* show this network */
4369 net = (NETWORK *)en->addr;
4370 np = net->parent;
4371 infstr = initinfstr();
4372 first = FALSE;
4373 for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
4374 {
4375 found = FALSE;
4376 if (ai->network == net) found = TRUE; else
4377 {
4378 if (net->buswidth > 1)
4379 {
4380 for(i=0; i<net->buswidth; i++)
4381 if (net->networklist[i] == ai->network) break;
4382 if (i < net->buswidth) found = TRUE;
4383 }
4384 if (ai->network->buswidth > 1)
4385 {
4386 for(i=0; i<ai->network->buswidth; i++)
4387 if (ai->network->networklist[i] == net) break;
4388 if (i < ai->network->buswidth) found = TRUE;
4389 }
4390 }
4391 if (!found) continue;
4392 if (first) addtoinfstr(infstr, '\n');
4393 first = TRUE;
4394 formatinfstr(infstr, x_("CELL=%s FROM=0%lo;-1;0"),
4395 describenodeproto(np), (INTBIG)ai->geom);
4396 }
4397 for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
4398 {
4399 if (pp->network != net) continue;
4400 if (first) addtoinfstr(infstr, '\n');
4401 first = TRUE;
4402 formatinfstr(infstr, x_("CELL=%s TEXT=0%lo;0%lo;0"),
4403 describenodeproto(np), (INTBIG)pp->subnodeinst->geom,
4404 (INTBIG)pp);
4405 }
4406 us_setmultiplehighlight(returninfstr(infstr), FALSE);
4407 us_showallhighlight();
4408 us_endchanges(NOWINDOWPART);
4409 return;
4410 }
4411 break;
4412 case EXNODETYPEEXPORT:
4413 switch (funct)
4414 {
4415 case 0: /* show this export */
4416 pp = (PORTPROTO *)en->addr;
4417 infstr = initinfstr();
4418 formatinfstr(infstr, x_("CELL=%s TEXT=0%lo;0%lo;0"),
4419 describenodeproto(pp->parent), (INTBIG)pp->subnodeinst->geom,
4420 (INTBIG)pp);
4421 us_setmultiplehighlight(returninfstr(infstr), FALSE);
4422 us_showallhighlight();
4423 us_endchanges(NOWINDOWPART);
4424 return;
4425 }
4426 break;
4427 case EXNODETYPEERROR:
4428 switch (funct)
4429 {
4430 case 0:
4431 reporterror((void *)en->addr);
4432 return;
4433 }
4434 break;
4435 case EXNODETYPEERRORGEOM:
4436 switch (funct)
4437 {
4438 case 0:
4439 us_clearhighlightcount();
4440 showerrorgeom((void *)en->addr);
4441 return;
4442 }
4443 break;
4444 case EXNODETYPESIMBRANCH:
4445 switch (funct)
4446 {
4447 case 0: /* open entire tree below here */
4448 us_explorrecurseexpand(w, ew, en, TRUE);
4449 us_exploreredisphandler(w);
4450 return;
4451 case 1: /* close entire tree below here */
4452 us_explorrecurseexpand(w, ew, en, FALSE);
4453 us_exploreredisphandler(w);
4454 return;
4455 }
4456 break;
4457 case EXNODETYPESIMLEAF:
4458 switch (funct)
4459 {
4460 case 0: /* overlay signal to waveform */
4461 case 1: /* add signal to waveform */
4462 infstr = initinfstr();
4463 for(;;)
4464 {
4465 if (en->enodetype != EXNODETYPESIMBRANCH &&
4466 en->enodetype != EXNODETYPESIMLEAF) break;
4467 altinfstr = initinfstr();
4468 addstringtoinfstr(altinfstr, (CHAR *)en->addr);
4469 pt = returninfstr(infstr);
4470 if (*pt != 0) formatinfstr(altinfstr, x_("%s%s"), sim_signalseparator(), pt);
4471 infstr = altinfstr;
4472
4473 en = en->parent;
4474 if (en == NOEXPLORERNODE) break;
4475 }
4476 allocstring(&strname, returninfstr(infstr), el_tempcluster);
4477 if (funct == 0) overlay = TRUE; else overlay = FALSE;
4478 sim_addsignal(w, strname, overlay);
4479 efree(strname);
4480 return;
4481 }
4482 break;
4483 case EXNODETYPELABEL:
4484 switch (funct)
4485 {
4486 case 0: /* toggle open/closed state */
4487 us_explorertoggleopenness(w, en);
4488 return;
4489 case 1: /* open entire tree below here */
4490 us_explorrecurseexpand(w, ew, en, TRUE);
4491 us_exploreredisphandler(w);
4492 return;
4493 case 2: /* close entire tree below here */
4494 us_explorrecurseexpand(w, ew, en, FALSE);
4495 us_exploreredisphandler(w);
4496 return;
4497 }
4498 break;
4499 }
4500 }
4501
4502 /*
4503 * Routine to recursively open or close all nodes below "en". Opens if "expand" is TRUE,
4504 * closes if "expand" is FALSE.
4505 */
us_explorrecurseexpand(WINDOWPART * w,EXPWINDOW * ew,EXPLORERNODE * en,BOOLEAN expand)4506 void us_explorrecurseexpand(WINDOWPART *w, EXPWINDOW *ew, EXPLORERNODE *en, BOOLEAN expand)
4507 {
4508 REGISTER EXPLORERNODE *suben;
4509
4510 if (expand)
4511 {
4512 if ((en->flags&EXNODEPLACEHOLDER) != 0)
4513 {
4514 if (en == ew->nodehierarchy)
4515 {
4516 us_buildexplorersturcthierarchical(ew, en);
4517 } else if (en == ew->nodecontents)
4518 {
4519 us_buildexplorersturctcontents(ew, en);
4520 } else if (en == ew->nodeerrors)
4521 {
4522 us_buildexplorersturcterrors(ew, en);
4523 } else if (en == ew->nodesimulation)
4524 {
4525 us_buildexplorersturctsimulation(w, ew, en);
4526 } else if (en->enodetype == EXNODETYPECELL)
4527 {
4528 us_buildexplorernodecontents(ew, (NODEPROTO *)en->addr, en);
4529 }
4530 }
4531 en->flags |= EXNODEOPEN;
4532 } else
4533 en->flags &= ~EXNODEOPEN;
4534 for(suben = en->subexplorernode; suben != NOEXPLORERNODE; suben = suben->nextsubexplorernode)
4535 us_explorrecurseexpand(w, ew, suben, expand);
4536 }
4537
us_evthumbtrackingtextcallback(INTBIG delta)4538 void us_evthumbtrackingtextcallback(INTBIG delta)
4539 {
4540 REGISTER INTBIG point, thumbarea, thumbpos, visiblelines;
4541 REGISTER EXPWINDOW *ew;
4542
4543 ew = (EXPWINDOW *)us_explorertrackwindow->expwindow;
4544 ew->deltasofar += delta;
4545 visiblelines = (us_explorertrackwindow->usehy - us_explorertrackwindow->usely + DISPLAYSLIDERSIZE) /
4546 us_exploretextheight;
4547 thumbpos = ew->initialthumb + ew->deltasofar;
4548 thumbarea = us_explorertrackwindow->usehy - us_explorertrackwindow->usely - DISPLAYSLIDERSIZE*3;
4549 point = (us_explorertrackwindow->usehy - DISPLAYSLIDERSIZE - thumbpos) * ew->totallines / thumbarea;
4550 if (point < 0) point = 0;
4551 if (point > ew->totallines-visiblelines) point = ew->totallines-visiblelines;
4552 ew->firstline = point;
4553 us_exploreredisphandler(us_explorertrackwindow);
4554 }
4555
us_ehthumbtrackingtextcallback(INTBIG delta)4556 void us_ehthumbtrackingtextcallback(INTBIG delta)
4557 {
4558 REGISTER INTBIG point, thumbarea, thumbpos, thumbsize, visiblechars, lx;
4559 REGISTER EXPWINDOW *ew;
4560
4561 ew = (EXPWINDOW *)us_explorertrackwindow->expwindow;
4562 lx = us_explorertrackwindow->uselx + DISPLAYSLIDERSIZE;
4563 ew->deltasofar += delta;
4564 visiblechars = (us_explorertrackwindow->usehx - lx - DISPLAYSLIDERSIZE) /
4565 us_exploretextwidth;
4566 thumbpos = ew->initialthumb + ew->deltasofar;
4567 thumbsize = (us_explorertrackwindow->usehx - lx - DISPLAYSLIDERSIZE*3) / 20;
4568 thumbarea = us_explorertrackwindow->usehx - lx - DISPLAYSLIDERSIZE*3 - thumbsize;
4569 point = thumbpos * MAXCELLEXPLHSCROLL / thumbarea - lx -
4570 DISPLAYSLIDERSIZE;
4571 if (point < 0) point = 0;
4572 if (point > MAXCELLEXPLHSCROLL) point = MAXCELLEXPLHSCROLL;
4573 ew->firstchar = point;
4574 us_exploreredisphandler(us_explorertrackwindow);
4575 }
4576
us_explorecharhandler(WINDOWPART * w,INTSML cmd,INTBIG special)4577 BOOLEAN us_explorecharhandler(WINDOWPART *w, INTSML cmd, INTBIG special)
4578 {
4579 REGISTER EXPWINDOW *ew;
4580
4581 ew = (EXPWINDOW *)w->expwindow;
4582 if (cmd == DELETEKEY || cmd == BACKSPACEKEY)
4583 {
4584 /* delete selected cell */
4585 if (ew->nodeselected == NOEXPLORERNODE)
4586 {
4587 ttyputerr(_("Select a cell name before deleting it"));
4588 return(FALSE);
4589 }
4590 us_explorerdelete(ew->nodeselected);
4591 return(FALSE);
4592 }
4593 return(us_charhandler(w, cmd, special));
4594 }
4595
4596 /*
4597 * Routine to delete the object pointed to by explorer node "en" (either a
4598 * cell or a library).
4599 */
us_explorerdelete(EXPLORERNODE * en)4600 void us_explorerdelete(EXPLORERNODE *en)
4601 {
4602 REGISTER NODEPROTO *np;
4603 extern COMCOMP us_yesnop;
4604 REGISTER INTBIG i;
4605 REGISTER void *infstr;
4606 CHAR *pars[3];
4607
4608 if (en->enodetype == EXNODETYPELIBRARY)
4609 {
4610 pars[0] = x_("kill");
4611 pars[1] = ((LIBRARY *)en->addr)->libname;
4612 us_library(2, pars);
4613 return;
4614 }
4615 if (en->enodetype == EXNODETYPECELL)
4616 {
4617 np = (NODEPROTO *)en->addr;
4618 if (np->firstinst != NONODEINST)
4619 {
4620 ttyputerr(_("Can only delete top-level cells"));
4621 return;
4622 }
4623 infstr = initinfstr();
4624 formatinfstr(infstr, _("Are you sure you want to delete cell %s"),
4625 describenodeproto(np));
4626 i = ttygetparam(returninfstr(infstr), &us_yesnop, 3, pars);
4627 if (i == 1)
4628 {
4629 if (pars[0][0] == 'y')
4630 {
4631 us_clearhighlightcount();
4632 pars[0] = describenodeproto(np);
4633 us_killcell(1, pars);
4634 }
4635 }
4636 return;
4637 }
4638 }
4639
4640 /*
4641 * Routine to rename the object pointed to by explorer node "en" (either a
4642 * cell or a library).
4643 */
us_explorerrename(EXPLORERNODE * en,WINDOWPART * w)4644 void us_explorerrename(EXPLORERNODE *en, WINDOWPART *w)
4645 {
4646 REGISTER LIBRARY *savelib;
4647
4648 if (en->enodetype == EXNODETYPELIBRARY)
4649 {
4650 savelib = el_curlib;
4651 selectlibrary((LIBRARY *)en->addr, FALSE);
4652 us_renamedlog(VLIBRARY);
4653 selectlibrary(savelib, FALSE);
4654 us_exploreredisphandler(w);
4655 return;
4656 }
4657 if (en->enodetype == EXNODETYPECELL)
4658 {
4659 savelib = el_curlib;
4660 selectlibrary(((NODEPROTO *)en->addr)->lib, FALSE);
4661 us_renamedlog(VNODEPROTO);
4662 selectlibrary(savelib, FALSE);
4663 us_exploreredisphandler(w);
4664 return;
4665 }
4666 }
4667
4668 #define EXPLORERNODECHUNKSIZE 50
4669
4670 /*
4671 * Routine to allocate a new "explorer node". Returs NOEXPLORERNODE on error.
4672 */
us_allocexplorernode(EXPWINDOW * ew)4673 EXPLORERNODE *us_allocexplorernode(EXPWINDOW *ew)
4674 {
4675 REGISTER EXPLORERNODE *en, *enlist;
4676 REGISTER INTBIG i;
4677
4678 if (us_explorernodefree == NOEXPLORERNODE)
4679 {
4680 /* free list is empty: load it up */
4681 enlist = (EXPLORERNODE *)emalloc(EXPLORERNODECHUNKSIZE * (sizeof (EXPLORERNODE)), us_tool->cluster);
4682 if (enlist == 0) return(NOEXPLORERNODE);
4683 for(i=0; i<EXPLORERNODECHUNKSIZE; i++)
4684 {
4685 en = &enlist[i];
4686 if (i == 0) en->allocated = TRUE; else
4687 en->allocated = FALSE;
4688 us_freeexplorernode(en);
4689 }
4690 }
4691
4692 /* take one off of the free list */
4693 en = us_explorernodefree;
4694 us_explorernodefree = en->nextexplorernode;
4695
4696 /* initialize it */
4697 en->subexplorernode = NOEXPLORERNODE;
4698 en->nextsubexplorernode = NOEXPLORERNODE;
4699 en->addr = 0;
4700 en->enodetype = EXNODETYPELABEL;
4701 en->flags = 0;
4702 en->count = 0;
4703
4704 /* put into the global linked list */
4705 en->nextexplorernode = ew->nodeused;
4706 ew->nodeused = en;
4707 return(en);
4708 }
4709
4710 /*
4711 * Routine to free explorer node "en" to the pool of unused nodes.
4712 */
us_freeexplorernode(EXPLORERNODE * en)4713 void us_freeexplorernode(EXPLORERNODE *en)
4714 {
4715 if (en->enodetype == EXNODETYPESIMBRANCH || en->enodetype == EXNODETYPESIMLEAF)
4716 efree((CHAR *)en->addr);
4717 en->nextexplorernode = us_explorernodefree;
4718 us_explorernodefree = en;
4719 }
4720
4721 /*********************************** ARC SUPPORT ***********************************/
4722
4723 /*
4724 * routine to modify the arcs in list "list". The "change" field has the
4725 * following meaning:
4726 * change=0 make arc rigid
4727 * change=1 make arc un-rigid
4728 * change=2 make arc fixed-angle
4729 * change=3 make arc not fixed-angle
4730 * change=4 make arc slidable
4731 * change=5 make arc nonslidable
4732 * change=6 make arc temporarily rigid
4733 * change=7 make arc temporarily un-rigid
4734 * change=8 make arc ends extend by half width
4735 * change=9 make arc ends not extend by half width
4736 * change=10 make arc directional
4737 * change=11 make arc not directional
4738 * change=12 make arc negated
4739 * change=13 make arc not negated
4740 * change=14 make arc skip the tail end
4741 * change=15 make arc not skip the tail end
4742 * change=16 make arc skip the head end
4743 * change=17 make arc not skip the head end
4744 * change=18 reverse ends of the arc
4745 * change=19 clear arc constraints
4746 * change=20 print arc constraints
4747 * change=21 set special arc constraint from "prop"
4748 * change=22 add to special arc constraint from "prop"
4749 * change=23 toggle arc rigid
4750 * change=24 toggle arc fixed-angle
4751 * change=25 toggle arc slidable
4752 * change=26 toggle arc ends extend
4753 * change=27 toggle arc directional
4754 * change=28 toggle arc negated
4755 * change=29 toggle arc skip the tail
4756 * change=30 toggle arc skip the head
4757 * If "redraw" is nonzero then the arcs are re-drawn to reflect the change.
4758 * The routine returns the number of arcs that were modified (-1 if an error
4759 * occurred).
4760 */
us_modarcbits(INTBIG change,BOOLEAN redraw,CHAR * prop,GEOM ** list)4761 INTBIG us_modarcbits(INTBIG change, BOOLEAN redraw, CHAR *prop, GEOM **list)
4762 {
4763 REGISTER INTBIG total, affected;
4764 REGISTER INTBIG newvalue;
4765 REGISTER ARCINST *ai;
4766
4767 /* must be a cell in the window */
4768 if (us_needcell() == NONODEPROTO) return(-1);
4769
4770 /* run through the list */
4771 affected = 0;
4772 for(total=0; list[total] != NOGEOM; total++)
4773 {
4774 if (list[total]->entryisnode) continue;
4775 ai = list[total]->entryaddr.ai;
4776
4777 /* notify system that arc is to be re-drawn */
4778 if (redraw) startobjectchange((INTBIG)ai, VARCINST);
4779
4780 /* change the arc state bits */
4781 switch (change)
4782 {
4783 case 0: /* arc rigid */
4784 if (!(*el_curconstraint->setobject)((INTBIG)ai, VARCINST, CHANGETYPERIGID, 0))
4785 affected++;
4786 break;
4787 case 1: /* arc un-rigid */
4788 if (!(*el_curconstraint->setobject)((INTBIG)ai, VARCINST, CHANGETYPEUNRIGID, 0))
4789 affected++;
4790 break;
4791 case 2: /* arc fixed-angle */
4792 if (!(*el_curconstraint->setobject)((INTBIG)ai, VARCINST, CHANGETYPEFIXEDANGLE, 0))
4793 affected++;
4794 break;
4795 case 3: /* arc not fixed-angle */
4796 if (!(*el_curconstraint->setobject)((INTBIG)ai, VARCINST, CHANGETYPENOTFIXEDANGLE, 0))
4797 affected++;
4798 break;
4799 case 4: /* arc slidable */
4800 if (!(*el_curconstraint->setobject)((INTBIG)ai, VARCINST, CHANGETYPESLIDABLE, 0))
4801 affected++;
4802 break;
4803 case 5: /* arc nonslidable */
4804 if (!(*el_curconstraint->setobject)((INTBIG)ai, VARCINST, CHANGETYPENOTSLIDABLE, 0))
4805 affected++;
4806 break;
4807 case 6: /* arc temporarily rigid */
4808 if (!(*el_curconstraint->setobject)((INTBIG)ai, VARCINST, CHANGETYPETEMPRIGID, 0))
4809 affected++;
4810 break;
4811 case 7: /* arc temporarily un-rigid */
4812 if (!(*el_curconstraint->setobject)((INTBIG)ai, VARCINST, CHANGETYPETEMPUNRIGID, 0))
4813 affected++;
4814 break;
4815 case 8: /* arc ends extend by half width */
4816 if ((ai->userbits&NOEXTEND) != 0) affected++;
4817 (void)setval((INTBIG)ai, VARCINST, x_("userbits"), ai->userbits & ~NOEXTEND, VINTEGER);
4818 break;
4819 case 9: /* arc ends not extend by half width */
4820 if ((ai->userbits&NOEXTEND) == 0) affected++;
4821 (void)setval((INTBIG)ai, VARCINST, x_("userbits"), ai->userbits|NOEXTEND, VINTEGER);
4822 break;
4823 case 10: /* arc directional */
4824 if ((ai->userbits&ISDIRECTIONAL) == 0) affected++;
4825 (void)setval((INTBIG)ai, VARCINST, x_("userbits"), ai->userbits|ISDIRECTIONAL, VINTEGER);
4826 break;
4827 case 11: /* arc not directional */
4828 if ((ai->userbits&ISDIRECTIONAL) != 0) affected++;
4829 (void)setval((INTBIG)ai, VARCINST, x_("userbits"), ai->userbits & ~ISDIRECTIONAL, VINTEGER);
4830 break;
4831 case 12: /* arc negated */
4832 if ((ai->userbits&ISNEGATED) == 0) affected++;
4833 newvalue = ai->userbits | ISNEGATED;
4834
4835 /* don't put the negation circle on a pin */
4836 if (((ai->end[0].nodeinst->proto->userbits&NFUNCTION) >> NFUNCTIONSH) == NPPIN &&
4837 ((ai->end[1].nodeinst->proto->userbits&NFUNCTION) >> NFUNCTIONSH) != NPPIN)
4838 newvalue |= REVERSEEND;
4839
4840 /* prefer output negation to input negation */
4841 if ((ai->end[0].portarcinst->proto->userbits&STATEBITS) == INPORT &&
4842 (ai->end[1].portarcinst->proto->userbits&STATEBITS) == OUTPORT)
4843 newvalue |= REVERSEEND;
4844
4845 (void)setval((INTBIG)ai, VARCINST, x_("userbits"), newvalue, VINTEGER);
4846 break;
4847 case 13: /* arc not negated */
4848 if ((ai->userbits&ISNEGATED) != 0) affected++;
4849 (void)setval((INTBIG)ai, VARCINST, x_("userbits"), ai->userbits & ~ISNEGATED, VINTEGER);
4850 break;
4851 case 14: /* arc skip the tail end */
4852 if ((ai->userbits&NOTEND0) == 0) affected++;
4853 (void)setval((INTBIG)ai, VARCINST, x_("userbits"), ai->userbits|NOTEND0, VINTEGER);
4854 break;
4855 case 15: /* arc not skip the tail end */
4856 if ((ai->userbits&NOTEND0) != 0) affected++;
4857 (void)setval((INTBIG)ai, VARCINST, x_("userbits"), ai->userbits & ~NOTEND0, VINTEGER);
4858 break;
4859 case 16: /* arc skip the head end */
4860 if ((ai->userbits&NOTEND1) == 0) affected++;
4861 (void)setval((INTBIG)ai, VARCINST, x_("userbits"), ai->userbits|NOTEND1, VINTEGER);
4862 break;
4863 case 17: /* arc not skip the head end */
4864 if ((ai->userbits&NOTEND1) != 0) affected++;
4865 (void)setval((INTBIG)ai, VARCINST, x_("userbits"), ai->userbits & ~NOTEND1, VINTEGER);
4866 break;
4867 case 18: /* reverse ends of the arc */
4868 affected++;
4869 newvalue = (ai->userbits & ~REVERSEEND) | ((~ai->userbits) & REVERSEEND);
4870 (void)setval((INTBIG)ai, VARCINST, x_("userbits"), newvalue, VINTEGER);
4871 break;
4872 case 19: /* clear special arc properties */
4873 if (!(*el_curconstraint->setobject)((INTBIG)ai, VARCINST,
4874 CHANGETYPEFIXEDANGLE, (INTBIG)x_(""))) affected++;
4875 break;
4876 case 20: /* print special arc properties */
4877 if (!(*el_curconstraint->setobject)((INTBIG)ai, VARCINST,
4878 CHANGETYPENOTFIXEDANGLE, (INTBIG)x_(""))) affected++;
4879 break;
4880 case 21: /* set special arc properties */
4881 if (!(*el_curconstraint->setobject)((INTBIG)ai, VARCINST,
4882 CHANGETYPERIGID, (INTBIG)prop)) affected++;
4883 break;
4884 case 22: /* add to special arc properties */
4885 if (!(*el_curconstraint->setobject)((INTBIG)ai, VARCINST,
4886 CHANGETYPEUNRIGID, (INTBIG)prop)) affected++;
4887 break;
4888 case 23: /* arc toggle rigid */
4889 if ((ai->userbits&FIXED) != 0)
4890 (void)(*el_curconstraint->setobject)((INTBIG)ai, VARCINST, CHANGETYPEUNRIGID, 0); else
4891 (void)(*el_curconstraint->setobject)((INTBIG)ai, VARCINST, CHANGETYPERIGID, 0);
4892 affected++;
4893 break;
4894 case 24: /* arc toggle fixed-angle */
4895 if ((ai->userbits&FIXANG) != 0)
4896 (void)(*el_curconstraint->setobject)((INTBIG)ai, VARCINST, CHANGETYPENOTFIXEDANGLE, 0); else
4897 (void)(*el_curconstraint->setobject)((INTBIG)ai, VARCINST, CHANGETYPEFIXEDANGLE, 0);
4898 affected++;
4899 break;
4900 case 25: /* arc toggle slidable */
4901 if ((ai->userbits&CANTSLIDE) != 0)
4902 (void)(*el_curconstraint->setobject)((INTBIG)ai, VARCINST, CHANGETYPESLIDABLE, 0); else
4903 (void)(*el_curconstraint->setobject)((INTBIG)ai, VARCINST, CHANGETYPENOTSLIDABLE, 0);
4904 affected++;
4905 break;
4906 case 26: /* arc toggle ends extend */
4907 if ((ai->userbits&NOEXTEND) != 0) newvalue = ai->userbits & ~NOEXTEND; else
4908 newvalue = ai->userbits | NOEXTEND;
4909 (void)setval((INTBIG)ai, VARCINST, x_("userbits"), newvalue, VINTEGER);
4910 affected++;
4911 break;
4912 case 27: /* arc toggle directional */
4913 if ((ai->userbits&ISDIRECTIONAL) != 0) newvalue = ai->userbits & ~ISDIRECTIONAL; else
4914 newvalue = ai->userbits | ISDIRECTIONAL;
4915 (void)setval((INTBIG)ai, VARCINST, x_("userbits"), newvalue, VINTEGER);
4916 affected++;
4917 break;
4918 case 28: /* arc toggle negated */
4919 if ((ai->userbits&ISNEGATED) != 0) newvalue = ai->userbits & ~ISNEGATED; else
4920 newvalue = ai->userbits | ISNEGATED;
4921
4922 if ((newvalue&ISNEGATED) != 0)
4923 {
4924 /* don't put the negation circle on a pin */
4925 if (((ai->end[0].nodeinst->proto->userbits&NFUNCTION) >> NFUNCTIONSH) == NPPIN &&
4926 ((ai->end[1].nodeinst->proto->userbits&NFUNCTION) >> NFUNCTIONSH) != NPPIN)
4927 newvalue |= REVERSEEND;
4928
4929 /* prefer output negation to input negation */
4930 if ((ai->end[0].portarcinst->proto->userbits&STATEBITS) == INPORT &&
4931 (ai->end[1].portarcinst->proto->userbits&STATEBITS) == OUTPORT)
4932 newvalue |= REVERSEEND;
4933 }
4934
4935 (void)setval((INTBIG)ai, VARCINST, x_("userbits"), newvalue, VINTEGER);
4936 affected++;
4937 break;
4938 case 29: /* arc toggle skip the tail */
4939 if ((ai->userbits&NOTEND0) != 0) newvalue = ai->userbits & ~NOTEND0; else
4940 newvalue = ai->userbits | NOTEND0;
4941 (void)setval((INTBIG)ai, VARCINST, x_("userbits"), newvalue, VINTEGER);
4942 affected++;
4943 break;
4944 case 30: /* arc toggle skip the head */
4945 if ((ai->userbits&NOTEND1) != 0) newvalue = ai->userbits & ~NOTEND1; else
4946 newvalue = ai->userbits | NOTEND1;
4947 (void)setval((INTBIG)ai, VARCINST, x_("userbits"), newvalue, VINTEGER);
4948 affected++;
4949 break;
4950 }
4951
4952 /* notify the system that the arc is done and can be re-drawn */
4953 if (redraw) endobjectchange((INTBIG)ai, VARCINST);
4954 }
4955
4956 return(affected);
4957 }
4958
4959 /*
4960 * routine to set the arc name of arc "ai" to the name "name". If "name" is
4961 * zero, remove the name
4962 */
us_setarcname(ARCINST * ai,CHAR * name)4963 void us_setarcname(ARCINST *ai, CHAR *name)
4964 {
4965 REGISTER VARIABLE *var;
4966 UINTBIG descript[TEXTDESCRIPTSIZE];
4967
4968 startobjectchange((INTBIG)ai, VARCINST);
4969 if (name == 0) (void)delvalkey((INTBIG)ai, VARCINST, el_arc_name_key); else
4970 {
4971 TDCLEAR(descript);
4972 defaulttextdescript(descript, ai->geom);
4973 var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name_key);
4974 if (var != NOVARIABLE)
4975 {
4976 if ((var->type&VDISPLAY) != 0)
4977 TDCOPY(descript, var->textdescript);
4978 }
4979 var = setvalkey((INTBIG)ai, VARCINST, el_arc_name_key, (INTBIG)name, VSTRING|VDISPLAY);
4980 if (var == NOVARIABLE) return;
4981 TDCOPY(var->textdescript, descript);
4982
4983 /* for zero-width arcs, adjust the location */
4984 if (ai->width == 0)
4985 {
4986 if (ai->end[0].ypos == ai->end[1].ypos)
4987 {
4988 /* zero-width horizontal arc has name above */
4989 TDSETPOS(descript, VTPOSUP);
4990 modifydescript((INTBIG)ai, VARCINST, var, descript);
4991 } else if (ai->end[0].xpos == ai->end[1].xpos)
4992 {
4993 /* zero-width vertical arc has name to right */
4994 TDSETPOS(descript, VTPOSRIGHT);
4995 modifydescript((INTBIG)ai, VARCINST, var, descript);
4996 }
4997 }
4998 }
4999 endobjectchange((INTBIG)ai, VARCINST);
5000 }
5001
5002 /*
5003 * routine to determine the "userbits" to use for an arc of type "ap".
5004 */
us_makearcuserbits(ARCPROTO * ap)5005 INTBIG us_makearcuserbits(ARCPROTO *ap)
5006 {
5007 REGISTER INTBIG bits, protobits;
5008 REGISTER VARIABLE *var;
5009
5010 var = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER, us_arcstylekey);
5011 if (var != NOVARIABLE) protobits = var->addr; else
5012 {
5013 var = getvalkey((INTBIG)ap, VARCPROTO, VINTEGER, us_arcstylekey);
5014 if (var != NOVARIABLE) protobits = var->addr; else
5015 protobits = ap->userbits;
5016 }
5017 bits = 0;
5018 if ((protobits&WANTFIXANG) != 0) bits |= FIXANG;
5019 if ((protobits&WANTFIX) != 0) bits |= FIXED;
5020 if ((protobits&WANTCANTSLIDE) != 0) bits |= CANTSLIDE;
5021 if ((protobits&WANTNOEXTEND) != 0) bits |= NOEXTEND;
5022 if ((protobits&WANTNEGATED) != 0) bits |= ISNEGATED;
5023 if ((protobits&WANTDIRECTIONAL) != 0) bits |= ISDIRECTIONAL;
5024 return(bits);
5025 }
5026
5027 /*
5028 * Routine to recompute the "FAR TEXT" bit on arc "ai".
5029 */
us_computearcfartextbit(ARCINST * ai)5030 void us_computearcfartextbit(ARCINST *ai)
5031 {
5032 REGISTER INTBIG i, sizelimit;
5033 INTBIG xw, yw;
5034 REGISTER VARIABLE *var;
5035 REGISTER NODEPROTO *savecell;
5036 HIGHLIGHT high;
5037
5038 /* must have a window in which to work */
5039 if (el_curwindowpart == NOWINDOWPART) return;
5040 ai->userbits &= ~AHASFARTEXT;
5041 high.status = HIGHTEXT;
5042 high.cell = ai->parent;
5043 high.fromgeom = ai->geom;
5044 high.frompoint = 0;
5045 high.fromport = NOPORTPROTO;
5046 high.fromvarnoeval = NOVARIABLE;
5047 sizelimit = lambdaofarc(ai) * FARTEXTLIMIT * 2;
5048 for(i=0; i<ai->numvar; i++)
5049 {
5050 var = &ai->firstvar[i];
5051 if ((var->type&VDISPLAY) == 0) continue;
5052 if (abs(TDGETXOFF(var->textdescript)) >= FARTEXTLIMIT*4 ||
5053 abs(TDGETYOFF(var->textdescript)) >= FARTEXTLIMIT*4)
5054 {
5055 ai->userbits |= AHASFARTEXT;
5056 return;
5057 }
5058 high.fromvar = var;
5059 savecell = el_curwindowpart->curnodeproto;
5060 el_curwindowpart->curnodeproto = ai->parent;
5061 us_gethightextsize(&high, &xw, &yw, el_curwindowpart);
5062 el_curwindowpart->curnodeproto = savecell;
5063 if (xw > sizelimit || yw > sizelimit)
5064 {
5065 ai->userbits |= AHASFARTEXT;
5066 return;
5067 }
5068 }
5069 }
5070
5071 /*
5072 * Routine to return the curvature for arc "ai" that will allow it to
5073 * curve about (xcur, ycur), a center point.
5074 */
us_curvearcaboutpoint(ARCINST * ai,INTBIG xcur,INTBIG ycur)5075 INTBIG us_curvearcaboutpoint(ARCINST *ai, INTBIG xcur, INTBIG ycur)
5076 {
5077 INTBIG x1, y1, x2, y2, ix, iy;
5078 REGISTER INTBIG acx, acy, r;
5079 REGISTER INTBIG ang;
5080
5081 /* get true center of arc through cursor */
5082 ang = ((ai->userbits&AANGLE) >> AANGLESH) * 10;
5083 acx = (ai->end[0].xpos + ai->end[1].xpos) / 2;
5084 acy = (ai->end[0].ypos + ai->end[1].ypos) / 2;
5085 (void)intersect(xcur, ycur, ang, acx, acy, (ang+900)%3600, &ix, &iy);
5086 r = computedistance(ai->end[0].xpos, ai->end[0].ypos, ix, iy);
5087
5088 /* now see if this point will be re-created */
5089 (void)findcenters(r, ai->end[0].xpos,ai->end[0].ypos,
5090 ai->end[1].xpos, ai->end[1].ypos, ai->length, &x1,&y1, &x2,&y2);
5091 if (abs(x1-ix)+abs(y1-iy) < abs(x2-ix)+abs(y2-iy)) r = -r;
5092 return(r);
5093 }
5094
5095 /*
5096 * Routine to return the curvature for arc "ai" that will allow it to
5097 * curve through (xcur, ycur), an edge point.
5098 */
us_curvearcthroughpoint(ARCINST * ai,INTBIG xcur,INTBIG ycur)5099 INTBIG us_curvearcthroughpoint(ARCINST *ai, INTBIG xcur, INTBIG ycur)
5100 {
5101 INTBIG x1, y1, x2, y2;
5102 REGISTER INTBIG r0x, r0y, r1x, r1y, r2x, r2y, rpx, rpy, r02x, r02y, rcx, rcy, r;
5103 REGISTER float u, v, t;
5104
5105 r0x = ai->end[0].xpos; r0y = ai->end[0].ypos;
5106 r1x = ai->end[1].xpos; r1y = ai->end[1].ypos;
5107 r2x = xcur; r2y = ycur;
5108 r02x = r2x-r0x; r02y = r2y-r0y;
5109 rpx = r0y-r1y; rpy = r1x-r0x;
5110 u = (float)r02x; u *= (r2x-r1x);
5111 v = (float)r02y; v *= (r2y-r1y);
5112 t = u + v;
5113 u = (float)r02x; u *= rpx;
5114 v = (float)r02y; v *= rpy;
5115 t /= (u + v) * 2.0f;
5116 rcx = r0x + (INTBIG)((r1x-r0x)/2 + t*rpx);
5117 rcy = r0y + (INTBIG)((r1y-r0y)/2 + t*rpy);
5118
5119 /* now see if this point will be re-created */
5120 r = computedistance(r0x, r0y, rcx, rcy);
5121 if (!findcenters(r, r0x, r0y, r1x, r1y, ai->length, &x1, &y1, &x2, &y2))
5122 {
5123 if (abs(x1-rcx)+abs(y1-rcy) < abs(x2-rcx)+abs(y2-rcy)) r = -r;
5124 } else
5125 {
5126 rcx = r0x + (r1x-r0x)/2;
5127 rcy = r0y + (r1y-r0y)/2;
5128 r = computedistance(r0x, r0y, rcx, rcy) + 1;
5129 }
5130 return(r);
5131 }
5132
5133 /*
5134 * Routine to replace arcs in "list" (that match the first arc there) with another of type
5135 * "ap", adding layer-change contacts
5136 * as needed to keep the connections. If "connected" is true, replace all such arcs
5137 * connected to this. If "thiscell" is true, replace all such arcs in the cell.
5138 */
us_replaceallarcs(NODEPROTO * cell,GEOM ** list,ARCPROTO * ap,BOOLEAN connected,BOOLEAN thiscell)5139 void us_replaceallarcs(NODEPROTO *cell, GEOM **list, ARCPROTO *ap, BOOLEAN connected, BOOLEAN thiscell)
5140 {
5141 REGISTER INTBIG lx, hx, ly, hy, cx, cy, wid, bits, i;
5142 INTBIG xs, ys;
5143 REGISTER NODEINST *ni, *newni, *nextni;
5144 NODEINST *ni0, *ni1;
5145 REGISTER ARCINST *ai, *newai, *nextai, *oldai;
5146 PORTPROTO *pp0, *pp1;
5147 REGISTER PORTARCINST *pi;
5148 REGISTER NODEPROTO *pin;
5149
5150 if (list[0] == NOGEOM) return;
5151 if (list[0]->entryisnode) return;
5152 oldai = list[0]->entryaddr.ai;
5153 us_clearhighlightcount();
5154
5155 /* mark the pin nodes that must be changed */
5156 for(ni = cell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
5157 ni->temp1 = 0;
5158 for(ai = cell->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
5159 ai->temp1 = 0;
5160
5161 for(i=0; list[i] != NOGEOM; i++)
5162 {
5163 if (list[i]->entryisnode) continue;
5164 ai = list[i]->entryaddr.ai;
5165 if (ai->proto != oldai->proto) continue;
5166 ai->temp1 = 1;
5167 }
5168 if (connected)
5169 {
5170 for(ai = cell->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
5171 if (ai->proto == oldai->proto && ai->network == oldai->network)
5172 ai->temp1 = 1;
5173 }
5174 if (thiscell)
5175 {
5176 for(ai = cell->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
5177 if (ai->proto == oldai->proto)
5178 ai->temp1 = 1;
5179 }
5180 for(ni = cell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
5181 {
5182 if (ni->proto->primindex == 0) continue;
5183 if (ni->firstportexpinst != NOPORTEXPINST) continue;
5184 if (nodefunction(ni) != NPPIN) continue;
5185 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
5186 if (pi->conarcinst->temp1 == 0) break;
5187 if (pi == NOPORTARCINST) ni->temp1 = 1;
5188 }
5189
5190 /* now create new pins where they belong */
5191 pin = getpinproto(ap);
5192 defaultnodesize(pin, &xs, &ys);
5193 for(ni = cell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
5194 {
5195 if (ni->temp1 == 0) continue;
5196 cx = (ni->lowx + ni->highx) / 2;
5197 cy = (ni->lowy + ni->highy) / 2;
5198 lx = cx - xs / 2; hx = lx + xs;
5199 ly = cy - ys / 2; hy = ly + ys;
5200 newni = newnodeinst(pin, lx, hx, ly, hy, 0, 0, cell);
5201 if (newni == NONODEINST) return;
5202 endobjectchange((INTBIG)newni, VNODEINST);
5203 newni->temp1 = 0;
5204 ni->temp1 = (INTBIG)newni;
5205 }
5206
5207 /* now create new arcs to replace the old ones */
5208 for(ai = cell->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
5209 {
5210 if (ai->temp1 == 0) continue;
5211 ni0 = ai->end[0].nodeinst;
5212 if (ni0->temp1 != 0)
5213 {
5214 ni0 = (NODEINST *)ni0->temp1;
5215 pp0 = ni0->proto->firstportproto;
5216 } else
5217 {
5218 /* need contacts to get to the right level */
5219 us_makecontactstack(ai, 0, ap, &ni0, &pp0);
5220 if (ni0 == NONODEINST) return;
5221 }
5222 ni1 = ai->end[1].nodeinst;
5223 if (ni1->temp1 != 0)
5224 {
5225 ni1 = (NODEINST *)ni1->temp1;
5226 pp1 = ni1->proto->firstportproto;
5227 } else
5228 {
5229 /* need contacts to get to the right level */
5230 us_makecontactstack(ai, 1, ap, &ni1, &pp1);
5231 if (ni1 == NONODEINST) return;
5232 }
5233
5234 wid = defaultarcwidth(ap);
5235 if (ai->width > wid) wid = ai->width;
5236 bits = us_makearcuserbits(ap);
5237 newai = newarcinst(ap, wid, bits, ni0, pp0, ai->end[0].xpos, ai->end[0].ypos,
5238 ni1, pp1, ai->end[1].xpos, ai->end[1].ypos, cell);
5239 if (newai == NOARCINST) return;
5240 (void)copyvars((INTBIG)ai, VARCINST, (INTBIG)newai, VARCINST, FALSE);
5241 newai->temp1 = 0;
5242 endobjectchange((INTBIG)newai, VARCINST);
5243 }
5244
5245 /* now remove the previous arcs and nodes */
5246 for(ai = cell->firstarcinst; ai != NOARCINST; ai = nextai)
5247 {
5248 nextai = ai->nextarcinst;
5249 if (ai->temp1 == 0) continue;
5250 startobjectchange((INTBIG)ai, VARCINST);
5251 (void)killarcinst(ai);
5252 }
5253 for(ni = cell->firstnodeinst; ni != NONODEINST; ni = nextni)
5254 {
5255 nextni = ni->nextnodeinst;
5256 if (ni->temp1 == 0) continue;
5257 startobjectchange((INTBIG)ni, VNODEINST);
5258 (void)killnodeinst(ni);
5259 }
5260 }
5261
5262 NODEPROTO *us_contactstack[100];
5263 ARCPROTO *us_contactstackarc[100];
5264
5265 /*
5266 * Routine to examine end "end" of arc "ai" and return a node at that position which
5267 * can connect to arcs of type "ap". This may require creation of one or more contacts
5268 * to change layers.
5269 */
us_makecontactstack(ARCINST * ai,INTBIG end,ARCPROTO * ap,NODEINST ** conni,PORTPROTO ** conpp)5270 void us_makecontactstack(ARCINST *ai, INTBIG end, ARCPROTO *ap, NODEINST **conni,
5271 PORTPROTO **conpp)
5272 {
5273 REGISTER NODEINST *lastni, *newni;
5274 REGISTER ARCPROTO *typ;
5275 REGISTER ARCINST *newai;
5276 REGISTER NODEPROTO *np, *cell;
5277 REGISTER PORTPROTO *lastpp;
5278 REGISTER INTBIG i, depth, cx, cy, lx, hx, ly, hy, wid, bits;
5279 INTBIG xs, ys;
5280
5281 lastni = ai->end[end].nodeinst;
5282 lastpp = ai->end[end].portarcinst->proto;
5283 for(np = ap->tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
5284 np->temp1 = 0;
5285 depth = us_findpathtoarc(lastpp, ap, 0);
5286 if (depth < 0)
5287 {
5288 *conni = NONODEINST;
5289 return;
5290 }
5291
5292 /* create the contacts */
5293 cell = ai->parent;
5294 *conni = lastni;
5295 *conpp = lastpp;
5296 cx = ai->end[end].xpos; cy = ai->end[end].ypos;
5297 for(i=0; i<depth; i++)
5298 {
5299 defaultnodesize(us_contactstack[i], &xs, &ys);
5300 lx = cx - xs / 2; hx = lx + xs;
5301 ly = cy - ys / 2; hy = ly + ys;
5302 newni = newnodeinst(us_contactstack[i], lx, hx, ly, hy, 0, 0, cell);
5303 if (newni == NONODEINST)
5304 {
5305 *conni = NONODEINST;
5306 return;
5307 }
5308 endobjectchange((INTBIG)newni, VNODEINST);
5309 *conni = newni;
5310 *conpp = newni->proto->firstportproto;
5311 typ = us_contactstackarc[i];
5312 wid = defaultarcwidth(typ);
5313 bits = us_makearcuserbits(typ);
5314 newai = newarcinst(typ, wid, bits, lastni, lastpp, cx, cy, *conni, *conpp, cx, cy, cell);
5315 if (newai == NOARCINST)
5316 {
5317 *conni = NONODEINST;
5318 return;
5319 }
5320 endobjectchange((INTBIG)newai, VARCINST);
5321 }
5322 }
5323
us_findpathtoarc(PORTPROTO * pp,ARCPROTO * ap,INTBIG depth)5324 INTBIG us_findpathtoarc(PORTPROTO *pp, ARCPROTO *ap, INTBIG depth)
5325 {
5326 REGISTER INTBIG i, j, fun, bestdepth, newdepth;
5327 REGISTER NODEPROTO *bestnp, *nextnp;
5328 REGISTER PORTPROTO *nextpp;
5329 REGISTER ARCPROTO *thisap, *bestap;
5330 REGISTER TECHNOLOGY *tech;
5331
5332 /* see if the connection is made */
5333 for(i=0; pp->connects[i] != NOARCPROTO; i++)
5334 if (pp->connects[i] == ap) return(depth);
5335
5336 /* look for a contact */
5337 bestnp = NONODEPROTO;
5338 tech = ap->tech;
5339 for(nextnp = tech->firstnodeproto; nextnp != NONODEPROTO; nextnp = nextnp->nextnodeproto)
5340 {
5341 if (nextnp->temp1 != 0) continue;
5342 fun = (nextnp->userbits&NFUNCTION) >> NFUNCTIONSH;
5343 if (fun != NPCONTACT) continue;
5344
5345 /* see if this contact connects to the destination */
5346 nextpp = nextnp->firstportproto;
5347 for(i=0; nextpp->connects[i] != NOARCPROTO; i++)
5348 {
5349 thisap = nextpp->connects[i];
5350 if (thisap->tech != tech) continue;
5351 for(j=0; pp->connects[j] != NOARCPROTO; j++)
5352 if (pp->connects[j] == thisap) break;
5353 if (pp->connects[j] != NOARCPROTO) break;
5354 }
5355 if (nextpp->connects[i] == NOARCPROTO) continue;
5356
5357 /* this contact is part of the chain */
5358 us_contactstack[depth] = nextnp;
5359 nextnp->temp1 = 1;
5360 newdepth = us_findpathtoarc(nextpp, ap, depth+1);
5361 nextnp->temp1 = 0;
5362 if (newdepth < 0) continue;
5363 if (bestnp == NONODEPROTO || newdepth < bestdepth)
5364 {
5365 bestdepth = newdepth;
5366 bestnp = nextnp;
5367 bestap = nextpp->connects[i];
5368 }
5369 }
5370 if (bestnp != NONODEPROTO)
5371 {
5372 us_contactstack[depth] = bestnp;
5373 us_contactstackarc[depth] = bestap;
5374 bestnp->temp1 = 1;
5375 newdepth = us_findpathtoarc(bestnp->firstportproto, ap, depth+1);
5376 bestnp->temp1 = 0;
5377 return(newdepth);
5378 }
5379 return(-1);
5380 }
5381
5382 /*********************************** NODE SUPPORT ***********************************/
5383
5384 /*
5385 * routine to travel through the network starting at nodeinst "ni" and set
5386 * "bit" in all nodes connected with arcs that do not spread.
5387 */
us_nettravel(NODEINST * ni,INTBIG bit)5388 void us_nettravel(NODEINST *ni, INTBIG bit)
5389 {
5390 REGISTER INTBIG i;
5391 REGISTER PORTARCINST *pi;
5392
5393 if ((ni->userbits & bit) != 0) return;
5394 ni->userbits |= bit;
5395
5396 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
5397 {
5398 if ((pi->conarcinst->userbits & ARCFLAGBIT) != 0) continue;
5399 for(i=0; i<2; i++) us_nettravel(pi->conarcinst->end[i].nodeinst, bit);
5400 }
5401 }
5402
5403 /*
5404 * Routine to return the proper "WIPED" bit for node "ni".
5405 */
us_computewipestate(NODEINST * ni)5406 UINTBIG us_computewipestate(NODEINST *ni)
5407 {
5408 REGISTER PORTARCINST *pi;
5409 REGISTER ARCINST *ai;
5410
5411 if ((ni->proto->userbits&ARCSWIPE) == 0) return(0);
5412 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
5413 {
5414 ai = pi->conarcinst;
5415 if ((ai->proto->userbits&CANWIPE) != 0) return(WIPED);
5416 }
5417 return(0);
5418 }
5419
5420 /*
5421 * Routine to spread around node "ni" in direction "direction" and distance "amount".
5422 * Returns nonzero if something was moved.
5423 */
us_spreadaround(NODEINST * ni,INTBIG amount,CHAR * direction)5424 INTBIG us_spreadaround(NODEINST *ni, INTBIG amount, CHAR *direction)
5425 {
5426 REGISTER NODEINST *no1, *no2, *inno;
5427 REGISTER NODEPROTO *cell;
5428 REGISTER ARCINST *ai;
5429 INTBIG plx, ply, phx, phy;
5430 REGISTER INTBIG xc1, yc1, xc2, yc2, i, slx, shx, sly, shy;
5431 REGISTER INTBIG doit, again, moved;
5432 REGISTER BOOLEAN mustbehor;
5433
5434 cell = ni->parent;
5435 nodesizeoffset(ni, &plx, &ply, &phx, &phy);
5436 slx = ni->lowx + plx;
5437 shx = ni->highx - phx;
5438 sly = ni->lowy + ply;
5439 shy = ni->highy - phy;
5440
5441 /* initialize by turning the marker bits off */
5442 for(inno = cell->firstnodeinst; inno != NONODEINST; inno = inno->nextnodeinst)
5443 inno->userbits &= ~NODEFLAGBIT;
5444 for(ai = cell->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
5445 ai->userbits &= ~ARCFLAGBIT;
5446
5447 /* set "already done" flag for nodes manhattan connected on spread line */
5448 if (*direction == 'l' || *direction == 'r') mustbehor = FALSE; else
5449 mustbehor = TRUE;
5450 us_manhattantravel(ni, mustbehor);
5451
5452 /* set "already done" flag for nodes that completely cover spread node or are in its line */
5453 for(inno = cell->firstnodeinst; inno != NONODEINST; inno = inno->nextnodeinst)
5454 {
5455 nodesizeoffset(inno, &plx, &ply, &phx, &phy);
5456 if (*direction == 'l' || *direction == 'r')
5457 {
5458 if (inno->lowx + plx < slx && inno->highx - phx > shx)
5459 inno->userbits |= NODEFLAGBIT;
5460 if ((inno->lowx+inno->highx)/2 == (slx+shx)/2)
5461 inno->userbits |= NODEFLAGBIT;
5462 } else
5463 {
5464 if (inno->lowy + ply < sly && inno->highy - phy > shy)
5465 inno->userbits |= NODEFLAGBIT;
5466 if ((inno->lowy+inno->highy)/2 == (sly+shy)/2)
5467 inno->userbits |= NODEFLAGBIT;
5468 }
5469 }
5470
5471 /* mark those arcinsts that should stretch during spread */
5472 for(ai = cell->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
5473 {
5474 no1 = ai->end[0].nodeinst; no2 = ai->end[1].nodeinst;
5475 xc1 = (no1->lowx + no1->highx) / 2; yc1 = (no1->lowy + no1->highy) / 2;
5476 xc2 = (no2->lowx + no2->highx) / 2; yc2 = (no2->lowy + no2->highy) / 2;
5477
5478 /* if one node is along spread line, make it "no1" */
5479 if ((no2->userbits&NODEFLAGBIT) != 0)
5480 {
5481 inno = no1; no1 = no2; no2 = inno;
5482 i = xc1; xc1 = xc2; xc2 = i;
5483 i = yc1; yc1 = yc2; yc2 = i;
5484 }
5485
5486 /* if both nodes are along spread line, leave arc alone */
5487 if ((no2->userbits&NODEFLAGBIT) != 0) continue;
5488
5489 i = 1;
5490
5491 if ((no1->userbits&NODEFLAGBIT) != 0)
5492 {
5493 /* handle arcs connected to spread line */
5494 switch (*direction)
5495 {
5496 case 'l': if (xc2 <= slx) i = 0; break;
5497 case 'r': if (xc2 >= shx) i = 0; break;
5498 case 'u': if (yc2 >= shy) i = 0; break;
5499 case 'd': if (yc2 <= sly) i = 0; break;
5500 }
5501 } else
5502 {
5503 /* handle arcs that cross the spread line */
5504 switch (*direction)
5505 {
5506 case 'l': if (xc1 > slx && xc2 <= slx) i = 0; else
5507 if (xc2 > slx && xc1 <= slx) i = 0;
5508 break;
5509 case 'r': if (xc1 < shx && xc2 >= shx) i = 0; else
5510 if (xc2 < shx && xc1 >= shx) i = 0;
5511 break;
5512 case 'u': if (yc1 > shy && yc2 <= shy) i = 0; else
5513 if (yc2 > shy && yc1 <= shy) i = 0;
5514 break;
5515 case 'd': if (yc1 < sly && yc2 >= sly) i = 0; else
5516 if (yc2 < sly && yc1 >= sly) i = 0;
5517 break;
5518 }
5519 }
5520 if (i == 0) ai->userbits |= ARCFLAGBIT;
5521 }
5522
5523 /* now look at every nodeinst in the cell */
5524 moved = 0;
5525 again = 1;
5526 while (again)
5527 {
5528 again = 0;
5529 for(inno = cell->firstnodeinst; inno != NONODEINST; inno = inno->nextnodeinst)
5530 {
5531 if (stopping(STOPREASONSPREAD)) break;
5532
5533 /* ignore this nodeinst if it has been spread already */
5534 if ((inno->userbits & NODEFLAGBIT) != 0) continue;
5535
5536 /* make sure nodeinst is on proper side of requested spread */
5537 xc1 = (inno->highx+inno->lowx) / 2;
5538 yc1 = (inno->highy+inno->lowy) / 2;
5539 doit = 0;
5540 switch (*direction)
5541 {
5542 case 'l': if (xc1 < slx) doit++; break;
5543 case 'r': if (xc1 > shx) doit++; break;
5544 case 'u': if (yc1 > shy) doit++; break;
5545 case 'd': if (yc1 < sly) doit++; break;
5546 }
5547 if (doit == 0) continue;
5548
5549 /* set every connecting nodeinst to be "spread" */
5550 for(ai = cell->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
5551 {
5552 if ((ai->userbits & ARCFLAGBIT) != 0)
5553 {
5554 /* make arc temporarily unrigid */
5555 (void)(*el_curconstraint->setobject)((INTBIG)ai, VARCINST,
5556 CHANGETYPETEMPUNRIGID, 0);
5557 } else
5558 {
5559 /* make arc temporarily rigid */
5560 (void)(*el_curconstraint->setobject)((INTBIG)ai, VARCINST,
5561 CHANGETYPETEMPRIGID, 0);
5562 }
5563 }
5564 us_nettravel(inno, NODEFLAGBIT);
5565
5566 /* move this nodeinst in proper direction to do spread */
5567 startobjectchange((INTBIG)inno, VNODEINST);
5568 switch(*direction)
5569 {
5570 case 'l':
5571 modifynodeinst(inno, -amount, 0, -amount, 0, 0,0);
5572 break;
5573 case 'r':
5574 modifynodeinst(inno, amount, 0, amount, 0, 0,0);
5575 break;
5576 case 'u':
5577 modifynodeinst(inno, 0, amount, 0, amount, 0,0);
5578 break;
5579 case 'd':
5580 modifynodeinst(inno, 0, -amount, 0, -amount, 0,0);
5581 break;
5582 }
5583 endobjectchange((INTBIG)inno, VNODEINST);
5584
5585 /* set loop iteration flag and node spread flag */
5586 moved++;
5587 again++;
5588 break;
5589 }
5590 }
5591
5592 /* report what was moved */
5593 (*el_curconstraint->solve)(cell);
5594 return(moved);
5595 }
5596
5597 /*
5598 * Helper routine for "us_rotate()" to mark selected nodes that need not be
5599 * connected with an invisible arc.
5600 */
us_spreadrotateconnection(NODEINST * theni)5601 void us_spreadrotateconnection(NODEINST *theni)
5602 {
5603 REGISTER PORTARCINST *pi;
5604 REGISTER ARCINST *ai;
5605 REGISTER NODEINST *ni;
5606 REGISTER INTBIG other;
5607
5608 for(pi = theni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
5609 {
5610 ai = pi->conarcinst;
5611 if (ai->temp1 == 0) continue;
5612 if (ai->end[0].portarcinst == pi) other = 1; else other = 0;
5613 ni = ai->end[other].nodeinst;
5614 if (ni->temp1 != 0) continue;
5615 ni->temp1 = 1;
5616 us_spreadrotateconnection(ni);
5617 }
5618 }
5619
5620 /*
5621 * Routine to recompute the "FAR TEXT" bit on node "ni".
5622 */
us_computenodefartextbit(NODEINST * ni)5623 void us_computenodefartextbit(NODEINST *ni)
5624 {
5625 REGISTER INTBIG i, sizelimit;
5626 INTBIG xw, yw;
5627 REGISTER VARIABLE *var;
5628 REGISTER PORTEXPINST *pe;
5629 REGISTER NODEPROTO *savecell;
5630 HIGHLIGHT high;
5631
5632 /* make sure there is a window */
5633 if (el_curwindowpart == NOWINDOWPART) return;
5634
5635 ni->userbits &= ~NHASFARTEXT;
5636 high.status = HIGHTEXT;
5637 high.cell = ni->parent;
5638 high.fromgeom = ni->geom;
5639 high.frompoint = 0;
5640 high.fromport = NOPORTPROTO;
5641 high.fromvarnoeval = NOVARIABLE;
5642 sizelimit = lambdaofnode(ni) * FARTEXTLIMIT * 2;
5643 for(i=0; i<ni->numvar; i++)
5644 {
5645 var = &ni->firstvar[i];
5646 if ((var->type&VDISPLAY) == 0) continue;
5647 if ((TDGETSIZE(var->textdescript)&TXTQLAMBDA) == 0)
5648 {
5649 ni->userbits |= NHASFARTEXT;
5650 return;
5651 }
5652 if (abs(TDGETXOFF(var->textdescript)) >= FARTEXTLIMIT*4 ||
5653 abs(TDGETYOFF(var->textdescript)) >= FARTEXTLIMIT*4)
5654 {
5655 ni->userbits |= NHASFARTEXT;
5656 return;
5657 }
5658 high.fromvar = var;
5659 savecell = el_curwindowpart->curnodeproto;
5660 el_curwindowpart->curnodeproto = ni->parent;
5661 us_gethightextsize(&high, &xw, &yw, el_curwindowpart);
5662 el_curwindowpart->curnodeproto = savecell;
5663 if (xw > sizelimit || yw > sizelimit)
5664 {
5665 ni->userbits |= NHASFARTEXT;
5666 return;
5667 }
5668 }
5669 high.fromvar = NOVARIABLE;
5670 for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
5671 {
5672 if (abs(TDGETXOFF(pe->exportproto->textdescript)) >= FARTEXTLIMIT*4 ||
5673 abs(TDGETYOFF(pe->exportproto->textdescript)) >= FARTEXTLIMIT*4)
5674 {
5675 ni->userbits |= NHASFARTEXT;
5676 return;
5677 }
5678 if ((TDGETSIZE(pe->exportproto->textdescript)&TXTQLAMBDA) == 0)
5679 {
5680 ni->userbits |= NHASFARTEXT;
5681 return;
5682 }
5683 high.fromport = pe->exportproto;
5684 savecell = el_curwindowpart->curnodeproto;
5685 el_curwindowpart->curnodeproto = ni->parent;
5686 us_gethightextsize(&high, &xw, &yw, el_curwindowpart);
5687 el_curwindowpart->curnodeproto = savecell;
5688 if (xw > sizelimit || yw > sizelimit)
5689 {
5690 ni->userbits |= NHASFARTEXT;
5691 return;
5692 }
5693 }
5694 }
5695
5696 /*
5697 * routine to recursively travel along all arcs coming out of nodeinst "ni"
5698 * and set the NODEFLAGBIT bit in the connecting nodeinst "userbits" if that node
5699 * is connected horizontally (if "hor" is true) or connected vertically (if
5700 * "hor" is zero). This is called from "spread" to propagate along manhattan
5701 * arcs that are in the correct orientation (along the spread line).
5702 */
us_manhattantravel(NODEINST * ni,BOOLEAN hor)5703 void us_manhattantravel(NODEINST *ni, BOOLEAN hor)
5704 {
5705 REGISTER PORTARCINST *pi;
5706 REGISTER NODEINST *other;
5707 REGISTER ARCINST *ai;
5708
5709 ni->userbits |= NODEFLAGBIT;
5710 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
5711 {
5712 ai = pi->conarcinst;
5713 if (hor)
5714 {
5715 /* "hor" is nonzero: only want horizontal arcs */
5716 if (((ai->userbits&AANGLE)>>AANGLESH) != 0 &&
5717 ((ai->userbits&AANGLE)>>AANGLESH) != 180) continue;
5718 } else
5719 {
5720 /* "hor" is zero: only want vertical arcs */
5721 if (((ai->userbits&AANGLE)>>AANGLESH) != 90 &&
5722 ((ai->userbits&AANGLE)>>AANGLESH) != 270) continue;
5723 }
5724 if (ai->end[0].portarcinst == pi) other = ai->end[1].nodeinst; else
5725 other = ai->end[0].nodeinst;
5726 if ((other->userbits&NODEFLAGBIT) != 0) continue;
5727 us_manhattantravel(other, hor);
5728 }
5729 }
5730
5731 /*
5732 * routine to replace node "oldni" with a new one of type "newnp"
5733 * and return the new node. Also removes any node-specific variables.
5734 */
us_replacenodeinst(NODEINST * oldni,NODEPROTO * newnp,BOOLEAN ignoreportnames,BOOLEAN allowmissingports)5735 NODEINST *us_replacenodeinst(NODEINST *oldni, NODEPROTO *newnp, BOOLEAN ignoreportnames,
5736 BOOLEAN allowmissingports)
5737 {
5738 REGISTER NODEINST *newni;
5739 REGISTER VARIABLE *var, *cvar;
5740 REGISTER NODEPROTO *cnp;
5741 REGISTER PORTARCINST *pi;
5742 REGISTER INTBIG i, j;
5743 typedef struct
5744 {
5745 CHAR *variablename;
5746 NODEPROTO **prim;
5747 } POSSIBLEVARIABLES;
5748 static POSSIBLEVARIABLES killvariables[] =
5749 {
5750 {x_("ATTR_length"), &sch_transistorprim},
5751 {x_("ATTR_length"), &sch_transistor4prim},
5752 {x_("ATTR_width"), &sch_transistorprim},
5753 {x_("ATTR_width"), &sch_transistor4prim},
5754 {x_("ATTR_area"), &sch_transistorprim},
5755 {x_("ATTR_area"), &sch_transistor4prim},
5756 {x_("SIM_spice_model"), &sch_sourceprim},
5757 {x_("SIM_spice_model"), &sch_transistorprim},
5758 {x_("SIM_spice_model"), &sch_transistor4prim},
5759 {x_("SCHEM_meter_type"), &sch_meterprim},
5760 {x_("SCHEM_diode"), &sch_diodeprim},
5761 {x_("SCHEM_capacitance"), &sch_capacitorprim},
5762 {x_("SCHEM_resistance"), &sch_resistorprim},
5763 {x_("SCHEM_inductance"), &sch_inductorprim},
5764 {x_("SCHEM_function"), &sch_bboxprim},
5765 {0, 0}
5766 };
5767
5768 /* first start changes to node and all arcs touching this node */
5769 startobjectchange((INTBIG)oldni, VNODEINST);
5770 for(pi = oldni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
5771 startobjectchange((INTBIG)pi->conarcinst, VARCINST);
5772
5773 /* replace the node */
5774 newni = replacenodeinst(oldni, newnp, ignoreportnames, allowmissingports);
5775 if (newni != NONODEINST)
5776 {
5777 /* remove variables that make no sense */
5778 for(i=0; killvariables[i].variablename != 0; i++)
5779 {
5780 if (newni->proto == *killvariables[i].prim) continue;
5781 var = getval((INTBIG)newni, VNODEINST, -1, killvariables[i].variablename);
5782 if (var != NOVARIABLE)
5783 (void)delval((INTBIG)newni, VNODEINST, killvariables[i].variablename);
5784 }
5785
5786 /* remove parameters that don't exist on the new object */
5787 for(i=0; i<newni->numvar; i++)
5788 {
5789 var = &newni->firstvar[i];
5790 if (TDGETISPARAM(var->textdescript) == 0) continue;
5791
5792 /* see if this parameter exists on the new prototype */
5793 cnp = contentsview(newnp);
5794 if (cnp == NONODEPROTO) cnp = newnp;
5795 for(j=0; j<cnp->numvar; j++)
5796 {
5797 cvar = &cnp->firstvar[j];
5798 if (var->key != cvar->key) continue;
5799 if (TDGETISPARAM(cvar->textdescript) != 0) break;
5800 }
5801 if (j >= cnp->numvar)
5802 {
5803 (void)delvalkey((INTBIG)newni, VNODEINST, var->key);
5804 i--;
5805 }
5806 }
5807
5808 /* now inherit parameters that now do exist */
5809 us_inheritattributes(newni);
5810
5811 /* remove node name if it is not visible */
5812 var = getvalkey((INTBIG)newni, VNODEINST, -1, el_node_name_key);
5813 if (var != NOVARIABLE && (var->type&VDISPLAY) == 0)
5814 (void)delvalkey((INTBIG)newni, VNODEINST, el_node_name_key);
5815
5816 /* end changes to node and all arcs touching this node */
5817 endobjectchange((INTBIG)newni, VNODEINST);
5818 for(pi = newni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
5819 endobjectchange((INTBIG)pi->conarcinst, VARCINST);
5820 }
5821 return(newni);
5822 }
5823
5824 /*
5825 * Routine to erase all of the objects in cell "np" that are in the NOGEOM-terminated
5826 * list of GEOM modules "list".
5827 */
us_eraseobjectsinlist(NODEPROTO * np,GEOM ** list)5828 void us_eraseobjectsinlist(NODEPROTO *np, GEOM **list)
5829 {
5830 REGISTER INTBIG i, otherend;
5831 REGISTER INTBIG deletedobjects;
5832 REGISTER NODEINST *ni, *nextni, *ni1, *ni2;
5833 ARCINST *ai;
5834 REGISTER PORTARCINST *pi;
5835
5836 /* mark all nodes touching arcs that are killed */
5837 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst) ni->temp1 = 0;
5838 for(i=0; list[i] != NOGEOM; i++)
5839 {
5840 if (list[i]->entryisnode) continue;
5841 ai = list[i]->entryaddr.ai;
5842 ai->end[0].nodeinst->temp1 = 1;
5843 ai->end[1].nodeinst->temp1 = 1;
5844 }
5845
5846 /* also mark all nodes on arcs that will be erased */
5847 for(i=0; list[i] != NOGEOM; i++)
5848 {
5849 if (!list[i]->entryisnode) continue;
5850 ni = list[i]->entryaddr.ni;
5851 if (ni->temp1 != 0) ni->temp1 = 2;
5852 }
5853
5854 /* also mark all nodes on the other end of arcs connected to erased nodes */
5855 for(i=0; list[i] != NOGEOM; i++)
5856 {
5857 if (!list[i]->entryisnode) continue;
5858 ni = list[i]->entryaddr.ni;
5859 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
5860 {
5861 ai = pi->conarcinst;
5862 if (ai->end[0].portarcinst == pi) otherend = 1; else
5863 otherend = 0;
5864 if (ai->end[otherend].nodeinst->temp1 == 0)
5865 ai->end[otherend].nodeinst->temp1 = 1;
5866 }
5867 }
5868
5869 /* see if this is a major change */
5870 deletedobjects = 0;
5871 for(i=0; list[i] != NOGEOM; i++)
5872 {
5873 if (!list[i]->entryisnode) deletedobjects++; else
5874 {
5875 ni = list[i]->entryaddr.ni;
5876 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
5877 deletedobjects++;
5878 }
5879 }
5880
5881 /* if this change is too vast, simply turn off network tool while it happens */
5882 if (deletedobjects > 100) toolturnoff(net_tool, FALSE);
5883
5884 /* now kill all of the arcs */
5885 for(i=0; list[i] != NOGEOM; i++)
5886 {
5887 if (list[i]->entryisnode) continue;
5888 ai = list[i]->entryaddr.ai;
5889
5890 /* see if nodes need to be undrawn to account for "Steiner Point" changes */
5891 ni1 = ai->end[0].nodeinst; ni2 = ai->end[1].nodeinst;
5892 if (ni1->temp1 == 1 && (ni1->proto->userbits&WIPEON1OR2) != 0)
5893 startobjectchange((INTBIG)ni1, VNODEINST);
5894 if (ni2->temp1 == 1 && (ni2->proto->userbits&WIPEON1OR2) != 0)
5895 startobjectchange((INTBIG)ni2, VNODEINST);
5896
5897 startobjectchange((INTBIG)ai, VARCINST);
5898 if (killarcinst(ai)) ttyputerr(_("Error killing arc"));
5899
5900 /* see if nodes need to be redrawn to account for "Steiner Point" changes */
5901 if (ni1->temp1 == 1 && (ni1->proto->userbits&WIPEON1OR2) != 0)
5902 endobjectchange((INTBIG)ni1, VNODEINST);
5903 if (ni2->temp1 == 1 && (ni2->proto->userbits&WIPEON1OR2) != 0)
5904 endobjectchange((INTBIG)ni2, VNODEINST);
5905 }
5906
5907 /* next kill all of the nodes */
5908 for(i=0; list[i] != NOGEOM; i++)
5909 {
5910 if (!list[i]->entryisnode) continue;
5911 ni = list[i]->entryaddr.ni;
5912 us_erasenodeinst(ni);
5913 }
5914
5915 /* kill all pin nodes that touched an arc and no longer do */
5916 for(ni = np->firstnodeinst; ni != NONODEINST; ni = nextni)
5917 {
5918 nextni = ni->nextnodeinst;
5919 if (ni->temp1 == 0) continue;
5920 if (ni->proto->primindex == 0) continue;
5921 if (((ni->proto->userbits&NFUNCTION) >> NFUNCTIONSH) != NPPIN) continue;
5922 if (ni->firstportarcinst != NOPORTARCINST || ni->firstportexpinst != NOPORTEXPINST)
5923 continue;
5924 us_erasenodeinst(ni);
5925 }
5926
5927 /* kill all unexported pin or bus nodes left in the middle of arcs */
5928 for(ni = np->firstnodeinst; ni != NONODEINST; ni = nextni)
5929 {
5930 nextni = ni->nextnodeinst;
5931 if (ni->temp1 == 0) continue;
5932 if (ni->proto->primindex == 0) continue;
5933 if (((ni->proto->userbits&NFUNCTION) >> NFUNCTIONSH) != NPPIN) continue;
5934 if (ni->firstportexpinst != NOPORTEXPINST) continue;
5935 (void)us_erasepassthru(ni, FALSE, &ai);
5936 }
5937
5938 /* if network was turned off, turn it back on */
5939 if (deletedobjects > 100) toolturnon(net_tool);
5940 }
5941
5942 /*
5943 * Routine to erase node "ni" and all associated arcs, exports, etc.
5944 */
us_erasenodeinst(NODEINST * ni)5945 void us_erasenodeinst(NODEINST *ni)
5946 {
5947 REGISTER PORTARCINST *pi, *npi;
5948 REGISTER ARCINST *ai;
5949 REGISTER NODEINST *ni1, *ni2;
5950
5951 /* erase all connecting arcs to this nodeinst */
5952 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = npi)
5953 {
5954 npi = pi->nextportarcinst;
5955
5956 /* don't delete if already dead */
5957 ai = pi->conarcinst;
5958 if ((ai->userbits&DEADA) != 0) continue;
5959
5960 /* see if nodes need to be undrawn to account for "Steiner Point" changes */
5961 ni1 = ai->end[0].nodeinst; ni2 = ai->end[1].nodeinst;
5962 if ((ni1->proto->userbits&WIPEON1OR2) != 0) startobjectchange((INTBIG)ni1, VNODEINST);
5963 if ((ni2->proto->userbits&WIPEON1OR2) != 0) startobjectchange((INTBIG)ni2, VNODEINST);
5964
5965 startobjectchange((INTBIG)ai, VARCINST);
5966 if (killarcinst(ai)) ttyputerr(_("Error killing arc"));
5967
5968 /* see if nodes need to be redrawn to account for "Steiner Point" changes */
5969 if ((ni1->proto->userbits&WIPEON1OR2) != 0) endobjectchange((INTBIG)ni1, VNODEINST);
5970 if ((ni2->proto->userbits&WIPEON1OR2) != 0) endobjectchange((INTBIG)ni2, VNODEINST);
5971 }
5972
5973 /* see if this nodeinst is a port of the cell */
5974 startobjectchange((INTBIG)ni, VNODEINST);
5975 if (ni->firstportexpinst != NOPORTEXPINST) us_undoportproto(ni, NOPORTPROTO);
5976
5977 /* now erase the nodeinst */
5978 if (killnodeinst(ni)) ttyputerr(_("Error from killnodeinst"));
5979 }
5980
5981 #define NORECONNECT ((RECONNECT *)-1)
5982
5983 typedef struct Ireconnect
5984 {
5985 NETWORK *net; /* network for this reconnection */
5986 INTBIG arcsfound; /* number of arcs found on this reconnection */
5987 INTBIG reconx[2], recony[2]; /* coordinate at other end of arc */
5988 INTBIG origx[2], origy[2]; /* coordinate where arc hits deleted node */
5989 INTBIG dx[2], dy[2]; /* distance between ends */
5990 NODEINST *reconno[2]; /* node at other end of arc */
5991 PORTPROTO *reconpt[2]; /* port at other end of arc */
5992 ARCINST *reconar[2]; /* arcinst being reconnected */
5993 ARCPROTO *ap; /* prototype of new arc */
5994 INTBIG wid; /* width of new arc */
5995 INTBIG bits; /* user bits of new arc */
5996 struct Ireconnect *nextreconnect;
5997 } RECONNECT;
5998
5999 /*
6000 * routine to kill a node between two arcs and join the arc as one. Returns an error
6001 * code according to its success. If it worked, the new arc is placed in "newai".
6002 */
us_erasepassthru(NODEINST * ni,BOOLEAN allowdiffs,ARCINST ** newai)6003 INTBIG us_erasepassthru(NODEINST *ni, BOOLEAN allowdiffs, ARCINST **newai)
6004 {
6005 INTBIG i, j, retval;
6006 PORTARCINST *pi;
6007 REGISTER ARCINST *ai;
6008 REGISTER NODEPROTO *cell;
6009 RECONNECT *firstrecon, *re, *nextre;
6010
6011 /* disallow erasing if lock is on */
6012 cell = ni->parent;
6013 if (us_cantedit(cell, ni, TRUE)) return(-1);
6014
6015 /* look for pairs arcs that will get reconnected */
6016 firstrecon = NORECONNECT;
6017 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
6018 {
6019 /* ignore arcs that connect from the node to itself */
6020 ai = pi->conarcinst;
6021 if (ai->end[0].nodeinst == ni && ai->end[1].nodeinst == ni) continue;
6022
6023 /* find a "reconnect" object with this network */
6024 for(re = firstrecon; re != NORECONNECT; re = re->nextreconnect)
6025 if (re->net == ai->network) break;
6026 if (re == NORECONNECT)
6027 {
6028 re = (RECONNECT *)emalloc(sizeof (RECONNECT), us_tool->cluster);
6029 if (re == 0) return(-1);
6030 re->net = ai->network;
6031 re->arcsfound = 0;
6032 re->nextreconnect = firstrecon;
6033 firstrecon = re;
6034 }
6035 j = re->arcsfound;
6036 re->arcsfound++;
6037 if (re->arcsfound > 2) continue;
6038 re->reconar[j] = ai;
6039 for(i=0; i<2; i++) if (ai->end[i].nodeinst != ni)
6040 {
6041 re->reconno[j] = ai->end[i].nodeinst;
6042 re->reconpt[j] = ai->end[i].portarcinst->proto;
6043 re->reconx[j] = ai->end[i].xpos;
6044 re->origx[j] = ai->end[1-i].xpos;
6045 re->dx[j] = re->reconx[j] - re->origx[j];
6046 re->recony[j] = ai->end[i].ypos;
6047 re->origy[j] = ai->end[1-i].ypos;
6048 re->dy[j] = re->recony[j] - re->origy[j];
6049 }
6050 }
6051
6052 /* examine all of the reconnection situations */
6053 for(re = firstrecon; re != NORECONNECT; re = re->nextreconnect)
6054 {
6055 if (re->arcsfound != 2) continue;
6056
6057 /* verify that the two arcs to merge have the same type */
6058 if (re->reconar[0]->proto != re->reconar[1]->proto) { re->arcsfound = -1; continue; }
6059 re->ap = re->reconar[0]->proto;
6060
6061 if (!allowdiffs)
6062 {
6063 /* verify that the two arcs to merge have the same width */
6064 if (re->reconar[0]->width != re->reconar[1]->width) { re->arcsfound = -2; continue; }
6065
6066 /* verify that the two arcs have the same slope */
6067 if ((re->dx[1]*re->dy[0]) != (re->dx[0]*re->dy[1])) { re->arcsfound = -3; continue; }
6068 if (re->origx[0] != re->origx[1] || re->origy[0] != re->origy[1])
6069 {
6070 /* did not connect at the same location: be sure that angle is consistent */
6071 if (re->dx[0] != 0 || re->dy[0] != 0)
6072 {
6073 if (((re->origx[0]-re->origx[1])*re->dy[0]) !=
6074 (re->dx[0]*(re->origy[0]-re->origy[1]))) { re->arcsfound = -3; continue; }
6075 } else if (re->dx[1] != 0 || re->dy[1] != 0)
6076 {
6077 if (((re->origx[0]-re->origx[1])*re->dy[1]) !=
6078 (re->dx[1]*(re->origy[0]-re->origy[1]))) { re->arcsfound = -3; continue; }
6079 } else { re->arcsfound = -3; continue; }
6080 }
6081 }
6082
6083 /* remember facts about the new arcinst */
6084 re->wid = re->reconar[0]->width;
6085 re->bits = re->reconar[0]->userbits | re->reconar[1]->userbits;
6086
6087 /* special code to handle directionality */
6088 if ((re->bits&(ISDIRECTIONAL|ISNEGATED|NOTEND0|NOTEND1|REVERSEEND)) != 0)
6089 {
6090 /* reverse ends if the arcs point the wrong way */
6091 for(i=0; i<2; i++)
6092 {
6093 if (re->reconar[i]->end[i].nodeinst == ni)
6094 {
6095 if ((re->reconar[i]->userbits&REVERSEEND) == 0)
6096 re->reconar[i]->userbits |= REVERSEEND; else
6097 re->reconar[i]->userbits &= ~REVERSEEND;
6098 }
6099 }
6100 re->bits = re->reconar[0]->userbits | re->reconar[1]->userbits;
6101
6102 /* two negations make a positive */
6103 if ((re->reconar[0]->userbits&ISNEGATED) != 0 &&
6104 (re->reconar[1]->userbits&ISNEGATED) != 0) re->bits &= ~ISNEGATED;
6105 }
6106 }
6107
6108 /* see if any reconnection will be done */
6109 for(re = firstrecon; re != NORECONNECT; re = re->nextreconnect)
6110 {
6111 retval = re->arcsfound;
6112 if (retval == 2) break;
6113 }
6114
6115 /* erase the nodeinst if reconnection will be done (this will erase connecting arcs) */
6116 if (retval == 2) us_erasenodeinst(ni);
6117
6118 /* reconnect the arcs */
6119 for(re = firstrecon; re != NORECONNECT; re = re->nextreconnect)
6120 {
6121 if (re->arcsfound != 2) continue;
6122
6123 /* make the new arcinst */
6124 *newai = newarcinst(re->ap, re->wid, re->bits, re->reconno[0], re->reconpt[0],
6125 re->reconx[0], re->recony[0], re->reconno[1], re->reconpt[1], re->reconx[1], re->recony[1],
6126 cell);
6127 if (*newai == NOARCINST) { re->arcsfound = -5; continue; }
6128
6129 (void)copyvars((INTBIG)re->reconar[0], VARCINST, (INTBIG)*newai, VARCINST, FALSE);
6130 (void)copyvars((INTBIG)re->reconar[1], VARCINST, (INTBIG)*newai, VARCINST, FALSE);
6131 endobjectchange((INTBIG)*newai, VARCINST);
6132 (*newai)->changed = 0;
6133 }
6134
6135 /* deallocate */
6136 for(re = firstrecon; re != NORECONNECT; re = nextre)
6137 {
6138 nextre = re->nextreconnect;
6139 efree((CHAR *)re);
6140 }
6141 return(retval);
6142 }
6143
6144 /*
6145 * Routine to set the variable "key" on object "geom" to the new value "newvalue". The former
6146 * type of that variable is "oldtype".
6147 */
us_setvariablevalue(GEOM * geom,INTBIG key,CHAR * newvalue,INTBIG oldtype,UINTBIG * descript)6148 void us_setvariablevalue(GEOM *geom, INTBIG key, CHAR *newvalue, INTBIG oldtype, UINTBIG *descript)
6149 {
6150 INTBIG newval, newtype, units;
6151 REGISTER VARIABLE *var;
6152 REGISTER NODEINST *ni;
6153 REGISTER ARCINST *ai;
6154
6155 if ((oldtype&(VCODE1|VCODE2)) != 0)
6156 {
6157 newval = (INTBIG)newvalue;
6158 } else
6159 {
6160 if (descript == 0) units = 0; else
6161 units = TDGETUNITS(descript);
6162 getsimpletype(newvalue, &newtype, &newval, units);
6163 oldtype = (oldtype & ~VTYPE) | newtype;
6164 }
6165 us_pushhighlight();
6166 us_clearhighlightcount();
6167 if (geom->entryisnode)
6168 {
6169 ni = geom->entryaddr.ni;
6170 startobjectchange((INTBIG)ni, VNODEINST);
6171 var = setvalkey((INTBIG)ni, VNODEINST, key, newval, oldtype);
6172 if (var != NOVARIABLE && descript != 0)
6173 TDCOPY(var->textdescript, descript);
6174 endobjectchange((INTBIG)ni, VNODEINST);
6175 } else
6176 {
6177 ai = geom->entryaddr.ai;
6178 startobjectchange((INTBIG)ai, VARCINST);
6179 var = setvalkey((INTBIG)ai, VARCINST, key, newval, oldtype);
6180 if (var != NOVARIABLE && descript != 0)
6181 TDCOPY(var->textdescript, descript);
6182 endobjectchange((INTBIG)ai, VARCINST);
6183 }
6184 us_pophighlight(FALSE);
6185 }
6186
6187 /*
6188 * Routine to set the variable "key" on object "geom" to the new value "value".
6189 */
us_setfloatvariablevalue(GEOM * geom,INTBIG key,VARIABLE * oldvar,float value)6190 void us_setfloatvariablevalue(GEOM *geom, INTBIG key, VARIABLE *oldvar, float value)
6191 {
6192 REGISTER VARIABLE *var;
6193 REGISTER BOOLEAN haddescript;
6194 REGISTER NODEINST *ni;
6195 REGISTER ARCINST *ai;
6196 UINTBIG descript[TEXTDESCRIPTSIZE];
6197
6198 if (oldvar == NOVARIABLE) haddescript = FALSE; else
6199 {
6200 haddescript = TRUE;
6201 TDCOPY(descript, oldvar->textdescript);
6202 }
6203
6204 us_pushhighlight();
6205 us_clearhighlightcount();
6206 if (geom->entryisnode)
6207 {
6208 ni = geom->entryaddr.ni;
6209 startobjectchange((INTBIG)ni, VNODEINST);
6210 var = setvalkey((INTBIG)ni, VNODEINST, key, castint(value), VFLOAT|VDISPLAY);
6211 if (var != NOVARIABLE && haddescript)
6212 TDCOPY(var->textdescript, descript);
6213 endobjectchange((INTBIG)ni, VNODEINST);
6214 } else
6215 {
6216 ai = geom->entryaddr.ai;
6217 startobjectchange((INTBIG)ai, VARCINST);
6218 var = setvalkey((INTBIG)ai, VARCINST, key, castint(value), VFLOAT|VDISPLAY);
6219 if (var != NOVARIABLE && haddescript)
6220 TDCOPY(var->textdescript, descript);
6221 endobjectchange((INTBIG)ai, VARCINST);
6222 }
6223 us_pophighlight(FALSE);
6224 }
6225
6226 /*
6227 * Routine to align the "total" nodes in "nodelist".
6228 * If "horizontal" is true, align them horizontally according to "direction"
6229 * (0 for left, 1 for right, 2 for center).
6230 * If "horizontal is false, align them veritcally according to "direction"
6231 * (0 for top, 1 for bottom, 2 for center).
6232 */
us_alignnodes(INTBIG total,NODEINST ** nodelist,BOOLEAN horizontal,INTBIG direction)6233 void us_alignnodes(INTBIG total, NODEINST **nodelist, BOOLEAN horizontal, INTBIG direction)
6234 {
6235 REGISTER INTBIG *dlx, *dly, *dhx, *dhy, *dxf, i, lx, hx, ly, hy;
6236 REGISTER NODEINST *ni;
6237
6238 if (total <= 0) return;
6239 dlx = (INTBIG *)emalloc(total * SIZEOFINTBIG, el_tempcluster);
6240 if (dlx == 0) return;
6241 dhx = (INTBIG *)emalloc(total * SIZEOFINTBIG, el_tempcluster);
6242 if (dhx == 0) return;
6243 dly = (INTBIG *)emalloc(total * SIZEOFINTBIG, el_tempcluster);
6244 if (dly == 0) return;
6245 dhy = (INTBIG *)emalloc(total * SIZEOFINTBIG, el_tempcluster);
6246 if (dhy == 0) return;
6247 dxf = (INTBIG *)emalloc(total * SIZEOFINTBIG, el_tempcluster);
6248 if (dxf == 0) return;
6249
6250 /* get bounds */
6251 for(i=0; i<total; i++)
6252 {
6253 ni = nodelist[i];
6254 if (i == 0)
6255 {
6256 lx = ni->geom->lowx;
6257 hx = ni->geom->highx;
6258 ly = ni->geom->lowy;
6259 hy = ni->geom->highy;
6260 } else
6261 {
6262 if (ni->geom->lowx < lx) lx = ni->geom->lowx;
6263 if (ni->geom->highx > hx) hx = ni->geom->highx;
6264 if (ni->geom->lowy < ly) ly = ni->geom->lowy;
6265 if (ni->geom->highy > hy) hy = ni->geom->highy;
6266 }
6267 }
6268
6269 /* determine motion */
6270 for(i=0; i<total; i++)
6271 {
6272 dlx[i] = dhx[i] = dly[i] = dhy[i] = dxf[i] = 0;
6273 ni = nodelist[i];
6274 if (horizontal)
6275 {
6276 /* horizontal alignment */
6277 switch (direction)
6278 {
6279 case 0: /* align to left */
6280 dlx[i] = dhx[i] = lx - ni->geom->lowx;
6281 break;
6282 case 1: /* align to right */
6283 dlx[i] = dhx[i] = hx - ni->geom->highx;
6284 break;
6285 case 2: /* align to center */
6286 dlx[i] = dhx[i] = (lx + hx) / 2 - (ni->geom->lowx + ni->geom->highx) / 2;
6287 break;
6288 }
6289 } else
6290 {
6291 /* vertical alignment */
6292 switch (direction)
6293 {
6294 case 0: /* align to top */
6295 dly[i] = dhy[i] = hy - ni->geom->highy;
6296 break;
6297 case 1: /* align to bottom */
6298 dly[i] = dhy[i] = ly - ni->geom->lowy;
6299 break;
6300 case 2: /* align to center */
6301 dly[i] = dhy[i] = (ly + hy) / 2 - (ni->geom->lowy + ni->geom->highy) / 2;
6302 break;
6303 }
6304 }
6305 }
6306
6307 us_pushhighlight();
6308 us_clearhighlightcount();
6309 for(i=0; i<total; i++) startobjectchange((INTBIG)nodelist[i], VNODEINST);
6310 modifynodeinsts(total, nodelist, dlx, dly, dhx, dhy, dxf, dxf);
6311 for(i=0; i<total; i++) endobjectchange((INTBIG)nodelist[i], VNODEINST);
6312 us_pophighlight(TRUE);
6313
6314 efree((CHAR *)dlx);
6315 efree((CHAR *)dhx);
6316 efree((CHAR *)dly);
6317 efree((CHAR *)dhy);
6318 efree((CHAR *)dxf);
6319 }
6320
6321 /*********************************** ARRAYING FROM A FILE ***********************************/
6322
6323 #define MAXLINE 200
6324
6325 #define NOARRAYALIGN ((ARRAYALIGN *)-1)
6326
6327 typedef struct Iarrayalign
6328 {
6329 CHAR *cell;
6330 CHAR *inport;
6331 CHAR *outport;
6332 struct Iarrayalign *nextarrayalign;
6333 } ARRAYALIGN;
6334
6335 #define NOPORTASSOCIATE ((PORTASSOCIATE *)-1)
6336
6337 typedef struct Iportassociate
6338 {
6339 NODEINST *ni;
6340 PORTPROTO *pp;
6341 PORTPROTO *corepp;
6342 struct Iportassociate *nextportassociate;
6343 } PORTASSOCIATE;
6344
6345 /*
6346 * Routine to read file "file" and create an array. The file has this format:
6347 * celllibrary LIBFILE [copy]
6348 * facet CELLNAME
6349 * core CELLNAME
6350 * align CELLNAME INPORT OUTPORT
6351 * place CELLNAME [gap=DIST] [padport=coreport]* [export padport=padexport]
6352 * rotate (c | cc)
6353 */
us_arrayfromfile(CHAR * file)6354 void us_arrayfromfile(CHAR *file)
6355 {
6356 FILE *io;
6357 CHAR *truename, line[MAXLINE], *pt, *start, save, *par[2],
6358 *libname, *style, *cellname, *exportname;
6359 REGISTER INTBIG lineno, gap, gapx, gapy, lx, ly, hx, hy, len, i, copycells, angle;
6360 INTBIG ax, ay, ox, oy, cx, cy;
6361 REGISTER PORTPROTO *pp, *exportpp;
6362 REGISTER LIBRARY *lib, *savelib;
6363 REGISTER NODEPROTO *np, *cell, *corenp;
6364 REGISTER NODEINST *ni, *lastni;
6365 REGISTER ARCINST *ai;
6366 REGISTER TECHNOLOGY *savetech;
6367 REGISTER ARRAYALIGN *aa, *firstaa;
6368 REGISTER PORTASSOCIATE *pa, *firstpa;
6369 static INTBIG filetypearray = -1;
6370 REGISTER void *infstr;
6371
6372 if (filetypearray < 0)
6373 filetypearray = setupfiletype(x_("arr"), x_("*.arr"), MACFSTAG('TEXT'), FALSE, x_("arrfile"), _("Array"));
6374 io = xopen(file, filetypearray, 0, &truename);
6375 if (io == 0) return;
6376
6377 firstaa = NOARRAYALIGN;
6378 firstpa = NOPORTASSOCIATE;
6379 lineno = 0;
6380 angle = 0;
6381 copycells = 0;
6382 cell = corenp = NONODEPROTO;
6383 lastni = NONODEINST;
6384 lib = NOLIBRARY;
6385 for(;;)
6386 {
6387 if (xfgets(line, MAXLINE, io) != 0) break;
6388 lineno++;
6389 pt = line;
6390 while (*pt == ' ' || *pt == '\t') pt++;
6391 if (*pt == 0 || *pt == ';') continue;
6392 start = pt;
6393 while (*pt != ' ' && *pt != '\t' && *pt != 0) pt++;
6394 if (*pt == 0)
6395 {
6396 us_abortcommand(_("Line %ld: too short"), lineno);
6397 break;
6398 }
6399 *pt++ = 0;
6400 if (namesame(start, x_("celllibrary")) == 0)
6401 {
6402 while (*pt == ' ' || *pt == '\t') pt++;
6403 start = pt;
6404 while (*pt != ' ' && *pt != '\t' && *pt != 0) pt++;
6405 if (*pt != 0) *pt++ = 0;
6406 libname = skippath(start);
6407 style = x_("binary");
6408 len = estrlen(libname);
6409 for(i=len-1; i>0; i--) if (libname[i] == '.') break;
6410 if (i > 0)
6411 {
6412 libname[i] = 0;
6413 if (namesame(&libname[i+1], x_("txt")) == 0) style = x_("text");
6414 }
6415 lib = getlibrary(libname);
6416 if (i > 0) libname[i] = '.';
6417 if (lib == NOLIBRARY)
6418 {
6419 lib = newlibrary(libname, start);
6420 if (asktool(io_tool, x_("read"), (INTBIG)lib, (INTBIG)style, 0) != 0)
6421 {
6422 us_abortcommand(_("Line %ld: cannot read library %s"), lineno,
6423 start);
6424 break;
6425 }
6426 }
6427 if (*pt != 0)
6428 {
6429 while (*pt == ' ' || *pt == '\t') pt++;
6430 if (namesame(pt, x_("copy")) == 0) copycells = 1;
6431 }
6432 continue;
6433 }
6434 if (namesame(start, x_("facet")) == 0)
6435 {
6436 while (*pt == ' ' || *pt == '\t') pt++;
6437 start = pt;
6438 while (*pt != ' ' && *pt != '\t' && *pt != 0) pt++;
6439 *pt = 0;
6440 cell = us_newnodeproto(start, el_curlib);
6441 if (cell == NONODEPROTO)
6442 {
6443 us_abortcommand(_("Line %ld: unable to create cell '%s'"), lineno, start);
6444 break;
6445 }
6446 continue;
6447 }
6448 if (namesame(start, x_("core")) == 0)
6449 {
6450 while (*pt == ' ' || *pt == '\t') pt++;
6451 start = pt;
6452 while (*pt != ' ' && *pt != '\t' && *pt != 0) pt++;
6453 *pt = 0;
6454 corenp = getnodeproto(start);
6455 if (corenp == NONODEPROTO)
6456 {
6457 us_abortcommand(_("Line %ld: cannot find core cell '%s'"), lineno, start);
6458 break;
6459 }
6460 continue;
6461 }
6462 if (namesame(start, x_("rotate")) == 0)
6463 {
6464 while (*pt == ' ' || *pt == '\t') pt++;
6465 start = pt;
6466 while (*pt != ' ' && *pt != '\t' && *pt != 0) pt++;
6467 *pt++ = 0;
6468 if (namesame(start, x_("c")) == 0) angle = (angle + 2700) % 3600; else
6469 if (namesame(start, x_("cc")) == 0) angle = (angle + 900) % 3600; else
6470 {
6471 us_abortcommand(_("Line %ld: incorrect rotation: %s"), lineno, start);
6472 break;
6473 }
6474 continue;
6475 }
6476 if (namesame(start, x_("align")) == 0)
6477 {
6478 aa = (ARRAYALIGN *)emalloc(sizeof (ARRAYALIGN), el_tempcluster);
6479 if (aa == 0) break;
6480 while (*pt == ' ' || *pt == '\t') pt++;
6481 start = pt;
6482 while (*pt != ' ' && *pt != '\t' && *pt != 0) pt++;
6483 if (*pt == 0)
6484 {
6485 us_abortcommand(_("Line %ld: missing 'in port' name"), lineno);
6486 break;
6487 }
6488 *pt++ = 0;
6489 (void)allocstring(&aa->cell, start, el_tempcluster);
6490
6491 while (*pt == ' ' || *pt == '\t') pt++;
6492 start = pt;
6493 while (*pt != ' ' && *pt != '\t' && *pt != 0) pt++;
6494 if (*pt == 0)
6495 {
6496 us_abortcommand(_("Line %ld: missing 'out port'"), lineno);
6497 break;
6498 }
6499 *pt++ = 0;
6500 (void)allocstring(&aa->inport, start, el_tempcluster);
6501
6502 while (*pt == ' ' || *pt == '\t') pt++;
6503 start = pt;
6504 while (*pt != ' ' && *pt != '\t' && *pt != 0) pt++;
6505 *pt = 0;
6506 (void)allocstring(&aa->outport, start, el_tempcluster);
6507 aa->nextarrayalign = firstaa;
6508 firstaa = aa;
6509 continue;
6510 }
6511 if (namesame(start, x_("place")) == 0)
6512 {
6513 if (cell == NONODEPROTO)
6514 {
6515 us_abortcommand(_("Line %ld: no 'facet' line specified for 'place'"),
6516 lineno);
6517 break;
6518 }
6519 while (*pt == ' ' || *pt == '\t') pt++;
6520 start = pt;
6521 while (*pt != ' ' && *pt != '\t' && *pt != 0) pt++;
6522 save = *pt;
6523 *pt = 0;
6524
6525 if (copycells != 0)
6526 {
6527 /* copying pads into this library: see if it is already there */
6528 np = getnodeproto(start);
6529 if (np == NONODEPROTO && lib != NOLIBRARY && lib != el_curlib)
6530 {
6531 /* not there: copy from pads library */
6532 savelib = el_curlib;
6533 el_curlib = lib;
6534 np = getnodeproto(start);
6535 el_curlib = savelib;
6536 if (np != NONODEPROTO)
6537 {
6538 np = us_copyrecursively(np, np->protoname,
6539 el_curlib, np->cellview, FALSE, FALSE, x_(""), FALSE, FALSE, FALSE);
6540 }
6541 }
6542 } else
6543 {
6544 /* simply make reference to the pads in the other library */
6545 infstr = initinfstr();
6546 formatinfstr(infstr, x_("%s:%s"), lib->libname, start);
6547 np = getnodeproto(returninfstr(infstr));
6548 }
6549 if (np == NONODEPROTO)
6550 {
6551 us_abortcommand(_("Line %ld: cannot find cell '%s'"), lineno, start);
6552 break;
6553 }
6554 *pt = save;
6555 gap = 0;
6556 exportpp = NOPORTPROTO;
6557 while (*pt != 0)
6558 {
6559 while (*pt == ' ' || *pt == '\t') pt++;
6560 if (*pt == 0) break;
6561 start = pt;
6562 while (*pt != ' ' && *pt != '\t' && *pt != '=' && *pt != 0) pt++;
6563 save = *pt;
6564 *pt = 0;
6565 if (namesame(start, x_("gap")) == 0)
6566 {
6567 *pt = save;
6568 if (*pt != '=')
6569 {
6570 us_abortcommand(_("Line %ld: missing '=' after 'gap'"), lineno);
6571 break;
6572 }
6573 pt++;
6574 while (*pt == ' ' || *pt == '\t') pt++;
6575 gap = atola(pt, 0);
6576 while (*pt != ' ' && *pt != '\t' && *pt != 0) pt++;
6577 } else if (namesame(start, x_("export")) == 0)
6578 {
6579 /* export a pad port */
6580 *pt = save;
6581 while (*pt == ' ' || *pt == '\t') pt++;
6582 if (*pt == 0)
6583 {
6584 us_abortcommand(_("Line %ld: missing port name after 'export'"),
6585 lineno);
6586 break;
6587 }
6588 start = pt;
6589 while (*pt != ' ' && *pt != '\t' && *pt != '=' && *pt != 0) pt++;
6590 save = *pt;
6591 *pt = 0;
6592 exportpp = getportproto(np, start);
6593 if (exportpp == NOPORTPROTO)
6594 {
6595 us_abortcommand(_("Line %ld: no port '%s' on cell '%s'"),
6596 lineno, start, describenodeproto(np));
6597 break;
6598 }
6599 *pt = save;
6600 while (*pt == ' ' || *pt == '\t') pt++;
6601 if (*pt++ != '=')
6602 {
6603 us_abortcommand(_("Line %ld: missing '=' name after 'export PORT'"),
6604 lineno);
6605 break;
6606 }
6607 while (*pt == ' ' || *pt == '\t') pt++;
6608 exportname = pt;
6609 while (*pt != ' ' && *pt != '\t' && *pt != 0) pt++;
6610 save = *pt;
6611 *pt = 0;
6612 if (*exportname == 0)
6613 {
6614 us_abortcommand(_("Line %ld: missing export name after 'export PORT='"),
6615 lineno);
6616 break;
6617 }
6618 if (save != 0) pt++;
6619 } else
6620 {
6621 pa = (PORTASSOCIATE *)emalloc(sizeof (PORTASSOCIATE), el_tempcluster);
6622 if (pa == 0) break;
6623 pa->ni = NONODEINST;
6624 pa->pp = getportproto(np, start);
6625 if (pa->pp == NOPORTPROTO)
6626 {
6627 us_abortcommand(_("Line %ld: no port '%s' on cell '%s'"),
6628 lineno, start, describenodeproto(np));
6629 break;
6630 }
6631 *pt = save;
6632 if (*pt != '=')
6633 {
6634 us_abortcommand(_("Line %ld: missing '=' after pad port name"),
6635 lineno);
6636 break;
6637 }
6638 pt++;
6639 while (*pt == ' ' || *pt == '\t') pt++;
6640 start = pt;
6641 while (*pt != ' ' && *pt != '\t' && *pt != 0) pt++;
6642 save = *pt;
6643 *pt = 0;
6644 if (corenp == NONODEPROTO)
6645 {
6646 us_abortcommand(_("Line %ld: no core cell for association"),
6647 lineno);
6648 break;
6649 }
6650 pa->corepp = getportproto(corenp, start);
6651 if (pa->corepp == NOPORTPROTO)
6652 {
6653 us_abortcommand(_("Line %ld: no port '%s' on cell '%s'"),
6654 lineno, start, describenodeproto(corenp));
6655 break;
6656 }
6657 *pt = save;
6658 pa->nextportassociate = firstpa;
6659 firstpa = pa;
6660 }
6661 }
6662
6663 /* place the pad */
6664 if (lastni != NONODEINST)
6665 {
6666 /* find the "outport" on the last node */
6667 cellname = nldescribenodeproto(lastni->proto);
6668 for(aa = firstaa; aa != NOARRAYALIGN; aa = aa->nextarrayalign)
6669 if (namesame(aa->cell, cellname) == 0) break;
6670 if (aa == NOARRAYALIGN)
6671 {
6672 us_abortcommand(_("Line %ld: no port alignment given for cell %s"),
6673 lineno, describenodeproto(lastni->proto));
6674 break;
6675 }
6676 pp = getportproto(lastni->proto, aa->outport);
6677 if (pp == NOPORTPROTO)
6678 {
6679 us_abortcommand(_("Line %ld: no port called '%s' on cell %s"),
6680 lineno, aa->outport, describenodeproto(lastni->proto));
6681 break;
6682 }
6683 portposition(lastni, pp, &ax, &ay);
6684
6685 /* find the "inport" on the new node */
6686 cellname = nldescribenodeproto(np);
6687 for(aa = firstaa; aa != NOARRAYALIGN; aa = aa->nextarrayalign)
6688 if (namesame(aa->cell, cellname) == 0) break;
6689 if (aa == NOARRAYALIGN)
6690 {
6691 us_abortcommand(_("Line %ld: no port alignment given for cell %s"),
6692 lineno, describenodeproto(np));
6693 break;
6694 }
6695 pp = getportproto(np, aa->inport);
6696 if (pp == NOPORTPROTO)
6697 {
6698 us_abortcommand(_("Line %ld: no port called '%s' on cell %s"),
6699 lineno, aa->inport, describenodeproto(np));
6700 break;
6701 }
6702 }
6703 corneroffset(NONODEINST, np, angle, 0, &ox, &oy, FALSE);
6704 lx = ox; hx = lx + (np->highx - np->lowx);
6705 ly = oy; hy = ly + (np->highy - np->lowy);
6706 ni = newnodeinst(np, lx, hx, ly, hy, 0, angle, cell);
6707 if (ni == NONODEINST)
6708 {
6709 us_abortcommand(_("Line %ld: problem creating %s instance"),
6710 lineno, describenodeproto(np));
6711 break;
6712 }
6713 if (lastni != NONODEINST)
6714 {
6715 switch (angle)
6716 {
6717 case 0: gapx = gap; gapy = 0; break;
6718 case 900: gapx = 0; gapy = gap; break;
6719 case 1800: gapx = -gap; gapy = 0; break;
6720 case 2700: gapx = 0; gapy = -gap; break;
6721 }
6722 portposition(ni, pp, &ox, &oy);
6723 modifynodeinst(ni, ax-ox+gapx, ay-oy+gapy, ax-ox+gapx, ay-oy+gapy, 0, 0);
6724 }
6725 endobjectchange((INTBIG)ni, VNODEINST);
6726 if (exportpp != NOPORTPROTO)
6727 {
6728 pp = newportproto(cell, ni, exportpp, exportname);
6729 endobjectchange((INTBIG)pp, VPORTPROTO);
6730 }
6731 lastni = ni;
6732
6733 /* fill in the port associations */
6734 for(pa = firstpa; pa != NOPORTASSOCIATE; pa = pa->nextportassociate)
6735 if (pa->ni == NONODEINST) pa->ni = ni;
6736 continue;
6737 }
6738 us_abortcommand(_("Line %ld: unknown keyword '%s'"), lineno, start);
6739 break;
6740 }
6741
6742 /* place the core if one was specified */
6743 if (corenp != NONODEPROTO)
6744 {
6745 (*el_curconstraint->solve)(cell);
6746 cx = (cell->lowx + cell->highx) / 2;
6747 cy = (cell->lowy + cell->highy) / 2;
6748 lx = cx - (corenp->highx - corenp->lowx) / 2;
6749 ly = cy - (corenp->highy - corenp->lowy) / 2;
6750 corneroffset(NONODEINST, corenp, 0, 0, &ax, &ay, FALSE);
6751 cx = lx + ax; cy = ly + ay;
6752 savetech = el_curtech; el_curtech = corenp->tech;
6753 gridalign(&cx, &cy, 1, cell);
6754 el_curtech = savetech;
6755 lx = cx - ax; ly = cy - ay;
6756 hx = lx + (corenp->highx - corenp->lowx);
6757 hy = ly + (corenp->highy - corenp->lowy);
6758
6759 ni = newnodeinst(corenp, lx, hx, ly, hy, 0, 0, cell);
6760 if (ni != NONODEINST)
6761 {
6762 endobjectchange((INTBIG)ni, VNODEINST);
6763 }
6764
6765 /* attach unrouted wires */
6766 for(pa = firstpa; pa != NOPORTASSOCIATE; pa = pa->nextportassociate)
6767 {
6768 if (pa->ni == NONODEINST) continue;
6769 portposition(ni, pa->corepp, &ox, &oy);
6770 portposition(pa->ni, pa->pp, &ax, &ay);
6771 ai = newarcinst(gen_unroutedarc, gen_unroutedarc->nominalwidth,
6772 us_makearcuserbits(gen_unroutedarc), ni, pa->corepp, ox, oy,
6773 pa->ni, pa->pp, ax, ay, cell);
6774 if (ai != NOARCINST)
6775 {
6776 endobjectchange((INTBIG)ai, VARCINST);
6777 }
6778 }
6779 }
6780
6781 /* done with the array file */
6782 xclose(io);
6783
6784 /* cleanup memory */
6785 while (firstpa != NOPORTASSOCIATE)
6786 {
6787 pa = firstpa;
6788 firstpa = pa->nextportassociate;
6789 efree((CHAR *)pa);
6790 }
6791 while (firstaa != NOARRAYALIGN)
6792 {
6793 aa = firstaa;
6794 firstaa = aa->nextarrayalign;
6795 efree(aa->cell);
6796 efree(aa->inport);
6797 efree(aa->outport);
6798 efree((CHAR *)aa);
6799 }
6800
6801 /* show the new cell */
6802 par[0] = describenodeproto(cell);
6803 us_editcell(1, par);
6804 }
6805
6806 /*********************************** NODE AND ARC SUPPORT ***********************************/
6807
6808 /*
6809 * routine to yank the contents of complex node instance "topno" into its
6810 * parent cell.
6811 */
us_yankonenode(NODEINST * topno)6812 void us_yankonenode(NODEINST *topno)
6813 {
6814 REGISTER NODEINST *ni, *newni, **nodelist;
6815 REGISTER ARCINST *ai, *newar, **arclist;
6816 REGISTER PORTARCINST *pi, *nextpi;
6817 REGISTER PORTEXPINST *pe, *nextpe;
6818 REGISTER PORTPROTO *pp;
6819 REGISTER NODEPROTO *np;
6820 REGISTER ARCPROTO *ap;
6821 NODEINST *noa[2];
6822 PORTPROTO *pta[2];
6823 REGISTER INTBIG wid, i, j, total, oldbits, lowx, highx, lowy, highy;
6824 XARRAY localtrans, localrot, trans;
6825 INTBIG nox[2], noy[2], newxc, newyc, xc, yc;
6826 INTBIG newang;
6827 static POLYGON *poly = NOPOLYGON;
6828
6829 /* get polygon */
6830 (void)needstaticpolygon(&poly, 4, us_tool->cluster);
6831
6832 /* make transformation matrix for this cell */
6833 np = topno->proto;
6834 maketrans(topno, localtrans);
6835 makerot(topno, localrot);
6836 transmult(localtrans, localrot, trans);
6837
6838 /* build a list of nodes to copy */
6839 total = 0;
6840 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst) total++;
6841 if (total == 0) return;
6842 nodelist = (NODEINST **)emalloc((total * (sizeof (NODEINST *))), el_tempcluster);
6843 if (nodelist == 0) return;
6844 for(i=0, ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
6845 nodelist[i++] = ni;
6846
6847 /* sort the nodes by name */
6848 esort(nodelist, total, sizeof (NODEINST *), us_sortnodesbyname);
6849
6850 /* copy the nodes */
6851 for(i=0; i<total; i++)
6852 {
6853 ni = nodelist[i];
6854
6855 /* do not yank "cell center" or "essential bounds" primitives */
6856 if (ni->proto == gen_cellcenterprim || ni->proto == gen_essentialprim)
6857 {
6858 ni->temp1 = (INTBIG)NONODEINST;
6859 continue;
6860 }
6861
6862 /* this "center" computation is unstable for odd size nodes */
6863 xc = (ni->lowx + ni->highx) / 2; yc = (ni->lowy + ni->highy) / 2;
6864 xform(xc, yc, &newxc, &newyc, trans);
6865 lowx = ni->lowx + newxc - xc;
6866 lowy = ni->lowy + newyc - yc;
6867 highx = ni->highx + newxc - xc;
6868 highy = ni->highy + newyc - yc;
6869 if (ni->transpose == 0) newang = ni->rotation + topno->rotation; else
6870 newang = ni->rotation + 3600 - topno->rotation;
6871 newang = newang % 3600; if (newang < 0) newang += 3600;
6872 newni = newnodeinst(ni->proto, lowx, highx, lowy, highy,
6873 (ni->transpose+topno->transpose)&1, newang, topno->parent);
6874 if (newni == NONODEINST)
6875 {
6876 us_abortcommand(_("Cannot create node in this cell"));
6877 return;
6878 }
6879 ni->temp1 = (INTBIG)newni;
6880 newni->userbits = ni->userbits;
6881 TDCOPY(newni->textdescript, ni->textdescript);
6882 (void)copyvars((INTBIG)ni, VNODEINST, (INTBIG)newni, VNODEINST, TRUE);
6883 endobjectchange((INTBIG)newni, VNODEINST);
6884 }
6885 efree((CHAR *)nodelist);
6886
6887 /* see if there are any arcs to extract */
6888 total = 0;
6889 for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst) total++;
6890 if (total != 0)
6891 {
6892 /* build a list of arcs to copy */
6893 arclist = (ARCINST **)emalloc((total * (sizeof (ARCINST *))), el_tempcluster);
6894 if (arclist == 0) return;
6895 for(i=0, ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
6896 arclist[i++] = ai;
6897
6898 /* sort the arcs by name */
6899 esort(arclist, total, sizeof (ARCINST *), us_sortarcsbyname);
6900
6901 /* copy the arcs */
6902 for(i=0; i<total; i++)
6903 {
6904 ai = arclist[i];
6905
6906 /* ignore arcs connected to nodes that didn't get yanked */
6907 if ((NODEINST *)ai->end[0].nodeinst->temp1 == NONODEINST ||
6908 (NODEINST *)ai->end[1].nodeinst->temp1 == NONODEINST) continue;
6909
6910 xform(ai->end[0].xpos, ai->end[0].ypos, &nox[0], &noy[0], trans);
6911 xform(ai->end[1].xpos, ai->end[1].ypos, &nox[1], &noy[1], trans);
6912
6913 /* make sure end 0 fits in the port */
6914 shapeportpoly((NODEINST *)ai->end[0].nodeinst->temp1, ai->end[0].portarcinst->proto, poly, FALSE);
6915 if (!isinside(nox[0], noy[0], poly))
6916 portposition((NODEINST *)ai->end[0].nodeinst->temp1,
6917 ai->end[0].portarcinst->proto, &nox[0], &noy[0]);
6918
6919 /* make sure end 1 fits in the port */
6920 shapeportpoly((NODEINST *)ai->end[1].nodeinst->temp1, ai->end[1].portarcinst->proto, poly, FALSE);
6921 if (!isinside(nox[1], noy[1], poly))
6922 portposition((NODEINST *)ai->end[1].nodeinst->temp1,
6923 ai->end[1].portarcinst->proto, &nox[1], &noy[1]);
6924
6925 newar = newarcinst(ai->proto, ai->width, ai->userbits, (NODEINST *)ai->end[0].nodeinst->temp1,
6926 ai->end[0].portarcinst->proto, nox[0], noy[0], (NODEINST *)ai->end[1].nodeinst->temp1,
6927 ai->end[1].portarcinst->proto, nox[1], noy[1], topno->parent);
6928 if (newar == NOARCINST)
6929 {
6930 us_abortcommand(_("Cannot create arc in this cell"));
6931 return;
6932 }
6933 (void)copyvars((INTBIG)ai, VARCINST, (INTBIG)newar, VARCINST, TRUE);
6934 for(j=0; j<2; j++)
6935 (void)copyvars((INTBIG)ai->end[j].portarcinst, VPORTARCINST,
6936 (INTBIG)newar->end[j].portarcinst, VPORTARCINST, FALSE);
6937 endobjectchange((INTBIG)newar, VARCINST);
6938 }
6939
6940 /* replace arcs to this cell */
6941 for(pi = topno->firstportarcinst; pi != NOPORTARCINST; pi = nextpi)
6942 {
6943 /* remember facts about this arcinst */
6944 nextpi = pi->nextportarcinst;
6945 ai = pi->conarcinst; ap = ai->proto;
6946 if ((ai->userbits&DEADA) != 0) continue;
6947 wid = ai->width; oldbits = ai->userbits;
6948 for(i=0; i<2; i++)
6949 {
6950 noa[i] = ai->end[i].nodeinst;
6951 pta[i] = ai->end[i].portarcinst->proto;
6952 nox[i] = ai->end[i].xpos; noy[i] = ai->end[i].ypos;
6953 if (noa[i] != topno) continue;
6954 noa[i] = (NODEINST *)ai->end[i].portarcinst->proto->subnodeinst->temp1;
6955 pta[i] = ai->end[i].portarcinst->proto->subportproto;
6956 }
6957 if (noa[0] == NONODEINST || noa[1] == NONODEINST) continue;
6958 startobjectchange((INTBIG)ai, VARCINST);
6959 if (killarcinst(ai)) ttyputerr(_("Error killing arc"));
6960 newar = newarcinst(ap, wid, oldbits, noa[0], pta[0], nox[0], noy[0],
6961 noa[1], pta[1], nox[1], noy[1], topno->parent);
6962 if (newar == NOARCINST)
6963 {
6964 us_abortcommand(_("Cannot create arc to this cell"));
6965 return;
6966 }
6967
6968 /* copy variables (this presumes killed arc is not yet deallocated) */
6969 (void)copyvars((INTBIG)ai, VARCINST, (INTBIG)newar, VARCINST, TRUE);
6970 for(i=0; i<2; i++)
6971 (void)copyvars((INTBIG)ai->end[i].portarcinst, VPORTARCINST,
6972 (INTBIG)newar->end[i].portarcinst, VPORTARCINST, FALSE);
6973 endobjectchange((INTBIG)newar, VARCINST);
6974 }
6975 efree((CHAR *)arclist);
6976 }
6977
6978 /* replace the exports */
6979 for(pe = topno->firstportexpinst; pe != NOPORTEXPINST; pe = nextpe)
6980 {
6981 nextpe = pe->nextportexpinst;
6982 pp = pe->proto;
6983 if ((NODEINST *)pp->subnodeinst->temp1 == NONODEINST) continue;
6984 if (moveportproto(topno->parent, pe->exportproto,
6985 (NODEINST *)pp->subnodeinst->temp1, pp->subportproto))
6986 ttyputerr(_("Moveportproto error"));
6987 }
6988
6989 /* copy the exports if requested */
6990 if ((us_useroptions&DUPCOPIESPORTS) != 0)
6991 {
6992 /* initialize for queueing creation of new exports */
6993 us_initqueuedexports();
6994
6995 for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
6996 {
6997 ni = (NODEINST *)pp->subnodeinst->temp1;
6998 if (ni == NONODEINST) continue;
6999
7000 /* don't copy if the port is already exported */
7001 for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
7002 if (pe->proto == pp->subportproto) break;
7003 if (pe != NOPORTEXPINST) continue;
7004
7005 /* copy the port */
7006 (void)us_queuenewexport(ni, pp->subportproto, pp);
7007 }
7008
7009 /* create any queued exports */
7010 us_createqueuedexports();
7011 }
7012
7013 /* delete the cell */
7014 startobjectchange((INTBIG)topno, VNODEINST);
7015 if (killnodeinst(topno)) ttyputerr(_("Killnodeinst error"));
7016 }
7017
7018 /*
7019 * Routine to cut the selected nodes and arcs from the current cell.
7020 * They are copied to the "clipboard" cell in the "clipboard" library.
7021 */
us_cutobjects(WINDOWPART * w)7022 void us_cutobjects(WINDOWPART *w)
7023 {
7024 REGISTER NODEPROTO *np;
7025 REGISTER GEOM **list;
7026 REGISTER VIEW *saveview;
7027
7028 /* get objects to cut */
7029 np = us_needcell();
7030 if (np == NONODEPROTO) return;
7031 list = us_gethighlighted(WANTNODEINST | WANTARCINST, 0, 0);
7032 if (list[0] == NOGEOM)
7033 {
7034 us_abortcommand(_("First select objects to copy"));
7035 return;
7036 }
7037
7038 /* remove contents of clipboard */
7039 us_clearclipboard();
7040
7041 /* copy objects to clipboard */
7042 saveview = us_clipboardcell->cellview;
7043 us_clipboardcell->cellview = np->cellview;
7044 us_copylisttocell(list, np, us_clipboardcell, FALSE, FALSE, FALSE);
7045 us_clipboardcell->cellview = saveview;
7046
7047 /* then delete it all */
7048 us_clearhighlightcount();
7049 us_eraseobjectsinlist(np, list);
7050 }
7051
7052 /*
7053 * Routine to copy the selected nodes and arcs from the current cell.
7054 * They are copied to the "clipboard" cell in the "clipboard" library.
7055 */
us_copyobjects(WINDOWPART * w)7056 void us_copyobjects(WINDOWPART *w)
7057 {
7058 REGISTER NODEPROTO *np;
7059 REGISTER GEOM **list;
7060 REGISTER VIEW *saveview;
7061
7062 /* get objects to copy */
7063 np = us_needcell();
7064 if (np == NONODEPROTO) return;
7065 list = us_gethighlighted(WANTNODEINST | WANTARCINST, 0, 0);
7066 if (list[0] == NOGEOM)
7067 {
7068 us_abortcommand(_("First select objects to copy"));
7069 return;
7070 }
7071
7072 /* remove contents of clipboard */
7073 us_clearclipboard();
7074
7075 /* copy objects to clipboard */
7076 saveview = us_clipboardcell->cellview;
7077 us_clipboardcell->cellview = np->cellview;
7078 us_copylisttocell(list, np, us_clipboardcell, FALSE, FALSE, FALSE);
7079 us_clipboardcell->cellview = saveview;
7080 }
7081
7082 /*
7083 * Routine to clear the contents of the clipboard.
7084 */
us_clearclipboard(void)7085 void us_clearclipboard(void)
7086 {
7087 while (us_clipboardcell->firstarcinst != NOARCINST)
7088 {
7089 startobjectchange((INTBIG)us_clipboardcell->firstarcinst, VARCINST);
7090 (void)killarcinst(us_clipboardcell->firstarcinst);
7091 }
7092 while (us_clipboardcell->firstportproto != NOPORTPROTO)
7093 (void)killportproto(us_clipboardcell, us_clipboardcell->firstportproto);
7094 while (us_clipboardcell->firstnodeinst != NONODEINST)
7095 {
7096 startobjectchange((INTBIG)us_clipboardcell->firstnodeinst, VNODEINST);
7097 killnodeinst(us_clipboardcell->firstnodeinst);
7098 }
7099 }
7100
7101 /*
7102 * Routine to paste nodes and arcs from the clipboard to the current cell.
7103 * They are copied from the "clipboard" cell in the "clipboard" library.
7104 */
us_pasteobjects(WINDOWPART * w)7105 void us_pasteobjects(WINDOWPART *w)
7106 {
7107 REGISTER NODEPROTO *np;
7108 REGISTER GEOM **list, **highlist;
7109 REGISTER INTBIG total, ntotal, atotal, i, overlaid;
7110 REGISTER NODEINST *ni;
7111 REGISTER ARCINST *ai;
7112 REGISTER BOOLEAN interactiveplace;
7113
7114 /* get objects to paste */
7115 np = us_needcell();
7116 if (np == NONODEPROTO) return;
7117 ntotal = atotal = 0;
7118 for(ni = us_clipboardcell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
7119 ntotal++;
7120 for(ai = us_clipboardcell->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
7121 atotal++;
7122 total = ntotal + atotal;
7123 if (total == 0)
7124 {
7125 us_abortcommand(_("Nothing in the clipboard to paste"));
7126 return;
7127 }
7128
7129 /* special case of pasting on top of selected objects */
7130 highlist = us_gethighlighted(WANTNODEINST|WANTARCINST, 0, 0);
7131 if (highlist[0] != NOGEOM)
7132 {
7133 /* can only paste a single object onto selection */
7134 if (ntotal == 2 && atotal == 1)
7135 {
7136 ai = us_clipboardcell->firstarcinst;
7137 ni = us_clipboardcell->firstnodeinst;
7138 if (ni == ai->end[0].nodeinst)
7139 {
7140 if (ni->nextnodeinst == ai->end[1].nodeinst) ntotal = 0;
7141 } else if (ni == ai->end[1].nodeinst)
7142 {
7143 if (ni->nextnodeinst == ai->end[0].nodeinst) ntotal = 0;
7144 }
7145 total = ntotal + atotal;
7146 }
7147 if (total > 1)
7148 {
7149 ttyputerr(_("Can only paste a single object on top of selected objects"));
7150 return;
7151 }
7152 us_clearhighlightcount();
7153 overlaid = 0;
7154 for(i=0; highlist[i] != NOGEOM; i++)
7155 {
7156 if (highlist[i]->entryisnode && ntotal == 1)
7157 {
7158 ni = highlist[i]->entryaddr.ni;
7159 highlist[i] = 0;
7160 ni = us_pastnodetonode(ni, us_clipboardcell->firstnodeinst);
7161 if (ni != NONODEINST)
7162 {
7163 highlist[i] = ni->geom;
7164 overlaid = 1;
7165 }
7166 } else if (!highlist[i]->entryisnode && atotal == 1)
7167 {
7168 ai = highlist[i]->entryaddr.ai;
7169 highlist[i] = 0;
7170 ai = us_pastarctoarc(ai, us_clipboardcell->firstarcinst);
7171 if (ai != NOARCINST)
7172 {
7173 highlist[i] = ai->geom;
7174 overlaid = 1;
7175 }
7176 }
7177 }
7178 if (overlaid == 0)
7179 ttyputmsg(_("Nothing was pasted"));
7180 return;
7181 }
7182
7183 list = (GEOM **)emalloc((total+1) * (sizeof (GEOM *)), el_tempcluster);
7184 if (list == 0) return;
7185 total = 0;
7186 for(ni = us_clipboardcell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
7187 list[total++] = ni->geom;
7188 for(ai = us_clipboardcell->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
7189 list[total++] = ai->geom;
7190 list[total] = NOGEOM;
7191
7192 /* paste them into the current cell */
7193 interactiveplace = TRUE;
7194 us_copylisttocell(list, us_clipboardcell, np, TRUE, interactiveplace, FALSE);
7195 efree((CHAR *)list);
7196 }
7197
7198 /*
7199 * Routine to "paste" node "srcnode" onto node "destnode", making them the same.
7200 * Returns the address of the destination node (NONODEINST on error).
7201 */
us_pastnodetonode(NODEINST * destnode,NODEINST * srcnode)7202 NODEINST *us_pastnodetonode(NODEINST *destnode, NODEINST *srcnode)
7203 {
7204 REGISTER INTBIG dx, dy, dlx, dhx, dly, dhy, i, movebits, newbits;
7205 REGISTER VARIABLE *srcvar, *destvar;
7206 REGISTER BOOLEAN checkagain;
7207
7208 /* make sure they have the same type */
7209 if (destnode->proto != srcnode->proto)
7210 {
7211 destnode = us_replacenodeinst(destnode, srcnode->proto, TRUE, TRUE);
7212 if (destnode == NONODEINST) return(NONODEINST);
7213 }
7214
7215 /* make the sizes the same if they are primitives */
7216 startobjectchange((INTBIG)destnode, VNODEINST);
7217 if (destnode->proto->primindex != 0)
7218 {
7219 dx = (srcnode->highx - srcnode->lowx) - (destnode->highx - destnode->lowx);
7220 dy = (srcnode->highy - srcnode->lowy) - (destnode->highy - destnode->lowy);
7221 if (dx != 0 || dy != 0)
7222 {
7223 dlx = -dx/2; dhx = dx/2;
7224 dly = -dy/2; dhy = dy/2;
7225 modifynodeinst(destnode, dlx, dly, dhx, dhy, 0, 0);
7226 }
7227 }
7228
7229 /* remove variables that are not on the pasted object */
7230 checkagain = TRUE;
7231 while (checkagain)
7232 {
7233 checkagain = FALSE;
7234 for(i=0; i<destnode->numvar; i++)
7235 {
7236 destvar = &destnode->firstvar[i];
7237 srcvar = getvalkey((INTBIG)srcnode, VNODEINST, -1, destvar->key);
7238 if (srcvar != NOVARIABLE) continue;
7239 delvalkey((INTBIG)destnode, VNODEINST, destvar->key);
7240 checkagain = TRUE;
7241 break;
7242 }
7243 }
7244
7245 /* make sure all variables are on the node */
7246 for(i=0; i<srcnode->numvar; i++)
7247 {
7248 srcvar = &srcnode->firstvar[i];
7249 destvar = setvalkey((INTBIG)destnode, VNODEINST, srcvar->key, srcvar->addr, srcvar->type);
7250 TDCOPY(destvar->textdescript, srcvar->textdescript);
7251 }
7252
7253 /* copy any special user bits */
7254 movebits = NEXPAND | NTECHBITS | HARDSELECTN | NVISIBLEINSIDE;
7255 newbits = (destnode->userbits & ~movebits) | (srcnode->userbits & movebits);
7256 setval((INTBIG)destnode, VNODEINST, x_("userbits"), newbits, VINTEGER);
7257
7258 endobjectchange((INTBIG)destnode, VNODEINST);
7259 return(destnode);
7260 }
7261
7262 /*
7263 * Routine to "paste" arc "srcarc" onto arc "destarc", making them the same.
7264 * Returns the address of the destination arc (NOARCINST on error).
7265 */
us_pastarctoarc(ARCINST * destarc,ARCINST * srcarc)7266 ARCINST *us_pastarctoarc(ARCINST *destarc, ARCINST *srcarc)
7267 {
7268 REGISTER INTBIG dw, i, movebits, newbits;
7269 REGISTER VARIABLE *srcvar, *destvar;
7270 REGISTER BOOLEAN checkagain;
7271
7272 /* make sure they have the same type */
7273 startobjectchange((INTBIG)destarc, VARCINST);
7274 if (destarc->proto != srcarc->proto)
7275 {
7276 destarc = replacearcinst(destarc, srcarc->proto);
7277 if (destarc == NOARCINST) return(NOARCINST);
7278 }
7279
7280 /* make the widths the same */
7281 dw = srcarc->width - destarc->width;
7282 if (dw != 0)
7283 (void)modifyarcinst(destarc, dw, 0, 0, 0, 0);
7284
7285 /* remove variables that are not on the pasted object */
7286 checkagain = TRUE;
7287 while (checkagain)
7288 {
7289 checkagain = FALSE;
7290 for(i=0; i<destarc->numvar; i++)
7291 {
7292 destvar = &destarc->firstvar[i];
7293 srcvar = getvalkey((INTBIG)srcarc, VARCINST, -1, destvar->key);
7294 if (srcvar != NOVARIABLE) continue;
7295 delvalkey((INTBIG)destarc, VARCINST, destvar->key);
7296 checkagain = TRUE;
7297 break;
7298 }
7299 }
7300
7301 /* make sure all variables are on the arc */
7302 for(i=0; i<srcarc->numvar; i++)
7303 {
7304 srcvar = &srcarc->firstvar[i];
7305 destvar = setvalkey((INTBIG)destarc, VARCINST, srcvar->key, srcvar->addr, srcvar->type);
7306 TDCOPY(destvar->textdescript, srcvar->textdescript);
7307 }
7308
7309 /* make sure the constraints and other userbits are the same */
7310 movebits = FIXED | FIXANG | NOEXTEND | ISNEGATED | ISDIRECTIONAL |
7311 NOTEND0 | NOTEND1 | REVERSEEND | CANTSLIDE | HARDSELECTA;
7312 newbits = (destarc->userbits & ~movebits) | (srcarc->userbits & movebits);
7313 setval((INTBIG)destarc, VARCINST, x_("userbits"), newbits, VINTEGER);
7314
7315 endobjectchange((INTBIG)destarc, VARCINST);
7316 return(destarc);
7317 }
7318
7319 /*
7320 * Routine to copy the list of objects in "list" (NOGEOM terminated) from "fromcell"
7321 * to "tocell". If "highlight" is true, highlight the objects in the new cell.
7322 * If "interactiveplace" is true, interactively select the location in the new cell.
7323 */
us_copylisttocell(GEOM ** list,NODEPROTO * fromcell,NODEPROTO * tocell,BOOLEAN highlight,BOOLEAN interactiveplace,BOOLEAN showoffset)7324 void us_copylisttocell(GEOM **list, NODEPROTO *fromcell, NODEPROTO *tocell, BOOLEAN highlight,
7325 BOOLEAN interactiveplace, BOOLEAN showoffset)
7326 {
7327 REGISTER NODEINST *ni, *newni, **nodelist;
7328 REGISTER ARCINST *ai, *newar, **arclist;
7329 REGISTER PORTEXPINST *pe;
7330 REGISTER INTBIG i, j, angle;
7331 REGISTER INTBIG wid, dx, dy, arccount, bits;
7332 INTBIG xcur, ycur, lx, hx, ly, hy, bestlx, bestly, total;
7333 BOOLEAN centeredprimitives, first;
7334 REGISTER void *infstr;
7335 REGISTER VARIABLE *var;
7336 static POLYGON *poly = NOPOLYGON;
7337
7338 /* make sure the destination cell can be modified */
7339 if (us_cantedit(tocell, NONODEINST, TRUE)) return;
7340
7341 /* get polygon */
7342 (void)needstaticpolygon(&poly, 4, us_tool->cluster);
7343
7344 /* make sure they are all in the same cell */
7345 for(i=0; list[i] != NOGEOM; i++)
7346 {
7347 if (fromcell != geomparent(list[i]))
7348 {
7349 us_abortcommand(_("All duplicated objects must be in the same cell"));
7350 return;
7351 }
7352 }
7353
7354 /* set the technology of the new cell from the old cell if the new is empty */
7355 if (tocell->firstnodeinst == NONODEINST ||
7356 (tocell->firstnodeinst->proto == gen_cellcenterprim &&
7357 tocell->firstnodeinst->nextnodeinst == NONODEINST))
7358 {
7359 tocell->tech = fromcell->tech;
7360 }
7361
7362 /* mark all nodes (including those touched by highlighted arcs) */
7363 for(ni = fromcell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
7364 ni->temp1 = 0;
7365 for(i=0; list[i] != NOGEOM; i++) if (!list[i]->entryisnode)
7366 {
7367 ai = list[i]->entryaddr.ai;
7368 ai->end[0].nodeinst->temp1 = ai->end[1].nodeinst->temp1 = 1;
7369 }
7370 for(i=0; list[i] != NOGEOM; i++)
7371 {
7372 if (!list[i]->entryisnode) continue;
7373 ni = list[i]->entryaddr.ni;
7374
7375 /* check for cell instance lock */
7376 if (ni->proto->primindex == 0 && (tocell->userbits&NPILOCKED) != 0)
7377 {
7378 if (us_cantedit(tocell, ni, TRUE)) continue;
7379 }
7380 ni->temp1 = 1;
7381 }
7382
7383 /* count the number of nodes */
7384 for(total=0, ni = fromcell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
7385 if (ni->temp1 != 0) total++;
7386 if (total == 0) return;
7387
7388 /* build a list that includes all nodes touching copied arcs */
7389 nodelist = (NODEINST **)emalloc((total * (sizeof (NODEINST *))), el_tempcluster);
7390 if (nodelist == 0) return;
7391 for(i=0, ni = fromcell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
7392 if (ni->temp1 != 0) nodelist[i++] = ni;
7393
7394 /* check for recursion */
7395 for(i=0; i<total; i++)
7396 if (nodelist[i]->proto->primindex == 0)
7397 {
7398 if (isachildof(tocell, nodelist[i]->proto))
7399 {
7400 us_abortcommand(_("Cannot: that would be recursive"));
7401 efree((CHAR *)nodelist);
7402 return;
7403 }
7404 }
7405
7406 /* figure out lower-left corner of this collection of objects */
7407 us_getlowleft(nodelist[0], &bestlx, &bestly);
7408 for(i=1; i<total; i++)
7409 {
7410 us_getlowleft(nodelist[i], &lx, &ly);
7411 if (lx < bestlx) bestlx = lx;
7412 if (ly < bestly) bestly = ly;
7413 }
7414 for(i=0; list[i] != NOGEOM; i++) if (!list[i]->entryisnode)
7415 {
7416 ai = list[i]->entryaddr.ai;
7417 wid = ai->width - arcwidthoffset(ai);
7418 makearcpoly(ai->length, wid, ai, poly, FILLED);
7419 getbbox(poly, &lx, &hx, &ly, &hy);
7420 if (lx < bestlx) bestlx = lx;
7421 if (ly < bestly) bestly = ly;
7422 }
7423
7424 /* adjust this corner so that, after grid alignment, objects are in the same location */
7425 gridalign(&bestlx, &bestly, 1, tocell);
7426
7427 /* special case when moving one node: account for cell center */
7428 if (total == 1 && list[1] == NOGEOM)
7429 {
7430 ni = nodelist[0];
7431 if ((us_useroptions&CENTEREDPRIMITIVES) != 0) centeredprimitives = TRUE; else
7432 centeredprimitives = FALSE;
7433 corneroffset(ni, ni->proto, ni->rotation, ni->transpose, &lx, &ly,
7434 centeredprimitives);
7435 bestlx = ni->lowx + lx;
7436 bestly = ni->lowy + ly;
7437
7438 if (ni->proto->primindex != 0 && (us_useroptions&CENTEREDPRIMITIVES) == 0)
7439 {
7440 /* adjust this corner so that, after grid alignment, objects are in the same location */
7441 gridalign(&bestlx, &bestly, 1, tocell);
7442 }
7443 }
7444
7445 /* remove highlighting if planning to highlight new stuff */
7446 if (highlight) us_clearhighlightcount();
7447 if (interactiveplace)
7448 {
7449 /* adjust the cursor position if selecting interactively */
7450 if ((us_tool->toolstate&INTERACTIVE) != 0)
7451 {
7452 var = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER, us_interactiveanglekey);
7453 if (var == NOVARIABLE) angle = 0; else
7454 angle = var->addr;
7455 us_multidraginit(bestlx, bestly, list, nodelist, total, angle, showoffset);
7456 trackcursor(FALSE, us_ignoreup, us_multidragbegin, us_multidragdown,
7457 us_stoponchar, us_multidragup, TRACKDRAGGING);
7458 if (el_pleasestop != 0)
7459 {
7460 efree((CHAR *)nodelist);
7461 return;
7462 }
7463 if (us_demandxy(&xcur, &ycur))
7464 {
7465 efree((CHAR *)nodelist);
7466 return;
7467 }
7468 bits = getbuckybits();
7469 if ((bits&CONTROLDOWN) != 0)
7470 us_getslide(angle, bestlx, bestly, xcur, ycur, &xcur, &ycur);
7471 } else
7472 {
7473 /* get aligned cursor co-ordinates */
7474 if (us_demandxy(&xcur, &ycur))
7475 {
7476 efree((CHAR *)nodelist);
7477 return;
7478 }
7479 }
7480 gridalign(&xcur, &ycur, 1, tocell);
7481
7482 dx = xcur-bestlx;
7483 dy = ycur-bestly;
7484 } else
7485 {
7486 if (!us_dupdistset)
7487 {
7488 us_dupdistset = TRUE;
7489 us_dupx = us_dupy = el_curlib->lambda[el_curtech->techindex] * 10;
7490 }
7491 dx = us_dupx;
7492 dy = us_dupy;
7493 }
7494
7495 /* initialize for queueing creation of new exports */
7496 us_initqueuedexports();
7497
7498 /* sort the nodes by name */
7499 esort(nodelist, total, sizeof (NODEINST *), us_sortnodesbyname);
7500
7501 /* create the new objects */
7502 for(i=0; i<total; i++)
7503 {
7504 ni = nodelist[i];
7505 newni = newnodeinst(ni->proto, ni->lowx+dx, ni->highx+dx, ni->lowy+dy,
7506 ni->highy+dy, ni->transpose, ni->rotation, tocell);
7507 if (newni == NONODEINST)
7508 {
7509 us_abortcommand(_("Cannot create node"));
7510 efree((CHAR *)nodelist);
7511 return;
7512 }
7513 newni->userbits = ni->userbits & ~(WIPED|NSHORT);
7514 TDCOPY(newni->textdescript, ni->textdescript);
7515 (void)copyvars((INTBIG)ni, VNODEINST, (INTBIG)newni, VNODEINST, TRUE);
7516 endobjectchange((INTBIG)newni, VNODEINST);
7517 ni->temp1 = (INTBIG)newni;
7518 if (i == 0) us_dupnode = newni;
7519
7520 /* copy the ports, too */
7521 if ((us_useroptions&DUPCOPIESPORTS) != 0)
7522 {
7523 for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
7524 {
7525 if (us_queuenewexport(newni, pe->proto, pe->exportproto))
7526 {
7527 efree((CHAR *)nodelist);
7528 return;
7529 }
7530 }
7531 }
7532 }
7533
7534 /* create any queued exports */
7535 us_createqueuedexports();
7536
7537 /* create a list of arcs to be copied */
7538 arccount = 0;
7539 for(i=0; list[i] != NOGEOM; i++)
7540 if (!list[i]->entryisnode) arccount++;
7541 if (arccount > 0)
7542 {
7543 arclist = (ARCINST **)emalloc(arccount * (sizeof (ARCINST *)), el_tempcluster);
7544 if (arclist == 0) return;
7545 arccount = 0;
7546 for(i=0; list[i] != NOGEOM; i++)
7547 if (!list[i]->entryisnode)
7548 arclist[arccount++] = list[i]->entryaddr.ai;
7549
7550 /* sort the arcs by name */
7551 esort(arclist, arccount, sizeof (ARCINST *), us_sortarcsbyname);
7552
7553 for(i=0; i<arccount; i++)
7554 {
7555 ai = arclist[i];
7556 newar = newarcinst(ai->proto, ai->width, ai->userbits,
7557 (NODEINST *)ai->end[0].nodeinst->temp1, ai->end[0].portarcinst->proto, ai->end[0].xpos+dx,
7558 ai->end[0].ypos+dy, (NODEINST *)ai->end[1].nodeinst->temp1,
7559 ai->end[1].portarcinst->proto, ai->end[1].xpos+dx, ai->end[1].ypos+dy, tocell);
7560 if (newar == NOARCINST)
7561 {
7562 us_abortcommand(_("Cannot create arc"));
7563 efree((CHAR *)nodelist);
7564 efree((CHAR *)arclist);
7565 return;
7566 }
7567 (void)copyvars((INTBIG)ai, VARCINST, (INTBIG)newar, VARCINST, TRUE);
7568 for(j=0; j<2; j++)
7569 (void)copyvars((INTBIG)ai->end[j].portarcinst, VPORTARCINST,
7570 (INTBIG)newar->end[j].portarcinst, VPORTARCINST, FALSE);
7571 endobjectchange((INTBIG)newar, VARCINST);
7572 ai->temp1 = (INTBIG)newar;
7573 }
7574 efree((CHAR *)arclist);
7575 }
7576
7577 /* highlight the copy */
7578 if (highlight)
7579 {
7580 infstr = initinfstr();
7581 first = FALSE;
7582 for(i=0; i<total; i++)
7583 {
7584 ni = (NODEINST *)nodelist[i]->temp1;
7585
7586 /* special case for displayable text on invisible pins */
7587 if (ni->proto == gen_invispinprim)
7588 {
7589 for(j=0; j<ni->numvar; j++)
7590 {
7591 var = &ni->firstvar[j];
7592 if ((var->type&VDISPLAY) != 0) break;
7593 }
7594 if (j < ni->numvar)
7595 {
7596 if (first) addtoinfstr(infstr, '\n');
7597 first = TRUE;
7598 formatinfstr(infstr, x_("CELL=%s TEXT=0%lo;-1;%s"),
7599 describenodeproto(tocell), (INTBIG)ni->geom, makename(var->key));
7600 continue;
7601 }
7602 }
7603 if (first) addtoinfstr(infstr, '\n');
7604 first = TRUE;
7605 formatinfstr(infstr, x_("CELL=%s FROM=0%lo;-1;0"),
7606 describenodeproto(tocell), (INTBIG)ni->geom);
7607 }
7608 for(i=0; list[i] != NOGEOM; i++) if (!list[i]->entryisnode)
7609 {
7610 ai = (ARCINST *)list[i]->entryaddr.ai->temp1;
7611 addtoinfstr(infstr, '\n');
7612 formatinfstr(infstr, x_("CELL=%s FROM=0%lo;-1;0"),
7613 describenodeproto(tocell), (INTBIG)ai->geom);
7614 }
7615 us_setmultiplehighlight(returninfstr(infstr), FALSE);
7616 us_showallhighlight();
7617 }
7618 efree((CHAR *)nodelist);
7619 }
7620
7621 /*
7622 * Initialization routine for queuing export creation. After this, call "us_queuenewexport"
7623 * many times, and then "us_createqueuedexports()" to actually create the exports.
7624 */
us_initqueuedexports(void)7625 void us_initqueuedexports(void)
7626 {
7627 us_queuedexportcount = 0;
7628 }
7629
7630 /*
7631 * Routine to queue the creation of an export from port "pp" of node "ni".
7632 * The port is being copied from an original port "origpp". Returns true on error.
7633 */
us_queuenewexport(NODEINST * ni,PORTPROTO * pp,PORTPROTO * origpp)7634 BOOLEAN us_queuenewexport(NODEINST *ni, PORTPROTO *pp, PORTPROTO *origpp)
7635 {
7636 REGISTER INTBIG newtotal, i;
7637 QUEUEDEXPORT *newqe;
7638
7639 if (us_queuedexportcount >= us_queuedexporttotal)
7640 {
7641 newtotal = us_queuedexporttotal * 2;
7642 if (newtotal == 0) newtotal = 10;
7643 newqe = (QUEUEDEXPORT *)emalloc(newtotal * (sizeof (QUEUEDEXPORT)), us_tool->cluster);
7644 if (newqe == 0) return(TRUE);
7645 for(i=0; i<us_queuedexportcount; i++)
7646 newqe[i] = us_queuedexport[i];
7647 if (us_queuedexporttotal > 0) efree((CHAR *)us_queuedexport);
7648 us_queuedexport = newqe;
7649 us_queuedexporttotal = newtotal;
7650 }
7651 us_queuedexport[us_queuedexportcount].ni = ni;
7652 us_queuedexport[us_queuedexportcount].pp = pp;
7653 us_queuedexport[us_queuedexportcount].origpp = origpp;
7654 us_queuedexportcount++;
7655 return(FALSE);
7656 }
7657
7658 /*
7659 * Helper routine for "esort" that makes queued exports go in ascending name order.
7660 */
us_queuedexportnameascending(const void * e1,const void * e2)7661 int us_queuedexportnameascending(const void *e1, const void *e2)
7662 {
7663 REGISTER QUEUEDEXPORT *qe1, *qe2;
7664 REGISTER PORTPROTO *c1, *c2;
7665
7666 qe1 = (QUEUEDEXPORT *)e1;
7667 qe2 = (QUEUEDEXPORT *)e2;
7668 c1 = qe1->origpp;
7669 c2 = qe2->origpp;
7670 return(namesamenumeric(c1->protoname, c2->protoname));
7671 }
7672
7673 /*
7674 * Helper routine for "esort" that makes arcs with names go in ascending name order.
7675 */
us_sortarcsbyname(const void * e1,const void * e2)7676 int us_sortarcsbyname(const void *e1, const void *e2)
7677 {
7678 REGISTER ARCINST *ai1, *ai2;
7679 REGISTER VARIABLE *var1, *var2;
7680 REGISTER CHAR *pt1, *pt2;
7681 CHAR empty[1];
7682
7683 ai1 = *((ARCINST **)e1);
7684 ai2 = *((ARCINST **)e2);
7685 var1 = getvalkey((INTBIG)ai1, VARCINST, -1, el_arc_name_key);
7686 var2 = getvalkey((INTBIG)ai2, VARCINST, -1, el_arc_name_key);
7687 empty[0] = 0;
7688 if (var1 == NOVARIABLE) pt1 = empty; else pt1 = (CHAR *)var1->addr;
7689 if (var2 == NOVARIABLE) pt2 = empty; else pt2 = (CHAR *)var2->addr;
7690 return(namesamenumeric(pt1, pt2));
7691 }
7692
7693 /*
7694 * Helper routine for "esort" that makes nodes with names go in ascending name order.
7695 */
us_sortnodesbyname(const void * e1,const void * e2)7696 int us_sortnodesbyname(const void *e1, const void *e2)
7697 {
7698 REGISTER NODEINST *ni1, *ni2;
7699 REGISTER VARIABLE *var1, *var2;
7700 REGISTER CHAR *pt1, *pt2;
7701 CHAR empty[1];
7702
7703 ni1 = *((NODEINST **)e1);
7704 ni2 = *((NODEINST **)e2);
7705 var1 = getvalkey((INTBIG)ni1, VNODEINST, -1, el_node_name_key);
7706 var2 = getvalkey((INTBIG)ni2, VNODEINST, -1, el_node_name_key);
7707 empty[0] = 0;
7708 if (var1 == NOVARIABLE) pt1 = empty; else pt1 = (CHAR *)var1->addr;
7709 if (var2 == NOVARIABLE) pt2 = empty; else pt2 = (CHAR *)var2->addr;
7710 return(namesamenumeric(pt1, pt2));
7711 }
7712
7713 /*
7714 * Termination routine for export queueing. Call this to actually create the
7715 * queued exports.
7716 */
us_createqueuedexports(void)7717 void us_createqueuedexports(void)
7718 {
7719 REGISTER NODEPROTO *np;
7720 REGISTER NODEINST *ni;
7721 REGISTER PORTPROTO *newpp, *pp, *origpp;
7722 REGISTER CHAR *portname;
7723 REGISTER INTBIG i;
7724
7725 /* sort the ports by their original name */
7726 esort(us_queuedexport, us_queuedexportcount, sizeof (QUEUEDEXPORT),
7727 us_queuedexportnameascending);
7728
7729 for(i=0; i<us_queuedexportcount; i++)
7730 {
7731 ni = us_queuedexport[i].ni;
7732 pp = us_queuedexport[i].pp;
7733 origpp = us_queuedexport[i].origpp;
7734
7735 np = ni->parent;
7736 portname = us_uniqueportname(origpp->protoname, np);
7737 newpp = us_makenewportproto(np, ni, pp, portname, -1, origpp->userbits, origpp->textdescript);
7738 if (newpp == NOPORTPROTO) return;
7739 TDCOPY(newpp->textdescript, origpp->textdescript);
7740 if (copyvars((INTBIG)origpp, VPORTPROTO, (INTBIG)newpp, VPORTPROTO, FALSE))
7741 return;
7742 if (copyvars((INTBIG)origpp->subportexpinst, VPORTEXPINST,
7743 (INTBIG)newpp->subportexpinst, VPORTEXPINST, FALSE)) return;
7744 }
7745 }
7746
7747 /*
7748 * routine to move the arcs in the GEOM module list "list" (terminated by
7749 * NOGEOM) and the "total" nodes in the list "nodelist" by (dx, dy).
7750 */
us_manymove(GEOM ** list,NODEINST ** nodelist,INTBIG total,INTBIG dx,INTBIG dy)7751 void us_manymove(GEOM **list, NODEINST **nodelist, INTBIG total, INTBIG dx, INTBIG dy)
7752 {
7753 REGISTER NODEINST *ni, **nis;
7754 REGISTER ARCINST *ai, *oai;
7755 REGISTER PORTARCINST *pi;
7756 REGISTER NODEPROTO *np;
7757 REGISTER INTBIG i, j, k, otherend, *dlx, *dhx, *dly, *dhy, *drot, *dtrn, valid,
7758 arcangle;
7759 NODEINST *nilist[2];
7760 INTBIG e[2], ix, iy, deltax[2], deltay[2], deltadummy[2];
7761
7762 /* special case if moving only one node */
7763 if (total == 1 && list[1] == NOGEOM)
7764 {
7765 ni = nodelist[0];
7766 startobjectchange((INTBIG)ni, VNODEINST);
7767 modifynodeinst(ni, dx, dy, dx, dy, 0, 0);
7768 endobjectchange((INTBIG)ni, VNODEINST);
7769 return;
7770 }
7771
7772 /* special case if moving diagonal fixed-angle arcs connected to single manhattan arcs */
7773 for(i=0; list[i] != NOGEOM; i++)
7774 {
7775 if (list[i]->entryisnode) break;
7776 ai = list[i]->entryaddr.ai;
7777 if (ai->end[0].xpos == ai->end[1].xpos ||
7778 ai->end[0].ypos == ai->end[1].ypos) break;
7779 if ((ai->userbits&FIXANG) == 0) break;
7780 if ((ai->userbits&FIXED) != 0) break;
7781 for(j=0; j<2; j++)
7782 {
7783 ni = ai->end[j].nodeinst;
7784 oai = NOARCINST;
7785 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
7786 {
7787 if (pi->conarcinst == ai) continue;
7788 if (oai == NOARCINST) oai = pi->conarcinst; else
7789 oai = NOARCINST;
7790 }
7791 if (oai == NOARCINST) break;
7792 if (oai->end[0].xpos != oai->end[1].xpos &&
7793 oai->end[0].ypos != oai->end[1].ypos) break;
7794 }
7795 if (j < 2) break;
7796 }
7797 if (list[i] == NOGEOM)
7798 {
7799 /* meets the test: make the special move to slide other orthogonal arcs */
7800 for(i=0; list[i] != NOGEOM; i++)
7801 {
7802 ai = list[i]->entryaddr.ai;
7803 deltax[0] = deltay[0] = deltax[1] = deltay[1] = 0;
7804 arcangle = ((ai->userbits&AANGLE)>>AANGLESH) * 10;
7805 for(j=0; j<2; j++)
7806 {
7807 ni = ai->end[j].nodeinst;
7808 nilist[j] = ni;
7809 oai = NOARCINST;
7810 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
7811 if (pi->conarcinst != ai) break;
7812 oai = pi->conarcinst;
7813 if (oai == NOARCINST) break;
7814 if (oai->end[0].xpos == oai->end[1].xpos)
7815 {
7816 intersect(oai->end[0].xpos, oai->end[0].ypos, 900,
7817 ai->end[0].xpos+dx, ai->end[0].ypos+dy, arcangle, &ix, &iy);
7818 deltax[j] = ix - ai->end[j].xpos;
7819 deltay[j] = iy - ai->end[j].ypos;
7820 } else if (oai->end[0].ypos == oai->end[1].ypos)
7821 {
7822 intersect(oai->end[0].xpos, oai->end[0].ypos, 0,
7823 ai->end[0].xpos+dx, ai->end[0].ypos+dy, arcangle, &ix, &iy);
7824 deltax[j] = ix - ai->end[j].xpos;
7825 deltay[j] = iy - ai->end[j].ypos;
7826 }
7827 }
7828 if (j < 2) continue;
7829 startobjectchange((INTBIG)nilist[0], VNODEINST);
7830 startobjectchange((INTBIG)nilist[1], VNODEINST);
7831 deltadummy[0] = deltadummy[1] = 0;
7832 modifynodeinsts(2, nilist, deltax, deltay, deltax, deltay, deltadummy, deltadummy);
7833 endobjectchange((INTBIG)nilist[0], VNODEINST);
7834 endobjectchange((INTBIG)nilist[1], VNODEINST);
7835 }
7836 return;
7837 }
7838
7839 /* special case if moving only arcs and they slide */
7840 for(i=0; list[i] != NOGEOM; i++)
7841 {
7842 if (list[i]->entryisnode) break;
7843 ai = list[i]->entryaddr.ai;
7844
7845 /* see if the arc moves in its ports */
7846 if ((ai->userbits&(FIXED|CANTSLIDE)) != 0) break;
7847 if (!db_stillinport(ai, 0, ai->end[0].xpos+dx, ai->end[0].ypos+dy)) break;
7848 if (!db_stillinport(ai, 1, ai->end[1].xpos+dx, ai->end[1].ypos+dy)) break;
7849 }
7850 if (list[i] == NOGEOM) total = 0;
7851
7852 /* remember the location of every node */
7853 np = geomparent(list[0]);
7854 if (np == NONODEPROTO) return;
7855 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
7856 {
7857 ni->temp1 = ni->lowx;
7858 ni->temp2 = ni->lowy;
7859 }
7860 for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
7861 {
7862 ai->temp1 = (ai->end[0].xpos + ai->end[1].xpos) / 2;
7863 ai->temp2 = (ai->end[0].ypos + ai->end[1].ypos) / 2;
7864 }
7865
7866 /* look at all nodes and move them appropriately */
7867 nis = (NODEINST **)emalloc(total * (sizeof (NODEINST *)), el_tempcluster);
7868 if (nis == 0) return;
7869 dlx = (INTBIG *)emalloc(total * SIZEOFINTBIG, el_tempcluster);
7870 if (dlx == 0) return;
7871 dhx = (INTBIG *)emalloc(total * SIZEOFINTBIG, el_tempcluster);
7872 if (dhx == 0) return;
7873 dly = (INTBIG *)emalloc(total * SIZEOFINTBIG, el_tempcluster);
7874 if (dly == 0) return;
7875 dhy = (INTBIG *)emalloc(total * SIZEOFINTBIG, el_tempcluster);
7876 if (dhy == 0) return;
7877 drot = (INTBIG *)emalloc(total * SIZEOFINTBIG, el_tempcluster);
7878 if (drot == 0) return;
7879 dtrn = (INTBIG *)emalloc(total * SIZEOFINTBIG, el_tempcluster);
7880 if (dtrn == 0) return;
7881 valid = 0;
7882 for(i=0; i<total; i++)
7883 {
7884 ni = nodelist[i];
7885 if (ni->lowx == ni->temp1 && ni->lowy == ni->temp2)
7886 {
7887 nis[valid] = ni;
7888 dlx[valid] = dx-(ni->lowx-ni->temp1);
7889 dly[valid] = dy-(ni->lowy-ni->temp2);
7890 dhx[valid] = dx-(ni->lowx-ni->temp1);
7891 dhy[valid] = dy-(ni->lowy-ni->temp2);
7892 drot[valid] = dtrn[valid] = 0;
7893 valid++;
7894 }
7895 }
7896 for(j=0; list[j] != NOGEOM; j++)
7897 if (!list[j]->entryisnode)
7898 (void)(*el_curconstraint->setobject)((INTBIG)list[j]->entryaddr.ai,
7899 VARCINST, CHANGETYPETEMPRIGID, 0);
7900 for(i=0; i<valid; i++)
7901 startobjectchange((INTBIG)nis[i], VNODEINST);
7902 modifynodeinsts(valid, nis, dlx, dly, dhx, dhy, drot, dtrn);
7903 for(i=0; i<valid; i++)
7904 endobjectchange((INTBIG)nis[i], VNODEINST);
7905 efree((CHAR *)nis);
7906 efree((CHAR *)dlx);
7907 efree((CHAR *)dly);
7908 efree((CHAR *)dhx);
7909 efree((CHAR *)dhy);
7910 efree((CHAR *)drot);
7911 efree((CHAR *)dtrn);
7912
7913 /* look at all arcs and move them appropriately */
7914 for(i=0; list[i] != NOGEOM; i++)
7915 {
7916 if (list[i]->entryisnode) continue;
7917 ai = list[i]->entryaddr.ai;
7918 if (ai->temp1 != (ai->end[0].xpos + ai->end[1].xpos) / 2 ||
7919 ai->temp2 != (ai->end[0].ypos + ai->end[1].ypos) / 2) continue;
7920
7921 /* see if the arc moves in its ports */
7922 if ((ai->userbits&(FIXED|CANTSLIDE)) != 0) e[0] = e[1] = 0; else
7923 {
7924 e[0] = db_stillinport(ai, 0, ai->end[0].xpos+dx, ai->end[0].ypos+dy);
7925 e[1] = db_stillinport(ai, 1, ai->end[1].xpos+dx, ai->end[1].ypos+dy);
7926 }
7927
7928 /* if both ends slide in their port, move the arc */
7929 if (e[0] != 0 && e[1] != 0)
7930 {
7931 startobjectchange((INTBIG)ai, VARCINST);
7932 (void)modifyarcinst(ai, 0, dx, dy, dx, dy);
7933 endobjectchange((INTBIG)ai, VARCINST);
7934 continue;
7935 }
7936
7937 /* if neither end can slide in its port, move the nodes */
7938 if (e[0] == 0 && e[1] == 0)
7939 {
7940 for(k=0; k<2; k++)
7941 {
7942 ni = ai->end[k].nodeinst;
7943 if (ni->lowx != ni->temp1 || ni->lowy != ni->temp2) continue;
7944
7945 /* fix all arcs that aren't sliding */
7946 for(j=0; list[j] != NOGEOM; j++)
7947 {
7948 if (list[j]->entryisnode) continue;
7949 oai = list[j]->entryaddr.ai;
7950 if (oai->temp1 != (oai->end[0].xpos + oai->end[1].xpos) / 2 ||
7951 oai->temp2 != (oai->end[0].ypos + oai->end[1].ypos) / 2) continue;
7952 if (db_stillinport(oai, 0, ai->end[0].xpos+dx, ai->end[0].ypos+dy) ||
7953 db_stillinport(oai, 1, ai->end[1].xpos+dx, ai->end[1].ypos+dy))
7954 continue;
7955 (void)(*el_curconstraint->setobject)((INTBIG)oai,
7956 VARCINST, CHANGETYPETEMPRIGID, 0);
7957 }
7958 startobjectchange((INTBIG)ni, VNODEINST);
7959 modifynodeinst(ni, dx-(ni->lowx-ni->temp1), dy-(ni->lowy-ni->temp2),
7960 dx-(ni->lowx-ni->temp1), dy-(ni->lowy-ni->temp2), 0, 0);
7961 endobjectchange((INTBIG)ni, VNODEINST);
7962 }
7963 continue;
7964 }
7965
7966 /* only one end is slidable: move other node and the arc */
7967 for(k=0; k<2; k++)
7968 {
7969 if (e[k] != 0) continue;
7970 ni = ai->end[k].nodeinst;
7971 if (ni->lowx == ni->temp1 && ni->lowy == ni->temp2)
7972 {
7973 /* node "ni" hasn't moved yet but must because arc motion forces it */
7974 for(j=0; list[j] != NOGEOM; j++)
7975 {
7976 if (list[j]->entryisnode) continue;
7977 oai = list[j]->entryaddr.ai;
7978 if (oai->temp1 != (oai->end[0].xpos + oai->end[1].xpos) / 2 ||
7979 oai->temp2 != (oai->end[0].ypos + oai->end[1].ypos) / 2) continue;
7980 if (oai->end[0].nodeinst == ni) otherend = 1; else otherend = 0;
7981 if (db_stillinport(oai, otherend, ai->end[otherend].xpos+dx,
7982 ai->end[otherend].ypos+dy)) continue;
7983 (void)(*el_curconstraint->setobject)((INTBIG)oai,
7984 VARCINST, CHANGETYPETEMPRIGID, 0);
7985 }
7986 startobjectchange((INTBIG)ni, VNODEINST);
7987 modifynodeinst(ni, dx-(ni->lowx-ni->temp1), dy-(ni->lowy-ni->temp2),
7988 dx-(ni->lowx-ni->temp1), dy-(ni->lowy-ni->temp2), 0, 0);
7989 endobjectchange((INTBIG)ni, VNODEINST);
7990
7991 if (ai->temp1 != (ai->end[0].xpos + ai->end[1].xpos) / 2 ||
7992 ai->temp2 != (ai->end[0].ypos + ai->end[1].ypos) / 2) continue;
7993 startobjectchange((INTBIG)ai, VARCINST);
7994 (void)modifyarcinst(ai, 0, dx, dy, dx, dy);
7995 endobjectchange((INTBIG)ai, VARCINST);
7996 }
7997 }
7998 }
7999 }
8000
8001 /*********************************** PORT SUPPORT ***********************************/
8002
8003 /*
8004 * routine to obtain details about a "port" command in "count" and "par".
8005 * The node under consideration is in "ni", and the port under consideration
8006 * if "ppt" (which is NOPORTPROTO if no particular port is under consideration).
8007 * The port characteristic bits are set in "bits" and the parts of these bits
8008 * that are set have mask bits set into "mask". The port to be affected is
8009 * returned. If "wantexp" is true, the desired port should already be
8010 * exported (otherwise it should not be an export). If "intendedname" is set,
8011 * it is the name that will be given to the port when it is exported. The referenced
8012 * name is returned in "refname". The routine returns NOPORTPROTO if there is an error.
8013 */
us_portdetails(PORTPROTO * ppt,INTBIG count,CHAR * par[],NODEINST * ni,INTBIG * bits,INTBIG * mask,BOOLEAN wantexp,CHAR * intendedname,CHAR ** refname)8014 PORTPROTO *us_portdetails(PORTPROTO *ppt, INTBIG count, CHAR *par[], NODEINST *ni,
8015 INTBIG *bits, INTBIG *mask, BOOLEAN wantexp, CHAR *intendedname, CHAR **refname)
8016 {
8017 REGISTER PORTPROTO *wantpp, *pp;
8018 HIGHLIGHT high;
8019 INTBIG x, y;
8020 REGISTER INTBIG i, l, m, pindex;
8021 BOOLEAN specify;
8022 REGISTER INTBIG onlx, only, onhx, onhy, bestx, besty;
8023 REGISTER PORTEXPINST *pe;
8024 REGISTER NODEPROTO *np;
8025 REGISTER VARIABLE *var;
8026 REGISTER CHAR *pt;
8027 NODEINST *nilist[1];
8028 CHAR *newpar;
8029 static struct
8030 {
8031 CHAR *name;
8032 INTBIG significant;
8033 UINTBIG bits, mask;
8034 } portparse[] =
8035 {
8036 {x_("input"), 1, INPORT, STATEBITS},
8037 {x_("output"), 1, OUTPORT, STATEBITS},
8038 {x_("bidirectional"), 2, BIDIRPORT, STATEBITS},
8039 {x_("power"), 1, PWRPORT, STATEBITS},
8040 {x_("ground"), 1, GNDPORT, STATEBITS},
8041 {x_("clock1"), 6, C1PORT, STATEBITS},
8042 {x_("clock2"), 6, C2PORT, STATEBITS},
8043 {x_("clock3"), 6, C3PORT, STATEBITS},
8044 {x_("clock4"), 6, C4PORT, STATEBITS},
8045 {x_("clock5"), 6, C5PORT, STATEBITS},
8046 {x_("clock6"), 6, C6PORT, STATEBITS},
8047 {x_("clock"), 1, CLKPORT, STATEBITS},
8048 {x_("refout"), 4, REFOUTPORT, STATEBITS},
8049 {x_("refin"), 4, REFINPORT, STATEBITS},
8050 {x_("refbase"), 4, REFBASEPORT, STATEBITS},
8051 {x_("none"), 1, 0, STATEBITS|PORTDRAWN|BODYONLY},
8052 {x_("always-drawn"), 1, PORTDRAWN, PORTDRAWN},
8053 {x_("body-only"), 2, BODYONLY, BODYONLY},
8054 {NULL, 0, 0, 0}
8055 };
8056
8057 /* quick sanity check first */
8058 np = ni->proto;
8059 if (np->firstportproto == NOPORTPROTO)
8060 {
8061 us_abortcommand(_("This node has no ports"));
8062 return(NOPORTPROTO);
8063 }
8064
8065 /* prepare to parse parameters */
8066 wantpp = NOPORTPROTO;
8067 specify = FALSE;
8068 *refname = x_("");
8069 *bits = *mask = 0;
8070
8071 /* look at all parameters */
8072 for(i=0; i<count; i++)
8073 {
8074 l = estrlen(pt = par[i]);
8075
8076 /* see if a referenced name is given */
8077 if (namesamen(pt, x_("refname"), l) == 0 && l >= 4 && i+1 < count)
8078 {
8079 i++;
8080 *refname = par[i];
8081 continue;
8082 }
8083
8084 /* check the basic characteristics from the table */
8085 for(m=0; portparse[m].name != 0; m++)
8086 if (namesamen(pt, portparse[m].name, l) == 0 && l >= portparse[m].significant)
8087 {
8088 *bits |= portparse[m].bits;
8089 *mask |= portparse[m].mask;
8090 break;
8091 }
8092 if (portparse[m].name != 0) continue;
8093
8094 if (namesamen(pt, x_("specify"), l) == 0 && l >= 1)
8095 { specify = TRUE; continue; }
8096 if (namesamen(pt, x_("use"), l) == 0 && l >= 1)
8097 {
8098 if (i+1 >= count)
8099 {
8100 ttyputusage(x_("port use PORTNAME"));
8101 return(NOPORTPROTO);
8102 }
8103 i++;
8104 if (!wantexp)
8105 {
8106 /* want to export: look for any port on the node */
8107 wantpp = getportproto(np, par[i]);
8108 if (wantpp == NOPORTPROTO)
8109 {
8110 us_abortcommand(_("No port called %s"), par[i]);
8111 return(NOPORTPROTO);
8112 }
8113 } else
8114 {
8115 /* want exports: look specificially for them */
8116 for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
8117 if (namesame(pe->exportproto->protoname, par[i]) == 0) break;
8118 if (pe != NOPORTEXPINST) wantpp = pe->exportproto; else
8119 {
8120 us_abortcommand(_("No exports called %s"), par[i]);
8121 return(NOPORTPROTO);
8122 }
8123 }
8124 continue;
8125 }
8126 ttyputbadusage(x_("port"));
8127 return(NOPORTPROTO);
8128 }
8129
8130 /* if no port explicitly found, use default (if any) */
8131 if (wantpp == NOPORTPROTO) wantpp = ppt;
8132
8133 /* if no port found and heuristics are allowed, try them */
8134 if (wantpp == NOPORTPROTO && !specify)
8135 {
8136 /* if there is only one possible port, use it */
8137 if (!wantexp)
8138 {
8139 /* look for only nonexport */
8140 pindex = 0;
8141 for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
8142 {
8143 for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
8144 if (pe->proto == pp) break;
8145 if (pe != NOPORTEXPINST) continue;
8146 pindex++;
8147 wantpp = pp;
8148 }
8149 if (pindex != 1) wantpp = NOPORTPROTO;
8150 } else
8151 {
8152 /* if there is one export, return it */
8153 pe = ni->firstportexpinst;
8154 if (pe != NOPORTEXPINST && pe->nextportexpinst == NOPORTEXPINST)
8155 wantpp = pe->exportproto;
8156 }
8157
8158 /* if a port is highlighted, use it */
8159 if (wantpp == NOPORTPROTO)
8160 {
8161 var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_highlightedkey);
8162 if (var != NOVARIABLE)
8163 {
8164 if (getlength(var) == 1)
8165 {
8166 (void)us_makehighlight(((CHAR **)var->addr)[0], &high);
8167 if ((high.status&HIGHTYPE) == HIGHFROM && high.fromport != NOPORTPROTO)
8168 {
8169 pp = high.fromport;
8170 if (!wantexp) wantpp = pp; else
8171 {
8172 for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
8173 if (pe->proto == pp) break;
8174 if (pe != NOPORTEXPINST) wantpp = pe->exportproto; else
8175 {
8176 us_abortcommand(_("Port %s must be an export first"), pp->protoname);
8177 return(NOPORTPROTO);
8178 }
8179 }
8180 }
8181 }
8182 }
8183 }
8184
8185 /* if exporting port with the same name as the subportinst, use it */
8186 if (wantpp == NOPORTPROTO && *intendedname != 0 && !wantexp)
8187 {
8188 for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
8189 if (namesame(intendedname, pp->protoname) == 0)
8190 {
8191 wantpp = pp;
8192 break;
8193 }
8194 }
8195
8196 /* if port is on one edge of the cell and is being exported, use it */
8197 if (wantpp == NOPORTPROTO && !wantexp)
8198 {
8199 if (ni->geom->lowx == ni->parent->lowx) onlx = 1; else onlx = 0;
8200 if (ni->geom->highx == ni->parent->highx) onhx = 1; else onhx = 0;
8201 if (ni->geom->lowy == ni->parent->lowy) only = 1; else only = 0;
8202 if (ni->geom->highy == ni->parent->highy) onhy = 1; else onhy = 0;
8203 if (onlx+onhx+only+onhy == 1)
8204 {
8205 /* look for one port on the node that is on the proper edge */
8206 bestx = (ni->lowx+ni->highx)/2;
8207 besty = (ni->lowy+ni->highy)/2;
8208 wantpp = NOPORTPROTO;
8209 for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
8210 {
8211 portposition(ni, pp, &x, &y);
8212 if (onlx != 0 && x == bestx) wantpp = NOPORTPROTO;
8213 if (onlx != 0 && x < bestx)
8214 {
8215 wantpp = pp; bestx = x;
8216 }
8217 if (onhx != 0 && x == bestx) wantpp = NOPORTPROTO;
8218 if (onhx != 0 && x > bestx)
8219 {
8220 wantpp = pp; bestx = x;
8221 }
8222 if (only != 0 && y == besty) wantpp = NOPORTPROTO;
8223 if (only != 0 && y < besty)
8224 {
8225 wantpp = pp; besty = y;
8226 }
8227 if (onhy != 0 && y == besty) wantpp = NOPORTPROTO;
8228 if (onhy != 0 && y > besty)
8229 {
8230 wantpp = pp; besty = y;
8231 }
8232 }
8233 }
8234 }
8235 }
8236
8237 /* if port is on offpage connector, use characteristics to choose the end */
8238 if (wantpp == NOPORTPROTO && ni->proto == sch_offpageprim)
8239 {
8240 if (((*bits)&STATEBITS) == OUTPORT) wantpp = ni->proto->firstportproto->nextportproto; else
8241 wantpp = ni->proto->firstportproto;
8242 }
8243
8244 /* give up and ask the port name wanted */
8245 if (wantpp == NOPORTPROTO)
8246 {
8247 nilist[0] = ni;
8248 us_identifyports(1, nilist, ni->proto, LAYERA, TRUE);
8249 ttyputerr(_("Which port of node %s is to be the port:"), describenodeproto(np));
8250 pindex = 0;
8251 for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
8252 ttyputmsg(x_("%ld: %s"), ++pindex, pp->protoname);
8253 for(;;)
8254 {
8255 newpar = ttygetline(_("Select a number: "));
8256 if (newpar == 0 || *newpar == 0)
8257 {
8258 us_abortedmsg();
8259 break;
8260 }
8261 i = eatoi(newpar);
8262 if (i <= 0 || i > pindex)
8263 {
8264 ttyputerr(_("Please select a number from 1 to %ld (default aborts)"), pindex);
8265 continue;
8266 }
8267
8268 /* convert to a port */
8269 x = 0;
8270 for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
8271 if (++x == i) break;
8272 if (!wantexp) wantpp = pp; else
8273 {
8274 for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
8275 if (pe->proto == pp) break;
8276 if (pe == NOPORTEXPINST)
8277 {
8278 ttyputerr(_("That port is not an export"));
8279 continue;
8280 }
8281 wantpp = pe->exportproto;
8282 }
8283 break;
8284 }
8285 us_identifyports(1, nilist, ni->proto, ALLOFF, TRUE);
8286 }
8287
8288 /* finally, return the port */
8289 return(wantpp);
8290 }
8291
8292 /*
8293 * routine to recursively delete ports at nodeinst "ni" and all arcs connected
8294 * to them anywhere. If "spt" is not NOPORTPROTO, delete only that portproto
8295 * on this nodeinst (and its hierarchically related ports). Otherwise delete
8296 * all portprotos on this nodeinst.
8297 */
us_undoportproto(NODEINST * ni,PORTPROTO * spt)8298 void us_undoportproto(NODEINST *ni, PORTPROTO *spt)
8299 {
8300 REGISTER PORTEXPINST *pe, *nextpe;
8301
8302 for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = nextpe)
8303 {
8304 nextpe = pe->nextportexpinst;
8305 if (spt != NOPORTPROTO && spt != pe->exportproto) continue;
8306 if (killportproto(pe->exportproto->parent, pe->exportproto))
8307 ttyputerr(_("killportproto error"));
8308 }
8309 }
8310
8311 /*
8312 * Routine to synchronize the ports in cells in the current library with
8313 * like-named cells in library "olib".
8314 */
us_portsynchronize(LIBRARY * olib)8315 void us_portsynchronize(LIBRARY *olib)
8316 {
8317 REGISTER NODEPROTO *np, *onp;
8318 REGISTER PORTPROTO *pp, *opp;
8319 REGISTER INTBIG lx, hx, ly, hy, newports, nocells;
8320 REGISTER NODEINST *ni, *oni;
8321
8322 newports = nocells = 0;
8323 for(np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
8324 {
8325 /* find this cell in the other library */
8326 for(onp = olib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
8327 {
8328 if (namesame(np->protoname, onp->protoname) != 0) continue;
8329
8330 /* synchronize the ports */
8331 for(opp = onp->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
8332 {
8333 /* see if that other cell's port is in this one */
8334 for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
8335 if (namesame(opp->protoname, pp->protoname) == 0) break;
8336 if (pp != NOPORTPROTO) continue;
8337
8338 /* must add port "opp" to cell "np" */
8339 oni = opp->subnodeinst;
8340 if (oni->proto->primindex == 0)
8341 {
8342 if (nocells == 0)
8343 ttyputerr(_("Cannot yet make exports that come from other cell instances (i.e. export %s in cell %s)"),
8344 opp->protoname, describenodeproto(onp));
8345 nocells = 1;
8346 continue;
8347 }
8348
8349 /* presume that the cells have the same coordinate system */
8350 lx = oni->lowx;
8351 hx = oni->highx;
8352 ly = oni->lowy;
8353 hy = oni->highy;
8354
8355 ni = newnodeinst(oni->proto, lx, hx, ly, hy, oni->transpose, oni->rotation, np);
8356 if (ni == NONODEINST) continue;
8357 pp = newportproto(np, ni, opp->subportproto, opp->protoname);
8358 if (pp == NOPORTPROTO) return;
8359 pp->userbits = opp->userbits;
8360 TDCOPY(pp->textdescript, opp->textdescript);
8361 if (copyvars((INTBIG)opp, VPORTPROTO, (INTBIG)pp, VPORTPROTO, FALSE))
8362 return;
8363 endobjectchange((INTBIG)ni, VNODEINST);
8364 newports++;
8365 }
8366 }
8367 }
8368 ttyputmsg(_("Created %ld new %s"), newports, makeplural(_("port"), newports));
8369 }
8370
8371 /*
8372 * routine to determine a path down from the currently highlighted port. Returns
8373 * the subnode and subport in "hini" and "hipp" (sets them to NONODEINST and
8374 * NOPORTPROTO if no lower path is defined).
8375 */
us_findlowerport(NODEINST ** hini,PORTPROTO ** hipp)8376 void us_findlowerport(NODEINST **hini, PORTPROTO **hipp)
8377 {
8378 HIGHLIGHT high;
8379 REGISTER VARIABLE *var;
8380 REGISTER INTBIG len;
8381 NODEINST *ni;
8382 REGISTER NODEPROTO *np, *onp;
8383 PORTPROTO *pp;
8384
8385 /* presume no lower port */
8386 *hini = NONODEINST;
8387 *hipp = NOPORTPROTO;
8388
8389 /* must be 1 highlighted object */
8390 var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_highlightedkey);
8391 if (var == NOVARIABLE) return;
8392 len = getlength(var);
8393 if (len > 1) return;
8394
8395 /* get the highlighted object */
8396 if (us_makehighlight(((CHAR **)var->addr)[0], &high)) return;
8397
8398 /* see if it is a port */
8399 if ((high.status&HIGHTYPE) == HIGHTEXT)
8400 {
8401 if (high.fromvar != NOVARIABLE || high.fromport == NOPORTPROTO) return;
8402 pp = high.fromport->subportproto;
8403 } else
8404 {
8405 if ((high.status&HIGHTYPE) != HIGHFROM || high.fromport == NOPORTPROTO) return;
8406 pp = high.fromport;
8407 }
8408
8409 /* see if port is on instance */
8410 ni = high.fromgeom->entryaddr.ni;
8411 np = ni->proto;
8412 if (np->primindex != 0) return;
8413
8414 /* describe source of the port */
8415 *hini = pp->subnodeinst;
8416 *hipp = pp->subportproto;
8417
8418 /* see if port is on an icon */
8419 if (np->cellview == el_iconview && !isiconof(np, ni->parent))
8420 {
8421 onp = contentsview(np);
8422 if (onp != NONODEPROTO)
8423 {
8424 *hipp = equivalentport(np, pp, onp);
8425 if (*hipp != NOPORTPROTO)
8426 {
8427 *hini = (*hipp)->subnodeinst;
8428 *hipp = (*hipp)->subportproto;
8429 }
8430 }
8431 }
8432 }
8433
8434 /*
8435 * Routine to interactively select an instance of "inp" higher in the hierarchy.
8436 * Returns the instance that was selected (NONODEINST if none).
8437 */
us_pickhigherinstance(NODEPROTO * inp)8438 NODEINST *us_pickhigherinstance(NODEPROTO *inp)
8439 {
8440 REGISTER NODEPROTO **newcelllist;
8441 REGISTER NODEINST *ni, **newinstlist;
8442 REGISTER POPUPMENU *pm;
8443 POPUPMENU *cpopup;
8444 REGISTER POPUPMENUITEM *mi, *selected;
8445 BOOLEAN butstate;
8446 REGISTER INTBIG cellcount, i, k, *newinstcount;
8447 CHAR buf[50];
8448 REGISTER void *infstr;
8449
8450 /* make a list of choices, up the hierarchy */
8451 cellcount = 0;
8452 for(ni = inp->firstinst; ni != NONODEINST; ni = ni->nextinst)
8453 {
8454 /* ignore if this is an icon in a contents */
8455 if (isiconof(ni->proto, ni->parent)) continue;
8456
8457 /* ignore instances on the clipboard */
8458 if ((ni->parent->lib->userbits&HIDDENLIBRARY) != 0) continue;
8459
8460 /* ignore this instance if it is a duplicate */
8461 for(i=0; i<cellcount; i++) if (us_pickinstcelllist[i] == ni->parent) break;
8462 if (i < cellcount)
8463 {
8464 us_pickinstinstcount[i]++;
8465 continue;
8466 }
8467
8468 /* ensure room in the list */
8469 if (cellcount >= us_pickinstlistsize)
8470 {
8471 k = us_pickinstlistsize + 32;
8472 newcelllist = (NODEPROTO **)emalloc(k * (sizeof (NODEPROTO *)), us_tool->cluster);
8473 newinstlist = (NODEINST **)emalloc(k * (sizeof (NODEINST *)), us_tool->cluster);
8474 newinstcount = (INTBIG *)emalloc(k * SIZEOFINTBIG, us_tool->cluster);
8475 if (newcelllist == 0 || newinstlist == 0 || newinstcount == 0) return(0);
8476 for(i=0; i<cellcount; i++)
8477 {
8478 newcelllist[i] = us_pickinstcelllist[i];
8479 newinstlist[i] = us_pickinstinstlist[i];
8480 newinstcount[i] = us_pickinstinstcount[i];
8481 }
8482 if (us_pickinstlistsize != 0)
8483 {
8484 efree((CHAR *)us_pickinstcelllist);
8485 efree((CHAR *)us_pickinstinstlist);
8486 efree((CHAR *)us_pickinstinstcount);
8487 }
8488 us_pickinstcelllist = newcelllist;
8489 us_pickinstinstlist = newinstlist;
8490 us_pickinstinstcount = newinstcount;
8491 us_pickinstlistsize = k;
8492 }
8493
8494 us_pickinstcelllist[cellcount] = ni->parent;
8495 us_pickinstinstlist[cellcount] = ni;
8496 us_pickinstinstcount[cellcount] = 1;
8497 cellcount++;
8498 }
8499
8500 /* if no instances of this cell found, exit */
8501 if (cellcount == 0) return(NONODEINST);
8502
8503 /* if only one instance, answer is easy */
8504 if (cellcount == 1)
8505 {
8506 return(us_pickinstinstlist[0]);
8507 }
8508
8509 /* make a menu of all cells connected to this export */
8510 pm = (POPUPMENU *)emalloc(sizeof(POPUPMENU), el_tempcluster);
8511 if (pm == 0) return(NONODEINST);
8512
8513 mi = (POPUPMENUITEM *)emalloc(cellcount * (sizeof (POPUPMENUITEM)), el_tempcluster);
8514 if (mi == 0)
8515 {
8516 efree((CHAR *)pm);
8517 return(NONODEINST);
8518 }
8519 for (i=0; i<cellcount; i++)
8520 {
8521 infstr = initinfstr();
8522 addstringtoinfstr(infstr, describenodeproto(us_pickinstcelllist[i]));
8523 if (us_pickinstinstcount[i] > 1)
8524 {
8525 (void)esnprintf(buf, 50, _(" (%ld instances)"), us_pickinstinstcount[i]);
8526 addstringtoinfstr(infstr, buf);
8527 }
8528 (void)allocstring(&mi[i].attribute, returninfstr(infstr), el_tempcluster);
8529 mi[i].value = 0;
8530 mi[i].valueparse = NOCOMCOMP;
8531 mi[i].maxlen = -1;
8532 mi[i].response = NOUSERCOM;
8533 mi[i].changed = FALSE;
8534 }
8535 pm->name = x_("noname");
8536 pm->list = mi;
8537 pm->total = cellcount;
8538 pm->header = _("Which cell up the hierarchy?");
8539
8540 /* display and select from the menu */
8541 butstate = FALSE;
8542 cpopup = pm;
8543 selected = us_popupmenu(&cpopup, &butstate, TRUE, -1, -1, 0);
8544
8545 /* free up allocated menu space */
8546 for (k=0; k<cellcount; k++)
8547 efree(mi[k].attribute);
8548 efree((CHAR *)mi);
8549 efree((CHAR *)pm);
8550
8551 /* stop if display doesn't support popup menus */
8552 if (selected == 0) return(NONODEINST);
8553 if (selected == NOPOPUPMENUITEM) return(NONODEINST);
8554 for (i=0; i<cellcount; i++)
8555 {
8556 if (selected != &mi[i]) continue;
8557 return(us_pickinstinstlist[i]);
8558 }
8559
8560 return(NONODEINST);
8561 }
8562
8563 /*
8564 * routine to re-export port "pp" on nodeinst "ni". Returns true if there
8565 * is an error
8566 */
us_reexportport(PORTPROTO * pp,NODEINST * ni)8567 BOOLEAN us_reexportport(PORTPROTO *pp, NODEINST *ni)
8568 {
8569 REGISTER VARIABLE *var;
8570 CHAR *portname, *sportname, *pt;
8571 REGISTER PORTPROTO *ppt;
8572 REGISTER void *infstr;
8573
8574 /* generate an initial guess for the new port name */
8575 infstr = initinfstr();
8576
8577 /* add in local node name if applicable */
8578 var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
8579 if (var != NOVARIABLE)
8580 {
8581 /* see if the original name has array markers */
8582 for(pt = pp->protoname; *pt != 0; pt++)
8583 if (*pt == '[') break;
8584 if (*pt == '[')
8585 {
8586 /* arrayed name: add node name to array index */
8587 *pt = 0;
8588 addstringtoinfstr(infstr, pp->protoname);
8589 addstringtoinfstr(infstr, (CHAR *)var->addr);
8590 *pt = '[';
8591 addstringtoinfstr(infstr, pt);
8592 } else
8593 {
8594 /* simple name: add node name to the end */
8595 addstringtoinfstr(infstr, pp->protoname);
8596 addstringtoinfstr(infstr, (CHAR *)var->addr);
8597 }
8598 } else
8599 {
8600 /* no node name: just gather the export name */
8601 addstringtoinfstr(infstr, pp->protoname);
8602 }
8603
8604 (void)allocstring(&sportname, returninfstr(infstr), el_tempcluster);
8605 portname = us_uniqueportname(sportname, ni->parent);
8606 efree(sportname);
8607
8608 /* make the export */
8609 ttyputmsg(_("Making export %s from node %s, port %s"), portname, describenodeinst(ni), pp->protoname);
8610 startobjectchange((INTBIG)ni, VNODEINST);
8611 ppt = newportproto(ni->parent, ni, pp, portname);
8612 if (ppt == NOPORTPROTO)
8613 {
8614 us_abortcommand(_("Error creating export %s"), portname);
8615 return(TRUE);
8616 }
8617 TDCOPY(ppt->textdescript, pp->textdescript);
8618 ppt->userbits = pp->userbits;
8619 if (copyvars((INTBIG)pp, VPORTPROTO, (INTBIG)ppt, VPORTPROTO, FALSE))
8620 return(FALSE);
8621 endobjectchange((INTBIG)ni, VNODEINST);
8622 return(FALSE);
8623 }
8624
8625 /*
8626 * routine to rename port "pp" to be "pt"
8627 */
us_renameport(PORTPROTO * pp,CHAR * pt)8628 void us_renameport(PORTPROTO *pp, CHAR *pt)
8629 {
8630 CHAR *ch, *newname;
8631 REGISTER BOOLEAN badname;
8632 REGISTER PORTPROTO *opp, *app;
8633 REGISTER NODEINST *ni;
8634 REGISTER NODEPROTO *np, *anp;
8635
8636 (void)allocstring(&newname, pt, us_tool->cluster);
8637 badname = FALSE;
8638 for(ch = newname; *ch != 0; ch++)
8639 {
8640 if (*ch > ' ' && *ch < 0177) continue;
8641 *ch = 'X';
8642 badname = TRUE;
8643 }
8644 if (badname)
8645 ttyputerr(_("Port has invalid characters, renamed to '%s'"), newname);
8646
8647 /* check for duplicate name */
8648 if (estrcmp(pp->protoname, newname) == 0)
8649 {
8650 ttyputmsg(_("Port name has not changed"));
8651 efree((CHAR *)newname);
8652 return;
8653 }
8654
8655 np = pp->parent;
8656 opp = getportproto(np, newname);
8657 if (opp == pp) opp = NOPORTPROTO;
8658 if (opp == NOPORTPROTO) ch = newname; else
8659 {
8660 ch = us_uniqueportname(newname, np);
8661 ttyputmsg(_("Already a port called %s, calling this %s"), newname, ch);
8662 }
8663
8664 /* see if an associated icon/contents cell will also be affected */
8665 if (np->cellview == el_iconview) anp = contentsview(np); else
8666 anp = iconview(np);
8667 if (anp == NONODEPROTO) app = NOPORTPROTO; else
8668 {
8669 app = equivalentport(np, pp, anp);
8670 opp = getportproto(anp, ch);
8671 if (opp != NOPORTPROTO && opp != app) app = NOPORTPROTO;
8672 }
8673
8674 /* erase all instances of this nodeproto on display */
8675 startobjectchange((INTBIG)pp->subnodeinst, VNODEINST);
8676 for(ni = np->firstinst; ni != NONODEINST; ni = ni->nextinst)
8677 {
8678 if ((ni->userbits & NEXPAND) == 0) startobjectchange((INTBIG)ni, VNODEINST);
8679 }
8680 if (app != NOPORTPROTO)
8681 {
8682 startobjectchange((INTBIG)app->subnodeinst, VNODEINST);
8683 for(ni = anp->firstinst; ni != NONODEINST; ni = ni->nextinst)
8684 {
8685 /* mark the library with the instance as "changed" */
8686 ni->parent->lib->userbits |= LIBCHANGEDMAJOR;
8687
8688 /* redraw if not expanded */
8689 if ((ni->userbits & NEXPAND) == 0) startobjectchange((INTBIG)ni, VNODEINST);
8690 }
8691 }
8692
8693 /* change the export name */
8694 ttyputverbose(M_("Export %s renamed to %s"), pp->protoname, ch);
8695 (void)setval((INTBIG)pp, VPORTPROTO, x_("protoname"), (INTBIG)ch, VSTRING);
8696 if (app != NOPORTPROTO)
8697 (void)setval((INTBIG)app, VPORTPROTO, x_("protoname"), (INTBIG)ch, VSTRING);
8698
8699 /* redraw all instances of this nodeproto on display */
8700 for(ni = np->firstinst; ni != NONODEINST; ni = ni->nextinst)
8701 {
8702 if ((ni->userbits & NEXPAND) == 0) endobjectchange((INTBIG)ni, VNODEINST);
8703 }
8704 endobjectchange((INTBIG)pp->subnodeinst, VNODEINST);
8705
8706 /* tell the network maintainter to reevaluate this cell */
8707 (void)asktool(net_tool, x_("re-number"), (INTBIG)np);
8708
8709 if (app != NOPORTPROTO)
8710 {
8711 for(ni = anp->firstinst; ni != NONODEINST; ni = ni->nextinst)
8712 {
8713 if ((ni->userbits & NEXPAND) == 0) endobjectchange((INTBIG)ni, VNODEINST);
8714 }
8715 endobjectchange((INTBIG)app->subnodeinst, VNODEINST);
8716
8717 /* tell the network maintainter to reevaluate this cell */
8718 (void)asktool(net_tool, x_("re-number"), (INTBIG)anp);
8719 }
8720 efree((CHAR *)newname);
8721 }
8722
8723 /*
8724 * routine to create a port in cell "np" called "portname". The port resides on
8725 * node "ni", port "pp" in the cell. The userbits field will have "bits" set where
8726 * "mask" points. The text descriptor is "textdescript".
8727 * Also, check across icon/contents boundary to create a parallel port if possible
8728 */
us_makenewportproto(NODEPROTO * np,NODEINST * ni,PORTPROTO * pp,CHAR * portname,INTBIG mask,INTBIG bits,UINTBIG * textdescript)8729 PORTPROTO *us_makenewportproto(NODEPROTO *np, NODEINST *ni, PORTPROTO *pp,
8730 CHAR *portname, INTBIG mask, INTBIG bits, UINTBIG *textdescript)
8731 {
8732 REGISTER NODEPROTO *onp;
8733 REGISTER NODEINST *boxni;
8734 REGISTER VARIABLE *var;
8735 INTBIG xpos, ypos, xbbpos, ybbpos, x, y, portcount, portlen, *newportlocation,
8736 lambda, style;
8737 REGISTER INTBIG boxlx, boxhx, boxly, boxhy, rangelx, rangehx, leadlength,
8738 rangely, rangehy, index;
8739 REGISTER PORTPROTO *ppt, *opp;
8740
8741 startobjectchange((INTBIG)ni, VNODEINST);
8742 ppt = newportproto(np, ni, pp, portname);
8743 if (ppt == NOPORTPROTO)
8744 {
8745 us_abortcommand(_("Error creating the port"));
8746 us_pophighlight(FALSE);
8747 return(NOPORTPROTO);
8748 }
8749 if ((mask&STATEBITS) != 0) ppt->userbits = (ppt->userbits & ~STATEBITS) | (bits & STATEBITS);
8750 if ((mask&PORTDRAWN) != 0) ppt->userbits = (ppt->userbits & ~PORTDRAWN) | (bits & PORTDRAWN);
8751 if ((mask&BODYONLY) != 0) ppt->userbits = (ppt->userbits & ~BODYONLY) | (bits & BODYONLY);
8752 if (ni->proto->primindex == 0)
8753 TDCOPY(ppt->textdescript, textdescript);
8754
8755 /* if this is a port on an offpage connector, adjust the position sensibly */
8756 if (ni->proto == sch_offpageprim)
8757 {
8758 if (namesame(pp->protoname, "y") == 0) TDSETPOS(ppt->textdescript, VTPOSRIGHT); else
8759 TDSETPOS(ppt->textdescript, VTPOSLEFT);
8760 }
8761 endobjectchange((INTBIG)ni, VNODEINST);
8762
8763 /* ignore new port if not intended for icon */
8764 if ((pp->userbits&BODYONLY) != 0) return(ppt);
8765
8766 /* see if there is an associated icon cell */
8767 onp = NONODEPROTO;
8768 if (np->cellview != el_iconview) onp = iconview(np);
8769 if (onp == NONODEPROTO) return(ppt);
8770
8771 /* icon cell found, quit if this port is already there */
8772 opp = getportproto(onp, ppt->protoname);
8773 if (opp != NOPORTPROTO) return(ppt);
8774
8775 /* get icon style controls */
8776 var = getval((INTBIG)us_tool, VTOOL, VINTEGER, x_("USER_icon_style"));
8777 if (var != NOVARIABLE) style = var->addr; else style = ICONSTYLEDEFAULT;
8778 var = getval((INTBIG)us_tool, VTOOL, VINTEGER, x_("USER_icon_lead_length"));
8779 if (var != NOVARIABLE) leadlength = var->addr; else leadlength = ICONLEADDEFLEN;
8780
8781 /* find the box in the icon cell */
8782 for(boxni = onp->firstnodeinst; boxni != NONODEINST; boxni = boxni->nextnodeinst)
8783 if (boxni->proto == sch_bboxprim) break;
8784 if (boxni == NONODEINST)
8785 {
8786 boxlx = boxhx = (onp->lowx + onp->highx) / 2;
8787 boxly = boxhy = (onp->lowy + onp->highy) / 2;
8788 rangelx = onp->lowx;
8789 rangehx = onp->highx;
8790 rangely = onp->lowy;
8791 rangehy = onp->highy;
8792 } else
8793 {
8794 rangelx = boxlx = boxni->lowx;
8795 rangehx = boxhx = boxni->highx;
8796 rangely = boxly = boxni->lowy;
8797 rangehy = boxhy = boxni->highy;
8798 }
8799
8800 /* count the number of ports */
8801 portlen = 0;
8802 for(opp = onp->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto) portlen++;
8803
8804 if (portlen > us_netportlimit)
8805 {
8806 newportlocation = (INTBIG *)emalloc(portlen * SIZEOFINTBIG, us_tool->cluster);
8807 if (newportlocation == 0) return(ppt);
8808 if (us_netportlimit > 0) efree((CHAR *)us_netportlocation);
8809 us_netportlocation = newportlocation;
8810 us_netportlimit = portlen;
8811 }
8812 portcount = 0;
8813 lambda = el_curlib->lambda[sch_tech->techindex];
8814 index = us_iconposition(ppt, style);
8815 switch (index)
8816 {
8817 case 0: /* left side */
8818 for(opp = onp->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
8819 {
8820 portposition(opp->subnodeinst, opp->subportproto, &x, &y);
8821 if (opp == onp->firstportproto) xpos = x; else
8822 {
8823 if (x < xpos) xpos = x;
8824 }
8825 if (x < boxlx) us_netportlocation[portcount++] = y;
8826 }
8827 ybbpos = ypos = us_findnewplace(us_netportlocation, portcount, rangely, rangehy);
8828 xbbpos = boxlx;
8829 break;
8830 case 1: /* right side */
8831 for(opp = onp->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
8832 {
8833 portposition(opp->subnodeinst, opp->subportproto, &x, &y);
8834 if (opp == onp->firstportproto) xpos = x; else
8835 {
8836 if (x > xpos) xpos = x;
8837 }
8838 if (x > boxhx) us_netportlocation[portcount++] = y;
8839 }
8840 ybbpos = ypos = us_findnewplace(us_netportlocation, portcount, rangely, rangehy);
8841 xbbpos = boxhx;
8842 break;
8843 case 2: /* top */
8844 for(opp = onp->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
8845 {
8846 portposition(opp->subnodeinst, opp->subportproto, &x, &y);
8847 if (opp == onp->firstportproto) ypos = y; else
8848 {
8849 if (y > ypos) ypos = y;
8850 }
8851 if (y > boxhy) us_netportlocation[portcount++] = x;
8852 }
8853 xbbpos = xpos = us_findnewplace(us_netportlocation, portcount, rangelx, rangehx);
8854 ybbpos = boxhy;
8855 break;
8856 case 3: /* bottom */
8857 for(opp = onp->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
8858 {
8859 portposition(opp->subnodeinst, opp->subportproto, &x, &y);
8860 if (opp == onp->firstportproto) ypos = y; else
8861 {
8862 if (y < ypos) ypos = y;
8863 }
8864 if (y < boxly) us_netportlocation[portcount++] = x;
8865 }
8866 xbbpos = xpos = us_findnewplace(us_netportlocation, portcount, rangelx, rangehx);
8867 ybbpos = boxly;
8868 break;
8869 }
8870
8871 /* make the icon export */
8872 gridalign(&xpos, &ypos, 1, onp);
8873 gridalign(&xbbpos, &ybbpos, 1, onp);
8874 (void)us_makeiconexport(ppt, style, index, xpos, ypos, xbbpos, ybbpos, onp);
8875 return(ppt);
8876 }
8877
8878 /*
8879 * routine to find the largest gap in an integer array that is within the bounds
8880 * "low" to "high". The array is sorted by this routine.
8881 */
us_findnewplace(INTBIG * arr,INTBIG count,INTBIG low,INTBIG high)8882 INTBIG us_findnewplace(INTBIG *arr, INTBIG count, INTBIG low, INTBIG high)
8883 {
8884 REGISTER INTBIG i, gapwid, gappos;
8885
8886 /* easy if nothing in the array */
8887 if (count <= 0) return((low + high) / 2);
8888
8889 /* first sort the array */
8890 esort(arr, count, SIZEOFINTBIG, sort_intbigascending);
8891
8892 /* now find the widest gap */
8893 gapwid = 0;
8894 gappos = (low + high) / 2;
8895 for(i=1; i<count; i++)
8896 {
8897 if (arr[i] - arr[i-1] > gapwid)
8898 {
8899 gapwid = arr[i] - arr[i-1];
8900 gappos = (arr[i-1] + arr[i]) / 2;
8901 }
8902 }
8903 if (arr[0] - low > gapwid)
8904 {
8905 gapwid = arr[0] - low;
8906 gappos = (low + arr[0]) / 2;
8907 }
8908 if (high - arr[count-1] > gapwid)
8909 {
8910 gapwid = high - arr[count-1];
8911 gappos = (arr[count-1] + high) / 2;
8912 }
8913 return(gappos);
8914 }
8915
8916 /*
8917 * Helper routine for "us_show()" that makes exports go in ascending order
8918 * by name within type
8919 */
us_exportnametypeascending(const void * e1,const void * e2)8920 int us_exportnametypeascending(const void *e1, const void *e2)
8921 {
8922 REGISTER PORTPROTO *pp1, *pp2;
8923 REGISTER UINTBIG s1, s2;
8924
8925 pp1 = *((PORTPROTO **)e1);
8926 pp2 = *((PORTPROTO **)e2);
8927 s1 = (pp1->userbits & STATEBITS) >> 1;
8928 s2 = (pp2->userbits & STATEBITS) >> 1;
8929 if (s1 != s2) return(s1-s2);
8930 return(namesamenumeric(pp1->protoname, pp2->protoname));
8931 }
8932
8933 /*
8934 * Helper routine for "us_show()" that makes exports go in ascending order
8935 * by index within name
8936 */
us_exportnameindexascending(const void * e1,const void * e2)8937 int us_exportnameindexascending(const void *e1, const void *e2)
8938 {
8939 REGISTER PORTPROTO *pp1, *pp2;
8940
8941 pp1 = *((PORTPROTO **)e1);
8942 pp2 = *((PORTPROTO **)e2);
8943 return(namesamenumeric(pp1->protoname, pp2->protoname));
8944 }
8945
8946 /*
8947 * Helper routine for "us_library()" that makes libraries go in ascending order
8948 * by their "temp1" field
8949 */
us_librarytemp1ascending(const void * e1,const void * e2)8950 int us_librarytemp1ascending(const void *e1, const void *e2)
8951 {
8952 REGISTER LIBRARY *lib1, *lib2;
8953
8954 lib1 = *((LIBRARY **)e1);
8955 lib2 = *((LIBRARY **)e2);
8956 return(lib1->temp1 - lib2->temp1);
8957 }
8958
8959 /*
8960 * Helper routine for "us_getproto" to sort popup menu items in ascending order.
8961 */
us_sortpopupmenuascending(const void * e1,const void * e2)8962 int us_sortpopupmenuascending(const void *e1, const void *e2)
8963 {
8964 REGISTER POPUPMENUITEM *pm1, *pm2;
8965
8966 pm1 = (POPUPMENUITEM *)e1;
8967 pm2 = (POPUPMENUITEM *)e2;
8968 return(namesame(pm1->attribute, pm2->attribute));
8969 }
8970
8971 /*
8972 * Helper routine to add all marked arc prototypes to the infinite string.
8973 * Marking is done by having the "temp1" field be nonzero.
8974 */
us_addpossiblearcconnections(void * infstr)8975 void us_addpossiblearcconnections(void *infstr)
8976 {
8977 REGISTER TECHNOLOGY *tech;
8978 REGISTER INTBIG i;
8979 REGISTER ARCPROTO *ap;
8980
8981 i = 0;
8982 for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
8983 for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
8984 if (ap->temp1 == 0) i++;
8985 if (i == 0) addstringtoinfstr(infstr, _(" EVERYTHING")); else
8986 {
8987 i = 0;
8988 for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
8989 {
8990 if (tech == gen_tech) continue;
8991 for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
8992 {
8993 if (ap->temp1 == 0) continue;
8994 if (i != 0) addtoinfstr(infstr, ',');
8995 i++;
8996 formatinfstr(infstr, x_(" %s"), ap->protoname);
8997 }
8998 }
8999 }
9000 }
9001
9002 /*********************************** FINDING ***********************************/
9003
9004 /*
9005 * routine to find an object/port close to (wantx, wanty) in the current cell.
9006 * If there is more than one object/port under the cursor, they are returned
9007 * in reverse sequential order, provided that the most recently found
9008 * object is described in "curhigh". The next close object is placed in
9009 * "curhigh". If "exclusively" is nonzero, find only nodes or arcs of the
9010 * current prototype. If "another" is nonzero, this is the second find,
9011 * and should not consider text objects. If "findport" is nonzero, port selection
9012 * is also desired. If "under" is nonzero, only find objects exactly under the
9013 * desired cursor location. If "special" is nonzero, special selection rules apply.
9014 */
us_findobject(INTBIG wantx,INTBIG wanty,WINDOWPART * win,HIGHLIGHT * curhigh,INTBIG exclusively,INTBIG another,INTBIG findport,INTBIG under,INTBIG special)9015 void us_findobject(INTBIG wantx, INTBIG wanty, WINDOWPART *win, HIGHLIGHT *curhigh,
9016 INTBIG exclusively, INTBIG another, INTBIG findport, INTBIG under, INTBIG special)
9017 {
9018 HIGHLIGHT best, lastdirect, prevdirect, bestdirect;
9019 REGISTER PORTPROTO *pp;
9020 REGISTER NODEINST *ni;
9021 REGISTER NODEPROTO *np;
9022 REGISTER INTBIG dist;
9023 REGISTER VARIABLE *var;
9024 VARIABLE *varnoeval;
9025 REGISTER INTBIG i, tot, phase, startphase;
9026 INTBIG looping, bestdist;
9027 static POLYGON *poly = NOPOLYGON;
9028
9029 /* get polygon */
9030 (void)needstaticpolygon(&poly, 4, us_tool->cluster);
9031
9032 /* initialize */
9033 bestdist = MAXINTBIG;
9034 looping = 0;
9035 best.fromgeom = NOGEOM; best.status = 0;
9036 bestdirect.fromgeom = NOGEOM; bestdirect.status = 0;
9037 lastdirect.fromgeom = NOGEOM; lastdirect.status = 0;
9038 prevdirect.fromgeom = NOGEOM; prevdirect.status = 0;
9039
9040 /* ignore cells if requested */
9041 startphase = 0;
9042 if (special == 0 && (us_useroptions&NOINSTANCESELECT) != 0) startphase = 1;
9043
9044 /* search the relevant objects in the circuit */
9045 np = win->curnodeproto;
9046 for(phase = startphase; phase < 3; phase++)
9047 {
9048 us_recursivelysearch(np->rtree, exclusively, another, findport,
9049 under, special, curhigh, &best, &bestdirect, &lastdirect, &prevdirect, &looping,
9050 &bestdist, wantx, wanty, win, phase);
9051 us_fartextsearch(np, exclusively, another, findport,
9052 under, special, curhigh, &best, &bestdirect, &lastdirect, &prevdirect, &looping,
9053 &bestdist, wantx, wanty, win, phase);
9054 }
9055
9056 /* check for displayable variables on the cell */
9057 tot = tech_displayablecellvars(np, win, &tech_oneprocpolyloop);
9058 for(i=0; i<tot; i++)
9059 {
9060 var = tech_filldisplayablecellvar(np, poly, win, &varnoeval, &tech_oneprocpolyloop);
9061
9062 /* cell variables are offset from (0,0) */
9063 us_maketextpoly(poly->string, win, 0, 0, NONODEINST, np->tech,
9064 var->textdescript, poly);
9065 poly->style = FILLED;
9066 dist = polydistance(poly, wantx, wanty);
9067 if (dist < 0)
9068 {
9069 if ((curhigh->status&HIGHTYPE) == HIGHTEXT && curhigh->fromgeom == NOGEOM &&
9070 (curhigh->fromvar == var || curhigh->fromport == NOPORTPROTO))
9071 {
9072 looping = 1;
9073 prevdirect.status = lastdirect.status;
9074 prevdirect.fromgeom = lastdirect.fromgeom;
9075 prevdirect.fromvar = lastdirect.fromvar;
9076 prevdirect.fromvarnoeval = lastdirect.fromvarnoeval;
9077 prevdirect.fromport = lastdirect.fromport;
9078 }
9079 lastdirect.status = HIGHTEXT;
9080 lastdirect.fromgeom = NOGEOM;
9081 lastdirect.fromport = NOPORTPROTO;
9082 lastdirect.fromvar = var;
9083 lastdirect.fromvarnoeval = varnoeval;
9084 if (dist < bestdist)
9085 {
9086 bestdirect.status = HIGHTEXT;
9087 bestdirect.fromgeom = NOGEOM;
9088 bestdirect.fromvar = var;
9089 bestdirect.fromvarnoeval = varnoeval;
9090 bestdirect.fromport = NOPORTPROTO;
9091 }
9092 }
9093
9094 /* see if it is closer than others */
9095 if (dist < bestdist)
9096 {
9097 best.status = HIGHTEXT;
9098 best.fromgeom = NOGEOM;
9099 best.fromvar = var;
9100 best.fromvarnoeval = varnoeval;
9101 best.fromport = NOPORTPROTO;
9102 bestdist = dist;
9103 }
9104 }
9105
9106 /* use best direct hit if one exists, otherwise best any-kind-of-hit */
9107 if (bestdirect.status != 0)
9108 {
9109 curhigh->status = bestdirect.status;
9110 curhigh->fromgeom = bestdirect.fromgeom;
9111 curhigh->fromvar = bestdirect.fromvar;
9112 curhigh->fromvarnoeval = bestdirect.fromvarnoeval;
9113 curhigh->fromport = bestdirect.fromport;
9114 curhigh->snapx = bestdirect.snapx;
9115 curhigh->snapy = bestdirect.snapy;
9116 } else
9117 {
9118 if (under == 0)
9119 {
9120 curhigh->status = best.status;
9121 curhigh->fromgeom = best.fromgeom;
9122 curhigh->fromvar = best.fromvar;
9123 curhigh->fromvarnoeval = best.fromvarnoeval;
9124 curhigh->fromport = best.fromport;
9125 curhigh->snapx = best.snapx;
9126 curhigh->snapy = best.snapy;
9127 } else
9128 {
9129 curhigh->status = 0;
9130 curhigh->fromgeom = NOGEOM;
9131 curhigh->fromvar = NOVARIABLE;
9132 curhigh->fromvarnoeval = NOVARIABLE;
9133 curhigh->fromport = NOPORTPROTO;
9134 curhigh->frompoint = 0;
9135 }
9136 }
9137
9138 /* see if looping through direct hits */
9139 if (looping != 0)
9140 {
9141 /* made direct hit on previously selected object: looping through */
9142 if (prevdirect.status != 0)
9143 {
9144 curhigh->status = prevdirect.status;
9145 curhigh->fromgeom = prevdirect.fromgeom;
9146 curhigh->fromvar = prevdirect.fromvar;
9147 curhigh->fromvarnoeval = prevdirect.fromvarnoeval;
9148 curhigh->fromport = prevdirect.fromport;
9149 curhigh->snapx = prevdirect.snapx;
9150 curhigh->snapy = prevdirect.snapy;
9151 } else if (lastdirect.status != 0)
9152 {
9153 curhigh->status = lastdirect.status;
9154 curhigh->fromgeom = lastdirect.fromgeom;
9155 curhigh->fromvar = lastdirect.fromvar;
9156 curhigh->fromvarnoeval = lastdirect.fromvarnoeval;
9157 curhigh->fromport = lastdirect.fromport;
9158 curhigh->snapx = lastdirect.snapx;
9159 curhigh->snapy = lastdirect.snapy;
9160 }
9161 }
9162
9163 if (curhigh->fromgeom == NOGEOM) curhigh->cell = np; else
9164 curhigh->cell = geomparent(curhigh->fromgeom);
9165
9166 /* quit now if nothing found */
9167 if (curhigh->status == 0) return;
9168
9169 /* reevaluate if this is code */
9170 if ((curhigh->status&HIGHTYPE) == HIGHTEXT && curhigh->fromvar != NOVARIABLE &&
9171 curhigh->fromvarnoeval != NOVARIABLE &&
9172 curhigh->fromvar != curhigh->fromvarnoeval)
9173 curhigh->fromvar = evalvar(curhigh->fromvarnoeval, 0, 0);
9174
9175 /* find the closest port if this is a nodeinst and no port hit directly */
9176 if ((curhigh->status&HIGHTYPE) == HIGHFROM && curhigh->fromgeom->entryisnode &&
9177 curhigh->fromport == NOPORTPROTO)
9178 {
9179 ni = curhigh->fromgeom->entryaddr.ni;
9180 for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
9181 {
9182 shapeportpoly(ni, pp, poly, FALSE);
9183
9184 /* get distance of desired point to polygon */
9185 dist = polydistance(poly, wantx, wanty);
9186 if (dist < 0)
9187 {
9188 curhigh->fromport = pp;
9189 break;
9190 }
9191 if (curhigh->fromport == NOPORTPROTO) bestdist = dist;
9192 if (dist > bestdist) continue;
9193 bestdist = dist; curhigh->fromport = pp;
9194 }
9195 }
9196 }
9197
9198 /*
9199 * routine to search cell "np" for "far text" objects that are close to (wantx, wanty)
9200 * in window "win". Those that are found are passed to "us_checkoutobject"
9201 * for proximity evaluation, along with the evaluation parameters "curhigh",
9202 * "best", "bestdirect", "lastdirect", "prevdirect", "looping", "bestdist",
9203 * "exclusively", "another", "findport", and "under". The "phase" value ranges
9204 * from 0 to 2 according to the type of object desired.
9205 */
us_fartextsearch(NODEPROTO * np,INTBIG exclusively,INTBIG another,INTBIG findport,INTBIG under,INTBIG findspecial,HIGHLIGHT * curhigh,HIGHLIGHT * best,HIGHLIGHT * bestdirect,HIGHLIGHT * lastdirect,HIGHLIGHT * prevdirect,INTBIG * looping,INTBIG * bestdist,INTBIG wantx,INTBIG wanty,WINDOWPART * win,INTBIG phase)9206 void us_fartextsearch(NODEPROTO *np, INTBIG exclusively, INTBIG another, INTBIG findport,
9207 INTBIG under, INTBIG findspecial, HIGHLIGHT *curhigh, HIGHLIGHT *best, HIGHLIGHT *bestdirect,
9208 HIGHLIGHT *lastdirect, HIGHLIGHT *prevdirect, INTBIG *looping, INTBIG *bestdist,
9209 INTBIG wantx, INTBIG wanty, WINDOWPART *win, INTBIG phase)
9210 {
9211 REGISTER NODEINST *ni;
9212 REGISTER ARCINST *ai;
9213
9214 switch (phase)
9215 {
9216 case 0: /* only allow complex nodes */
9217 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
9218 {
9219 if ((ni->userbits&NHASFARTEXT) == 0) continue;
9220 if (ni->proto->primindex != 0) continue;
9221 us_checkoutobject(ni->geom, 1, exclusively, another, findport, findspecial,
9222 curhigh, best, bestdirect, lastdirect, prevdirect, looping,
9223 bestdist, wantx, wanty, win);
9224 }
9225 break;
9226 case 1: /* only allow arcs */
9227 for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
9228 {
9229 if ((ai->userbits&AHASFARTEXT) == 0) continue;
9230 us_checkoutobject(ai->geom, 1, exclusively, another, findport, findspecial,
9231 curhigh, best, bestdirect, lastdirect, prevdirect, looping,
9232 bestdist, wantx, wanty, win);
9233 }
9234 break;
9235 case 2: /* only allow primitive nodes */
9236 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
9237 {
9238 if ((ni->userbits&NHASFARTEXT) == 0) continue;
9239 if (ni->proto->primindex == 0) continue;
9240 us_checkoutobject(ni->geom, 1, exclusively, another, findport, findspecial,
9241 curhigh, best, bestdirect, lastdirect, prevdirect, looping,
9242 bestdist, wantx, wanty, win);
9243 }
9244 break;
9245 }
9246 }
9247
9248 /*
9249 * routine to search R-tree "rtree" for objects that are close to (wantx, wanty)
9250 * in window "win". Those that are found are passed to "us_checkoutobject"
9251 * for proximity evaluation, along with the evaluation parameters "curhigh",
9252 * "best", "bestdirect", "lastdirect", "prevdirect", "looping", "bestdist",
9253 * "exclusively", "another", "findport", and "under". The "phase" value ranges
9254 * from 0 to 2 according to the type of object desired.
9255 */
us_recursivelysearch(RTNODE * rtree,INTBIG exclusively,INTBIG another,INTBIG findport,INTBIG under,INTBIG findspecial,HIGHLIGHT * curhigh,HIGHLIGHT * best,HIGHLIGHT * bestdirect,HIGHLIGHT * lastdirect,HIGHLIGHT * prevdirect,INTBIG * looping,INTBIG * bestdist,INTBIG wantx,INTBIG wanty,WINDOWPART * win,INTBIG phase)9256 void us_recursivelysearch(RTNODE *rtree, INTBIG exclusively, INTBIG another, INTBIG findport,
9257 INTBIG under, INTBIG findspecial, HIGHLIGHT *curhigh, HIGHLIGHT *best, HIGHLIGHT *bestdirect,
9258 HIGHLIGHT *lastdirect, HIGHLIGHT *prevdirect, INTBIG *looping, INTBIG *bestdist,
9259 INTBIG wantx, INTBIG wanty, WINDOWPART *win, INTBIG phase)
9260 {
9261 REGISTER GEOM *geom;
9262 REGISTER INTBIG i, bestrt;
9263 BOOLEAN found;
9264 REGISTER INTBIG disttort, bestdisttort, slop, directhitdist;
9265 INTBIG lx, hx, ly, hy;
9266
9267 found = FALSE;
9268 bestdisttort = MAXINTBIG;
9269 slop = el_curlib->lambda[el_curtech->techindex] * FARTEXTLIMIT;
9270 directhitdist = muldiv(EXACTSELECTDISTANCE, win->screenhx - win->screenlx, win->usehx - win->uselx);
9271 if (directhitdist > slop) slop = directhitdist;
9272 for(i=0; i<rtree->total; i++)
9273 {
9274 db_rtnbbox(rtree, i, &lx, &hx, &ly, &hy);
9275
9276 /* accumulate best R-tree module in case none are direct hits */
9277 disttort = abs(wantx - (lx+hx)/2) + abs(wanty - (ly+hy)/2);
9278 if (disttort < bestdisttort)
9279 {
9280 bestdisttort = disttort;
9281 bestrt = i;
9282 }
9283
9284 /* see if this R-tree node is a direct hit */
9285 if (exclusively == 0 &&
9286 (lx > wantx+slop || hx < wantx-slop || ly > wanty+slop || hy < wanty-slop)) continue;
9287 found = TRUE;
9288
9289 /* search it */
9290 if (rtree->flag != 0)
9291 {
9292 if (stopping(STOPREASONSELECT)) break;
9293 geom = (GEOM *)rtree->pointers[i];
9294 switch (phase)
9295 {
9296 case 0: /* only allow complex nodes */
9297 if (!geom->entryisnode) break;
9298 if (geom->entryaddr.ni->proto->primindex != 0) break;
9299 us_checkoutobject(geom, 0, exclusively, another, findport, findspecial,
9300 curhigh, best, bestdirect, lastdirect, prevdirect, looping,
9301 bestdist, wantx, wanty, win);
9302 break;
9303 case 1: /* only allow arcs */
9304 if (geom->entryisnode) break;
9305 us_checkoutobject(geom, 0, exclusively, another, findport, findspecial,
9306 curhigh, best, bestdirect, lastdirect, prevdirect, looping,
9307 bestdist, wantx, wanty, win);
9308 break;
9309 case 2: /* only allow primitive nodes */
9310 if (!geom->entryisnode) break;
9311 if (geom->entryaddr.ni->proto->primindex == 0) break;
9312 us_checkoutobject(geom, 0, exclusively, another, findport, findspecial,
9313 curhigh, best, bestdirect, lastdirect, prevdirect, looping,
9314 bestdist, wantx, wanty, win);
9315 break;
9316 }
9317 } else us_recursivelysearch((RTNODE *)rtree->pointers[i], exclusively,
9318 another, findport, under, findspecial, curhigh, best, bestdirect, lastdirect,
9319 prevdirect, looping, bestdist, wantx, wanty, win, phase);
9320 }
9321
9322 if (found) return;
9323 if (bestdisttort == MAXINTBIG) return;
9324 if (under != 0) return;
9325
9326 /* nothing found, use the closest */
9327 if (rtree->flag != 0)
9328 {
9329 geom = (GEOM *)rtree->pointers[bestrt];
9330 switch (phase)
9331 {
9332 case 0: /* only allow complex nodes */
9333 if (!geom->entryisnode) break;
9334 if (geom->entryaddr.ni->proto->primindex != 0) break;
9335 us_checkoutobject(geom, 0, exclusively, another, findport, findspecial,
9336 curhigh, best, bestdirect, lastdirect, prevdirect, looping,
9337 bestdist, wantx, wanty, win);
9338 break;
9339 case 1: /* only allow arcs */
9340 if (geom->entryisnode) break;
9341 us_checkoutobject(geom, 0, exclusively, another, findport, findspecial,
9342 curhigh, best, bestdirect, lastdirect, prevdirect, looping,
9343 bestdist, wantx, wanty, win);
9344 break;
9345 case 2: /* only allow primitive nodes */
9346 if (!geom->entryisnode) break;
9347 if (geom->entryaddr.ni->proto->primindex == 0) break;
9348 us_checkoutobject(geom, 0, exclusively, another, findport, findspecial,
9349 curhigh, best, bestdirect, lastdirect, prevdirect, looping,
9350 bestdist, wantx, wanty, win);
9351 break;
9352 }
9353 } else us_recursivelysearch((RTNODE *)rtree->pointers[bestrt], exclusively,
9354 another, findport, under, findspecial, curhigh, best, bestdirect, lastdirect,
9355 prevdirect, looping, bestdist, wantx, wanty, win, phase);
9356 }
9357
9358 /*
9359 * search helper routine to include object "geom" in the search for the
9360 * closest object to the cursor position at (wantx, wanty) in window "win".
9361 * If "fartext" is nonzero, only look for far-away text on the object.
9362 * If "exclusively" is nonzero, ignore nodes or arcs that are not of the
9363 * current type. If "another" is nonzero, ignore text objects. If "findport"
9364 * is nonzero, ports are being selected so cell names should not. The closest
9365 * object is "*bestdist" away and is described in "best". The closest direct
9366 * hit is in "bestdirect". If that direct hit is the same as the last hit
9367 * (kept in "curhigh") then the last direct hit (kept in "lastdirect") is
9368 * moved to the previous direct hit (kept in "prevdirect") and the "looping"
9369 * flag is set. This indicates that the "prevdirect" object should be used
9370 * (if it exists) and that the "lastdirect" object should be used failing that.
9371 */
us_checkoutobject(GEOM * geom,INTBIG fartext,INTBIG exclusively,INTBIG another,INTBIG findport,INTBIG findspecial,HIGHLIGHT * curhigh,HIGHLIGHT * best,HIGHLIGHT * bestdirect,HIGHLIGHT * lastdirect,HIGHLIGHT * prevdirect,INTBIG * looping,INTBIG * bestdist,INTBIG wantx,INTBIG wanty,WINDOWPART * win)9372 void us_checkoutobject(GEOM *geom, INTBIG fartext, INTBIG exclusively, INTBIG another,
9373 INTBIG findport, INTBIG findspecial, HIGHLIGHT *curhigh, HIGHLIGHT *best,
9374 HIGHLIGHT *bestdirect, HIGHLIGHT *lastdirect, HIGHLIGHT *prevdirect, INTBIG *looping,
9375 INTBIG *bestdist, INTBIG wantx, INTBIG wanty, WINDOWPART *win)
9376 {
9377 REGISTER PORTPROTO *pp;
9378 VARIABLE *var, *varnoeval;
9379 REGISTER NODEINST *ni;
9380 REGISTER ARCINST *ai;
9381 static POLYGON *poly = NOPOLYGON;
9382 PORTPROTO *port;
9383 REGISTER INTBIG i, dist, directhitdist;
9384
9385 /* get polygon */
9386 (void)needstaticpolygon(&poly, 4, us_tool->cluster);
9387
9388 /* compute threshold for direct hits */
9389 directhitdist = muldiv(EXACTSELECTDISTANCE, win->screenhx - win->screenlx, win->usehx - win->uselx);
9390
9391 if (geom->entryisnode)
9392 {
9393 /* examine a node object */
9394 ni = geom->entryaddr.ni;
9395
9396 /* do not "find" hard-to-find nodes if "findspecial" is not set */
9397 if (findspecial == 0 && (ni->userbits&HARDSELECTN) != 0) return;
9398
9399 /* do not include primitives that have all layers invisible */
9400 if (ni->proto->primindex != 0 && (ni->proto->userbits&NINVISIBLE) != 0)
9401 return;
9402
9403 /* skip if being exclusive */
9404 if (exclusively != 0 && ni->proto != us_curnodeproto) return;
9405
9406 /* try text on the node (if not searching for "another") */
9407 if (another == 0 && exclusively == 0)
9408 {
9409 us_initnodetext(ni, findspecial, win);
9410 for(;;)
9411 {
9412 if (us_getnodetext(ni, win, poly, &var, &varnoeval, &port)) break;
9413
9414 /* get distance of desired point to polygon */
9415 dist = polydistance(poly, wantx, wanty);
9416
9417 /* direct hit */
9418 if (dist < directhitdist)
9419 {
9420 if (curhigh->fromgeom == geom && (curhigh->status&HIGHTYPE) == HIGHTEXT &&
9421 curhigh->fromvar == var && curhigh->fromport == port)
9422 {
9423 *looping = 1;
9424 prevdirect->status = lastdirect->status;
9425 prevdirect->fromgeom = lastdirect->fromgeom;
9426 prevdirect->fromvar = lastdirect->fromvar;
9427 prevdirect->fromvarnoeval = lastdirect->fromvarnoeval;
9428 prevdirect->fromport = lastdirect->fromport;
9429 }
9430 lastdirect->status = HIGHTEXT;
9431 lastdirect->fromgeom = geom;
9432 lastdirect->fromport = port;
9433 lastdirect->fromvar = var;
9434 lastdirect->fromvarnoeval = varnoeval;
9435 if (dist < *bestdist)
9436 {
9437 bestdirect->status = HIGHTEXT;
9438 bestdirect->fromgeom = geom;
9439 bestdirect->fromvar = var;
9440 bestdirect->fromvarnoeval = varnoeval;
9441 bestdirect->fromport = port;
9442 }
9443 }
9444
9445 /* see if it is closer than others */
9446 if (dist < *bestdist)
9447 {
9448 best->status = HIGHTEXT;
9449 best->fromgeom = geom;
9450 best->fromvar = var;
9451 best->fromvarnoeval = varnoeval;
9452 best->fromport = port;
9453 *bestdist = dist;
9454 }
9455 }
9456 }
9457
9458 if (fartext != 0) return;
9459
9460 /* do not "find" Invisible-Pins if they have text or exports */
9461 if (ni->proto == gen_invispinprim)
9462 {
9463 if (ni->firstportexpinst != NOPORTEXPINST) return;
9464 for(i=0; i<ni->numvar; i++)
9465 {
9466 var = &ni->firstvar[i];
9467 if ((var->type&VDISPLAY) != 0) return;
9468 }
9469 }
9470
9471 /* get the distance to the object */
9472 dist = us_disttoobject(wantx, wanty, geom);
9473
9474 /* direct hit */
9475 if (dist < directhitdist)
9476 {
9477 if (curhigh->fromgeom == geom && (curhigh->status&HIGHTYPE) != HIGHTEXT)
9478 {
9479 *looping = 1;
9480 prevdirect->status = lastdirect->status;
9481 prevdirect->fromgeom = lastdirect->fromgeom;
9482 prevdirect->fromvar = lastdirect->fromvar;
9483 prevdirect->fromvarnoeval = lastdirect->fromvarnoeval;
9484 prevdirect->fromport = lastdirect->fromport;
9485 prevdirect->snapx = lastdirect->snapx;
9486 prevdirect->snapy = lastdirect->snapy;
9487
9488 /* see if there is another port under the cursor */
9489 if (curhigh->fromport != NOPORTPROTO)
9490 {
9491 for(pp = curhigh->fromport->nextportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
9492 {
9493 shapeportpoly(ni, pp, poly, FALSE);
9494 if (isinside(wantx, wanty, poly))
9495 {
9496 prevdirect->status = HIGHFROM;
9497 prevdirect->fromgeom = geom;
9498 prevdirect->fromport = pp;
9499 break;
9500 }
9501 }
9502 }
9503 }
9504 lastdirect->status = HIGHFROM;
9505 lastdirect->fromgeom = geom;
9506 lastdirect->fromport = NOPORTPROTO;
9507 us_selectsnap(lastdirect, wantx, wanty);
9508 if (dist < *bestdist)
9509 {
9510 bestdirect->status = HIGHFROM;
9511 bestdirect->fromgeom = geom;
9512 bestdirect->fromport = NOPORTPROTO;
9513 us_selectsnap(bestdirect, wantx, wanty);
9514 }
9515 }
9516
9517 /* see if it is closer than others */
9518 if (dist < *bestdist)
9519 {
9520 best->status = HIGHFROM;
9521 best->fromgeom = geom;
9522 best->fromport = NOPORTPROTO;
9523 us_selectsnap(best, wantx, wanty);
9524 *bestdist = dist;
9525 }
9526 } else
9527 {
9528 /* examine an arc object */
9529 ai = geom->entryaddr.ai;
9530
9531 /* do not "find" hard-to-find arcs if "findspecial" is not set */
9532 if (findspecial == 0 && (ai->userbits&HARDSELECTA) != 0) return;
9533
9534 /* do not include arcs that have all layers invisible */
9535 if ((ai->proto->userbits&AINVISIBLE) != 0) return;
9536
9537 /* skip if being exclusive */
9538 if (exclusively != 0 && ai->proto != us_curarcproto) return;
9539
9540 /* try text on the arc (if not searching for "another") */
9541 if (exclusively == 0)
9542 {
9543 us_initarctext(ai, findspecial, win);
9544 for(;;)
9545 {
9546 if (us_getarctext(ai, win, poly, &var, &varnoeval)) break;
9547
9548 /* get distance of desired point to polygon */
9549 dist = polydistance(poly, wantx, wanty);
9550
9551 /* direct hit */
9552 if (dist < directhitdist)
9553 {
9554 if (curhigh->fromgeom == geom && (curhigh->status&HIGHTYPE) == HIGHTEXT &&
9555 curhigh->fromvar == var)
9556 {
9557 *looping = 1;
9558 prevdirect->status = lastdirect->status;
9559 prevdirect->fromgeom = lastdirect->fromgeom;
9560 prevdirect->fromvar = lastdirect->fromvar;
9561 prevdirect->fromvarnoeval = lastdirect->fromvarnoeval;
9562 prevdirect->fromport = lastdirect->fromport;
9563 }
9564 lastdirect->status = HIGHTEXT;
9565 lastdirect->fromgeom = geom;
9566 lastdirect->fromvar = var;
9567 lastdirect->fromvarnoeval = varnoeval;
9568 lastdirect->fromport = NOPORTPROTO;
9569 if (dist < *bestdist)
9570 {
9571 bestdirect->status = HIGHTEXT;
9572 bestdirect->fromgeom = geom;
9573 bestdirect->fromvar = var;
9574 bestdirect->fromvarnoeval = varnoeval;
9575 us_selectsnap(bestdirect, wantx, wanty);
9576 bestdirect->fromport = NOPORTPROTO;
9577 }
9578 }
9579
9580 /* see if it is closer than others */
9581 if (dist < *bestdist)
9582 {
9583 best->status = HIGHTEXT;
9584 best->fromgeom = geom;
9585 best->fromvar = var;
9586 best->fromvarnoeval = varnoeval;
9587 best->fromport = NOPORTPROTO;
9588 us_selectsnap(best, wantx, wanty);
9589 *bestdist = dist;
9590 }
9591 }
9592 }
9593
9594 if (fartext != 0) return;
9595
9596 /* get distance to arc */
9597 dist = us_disttoobject(wantx, wanty, geom);
9598
9599 /* direct hit */
9600 if (dist < directhitdist)
9601 {
9602 if (curhigh->fromgeom == geom && (curhigh->status&HIGHTYPE) != HIGHTEXT)
9603 {
9604 *looping = 1;
9605 prevdirect->status = lastdirect->status;
9606 prevdirect->fromgeom = lastdirect->fromgeom;
9607 prevdirect->fromvar = lastdirect->fromvar;
9608 prevdirect->fromvarnoeval = lastdirect->fromvarnoeval;
9609 prevdirect->fromport = lastdirect->fromport;
9610 prevdirect->snapx = lastdirect->snapx;
9611 prevdirect->snapy = lastdirect->snapy;
9612 }
9613 lastdirect->status = HIGHFROM;
9614 lastdirect->fromgeom = geom;
9615 lastdirect->fromport = NOPORTPROTO;
9616 us_selectsnap(lastdirect, wantx, wanty);
9617 if (dist < *bestdist)
9618 {
9619 bestdirect->status = HIGHFROM;
9620 bestdirect->fromgeom = geom;
9621 bestdirect->fromvar = NOVARIABLE;
9622 bestdirect->fromvarnoeval = NOVARIABLE;
9623 bestdirect->fromport = NOPORTPROTO;
9624 us_selectsnap(bestdirect, wantx, wanty);
9625 }
9626 }
9627
9628 /* see if it is closer than others */
9629 if (dist < *bestdist)
9630 {
9631 best->status = HIGHFROM;
9632 best->fromgeom = geom;
9633 best->fromvar = NOVARIABLE;
9634 best->fromvarnoeval = NOVARIABLE;
9635 best->fromport = NOPORTPROTO;
9636 us_selectsnap(best, wantx, wanty);
9637 *bestdist = dist;
9638 }
9639 }
9640 }
9641
9642 /*
9643 * routine to determine whether the cursor (xcur, ycur) is over the object in "high".
9644 */
us_cursoroverhigh(HIGHLIGHT * high,INTBIG xcur,INTBIG ycur,WINDOWPART * win)9645 BOOLEAN us_cursoroverhigh(HIGHLIGHT *high, INTBIG xcur, INTBIG ycur, WINDOWPART *win)
9646 {
9647 VARIABLE *var, *varnoeval;
9648 REGISTER NODEINST *ni;
9649 REGISTER ARCINST *ai;
9650 REGISTER NODEPROTO *np;
9651 static POLYGON *poly = NOPOLYGON;
9652 PORTPROTO *port;
9653 REGISTER INTBIG i, tot;
9654 REGISTER INTBIG directhitdist;
9655
9656 /* must be in the same cell */
9657 if (high->cell != win->curnodeproto) return(FALSE);
9658
9659 /* get polygon */
9660 (void)needstaticpolygon(&poly, 4, us_tool->cluster);
9661
9662 /* compute threshold for direct hits */
9663 directhitdist = muldiv(EXACTSELECTDISTANCE, win->screenhx - win->screenlx,
9664 win->usehx - win->uselx);
9665
9666 /* could be selected text */
9667 if ((high->status&HIGHTEXT) != 0)
9668 {
9669 if (high->fromgeom == NOGEOM)
9670 {
9671 np = high->cell;
9672 tot = tech_displayablecellvars(np, win, &tech_oneprocpolyloop);
9673 for(i=0; i<tot; i++)
9674 {
9675 var = tech_filldisplayablecellvar(np, poly, win, &varnoeval, &tech_oneprocpolyloop);
9676 if (high->fromvarnoeval != varnoeval) continue;
9677
9678 /* cell variables are offset from (0,0) */
9679 us_maketextpoly(poly->string, win, 0, 0, NONODEINST, np->tech,
9680 var->textdescript, poly);
9681 poly->style = FILLED;
9682 if (polydistance(poly, xcur, ycur) <= directhitdist) return(TRUE);
9683 }
9684 return(FALSE);
9685 }
9686
9687 /* examine all text on the object */
9688 if (high->fromgeom->entryisnode)
9689 {
9690 ni = high->fromgeom->entryaddr.ni;
9691 us_initnodetext(ni, 1, win);
9692 } else
9693 {
9694 ai = high->fromgeom->entryaddr.ai;
9695 us_initarctext(ai, 1, win);
9696 }
9697
9698 for(;;)
9699 {
9700 if (high->fromgeom->entryisnode)
9701 {
9702 if (us_getnodetext(ni, win, poly, &var, &varnoeval, &port)) break;
9703 } else
9704 {
9705 if (us_getarctext(ai, win, poly, &var, &varnoeval)) break;
9706 port = NOPORTPROTO;
9707 }
9708 if (high->fromvar != var || high->fromport != port) continue;
9709
9710 /* accept if on */
9711 if (polydistance(poly, xcur, ycur) <= directhitdist) return(TRUE);
9712 }
9713 return(FALSE);
9714 }
9715
9716 /* must be a single node or arc selected */
9717 if ((high->status&HIGHFROM) == 0) return(FALSE);
9718
9719 /* see if the point is over the object */
9720 if (us_disttoobject(xcur, ycur, high->fromgeom) <= directhitdist) return(TRUE);
9721 return(FALSE);
9722 }
9723
9724 /*
9725 * routine to add snapping selection to the highlight in "best".
9726 */
us_selectsnap(HIGHLIGHT * best,INTBIG wantx,INTBIG wanty)9727 void us_selectsnap(HIGHLIGHT *best, INTBIG wantx, INTBIG wanty)
9728 {
9729 REGISTER NODEINST *ni;
9730 REGISTER ARCINST *ai;
9731 REGISTER INTBIG j, k;
9732 INTBIG cx, cy;
9733 static POLYGON *poly = NOPOLYGON;
9734 XARRAY trans;
9735
9736 if ((us_state&SNAPMODE) == SNAPMODENONE) return;
9737
9738 /* get polygon */
9739 (void)needstaticpolygon(&poly, 4, us_tool->cluster);
9740
9741 if (best->fromgeom->entryisnode)
9742 {
9743 ni = best->fromgeom->entryaddr.ni;
9744 if (ni->proto->primindex == 0)
9745 {
9746 if ((us_state&SNAPMODE) == SNAPMODECENTER)
9747 {
9748 corneroffset(ni, ni->proto, ni->rotation, ni->transpose, &cx, &cy, FALSE);
9749 us_setbestsnappoint(best, wantx, wanty, ni->lowx+cx, ni->lowy+cy, FALSE, FALSE);
9750 }
9751 return;
9752 }
9753 makerot(ni, trans);
9754 k = nodepolys(ni, 0, NOWINDOWPART);
9755 for(j=0; j<k; j++)
9756 {
9757 shapenodepoly(ni, j, poly);
9758 xformpoly(poly, trans);
9759 us_selectsnappoly(best, poly, wantx, wanty);
9760 }
9761 } else
9762 {
9763 ai = best->fromgeom->entryaddr.ai;
9764 k = arcpolys(ai, NOWINDOWPART);
9765 for(j=0; j<k; j++)
9766 {
9767 shapearcpoly(ai, j, poly);
9768 us_selectsnappoly(best, poly, wantx, wanty);
9769 }
9770 }
9771 }
9772
us_selectsnappoly(HIGHLIGHT * best,POLYGON * poly,INTBIG wantx,INTBIG wanty)9773 void us_selectsnappoly(HIGHLIGHT *best, POLYGON *poly, INTBIG wantx, INTBIG wanty)
9774 {
9775 REGISTER INTBIG j, k, radius, sea;
9776 REGISTER BOOLEAN tan, perp;
9777 INTBIG testx, testy;
9778 REGISTER NODEINST *ni;
9779 REGISTER ARCINST *ai;
9780 REGISTER GEOM *geom;
9781 XARRAY trans;
9782 REGISTER INTBIG angle, otherang, i, last;
9783 static POLYGON *interpoly = NOPOLYGON;
9784
9785 switch (us_state&SNAPMODE)
9786 {
9787 case SNAPMODECENTER:
9788 if (poly->style == CIRCLE || poly->style == THICKCIRCLE ||
9789 poly->style == DISC || poly->style == CIRCLEARC ||
9790 poly->style == THICKCIRCLEARC)
9791 {
9792 us_setbestsnappoint(best, wantx, wanty, poly->xv[0], poly->yv[0], FALSE, FALSE);
9793 } else if (poly->style != OPENED && poly->style != OPENEDT1 && poly->style != OPENEDT2 &&
9794 poly->style != OPENEDT3 && poly->style != CLOSED)
9795 {
9796 getcenter(poly, &testx, &testy);
9797 us_setbestsnappoint(best, wantx, wanty, testx, testy, FALSE, FALSE);
9798 }
9799 break;
9800 case SNAPMODEMIDPOINT:
9801 if (poly->style == OPENED || poly->style == OPENEDT1 || poly->style == OPENEDT2 ||
9802 poly->style == OPENEDT3 || poly->style == CLOSED)
9803 {
9804 for(i=0; i<poly->count; i++)
9805 {
9806 if (i == 0)
9807 {
9808 if (poly->style != CLOSED) continue;
9809 last = poly->count - 1;
9810 } else last = i-1;
9811 testx = (poly->xv[last] + poly->xv[i]) / 2;
9812 testy = (poly->yv[last] + poly->yv[i]) / 2;
9813 us_setbestsnappoint(best, wantx, wanty, testx, testy, FALSE, FALSE);
9814 }
9815 } else if (poly->style == VECTORS)
9816 {
9817 for(i=0; i<poly->count; i += 2)
9818 {
9819 testx = (poly->xv[i+1] + poly->xv[i]) / 2;
9820 testy = (poly->yv[i+1] + poly->yv[i]) / 2;
9821 us_setbestsnappoint(best, wantx, wanty, testx, testy, FALSE, FALSE);
9822 }
9823 }
9824 break;
9825 case SNAPMODEENDPOINT:
9826 if (poly->style == OPENED || poly->style == OPENEDT1 || poly->style == OPENEDT2 ||
9827 poly->style == OPENEDT3 || poly->style == CLOSED)
9828 {
9829 for(i=0; i<poly->count; i++)
9830 us_setbestsnappoint(best, wantx, wanty, poly->xv[i], poly->yv[i], FALSE, FALSE);
9831 } else if (poly->style == VECTORS)
9832 {
9833 for(i=0; i<poly->count; i += 2)
9834 {
9835 us_setbestsnappoint(best, wantx, wanty, poly->xv[i], poly->yv[i], FALSE, FALSE);
9836 us_setbestsnappoint(best, wantx, wanty, poly->xv[i+1], poly->yv[i+1], FALSE, FALSE);
9837 }
9838 } else if (poly->style == CIRCLEARC || poly->style == THICKCIRCLEARC)
9839 {
9840 us_setbestsnappoint(best, wantx, wanty, poly->xv[1], poly->yv[1], FALSE, FALSE);
9841 us_setbestsnappoint(best, wantx, wanty, poly->xv[2], poly->yv[2], FALSE, FALSE);
9842 }
9843 break;
9844 case SNAPMODETANGENT:
9845 case SNAPMODEPERP:
9846 if (poly->style == OPENED || poly->style == OPENEDT1 || poly->style == OPENEDT2 ||
9847 poly->style == OPENEDT3 || poly->style == CLOSED)
9848 {
9849 for(i=0; i<poly->count; i++)
9850 {
9851 if (i == 0)
9852 {
9853 if (poly->style != CLOSED) continue;
9854 last = poly->count - 1;
9855 } else last = i-1;
9856 angle = figureangle(poly->xv[last],poly->yv[last], poly->xv[i],poly->yv[i]);
9857 otherang = (angle+900) % 3600;
9858 if (intersect(poly->xv[last],poly->yv[last], angle, wantx, wanty, otherang,
9859 &testx, &testy) >= 0)
9860 {
9861 if (testx >= mini(poly->xv[last], poly->xv[i]) &&
9862 testx <= maxi(poly->xv[last], poly->xv[i]) &&
9863 testy >= mini(poly->yv[last], poly->yv[i]) &&
9864 testy <= maxi(poly->yv[last], poly->yv[i]))
9865 {
9866 us_setbestsnappoint(best, wantx, wanty, testx, testy, FALSE, TRUE);
9867 }
9868 }
9869 }
9870 } else if (poly->style == VECTORS)
9871 {
9872 for(i=0; i<poly->count; i += 2)
9873 {
9874 angle = figureangle(poly->xv[i],poly->yv[i], poly->xv[i+1],poly->yv[i+1]);
9875 otherang = (angle+900) % 3600;
9876 if (intersect(poly->xv[i],poly->yv[i], angle, wantx, wanty, otherang,
9877 &testx, &testy) >= 0)
9878 {
9879 if (testx >= mini(poly->xv[i], poly->xv[i+1]) &&
9880 testx <= maxi(poly->xv[i], poly->xv[i+1]) &&
9881 testy >= mini(poly->yv[i], poly->yv[i+1]) &&
9882 testy <= maxi(poly->yv[i], poly->yv[i+1]))
9883 {
9884 us_setbestsnappoint(best, wantx, wanty, testx, testy, FALSE, TRUE);
9885 }
9886 }
9887 }
9888 } else if (poly->style == CIRCLE || poly->style == THICKCIRCLE ||
9889 poly->style == DISC || poly->style == CIRCLEARC ||
9890 poly->style == THICKCIRCLEARC)
9891 {
9892 if (poly->xv[0] == wantx && poly->yv[0] == wanty) break;
9893 angle = figureangle(poly->xv[0],poly->yv[0], wantx,wanty);
9894 radius = computedistance(poly->xv[0],poly->yv[0], poly->xv[1],poly->yv[1]);
9895 testx = poly->xv[0] + mult(radius, cosine(angle));
9896 testy = poly->yv[0] + mult(radius, sine(angle));
9897 if ((poly->style == CIRCLEARC || poly->style == THICKCIRCLEARC) &&
9898 !us_pointonarc(testx, testy, poly)) break;
9899 if ((us_state&SNAPMODE) == SNAPMODETANGENT) tan = TRUE; else tan = FALSE;
9900 if ((us_state&SNAPMODE) == SNAPMODEPERP) perp = TRUE; else perp = FALSE;
9901 us_setbestsnappoint(best, wantx, wanty, testx, testy, tan, perp);
9902 }
9903 break;
9904 case SNAPMODEQUAD:
9905 if (poly->style == CIRCLE || poly->style == THICKCIRCLE || poly->style == DISC)
9906 {
9907 radius = computedistance(poly->xv[0],poly->yv[0], poly->xv[1],poly->yv[1]);
9908 us_setbestsnappoint(best, wantx, wanty, poly->xv[0]+radius, poly->yv[0], FALSE, FALSE);
9909 us_setbestsnappoint(best, wantx, wanty, poly->xv[0]-radius, poly->yv[0], FALSE, FALSE);
9910 us_setbestsnappoint(best, wantx, wanty, poly->xv[0], poly->yv[0]+radius, FALSE, FALSE);
9911 us_setbestsnappoint(best, wantx, wanty, poly->xv[0], poly->yv[0]-radius, FALSE, FALSE);
9912 } else if (poly->style == CIRCLEARC || poly->style == THICKCIRCLEARC)
9913 {
9914 radius = computedistance(poly->xv[0],poly->yv[0], poly->xv[1],poly->yv[1]);
9915 if (us_pointonarc(poly->xv[0]+radius, poly->yv[0], poly))
9916 us_setbestsnappoint(best, wantx, wanty, poly->xv[0]+radius, poly->yv[0], FALSE, FALSE);
9917 if (us_pointonarc(poly->xv[0]-radius, poly->yv[0], poly))
9918 us_setbestsnappoint(best, wantx, wanty, poly->xv[0]-radius, poly->yv[0], FALSE, FALSE);
9919 if (us_pointonarc(poly->xv[0], poly->yv[0]+radius, poly))
9920 us_setbestsnappoint(best, wantx, wanty, poly->xv[0], poly->yv[0]+radius, FALSE, FALSE);
9921 if (us_pointonarc(poly->xv[0], poly->yv[0]-radius, poly))
9922 us_setbestsnappoint(best, wantx, wanty, poly->xv[0], poly->yv[0]-radius, FALSE, FALSE);
9923 }
9924 break;
9925 case SNAPMODEINTER:
9926 /* get intersection polygon */
9927 (void)needstaticpolygon(&interpoly, 4, us_tool->cluster);
9928
9929 /* search in area around this object */
9930 sea = initsearch(best->fromgeom->lowx, best->fromgeom->highx, best->fromgeom->lowy,
9931 best->fromgeom->highy, geomparent(best->fromgeom));
9932 for(;;)
9933 {
9934 geom = nextobject(sea);
9935 if (geom == NOGEOM) break;
9936 if (geom == best->fromgeom) continue;
9937 if (geom->entryisnode)
9938 {
9939 ni = geom->entryaddr.ni;
9940 if (ni->proto->primindex == 0) continue;
9941 makerot(ni, trans);
9942 k = nodepolys(ni, 0, NOWINDOWPART);
9943 for(j=0; j<k; j++)
9944 {
9945 shapenodepoly(ni, j, interpoly);
9946 xformpoly(interpoly, trans);
9947 us_intersectsnappoly(best, poly, interpoly, wantx, wanty);
9948 }
9949 } else
9950 {
9951 ai = geom->entryaddr.ai;
9952 k = arcpolys(ai, NOWINDOWPART);
9953 for(j=0; j<k; j++)
9954 {
9955 shapearcpoly(ai, j, interpoly);
9956 us_intersectsnappoly(best, poly, interpoly, wantx, wanty);
9957 }
9958 }
9959 }
9960 break;
9961 }
9962 }
9963
9964 /*
9965 * routine to find the intersection between polygons "poly" and "interpoly" and set this as the snap
9966 * point in highlight "best" (the cursor is at (wantx,wanty).
9967 */
us_intersectsnappoly(HIGHLIGHT * best,POLYGON * poly,POLYGON * interpoly,INTBIG wantx,INTBIG wanty)9968 void us_intersectsnappoly(HIGHLIGHT *best, POLYGON *poly, POLYGON *interpoly, INTBIG wantx, INTBIG wanty)
9969 {
9970 REGISTER POLYGON *swappoly;
9971 REGISTER INTBIG i, last;
9972
9973 if (interpoly->style == OPENED || interpoly->style == OPENEDT1 || interpoly->style == OPENEDT2 ||
9974 interpoly->style == OPENEDT3 || interpoly->style == CLOSED || interpoly->style == VECTORS)
9975 {
9976 swappoly = poly; poly = interpoly; interpoly = swappoly;
9977 }
9978
9979 if (poly->style == OPENED || poly->style == OPENEDT1 || poly->style == OPENEDT2 ||
9980 poly->style == OPENEDT3 || poly->style == CLOSED)
9981 {
9982 for(i=0; i<poly->count; i++)
9983 {
9984 if (i == 0)
9985 {
9986 if (poly->style != CLOSED) continue;
9987 last = poly->count - 1;
9988 } else last = i-1;
9989 us_intersectsnapline(best, poly->xv[last],poly->yv[last], poly->xv[i],poly->yv[i],
9990 interpoly, wantx, wanty);
9991 }
9992 return;
9993 }
9994 if (poly->style == VECTORS)
9995 {
9996 for(i=0; i<poly->count; i += 2)
9997 {
9998 us_intersectsnapline(best, poly->xv[i],poly->yv[i], poly->xv[i+1],poly->yv[i+1],
9999 interpoly, wantx, wanty);
10000 }
10001 return;
10002 }
10003 }
10004
10005 /*
10006 * routine to find the intersection between the line from (x1,y1) to (x2,y2) and polygon "interpoly".
10007 * This is set this as the snap point in highlight "best" (the cursor is at (wantx,wanty).
10008 */
us_intersectsnapline(HIGHLIGHT * best,INTBIG x1,INTBIG y1,INTBIG x2,INTBIG y2,POLYGON * interpoly,INTBIG wantx,INTBIG wanty)10009 void us_intersectsnapline(HIGHLIGHT *best, INTBIG x1, INTBIG y1, INTBIG x2, INTBIG y2,
10010 POLYGON *interpoly, INTBIG wantx, INTBIG wanty)
10011 {
10012 REGISTER INTBIG i, last, angle, interangle;
10013 INTBIG ix, iy, ix1, iy1, ix2, iy2;
10014
10015 angle = figureangle(x1,y1, x2,y2);
10016 if (interpoly->style == OPENED || interpoly->style == OPENEDT1 || interpoly->style == OPENEDT2 ||
10017 interpoly->style == OPENEDT3 || interpoly->style == CLOSED)
10018 {
10019 for(i=0; i<interpoly->count; i++)
10020 {
10021 if (i == 0)
10022 {
10023 if (interpoly->style != CLOSED) continue;
10024 last = interpoly->count - 1;
10025 } else last = i-1;
10026 interangle = figureangle(interpoly->xv[last],interpoly->yv[last], interpoly->xv[i],interpoly->yv[i]);
10027 if (intersect(x1,y1, angle, interpoly->xv[last],interpoly->yv[last], interangle, &ix, &iy) < 0)
10028 continue;
10029 if (ix < mini(x1,x2)) continue;
10030 if (ix > maxi(x1,x2)) continue;
10031 if (iy < mini(y1,y2)) continue;
10032 if (iy > maxi(y1,y2)) continue;
10033 if (ix < mini(interpoly->xv[last],interpoly->xv[i])) continue;
10034 if (ix > maxi(interpoly->xv[last],interpoly->xv[i])) continue;
10035 if (iy < mini(interpoly->yv[last],interpoly->yv[i])) continue;
10036 if (iy > maxi(interpoly->yv[last],interpoly->yv[i])) continue;
10037 us_setbestsnappoint(best, wantx, wanty, ix, iy, FALSE, FALSE);
10038 }
10039 return;
10040 }
10041 if (interpoly->style == VECTORS)
10042 {
10043 for(i=0; i<interpoly->count; i += 2)
10044 {
10045 interangle = figureangle(interpoly->xv[i],interpoly->yv[i], interpoly->xv[i+1],interpoly->yv[i+1]);
10046 if (intersect(x1,y1, angle, interpoly->xv[i],interpoly->yv[i], interangle, &ix, &iy) < 0)
10047 continue;
10048 if (ix < mini(x1,x2)) continue;
10049 if (ix > maxi(x1,x2)) continue;
10050 if (iy < mini(y1,y2)) continue;
10051 if (iy > maxi(y1,y2)) continue;
10052 if (ix < mini(interpoly->xv[i],interpoly->xv[i+1])) continue;
10053 if (ix > maxi(interpoly->xv[i],interpoly->xv[i+1])) continue;
10054 if (iy < mini(interpoly->yv[i],interpoly->yv[i+1])) continue;
10055 if (iy > maxi(interpoly->yv[i],interpoly->yv[i+1])) continue;
10056 us_setbestsnappoint(best, wantx, wanty, ix, iy, FALSE, FALSE);
10057 }
10058 return;
10059 }
10060 if (interpoly->style == CIRCLEARC || interpoly->style == THICKCIRCLEARC)
10061 {
10062 i = circlelineintersection(interpoly->xv[0], interpoly->yv[0], interpoly->xv[1], interpoly->yv[1],
10063 x1, y1, x2, y2, &ix1, &iy1, &ix2, &iy2, 0);
10064 if (i >= 1)
10065 {
10066 if (us_pointonarc(ix1, iy1, interpoly))
10067 us_setbestsnappoint(best, wantx, wanty, ix1, iy1, FALSE, FALSE);
10068 if (i >= 2)
10069 {
10070 if (us_pointonarc(ix2, iy2, interpoly))
10071 us_setbestsnappoint(best, wantx, wanty, ix2, iy2, FALSE, FALSE);
10072 }
10073 }
10074 return;
10075 }
10076 if (interpoly->style == CIRCLE || interpoly->style == THICKCIRCLE)
10077 {
10078 i = circlelineintersection(interpoly->xv[0], interpoly->yv[0], interpoly->xv[1], interpoly->yv[1],
10079 x1, y1, x2, y2, &ix1, &iy1, &ix2, &iy2, 0);
10080 if (i >= 1)
10081 {
10082 us_setbestsnappoint(best, wantx, wanty, ix1, iy1, FALSE, FALSE);
10083 if (i >= 2)
10084 {
10085 us_setbestsnappoint(best, wantx, wanty, ix2, iy2, FALSE, FALSE);
10086 }
10087 }
10088 return;
10089 }
10090 }
10091
10092 /*
10093 * Routine to adjust the two highlight modules "firsthigh" and "secondhigh" to account for the
10094 * fact that one or both has a tangent snap point that must be tangent to the other's snap point.
10095 */
us_adjusttangentsnappoints(HIGHLIGHT * firsthigh,HIGHLIGHT * secondhigh)10096 void us_adjusttangentsnappoints(HIGHLIGHT *firsthigh, HIGHLIGHT *secondhigh)
10097 {
10098 INTBIG fx, fy, sx, sy, pfx[4], pfy[4], psx[4], psy[4], ix1, iy1, ix2, iy2;
10099 REGISTER INTBIG frad, srad, rad, dist, bestdist;
10100 REGISTER INTBIG j, k, dps, bestone;
10101 double ang, oang, dx, dy;
10102 static POLYGON *firstpoly = NOPOLYGON, *secondpoly = NOPOLYGON;
10103 POLYGON *swappoly;
10104 HIGHLIGHT *swaphighlight;
10105 REGISTER NODEINST *ni;
10106 XARRAY trans;
10107
10108 /* get polygon describing first object */
10109 if ((firsthigh->status&HIGHSNAPTAN) != 0)
10110 {
10111 (void)needstaticpolygon(&firstpoly, 4, us_tool->cluster);
10112 if (!firsthigh->fromgeom->entryisnode) return;
10113 ni = firsthigh->fromgeom->entryaddr.ni;
10114 if (ni->proto->primindex == 0) return;
10115 makerot(ni, trans);
10116 k = nodepolys(ni, 0, NOWINDOWPART);
10117 for(j=0; j<k; j++)
10118 {
10119 shapenodepoly(ni, j, firstpoly);
10120 if (firstpoly->style == CIRCLEARC || firstpoly->style == THICKCIRCLEARC ||
10121 firstpoly->style == CIRCLE || firstpoly->style == THICKCIRCLE ||
10122 firstpoly->style == DISC) break;
10123 }
10124 if (j >= k) return;
10125 xformpoly(firstpoly, trans);
10126 }
10127
10128 /* get polygon describing second object */
10129 if ((secondhigh->status&HIGHSNAPTAN) != 0)
10130 {
10131 (void)needstaticpolygon(&secondpoly, 4, us_tool->cluster);
10132 if (!secondhigh->fromgeom->entryisnode) return;
10133 ni = secondhigh->fromgeom->entryaddr.ni;
10134 if (ni->proto->primindex == 0) return;
10135 makerot(ni, trans);
10136 k = nodepolys(ni, 0, NOWINDOWPART);
10137 for(j=0; j<k; j++)
10138 {
10139 shapenodepoly(ni, j, secondpoly);
10140 if (secondpoly->style == CIRCLEARC || secondpoly->style == THICKCIRCLEARC ||
10141 secondpoly->style == CIRCLE || secondpoly->style == THICKCIRCLE ||
10142 secondpoly->style == DISC) break;
10143 }
10144 if (j >= k) return;
10145 xformpoly(secondpoly, trans);
10146 }
10147
10148 if ((firsthigh->status&HIGHSNAPTAN) != 0)
10149 {
10150 if ((secondhigh->status&HIGHSNAPTAN) != 0)
10151 {
10152 /* tangent on both curves: find radii and make sure first is larger */
10153 frad = computedistance(firstpoly->xv[0], firstpoly->yv[0],
10154 firstpoly->xv[1], firstpoly->yv[1]);
10155 srad = computedistance(secondpoly->xv[0], secondpoly->yv[0],
10156 secondpoly->xv[1], secondpoly->yv[1]);
10157 if (frad < srad)
10158 {
10159 swappoly = firstpoly; firstpoly = secondpoly; secondpoly = swappoly;
10160 swaphighlight = firsthigh; firsthigh = secondhigh; secondhigh = swaphighlight;
10161 rad = frad; frad = srad; srad = rad;
10162 }
10163
10164 /* find tangent lines along outside of two circles */
10165 dps = 0;
10166 if (frad == srad)
10167 {
10168 /* special case when radii are equal: construct simple outside tangent lines */
10169 dx = (double)(secondpoly->xv[0]-firstpoly->xv[0]);
10170 dy = (double)(secondpoly->yv[0]-firstpoly->yv[0]);
10171 if (dx == 0.0 && dy == 0.0)
10172 {
10173 us_abortcommand(_("Domain error during tangent computation"));
10174 return;
10175 }
10176 ang = atan2(dy, dx);
10177 oang = ang + EPI / 2.0;
10178 if (oang > EPI * 2.0) oang -= EPI * 2.0;
10179 pfx[dps] = firstpoly->xv[0] + rounddouble(cos(oang) * (double)frad);
10180 pfy[dps] = firstpoly->yv[0] + rounddouble(sin(oang) * (double)frad);
10181 psx[dps] = secondpoly->xv[0] + rounddouble(cos(oang) * (double)srad);
10182 psy[dps] = secondpoly->yv[0] + rounddouble(sin(oang) * (double)srad);
10183 dps++;
10184
10185 oang = ang - EPI / 2.0;
10186 if (oang < -EPI * 2.0) oang += EPI * 2.0;
10187 pfx[dps] = firstpoly->xv[0] + rounddouble(cos(oang) * (double)frad);
10188 pfy[dps] = firstpoly->yv[0] + rounddouble(sin(oang) * (double)frad);
10189 psx[dps] = secondpoly->xv[0] + rounddouble(cos(oang) * (double)srad);
10190 psy[dps] = secondpoly->yv[0] + rounddouble(sin(oang) * (double)srad);
10191 dps++;
10192 } else
10193 {
10194 if (!circletangents(secondpoly->xv[0], secondpoly->yv[0],
10195 firstpoly->xv[0], firstpoly->yv[0], firstpoly->xv[0]+frad-srad, firstpoly->yv[0],
10196 &ix1, &iy1, &ix2, &iy2))
10197 {
10198 dx = (double)(ix1-firstpoly->xv[0]); dy = (double)(iy1-firstpoly->yv[0]);
10199 if (dx == 0.0 && dy == 0.0)
10200 {
10201 us_abortcommand(_("Domain error during tangent computation"));
10202 return;
10203 }
10204 ang = atan2(dy, dx);
10205 pfx[dps] = firstpoly->xv[0] + rounddouble(cos(ang) * (double)frad);
10206 pfy[dps] = firstpoly->yv[0] + rounddouble(sin(ang) * (double)frad);
10207 psx[dps] = secondpoly->xv[0] + rounddouble(cos(ang) * (double)srad);
10208 psy[dps] = secondpoly->yv[0] + rounddouble(sin(ang) * (double)srad);
10209 dps++;
10210
10211 dx = (double)(ix2-firstpoly->xv[0]); dy = (double)(iy2-firstpoly->yv[0]);
10212 if (dx == 0.0 && dy == 0.0)
10213 {
10214 us_abortcommand(_("Domain error during tangent computation"));
10215 return;
10216 }
10217 ang = atan2(dy, dx);
10218 pfx[dps] = firstpoly->xv[0] + rounddouble(cos(ang) * (double)frad);
10219 pfy[dps] = firstpoly->yv[0] + rounddouble(sin(ang) * (double)frad);
10220 psx[dps] = secondpoly->xv[0] + rounddouble(cos(ang) * (double)srad);
10221 psy[dps] = secondpoly->yv[0] + rounddouble(sin(ang) * (double)srad);
10222 dps++;
10223 }
10224 }
10225
10226 /* find tangent lines that cross between two circles */
10227 if (!circletangents(secondpoly->xv[0], secondpoly->yv[0],
10228 firstpoly->xv[0], firstpoly->yv[0], firstpoly->xv[0]+frad+srad, firstpoly->yv[0],
10229 &ix1, &iy1, &ix2, &iy2))
10230 {
10231 dx = (double)(ix1-firstpoly->xv[0]); dy = (double)(iy1-firstpoly->yv[0]);
10232 if (dx == 0.0 && dy == 0.0)
10233 {
10234 us_abortcommand(_("Domain error during tangent computation"));
10235 return;
10236 }
10237 ang = atan2(dy, dx);
10238 pfx[dps] = firstpoly->xv[0] + rounddouble(cos(ang) * (double)frad);
10239 pfy[dps] = firstpoly->yv[0] + rounddouble(sin(ang) * (double)frad);
10240 psx[dps] = secondpoly->xv[0] - rounddouble(cos(ang) * (double)srad);
10241 psy[dps] = secondpoly->yv[0] - rounddouble(sin(ang) * (double)srad);
10242 dps++;
10243
10244 dx = (double)(ix2-firstpoly->xv[0]); dy = (double)(iy2-firstpoly->yv[0]);
10245 if (dx == 0.0 && dy == 0.0)
10246 {
10247 us_abortcommand(_("Domain error during tangent computation"));
10248 return;
10249 }
10250 ang = atan2(dy, dx);
10251 pfx[dps] = firstpoly->xv[0] + rounddouble(cos(ang) * (double)frad);
10252 pfy[dps] = firstpoly->yv[0] + rounddouble(sin(ang) * (double)frad);
10253 psx[dps] = secondpoly->xv[0] - rounddouble(cos(ang) * (double)srad);
10254 psy[dps] = secondpoly->yv[0] - rounddouble(sin(ang) * (double)srad);
10255 dps++;
10256 }
10257
10258 /* screen out points that are not on arcs */
10259 k = 0;
10260 for(j=0; j<dps; j++)
10261 {
10262 if ((firstpoly->style == CIRCLEARC || firstpoly->style == THICKCIRCLEARC) &&
10263 !us_pointonarc(pfx[j], pfy[j], firstpoly)) continue;
10264 if ((secondpoly->style == CIRCLEARC || secondpoly->style == THICKCIRCLEARC) &&
10265 !us_pointonarc(psx[j], psy[j], secondpoly)) continue;
10266 pfx[k] = pfx[j]; pfy[k] = pfy[j];
10267 psx[k] = psx[j]; psy[k] = psy[j];
10268 k++;
10269 }
10270 dps = k;
10271 if (dps == 0) return;
10272
10273 /* now find the tangent line that is closest to the snap points */
10274 us_getsnappoint(firsthigh, &fx, &fy);
10275 us_getsnappoint(secondhigh, &sx, &sy);
10276 for(j=0; j<dps; j++)
10277 {
10278 dist = computedistance(pfx[j],pfy[j], fx,fy) + computedistance(psx[j],psy[j], sx,sy);
10279
10280 /* LINTED "bestdist" used in proper order */
10281 if (j == 0 || dist < bestdist)
10282 {
10283 bestdist = dist;
10284 bestone = j;
10285 }
10286 }
10287
10288 /* set the best one */
10289 us_xformpointtonode(pfx[bestone], pfy[bestone], firsthigh->fromgeom->entryaddr.ni,
10290 &firsthigh->snapx, &firsthigh->snapy);
10291 us_xformpointtonode(psx[bestone], psy[bestone], secondhigh->fromgeom->entryaddr.ni,
10292 &secondhigh->snapx, &secondhigh->snapy);
10293 } else
10294 {
10295 /* compute tangent to first object */
10296 us_getsnappoint(secondhigh, &sx, &sy);
10297 us_adjustonetangent(firsthigh, firstpoly, sx, sy);
10298 }
10299 } else
10300 {
10301 if ((secondhigh->status&HIGHSNAPTAN) != 0)
10302 {
10303 us_getsnappoint(firsthigh, &fx, &fy);
10304 us_adjustonetangent(secondhigh, secondpoly, fx, fy);
10305 }
10306 }
10307 }
10308
10309 /*
10310 * Routine to adjust the snap point on "high" so that it is tangent to its curved
10311 * polygon "poly" and runs through (x, y).
10312 */
us_adjustonetangent(HIGHLIGHT * high,POLYGON * poly,INTBIG x,INTBIG y)10313 void us_adjustonetangent(HIGHLIGHT *high, POLYGON *poly, INTBIG x, INTBIG y)
10314 {
10315 REGISTER NODEINST *ni;
10316 INTBIG ix1, iy1, ix2, iy2, fx, fy;
10317 REGISTER INTBIG xv, yv;
10318
10319 if (!high->fromgeom->entryisnode) return;
10320 ni = high->fromgeom->entryaddr.ni;
10321 us_getsnappoint(high, &fx, &fy);
10322 if (circletangents(x, y, poly->xv[0], poly->yv[0], poly->xv[1], poly->yv[1],
10323 &ix1, &iy1, &ix2, &iy2)) return;
10324 if (computedistance(fx, fy, ix1, iy1) > computedistance(fx, fy, ix2, iy2))
10325 {
10326 xv = ix1; ix1 = ix2; ix2 = xv;
10327 yv = iy1; iy1 = iy2; iy2 = yv;
10328 }
10329
10330 if ((poly->style != CIRCLEARC && poly->style != THICKCIRCLEARC) ||
10331 us_pointonarc(ix1, iy1, poly))
10332 {
10333 us_xformpointtonode(ix1, iy1, ni, &high->snapx, &high->snapy);
10334 return;
10335 }
10336 if ((poly->style != CIRCLEARC && poly->style != THICKCIRCLEARC) ||
10337 us_pointonarc(ix2, iy2, poly))
10338 {
10339 us_xformpointtonode(ix2, iy2, ni, &high->snapx, &high->snapy);
10340 return;
10341 }
10342 }
10343
10344 /*
10345 * Routine to adjust the two highlight modules "firsthigh" and "secondhigh" to account for the
10346 * fact that one or both has a perpendicular snap point that must be perpendicular
10347 * to the other's snap point.
10348 */
us_adjustperpendicularsnappoints(HIGHLIGHT * firsthigh,HIGHLIGHT * secondhigh)10349 void us_adjustperpendicularsnappoints(HIGHLIGHT *firsthigh, HIGHLIGHT *secondhigh)
10350 {
10351 INTBIG fx, fy;
10352 static POLYGON *secondpoly = NOPOLYGON;
10353 REGISTER NODEINST *ni;
10354 XARRAY trans;
10355
10356 if ((secondhigh->status&HIGHSNAPPERP) != 0)
10357 {
10358 /* get polygon describing second object */
10359 (void)needstaticpolygon(&secondpoly, 4, us_tool->cluster);
10360 if (!secondhigh->fromgeom->entryisnode) return;
10361 ni = secondhigh->fromgeom->entryaddr.ni;
10362 if (ni->proto->primindex == 0) return;
10363 makerot(ni, trans);
10364 (void)nodepolys(ni, 0, NOWINDOWPART);
10365 shapenodepoly(ni, 0, secondpoly);
10366 xformpoly(secondpoly, trans);
10367
10368 us_getsnappoint(firsthigh, &fx, &fy);
10369 us_adjustoneperpendicular(secondhigh, secondpoly, fx, fy);
10370 }
10371 }
10372
10373 /*
10374 * Routine to adjust the snap point on "high" so that it is perpendicular to
10375 * polygon "poly" and point (x, y).
10376 */
us_adjustoneperpendicular(HIGHLIGHT * high,POLYGON * poly,INTBIG x,INTBIG y)10377 void us_adjustoneperpendicular(HIGHLIGHT *high, POLYGON *poly, INTBIG x, INTBIG y)
10378 {
10379 REGISTER NODEINST *ni;
10380 REGISTER INTBIG rad;
10381 INTBIG ix, iy;
10382 REGISTER INTBIG ang;
10383
10384 if (!high->fromgeom->entryisnode) return;
10385 ni = high->fromgeom->entryaddr.ni;
10386
10387 if (poly->style == CIRCLE || poly->style == THICKCIRCLE ||
10388 poly->style == CIRCLEARC || poly->style == THICKCIRCLEARC)
10389 {
10390 /* compute perpendicular point */
10391 ang = figureangle(poly->xv[0], poly->yv[0], x, y);
10392 rad = computedistance(poly->xv[0], poly->yv[0], poly->xv[1], poly->yv[1]);
10393 ix = poly->xv[0] + mult(cosine(ang), rad);
10394 iy = poly->yv[0] + mult(sine(ang), rad);
10395 if (poly->style == CIRCLEARC || poly->style == THICKCIRCLEARC ||
10396 !us_pointonarc(ix, iy, poly)) return;
10397 us_xformpointtonode(ix, iy, ni, &high->snapx, &high->snapy);
10398 return;
10399 }
10400
10401 /* handle straight line perpendiculars */
10402 ix = x; iy = y;
10403 (void)closestpointtosegment(poly->xv[0], poly->yv[0], poly->xv[1], poly->yv[1], &ix, &iy);
10404 if (ix != x || iy != y) us_xformpointtonode(ix, iy, ni, &high->snapx, &high->snapy);
10405 }
10406
10407 /*
10408 * routine to determine whether the point (x, y) is on the arc in "poly".
10409 * returns true if so.
10410 */
us_pointonarc(INTBIG x,INTBIG y,POLYGON * poly)10411 BOOLEAN us_pointonarc(INTBIG x, INTBIG y, POLYGON *poly)
10412 {
10413 REGISTER INTBIG angle, startangle, endangle;
10414
10415 if (poly->style != CIRCLEARC && poly->style != THICKCIRCLEARC) return(FALSE);
10416
10417 angle = figureangle(poly->xv[0], poly->yv[0], x, y);
10418 endangle = figureangle(poly->xv[0], poly->yv[0], poly->xv[1], poly->yv[1]);
10419 startangle = figureangle(poly->xv[0], poly->yv[0], poly->xv[2], poly->yv[2]);
10420
10421 if (endangle > startangle)
10422 {
10423 if (angle >= startangle && angle <= endangle) return(TRUE);
10424 } else
10425 {
10426 if (angle >= startangle || angle <= endangle) return(TRUE);
10427 }
10428 return(FALSE);
10429 }
10430
10431 /*
10432 * routine to get the true coordinate of the snap point in "high" and place it in (x,y).
10433 */
us_getsnappoint(HIGHLIGHT * high,INTBIG * x,INTBIG * y)10434 void us_getsnappoint(HIGHLIGHT *high, INTBIG *x, INTBIG *y)
10435 {
10436 REGISTER NODEINST *ni;
10437 XARRAY trans;
10438 INTBIG xt, yt;
10439
10440 if (high->fromgeom->entryisnode)
10441 {
10442 ni = high->fromgeom->entryaddr.ni;
10443 makeangle(ni->rotation, ni->transpose, trans);
10444 xform(high->snapx, high->snapy, &xt, &yt, trans);
10445 *x = (ni->highx + ni->lowx) / 2 + xt;
10446 *y = (ni->highy + ni->lowy) / 2 + yt;
10447 } else
10448 {
10449 *x = (high->fromgeom->highx + high->fromgeom->lowx) / 2 + high->snapx;
10450 *y = (high->fromgeom->highy + high->fromgeom->lowy) / 2 + high->snapy;
10451 }
10452 }
10453
us_setbestsnappoint(HIGHLIGHT * best,INTBIG wantx,INTBIG wanty,INTBIG newx,INTBIG newy,BOOLEAN tan,BOOLEAN perp)10454 void us_setbestsnappoint(HIGHLIGHT *best, INTBIG wantx, INTBIG wanty, INTBIG newx, INTBIG newy,
10455 BOOLEAN tan, BOOLEAN perp)
10456 {
10457 REGISTER INTBIG olddist, newdist;
10458 INTBIG oldx, oldy;
10459
10460 if ((best->status & HIGHSNAP) != 0)
10461 {
10462 us_getsnappoint(best, &oldx, &oldy);
10463 olddist = computedistance(wantx, wanty, oldx, oldy);
10464 newdist = computedistance(wantx, wanty, newx, newy);
10465 if (newdist >= olddist) return;
10466 }
10467
10468 /* set the snap point */
10469 if (best->fromgeom->entryisnode)
10470 {
10471 us_xformpointtonode(newx, newy, best->fromgeom->entryaddr.ni, &best->snapx, &best->snapy);
10472 } else
10473 {
10474 best->snapx = newx - (best->fromgeom->highx + best->fromgeom->lowx) / 2;
10475 best->snapy = newy - (best->fromgeom->highy + best->fromgeom->lowy) / 2;
10476 }
10477 best->status |= HIGHSNAP;
10478 if (tan) best->status |= HIGHSNAPTAN;
10479 if (perp) best->status |= HIGHSNAPPERP;
10480 }
10481
us_xformpointtonode(INTBIG x,INTBIG y,NODEINST * ni,INTBIG * xo,INTBIG * yo)10482 void us_xformpointtonode(INTBIG x, INTBIG y, NODEINST *ni, INTBIG *xo, INTBIG *yo)
10483 {
10484 XARRAY trans;
10485 INTBIG xv, yv;
10486
10487 if (ni->transpose != 0) makeangle(ni->rotation, ni->transpose, trans); else
10488 makeangle((3600 - ni->rotation)%3600, 0, trans);
10489 xv = x - (ni->highx + ni->lowx) / 2;
10490 yv = y - (ni->highy + ni->lowy) / 2;
10491 xform(xv, yv, xo, yo, trans);
10492 }
10493
10494 /*
10495 * routine to return the object that is closest to point (rdx, rdy)
10496 * or within "slop" of that point in cell "cell". Searches nodes first.
10497 * This is used in the "create join-angle" command.
10498 */
us_getclosest(INTBIG rdx,INTBIG rdy,INTBIG slop,NODEPROTO * cell)10499 GEOM *us_getclosest(INTBIG rdx, INTBIG rdy, INTBIG slop, NODEPROTO *cell)
10500 {
10501 REGISTER GEOM *geom, *highgeom, *bestgeom;
10502 REGISTER NODEINST *ni;
10503 REGISTER ARCINST *ai;
10504 REGISTER INTBIG sea, bestdist, dist;
10505 static POLYGON *poly = NOPOLYGON;
10506 REGISTER VARIABLE *var;
10507 HIGHLIGHT high;
10508
10509 (void)needstaticpolygon(&poly, 4, us_tool->cluster);
10510
10511 highgeom = NOGEOM;
10512 var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_highlightedkey);
10513 if (var != NOVARIABLE)
10514 {
10515 if (getlength(var) == 1)
10516 {
10517 (void)us_makehighlight(((CHAR **)var->addr)[0], &high);
10518 highgeom = high.fromgeom;
10519 }
10520 }
10521
10522 /* see if there is a direct hit on another node */
10523 sea = initsearch(rdx-slop, rdx+slop, rdy-slop, rdy+slop, cell);
10524 bestdist = MAXINTBIG;
10525 for(;;)
10526 {
10527 geom = nextobject(sea);
10528 if (geom == NOGEOM) break;
10529 if (geom == highgeom) continue;
10530 if (!geom->entryisnode) continue;
10531 ni = geom->entryaddr.ni;
10532 if ((ni->userbits&HARDSELECTN) != 0) continue;
10533 if (ni->proto->primindex != 0 && (ni->proto->userbits&NINVISIBLE) != 0)
10534 continue;
10535 dist = us_disttoobject(rdx, rdy, geom);
10536 if (dist > bestdist) continue;
10537 bestdist = dist;
10538 bestgeom = geom;
10539 }
10540 if (bestdist < 0) return(bestgeom);
10541
10542 /* look at arcs second */
10543 bestdist = MAXINTBIG;
10544 sea = initsearch(rdx-slop, rdx+slop, rdy-slop, rdy+slop, cell);
10545 for(;;)
10546 {
10547 geom = nextobject(sea);
10548 if (geom == NOGEOM) break;
10549 if (geom == highgeom) continue;
10550 if (geom->entryisnode) continue;
10551 ai = geom->entryaddr.ai;
10552 if ((ai->userbits&HARDSELECTA) != 0) continue;
10553 if ((ai->proto->userbits&AINVISIBLE) != 0) continue;
10554 dist = us_disttoobject(rdx, rdy, geom);
10555 if (dist > bestdist) continue;
10556 bestdist = dist;
10557 bestgeom = geom;
10558 }
10559 if (bestdist < 0) return(bestgeom);
10560 return(NOGEOM);
10561 }
10562
10563 /*
10564 * Routine to return the distance from point (x,y) to object "geom".
10565 * Negative values are direct hits.
10566 */
us_disttoobject(INTBIG x,INTBIG y,GEOM * geom)10567 INTBIG us_disttoobject(INTBIG x, INTBIG y, GEOM *geom)
10568 {
10569 XARRAY trans;
10570 REGISTER INTBIG wid, bestdist, dist, fun;
10571 REGISTER INTBIG count, box;
10572 static POLYGON *poly = NOPOLYGON;
10573 REGISTER NODEINST *ni;
10574 REGISTER ARCINST *ai;
10575 INTBIG plx, ply, phx, phy;
10576
10577 (void)needstaticpolygon(&poly, 4, us_tool->cluster);
10578 if (geom->entryisnode)
10579 {
10580 ni = geom->entryaddr.ni;
10581 makerot(ni, trans);
10582
10583 /* special case for MOS transistors: examine the gate/active tabs */
10584 fun = (ni->proto->userbits&NFUNCTION) >> NFUNCTIONSH;
10585 if (fun == NPTRANMOS || fun == NPTRAPMOS || fun == NPTRADMOS)
10586 {
10587 count = nodepolys(ni, 0, NOWINDOWPART);
10588 bestdist = MAXINTBIG;
10589 for(box=0; box<count; box++)
10590 {
10591 shapenodepoly(ni, box, poly);
10592 fun = layerfunction(ni->proto->tech, poly->layer) & LFTYPE;
10593 if (!layerispoly(fun) && fun != LFDIFF) continue;
10594 xformpoly(poly, trans);
10595 dist = polydistance(poly, x, y);
10596 if (dist < bestdist) bestdist = dist;
10597 }
10598 return(bestdist);
10599 }
10600
10601 /* special case for 1-polygon primitives: check precise distance to cursor */
10602 if (ni->proto->primindex != 0 && (ni->proto->userbits&NEDGESELECT) != 0)
10603 {
10604 count = nodepolys(ni, 0, NOWINDOWPART);
10605 bestdist = MAXINTBIG;
10606 for(box=0; box<count; box++)
10607 {
10608 shapenodepoly(ni, box, poly);
10609 if ((poly->desc->colstyle&INVISIBLE) != 0) continue;
10610 xformpoly(poly, trans);
10611 dist = polydistance(poly, x, y);
10612 if (dist < bestdist) bestdist = dist;
10613 }
10614 return(bestdist);
10615 }
10616
10617 /* get the bounds of the node in a polygon */
10618 nodesizeoffset(ni, &plx, &ply, &phx, &phy);
10619 maketruerectpoly(ni->lowx+plx, ni->highx-phx, ni->lowy+ply, ni->highy-phy, poly);
10620 poly->style = FILLEDRECT;
10621 xformpoly(poly, trans);
10622 return(polydistance(poly, x, y));
10623 }
10624
10625 /* determine distance to arc */
10626 ai = geom->entryaddr.ai;
10627
10628 /* if arc is selectable precisely, check distance to cursor */
10629 if ((ai->proto->userbits&AEDGESELECT) != 0)
10630 {
10631 count = arcpolys(ai, NOWINDOWPART);
10632 bestdist = MAXINTBIG;
10633 for(box=0; box<count; box++)
10634 {
10635 shapearcpoly(ai, box, poly);
10636 if ((poly->desc->colstyle&INVISIBLE) != 0) continue;
10637 dist = polydistance(poly, x, y);
10638 if (dist < bestdist) bestdist = dist;
10639 }
10640 return(bestdist);
10641 }
10642
10643 /* standard distance to the arc */
10644 wid = ai->width - arcwidthoffset(ai);
10645 if (wid == 0) wid = lambdaofarc(ai);
10646 if (curvedarcoutline(ai, poly, FILLED, wid))
10647 makearcpoly(ai->length, wid, ai, poly, FILLED);
10648 return(polydistance(poly, x, y));
10649 }
10650
10651 /*********************************** TEXT ON NODES/ARCS ***********************************/
10652
10653 static INTSML us_nodearcvarptr;
10654 static INTBIG us_nodearcvarcount;
10655 static INTSML us_portvarptr;
10656 static INTBIG us_portvarcount;
10657 static BOOLEAN us_nodenameflg;
10658 static PORTEXPINST *us_nodeexpptr;
10659 static PORTPROTO *us_portptr;
10660
us_initnodetext(NODEINST * ni,INTBIG findspecial,WINDOWPART * win)10661 void us_initnodetext(NODEINST *ni, INTBIG findspecial, WINDOWPART *win)
10662 {
10663 us_nodearcvarptr = 0;
10664 us_nodearcvarcount = tech_displayablenvars(ni, win, &tech_oneprocpolyloop);
10665 us_portvarptr = 0;
10666 us_portvarcount = 0;
10667
10668 us_nodenameflg = TRUE;
10669 if (findspecial != 0)
10670 {
10671 /* only select cell instance names if visible */
10672 if ((us_useroptions&HIDETXTINSTNAME) == 0)
10673 us_nodenameflg = FALSE;
10674 } else
10675 {
10676 /* if the "special" option is not set and node text is disabled, skip it */
10677 if ((us_useroptions&NOTEXTSELECT) != 0) us_nodearcvarptr = ni->numvar;
10678 }
10679 if ((us_useroptions&HIDETXTEXPORT) != 0) us_nodeexpptr = NOPORTEXPINST; else
10680 us_nodeexpptr = ni->firstportexpinst;
10681 }
10682
us_getnodetext(NODEINST * ni,WINDOWPART * win,POLYGON * poly,VARIABLE ** var,VARIABLE ** varnoeval,PORTPROTO ** port)10683 BOOLEAN us_getnodetext(NODEINST *ni, WINDOWPART *win, POLYGON *poly, VARIABLE **var,
10684 VARIABLE **varnoeval, PORTPROTO **port)
10685 {
10686 INTBIG xc, yc, lx, hx, ly, hy;
10687 UINTBIG descript[TEXTDESCRIPTSIZE];
10688 REGISTER INTBIG portstyle, i;
10689 REGISTER PORTEXPINST *pe;
10690 XARRAY trans;
10691
10692 for(;;)
10693 {
10694 if (!us_nodenameflg)
10695 {
10696 us_nodenameflg = TRUE;
10697 if (ni->proto->primindex == 0 && (ni->userbits&NEXPAND) == 0)
10698 {
10699 *var = *varnoeval = NOVARIABLE;
10700 *port = NOPORTPROTO;
10701 us_maketextpoly(describenodeproto(ni->proto), win,
10702 (ni->lowx + ni->highx) / 2, (ni->lowy + ni->highy) / 2,
10703 ni, ni->parent->tech, ni->textdescript, poly);
10704 poly->style = FILLED;
10705 return(FALSE);
10706 }
10707 }
10708 if (us_nodearcvarptr < us_nodearcvarcount)
10709 {
10710 *var = tech_filldisplayablenvar(ni, poly, win, varnoeval, &tech_oneprocpolyloop);
10711 makerot(ni, trans);
10712 TDCOPY(descript, (*var)->textdescript);
10713 if (TDGETPOS(descript) == VTPOSBOXED)
10714 {
10715 xformpoly(poly, trans);
10716 getbbox(poly, &lx, &hx, &ly, &hy);
10717 us_filltextpoly(poly->string, win, (lx + hx) / 2, (ly + hy) / 2,
10718 trans, ni->parent->tech, descript, ni->geom, poly);
10719 for(i=0; i<poly->count; i++)
10720 {
10721 if (poly->xv[i] < lx) poly->xv[i] = lx;
10722 if (poly->xv[i] > hx) poly->xv[i] = hx;
10723 if (poly->yv[i] < ly) poly->yv[i] = ly;
10724 if (poly->yv[i] > hy) poly->yv[i] = hy;
10725 }
10726 } else
10727 {
10728 xform(poly->xv[0], poly->yv[0], &poly->xv[0], &poly->yv[0], trans);
10729 us_filltextpoly(poly->string, win, poly->xv[0], poly->yv[0],
10730 trans, ni->parent->tech, descript, ni->geom, poly);
10731 }
10732 *port = NOPORTPROTO;
10733 us_nodearcvarptr++;
10734 poly->style = FILLED;
10735 return(FALSE);
10736 }
10737
10738 if (us_portvarptr < us_portvarcount)
10739 {
10740 *var = tech_filldisplayableportvar(us_portptr, poly, win, varnoeval, &tech_oneprocpolyloop);
10741 portposition(us_portptr->subnodeinst, us_portptr->subportproto, &xc, &yc);
10742 us_maketextpoly(poly->string, win, xc, yc, us_portptr->subnodeinst,
10743 us_portptr->subnodeinst->parent->tech, (*var)->textdescript, poly);
10744 *port = us_portptr;
10745 us_portvarptr++;
10746 poly->style = FILLED;
10747 return(FALSE);
10748 }
10749
10750 /* check exports on the node */
10751 if (us_nodeexpptr != NOPORTEXPINST)
10752 {
10753 pe = us_nodeexpptr;
10754 us_nodeexpptr = pe->nextportexpinst;
10755 us_portptr = pe->exportproto;
10756 us_portvarcount = tech_displayableportvars(us_portptr, win, &tech_oneprocpolyloop);
10757 us_portvarptr = 0;
10758
10759 portstyle = us_useroptions & EXPORTLABELS;
10760 if (portstyle == EXPORTSCROSS) continue;
10761 *port = pe->exportproto;
10762 *var = *varnoeval = NOVARIABLE;
10763
10764 /* build polygon that surrounds text */
10765 portposition(ni, (*port)->subportproto, &xc, &yc);
10766 us_maketextpoly(us_displayedportname(*port, portstyle >> EXPORTLABELSSH),
10767 win, xc, yc, ni, ni->parent->tech, (*port)->textdescript, poly);
10768 poly->style = FILLED;
10769 return(FALSE);
10770 }
10771 break;
10772 }
10773
10774 return(TRUE);
10775 }
10776
us_initarctext(ARCINST * ai,INTBIG findspecial,WINDOWPART * win)10777 void us_initarctext(ARCINST *ai, INTBIG findspecial, WINDOWPART *win)
10778 {
10779 us_nodearcvarptr = 0;
10780 us_nodearcvarcount = tech_displayableavars(ai, win, &tech_oneprocpolyloop);
10781 if (findspecial == 0)
10782 {
10783 /* if the "special" option is not set and arc text is disabled, skip it */
10784 if ((us_useroptions&NOTEXTSELECT) != 0) us_nodearcvarptr = ai->numvar;
10785 }
10786 }
10787
us_getarctext(ARCINST * ai,WINDOWPART * win,POLYGON * poly,VARIABLE ** var,VARIABLE ** varnoeval)10788 BOOLEAN us_getarctext(ARCINST *ai, WINDOWPART *win, POLYGON *poly, VARIABLE **var, VARIABLE **varnoeval)
10789 {
10790 if (us_nodearcvarptr < us_nodearcvarcount)
10791 {
10792 *var = tech_filldisplayableavar(ai, poly, win, varnoeval, &tech_oneprocpolyloop);
10793 us_maketextpoly(poly->string, win,
10794 (ai->end[0].xpos + ai->end[1].xpos) / 2, (ai->end[0].ypos + ai->end[1].ypos) / 2,
10795 NONODEINST, ai->parent->tech, (*var)->textdescript, poly);
10796 us_nodearcvarptr++;
10797 poly->style = FILLED;
10798 return(FALSE);
10799 }
10800 return(TRUE);
10801 }
10802
10803 /*
10804 * routine to build a polygon in "poly" that has four points describing the
10805 * text in "str" with descriptor "descript". The text is in window "win"
10806 * on an object whose center is (xc,yc) and is on node "ni" (or not if NONODEINST)
10807 * and uses technology "tech".
10808 */
us_maketextpoly(CHAR * str,WINDOWPART * win,INTBIG xc,INTBIG yc,NODEINST * ni,TECHNOLOGY * tech,UINTBIG * descript,POLYGON * poly)10809 void us_maketextpoly(CHAR *str, WINDOWPART *win, INTBIG xc, INTBIG yc, NODEINST *ni,
10810 TECHNOLOGY *tech, UINTBIG *descript, POLYGON *poly)
10811 {
10812 INTBIG newxc, newyc, lambda;
10813 XARRAY trans;
10814 REGISTER GEOM *geom;
10815
10816 /* determine location of text */
10817 if (ni == NONODEINST)
10818 {
10819 transid(trans);
10820 lambda = el_curlib->lambda[tech->techindex];
10821 } else
10822 {
10823 makeangle(ni->rotation, ni->transpose, trans);
10824 lambda = ni->parent->lib->lambda[tech->techindex];
10825 }
10826
10827 newxc = TDGETXOFF(descript);
10828 newxc = newxc * lambda / 4;
10829 newyc = TDGETYOFF(descript);
10830 newyc = newyc * lambda / 4;
10831 xform(newxc, newyc, &newxc, &newyc, trans);
10832 xc += newxc; yc += newyc;
10833 if (ni == NONODEINST) geom = NOGEOM; else
10834 geom = ni->geom;
10835 us_filltextpoly(str, win, xc, yc, trans, tech, descript, geom, poly);
10836 }
10837
us_filltextpoly(CHAR * str,WINDOWPART * win,INTBIG xc,INTBIG yc,XARRAY trans,TECHNOLOGY * tech,UINTBIG * descript,GEOM * geom,POLYGON * poly)10838 void us_filltextpoly(CHAR *str, WINDOWPART *win, INTBIG xc, INTBIG yc, XARRAY trans,
10839 TECHNOLOGY *tech, UINTBIG *descript, GEOM *geom, POLYGON *poly)
10840 {
10841 INTBIG xw, yw;
10842
10843 /* determine size of text */
10844 us_gettextscreensize(str, descript, win, tech, geom, &xw, &yw);
10845
10846 switch (TDGETPOS(descript))
10847 {
10848 case VTPOSCENT: poly->style = TEXTCENT; break;
10849 case VTPOSBOXED: poly->style = TEXTBOX; break;
10850 case VTPOSUP: poly->style = TEXTBOT; break;
10851 case VTPOSDOWN: poly->style = TEXTTOP; break;
10852 case VTPOSLEFT: poly->style = TEXTRIGHT; break;
10853 case VTPOSRIGHT: poly->style = TEXTLEFT; break;
10854 case VTPOSUPLEFT: poly->style = TEXTBOTRIGHT; break;
10855 case VTPOSUPRIGHT: poly->style = TEXTBOTLEFT; break;
10856 case VTPOSDOWNLEFT: poly->style = TEXTTOPRIGHT; break;
10857 case VTPOSDOWNRIGHT: poly->style = TEXTTOPLEFT; break;
10858 }
10859 poly->style = rotatelabel(poly->style, TDGETROTATION(descript), trans);
10860
10861 switch (poly->style)
10862 {
10863 case TEXTTOP: yc -= yw/2; break;
10864 case TEXTBOT: yc += yw/2; break;
10865 case TEXTLEFT: xc += xw/2; break;
10866 case TEXTRIGHT: xc -= xw/2; break;
10867 case TEXTTOPLEFT: xc += xw/2; yc -= yw/2; break;
10868 case TEXTBOTLEFT: xc += xw/2; yc += yw/2; break;
10869 case TEXTTOPRIGHT: xc -= xw/2; yc -= yw/2; break;
10870 case TEXTBOTRIGHT: xc -= xw/2; yc += yw/2; break;
10871 }
10872
10873 /* construct polygon with actual size */
10874 poly->xv[0] = xc - xw/2; poly->yv[0] = yc - yw/2;
10875 poly->xv[1] = xc - xw/2; poly->yv[1] = yc + yw/2;
10876 poly->xv[2] = xc + xw/2; poly->yv[2] = yc + yw/2;
10877 poly->xv[3] = xc + xw/2; poly->yv[3] = yc - yw/2;
10878 poly->count = 4;
10879 poly->layer = -1;
10880 poly->style = CLOSED;
10881 }
10882
10883 /*
10884 * Routine to determine the size (in database units) of the string "str", drawn in window "w"
10885 * with text descriptor "descript". The text is on object "geom", technology "tech". The size
10886 * is returned in (xw,yw).
10887 */
us_gettextscreensize(CHAR * str,UINTBIG * descript,WINDOWPART * w,TECHNOLOGY * tech,GEOM * geom,INTBIG * xw,INTBIG * yw)10888 void us_gettextscreensize(CHAR *str, UINTBIG *descript, WINDOWPART *w, TECHNOLOGY *tech, GEOM *geom,
10889 INTBIG *xw, INTBIG *yw)
10890 {
10891 REGISTER INTBIG lambda, newsize, oldsize, abssize, xabssize, yabssize,
10892 sizex, sizey;
10893 INTBIG sslx, sshx, ssly, sshy;
10894 float sscalex, sscaley;
10895 INTBIG tsx, tsy;
10896 REGISTER LIBRARY *lib;
10897 static BOOLEAN canscalefonts, fontscalingunknown = TRUE;
10898 REGISTER BOOLEAN reltext;
10899
10900 /* see if relative font scaling should be done */
10901 if (fontscalingunknown)
10902 {
10903 fontscalingunknown = FALSE;
10904 canscalefonts = graphicshas(CANSCALEFONTS);
10905 }
10906 reltext = FALSE;
10907 if (canscalefonts)
10908 {
10909 if ((TDGETSIZE(descript)&TXTQLAMBDA) != 0) reltext = TRUE;
10910 }
10911 if (TDGETPOS(descript) == VTPOSBOXED)
10912 {
10913 if (geom == NOGEOM) TDSETPOS(descript, VTPOSCENT); else
10914 {
10915 sizex = roundfloat((geom->highx - geom->lowx) * w->scalex);
10916 sizey = roundfloat((geom->highy - geom->lowy) * w->scaley);
10917 }
10918 }
10919 if (reltext)
10920 {
10921 /* relative size text */
10922 if (w->curnodeproto == NONODEPROTO) lib = el_curlib; else
10923 lib = w->curnodeproto->lib;
10924 lambda = lib->lambda[tech->techindex];
10925 sslx = w->screenlx; w->screenlx = w->uselx * lambda / 12;
10926 sshx = w->screenhx; w->screenhx = w->usehx * lambda / 12;
10927 ssly = w->screenly; w->screenly = w->usely * lambda / 12;
10928 sshy = w->screenhy; w->screenhy = w->usehy * lambda / 12;
10929 sscalex = w->scalex; sscaley = w->scaley;
10930 computewindowscale(w);
10931
10932 if (TDGETPOS(descript) == VTPOSBOXED)
10933 {
10934 for(;;)
10935 {
10936 screensettextinfo(w, tech, descript);
10937 screengettextsize(w, str, &tsx, &tsy);
10938 if (tsx <= sizex && tsy <= sizey) break;
10939 newsize = TXTGETQLAMBDA(TDGETSIZE(descript)) - 1;
10940 if (newsize <= 0) break;
10941 TDSETSIZE(descript, TXTSETQLAMBDA(newsize));
10942 }
10943 } else
10944 {
10945 screensettextinfo(w, tech, descript);
10946 screengettextsize(w, str, &tsx, &tsy);
10947 }
10948 *xw = muldiv(tsx, w->screenhx-w->screenlx, w->usehx-w->uselx);
10949 *yw = muldiv(tsy, w->screenhy-w->screenly, w->usehy-w->usely);
10950 w->screenlx = sslx; w->screenhx = sshx;
10951 w->screenly = ssly; w->screenhy = sshy;
10952 w->scalex = sscalex; w->scaley = sscaley;
10953 } else
10954 {
10955 /* absolute size text */
10956 if (TDGETPOS(descript) == VTPOSBOXED)
10957 {
10958 for(;;)
10959 {
10960 screensettextinfo(w, tech, descript);
10961 screengettextsize(w, str, &tsx, &tsy);
10962 if (tsx <= sizex && tsy <= sizey) break;
10963 oldsize = TDGETSIZE(descript);
10964 abssize = TXTGETPOINTS(oldsize);
10965
10966 /* jump quickly to the proper font size */
10967 if (tsx <= sizex) xabssize = abssize; else
10968 xabssize = abssize * sizex / tsx;
10969 if (tsy <= sizey) yabssize = abssize; else
10970 yabssize = abssize * sizey / tsy;
10971 newsize = mini(xabssize, yabssize);
10972 if (newsize < 4) break;
10973 TDSETSIZE(descript, TXTSETPOINTS(newsize));
10974 }
10975 } else
10976 {
10977 screensettextinfo(w, tech, descript);
10978 screengettextsize(w, str, &tsx, &tsy);
10979 }
10980 *xw = muldiv(tsx, w->screenhx-w->screenlx, w->usehx-w->uselx);
10981 *yw = muldiv(tsy, w->screenhy-w->screenly, w->usehy-w->usely);
10982 }
10983 }
10984