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