1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: usrmisc.c
6  * User interface tool: miscellaneous control
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 "efunction.h"
35 #include "edialogs.h"
36 #include "usr.h"
37 #include "usreditemacs.h"
38 #include "usreditpac.h"
39 #include "usrtrack.h"
40 #include "tecgen.h"
41 #include "tecart.h"
42 #include "sim.h"
43 #include <math.h>
44 
45 #define ALLTEXTWIDTH	70		/* width of side menu when it is all text */
46 
47 /******************** INITIALIZATION ********************/
48 
49 #define INITIALMENUX    18	/* default height of component menu */
50 #define INITIALMENUY    2	/* default width of component menu */
51 
52 /*
53  * default tablet state: the table below shows the set of commands
54  * that will be bound to the tablet buttons.  If there is a button
55  * with the name in "us_tablet[i].but[0]" or "us_tablet[i].but[1]"
56  * then that button will be bound to the command "us_tablet[i].com".
57  * If no tablet button exists for an entry "i", then the command
58  * there will be bound to the key "us_tablet[i].key".  Thus, tablets
59  * with varying numbers of buttons can be handled by placing commands
60  * on the keys if they don't fit on the buttons.
61  */
62 typedef struct
63 {
64 	CHAR   *but[3];		/* the button names for this command */
65 	CHAR   *key;		/* the key name for this command */
66 	CHAR   *com;		/* the actual command */
67 	INTBIG  count;		/* number of parameters to the command */
68 	CHAR   *args[2];	/* parameters to the command */
69 	BOOLEAN used;		/* set when the command is bound */
70 } INITBUTTONS;
71 
72 static INITBUTTONS us_initialbutton[] =
73 {
74 	/* left/middle/right (white/yellow/blue) are basic commands */
75 	{{x_("LEFT"),x_("WHITE"),x_("BUTTON")},x_("f"), x_("find"),    2, {x_("port"),          x_("extra-info")}, FALSE},
76 	{{x_("RIGHT"),x_("YELLOW"),x_("")},    x_("m"), x_("move"),    0, {x_(""),              x_("")},           FALSE},
77 	{{x_("MIDDLE"),x_("BLUE"),x_("")},     x_("n"), x_("create"),  0, {x_(""),              x_("")},           FALSE},
78 
79 	/* on four-button puck, add one more basic command */
80 	{{x_("GREEN"),x_(""),x_("")},          x_("o"), x_("find"),    2, {x_("another"),       x_("port")},       FALSE},
81 
82 	/* on mice with shift-buttons, add in others still */
83 	{{x_("SLEFT"),x_(""),x_("")},          x_(""),  x_("find"),    1, {x_("more"),          x_("")},           FALSE},
84 	{{x_("SRIGHT"),x_(""),x_("")},         x_(""),  x_("var"),     2, {x_("textedit"),      x_("~")},          FALSE},
85 	{{x_("SMIDDLE"),x_(""),x_("")},        x_(""),  x_("create"),  1, {x_("join-angle"),    x_("")},           FALSE},
86 	{{NULL, NULL, NULL}, NULL, NULL, 0, {NULL, NULL}, FALSE} /* 0 */
87 };
88 
89 /*
90  * default keyboard state: the table below shows the set of commands
91  * that will be bound to the keyboard keys.
92  */
93 typedef struct
94 {
95 	CHAR  *key;
96 	CHAR  *command;
97 	INTBIG count;
98 	CHAR  *args[2];
99 } INITKEYS;
100 
101 static INITKEYS us_initialkeyboard[] =
102 {
103 	{x_("-"), x_("telltool"), 1, {x_("user"), x_("")}},
104 	{NULL, NULL, 0, {NULL, NULL}} /* 0 */
105 };
106 
107 /*
108  * When information, detected during broadcast, evokes a reaction that causes
109  * change, that change must be queued until the next slice.  For example:
110  * deletion of the variable associated with a text window.
111  * These routines queue the changes and then execute them when requested
112  */
113 #define NOUBCHANGE ((UBCHANGE *)-1)
114 #define UBKILLFM          1		/* remove cell_message variable */
115 #define UBNEWFC           2		/* add cell-center */
116 #define UBSPICEPARTS      3		/* check for new spice parts package */
117 #define UBTECEDDELLAYER   4		/* deleted a technology-edit layer cell */
118 #define UBTECEDDELNODE    5		/* deleted a technology-edit node cell */
119 #define UBTECEDRENAME     6		/* renamed a technology-edit cell */
120 #define UBTOOLISON        7		/* tool was turned on */
121 
122 typedef struct Iubchange
123 {
124 	INTBIG     change;		/* type of change */
125 	void      *object;		/* object that is being changed */
126 	void      *parameter;	/* parameter that is being changed */
127 	struct Iubchange *nextubchange;
128 } UBCHANGE;
129 static UBCHANGE *us_ubchangefree = NOUBCHANGE;
130 static UBCHANGE *us_ubchanges = NOUBCHANGE;
131 
132 static NODEPROTO *us_layouttextprim;
133 static INTBIG    *us_printcolordata = 0;
134 
135 /* prototypes for local routines */
136 static void       us_splitwindownames(CHAR*, CHAR*, CHAR*, CHAR*, CHAR*);
137 static BOOLEAN    us_newubchange(INTBIG change, void *object, void *parameter);
138 static void       us_freeubchange(UBCHANGE*);
139 static BOOLEAN    us_pointonexparc(INTBIG cx, INTBIG cy, INTBIG sx, INTBIG sy, INTBIG ex, INTBIG ey, INTBIG x, INTBIG y);
140 static void       us_rotatedescriptArb(GEOM *geom, UINTBIG *descript, BOOLEAN invert);
141 static void       us_scanquickkeys(POPUPMENU *pm, CHAR **quickkeylist, INTBIG quickkeycount, BOOLEAN *warnofchanges);
142 static void       us_layouttextpolygon(INTBIG layer, TECHNOLOGY *tech, INTBIG *x, INTBIG *y, INTBIG count);
143 static INTBIG     us_inheritaddress(INTBIG addr, INTBIG type, VARIABLE *var);
144 static NODEPROTO *us_findcellinotherlib(NODEPROTO *cell, LIBRARY *lib);
145 static void       us_correctxlibref(VARIABLE **firstvar, INTSML *numvar, LIBRARY *oldlib, LIBRARY *newlib);
146 static void       us_adjustpopupmenu(POPUPMENU *pm, INTBIG pindex);
147 static void       us_inheritexportattributes(PORTPROTO *pp, NODEINST *ni, NODEPROTO *np);
148 static void       us_inheritcellattribute(VARIABLE *var, NODEINST *ni, NODEPROTO *np, NODEINST *icon);
149 static void       us_advancecircuittext(CHAR *search, INTBIG bits);
150 static INTBIG     us_stringinstring(CHAR *string, CHAR *search, INTBIG bits);
151 
152 /*
153  * Routine to free all memory associated with this module.
154  */
us_freemiscmemory(void)155 void us_freemiscmemory(void)
156 {
157 	UBCHANGE *ubc;
158 
159 	if (us_printcolordata != 0)
160 		efree((CHAR *)us_printcolordata);
161 	while(us_ubchanges != NOUBCHANGE)
162 	{
163 		ubc = us_ubchanges;
164 		us_ubchanges = ubc->nextubchange;
165 		us_freeubchange(ubc);
166 	}
167 	while(us_ubchangefree != NOUBCHANGE)
168 	{
169 		ubc = us_ubchangefree;
170 		us_ubchangefree = ubc->nextubchange;
171 		efree((CHAR *)ubc);
172 	}
173 }
174 
175 /*
176  * initialization routine to bind keys and buttons to functions
177  * returns true upon error
178  */
us_initialbinding(void)179 BOOLEAN us_initialbinding(void)
180 {
181 	REGISTER INTBIG i, k, menusave, menux, menuy;
182 	INTBIG j;
183 	CHAR si[50], sj[20], *par[MAXPARS+7];
184 	REGISTER CHAR **temp;
185 
186 	/* make the variables with the bindings */
187 	i = maxi(maxi(NUMKEYS, NUMBUTS), INITIALMENUX*INITIALMENUY);
188 	temp = (CHAR **)emalloc(i * (sizeof (CHAR *)), el_tempcluster);
189 	if (temp == 0) return(TRUE);
190 	temp[0] = x_("a/");
191 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_binding_keys_key, (INTBIG)temp,
192 		VSTRING|VISARRAY|VDONTSAVE|(1<<VLENGTHSH));
193 	for(j=0; j<i; j++) temp[j] = x_("");
194 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_binding_buttons_key, (INTBIG)temp,
195 		VSTRING|VISARRAY|VDONTSAVE|(NUMBUTS<<VLENGTHSH));
196 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_binding_menu_key, (INTBIG)temp,
197 		VSTRING|VISARRAY|VDONTSAVE|((INITIALMENUX*INITIALMENUY)<<VLENGTHSH));
198 	efree((CHAR *)temp);
199 
200 	/* bind the keys */
201 	for(i=0; us_initialkeyboard[i].key != 0; i++)
202 	{
203 		par[0] = x_("set");   par[1] = x_("key");
204 		par[2] = us_initialkeyboard[i].key;
205 		par[3] = us_initialkeyboard[i].command;
206 		for(j=0; j<us_initialkeyboard[i].count; j++)
207 			par[j+4] = us_initialkeyboard[i].args[j];
208 		us_bind(us_initialkeyboard[i].count+4, par);
209 	}
210 
211 	/* bind the mouse commands that fit on the mouse */
212 	for(i=0; i<buttoncount(); i++)
213 	{
214 		(void)estrcpy(si, buttonname(i, &j));
215 		for(j=0; us_initialbutton[j].but[0] != 0; j++)
216 			if (!us_initialbutton[j].used)
217 		{
218 			for(k=0; k<3; k++)
219 				if (namesame(si, us_initialbutton[j].but[k]) == 0)
220 			{
221 				par[0] = x_("set");   par[1] = x_("button");   par[2] = si;
222 				par[3] = us_initialbutton[j].com;
223 				for(k=0; k<us_initialbutton[j].count; k++)
224 					par[k+4] = us_initialbutton[j].args[k];
225 				us_bind(us_initialbutton[j].count+4, par);
226 				us_initialbutton[j].used = TRUE;
227 				break;
228 			}
229 		}
230 	}
231 
232 	/* now bind those mouse commands that can't fit on the mouse */
233 	for(j=0; us_initialbutton[j].but[0] != 0; j++)
234 		if (!us_initialbutton[j].used && *us_initialbutton[j].key != 0)
235 	{
236 		par[0] = x_("set");   par[1] = x_("key");   par[2] = us_initialbutton[j].key;
237 		par[3] = us_initialbutton[j].com;
238 		for(k=0; k<us_initialbutton[j].count; k++)
239 			par[k+4] = us_initialbutton[j].args[k];
240 		us_bind(us_initialbutton[j].count+4, par);
241 	}
242 
243 	/* bind the component menu entries to all "getproto" */
244 	if (us_menupos <= 1)
245 	{
246 		menux = INITIALMENUX;
247 		menuy = INITIALMENUY;
248 	} else
249 	{
250 		menux = INITIALMENUY;
251 		menuy = INITIALMENUX;
252 	}
253 	us_setmenusize(menux, menuy, us_menupos, FALSE);
254 	menusave = us_tool->toolstate&MENUON;   us_tool->toolstate &= ~MENUON;
255 	for(i=0; i<(INITIALMENUX*INITIALMENUY); i++)
256 	{
257 		par[0] = x_("set");   par[1] = x_("menu");
258 		(void)esnprintf(si, 50, x_("%ld"), i%INITIALMENUX);
259 		(void)esnprintf(sj, 20, x_("%ld"), i/INITIALMENUX);
260 		if (us_menupos <= 1)
261 		{
262 			par[2] = sj;  par[3] = si;
263 		} else
264 		{
265 			par[2] = si;  par[3] = sj;
266 		}
267 		par[4] = x_("rem");
268 		par[5] = x_("getproto");
269 		us_bind(6, par);
270 	}
271 
272 	/* now fill in the "getproto" commands properly */
273 	us_setmenunodearcs();
274 
275 	if (menusave != 0) us_tool->toolstate |= MENUON; else
276 		us_tool->toolstate &= ~MENUON;
277 	return(FALSE);
278 }
279 
280 /*
281  * routine to determine for technology "tech" which node and arc prototypes
282  * have opaque layers and set the bits in the prototype->userbits.
283  * The rules for layer orderings are that the transparent layers
284  * must come first followed by the opaque layers.  The field that is
285  * set in the "userbits" is then the index of the first opaque layer.
286  */
us_figuretechopaque(TECHNOLOGY * tech)287 void us_figuretechopaque(TECHNOLOGY *tech)
288 {
289 	REGISTER INTBIG j, k;
290 	REGISTER NODEPROTO *np;
291 	REGISTER ARCPROTO *ap;
292 	REGISTER NODEINST *ni;
293 	NODEINST node;
294 	REGISTER ARCINST *ai;
295 	ARCINST arc;
296 	static POLYGON *poly = NOPOLYGON;
297 
298 	/* get polygon */
299 	(void)needstaticpolygon(&poly, 4, us_tool->cluster);
300 
301 	for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
302 	{
303 		if ((np->userbits&NHASOPA) != 0) continue;
304 		np->userbits &= ~NHASOPA;
305 		ni = &node;   initdummynode(ni);
306 		ni->proto = np;
307 		ni->lowx = np->lowx;   ni->highx = np->highx;
308 		ni->lowy = np->lowy;   ni->highy = np->highy;
309 		j = nodepolys(ni, 0, NOWINDOWPART);
310 		for(k=0; k<j; k++)
311 		{
312 			shapenodepoly(ni, k, poly);
313 			if (poly->desc->bits == LAYERN) continue;
314 			if ((poly->desc->bits & ~(LAYERT1|LAYERT2|LAYERT3|LAYERT4|LAYERT5)) == 0)
315 			{
316 				/* transparent layer found, make sure it is at start */
317 				if ((np->userbits&NHASOPA) != 0)
318 					ttyputerr(_("%s: node %s has layers out of order!"), tech->techname, np->protoname);
319 				continue;
320 			}
321 
322 			/* opaque layer found, mark its index if it is the first */
323 			if ((np->userbits&NHASOPA) == 0)
324 				np->userbits = (np->userbits & ~NFIRSTOPA) | (k << NFIRSTOPASH);
325 			np->userbits |= NHASOPA;
326 		}
327 	}
328 	for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
329 	{
330 		ap->userbits &= ~AHASOPA;
331 		ai = &arc;   initdummyarc(ai);
332 		ai->proto = ap;
333 		ai->userbits = ISDIRECTIONAL;
334 		j = arcpolys(ai, NOWINDOWPART);
335 		for(k=0; k<j; k++)
336 		{
337 			shapearcpoly(ai, k, poly);
338 			if (poly->desc->bits == LAYERN) continue;
339 			if ((poly->desc->bits & ~(LAYERT1|LAYERT2|LAYERT3|LAYERT4|LAYERT5)) == 0)
340 			{
341 				/* transparent layer found, make sure it is at start */
342 				if ((ap->userbits&AHASOPA) != 0)
343 					ttyputerr(_("Arc %s:%s has layers out of order!"), tech->techname, ap->protoname);
344 				continue;
345 			}
346 
347 			/* opaque layer found, mark its index if it is the first */
348 			if ((ap->userbits&AHASOPA) == 0)
349 				ap->userbits = (ap->userbits & ~AFIRSTOPA) | (k << AFIRSTOPASH);
350 			ap->userbits |= AHASOPA;
351 		}
352 	}
353 }
354 
355 /*
356  * Routine to recompute the "NINVISIBLE" and "AINVISIBLE" bits on node and arc protos
357  * according to whether or not all layers are invisible.
358  */
us_figuretechselectability(void)359 void us_figuretechselectability(void)
360 {
361 	REGISTER INTBIG j, k;
362 	REGISTER TECHNOLOGY *tech;
363 	REGISTER NODEPROTO *np;
364 	REGISTER ARCPROTO *ap;
365 	REGISTER NODEINST *ni;
366 	NODEINST node;
367 	REGISTER ARCINST *ai;
368 	ARCINST arc;
369 	static POLYGON *poly = NOPOLYGON;
370 
371 	/* get polygon */
372 	(void)needstaticpolygon(&poly, 4, us_tool->cluster);
373 
374 	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
375 	{
376 		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
377 		{
378 			np->userbits &= ~NINVISIBLE;
379 			ni = &node;   initdummynode(ni);
380 			ni->proto = np;
381 			ni->lowx = np->lowx;   ni->highx = np->highx;
382 			ni->lowy = np->lowy;   ni->highy = np->highy;
383 			j = nodepolys(ni, 0, NOWINDOWPART);
384 			for(k=0; k<j; k++)
385 			{
386 				shapenodepoly(ni, k, poly);
387 				if (poly->desc->bits == LAYERN) continue;
388 				if ((poly->desc->colstyle&INVISIBLE) == 0) break;
389 			}
390 			if (k >= j) np->userbits |= NINVISIBLE;
391 		}
392 		for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
393 		{
394 			ap->userbits &= ~AINVISIBLE;
395 			ai = &arc;   initdummyarc(ai);
396 			ai->proto = ap;
397 			j = arcpolys(ai, NOWINDOWPART);
398 			for(k=0; k<j; k++)
399 			{
400 				shapearcpoly(ai, k, poly);
401 				if (poly->desc->bits == LAYERN) continue;
402 				if ((poly->desc->colstyle&INVISIBLE) == 0) break;
403 			}
404 			if (k >= j) ap->userbits |= AINVISIBLE;
405 		}
406 	}
407 }
408 
409 /*
410  * routine to examine the current window structure and fit their sizes
411  * to the screen.  If "placemenu" is nonzero, set the menu location too.
412  */
us_windowfit(WINDOWFRAME * whichframe,BOOLEAN placemenu,INTBIG scaletofit)413 void us_windowfit(WINDOWFRAME *whichframe, BOOLEAN placemenu, INTBIG scaletofit)
414 {
415 	REGISTER WINDOWPART *w;
416 	REGISTER INTBIG lowy, highy, ulx, uhx, uly, uhy, drawlx, drawhx, drawly, drawhy,
417 		mtop, mleft, alltext;
418 	INTBIG swid, shei, mwid, mhei, pwid;
419 	REGISTER INTBIG i, total, newwid, newhei, offx, offy;
420 	INTBIG slx, shx, sly, shy;
421 	REGISTER WINDOWFRAME *frame;
422 	REGISTER VARIABLE *var;
423 	COMMANDBINDING commandbinding;
424 
425 	for(frame = el_firstwindowframe; frame != NOWINDOWFRAME; frame = frame->nextwindowframe)
426 	{
427 		if (whichframe != NOWINDOWFRAME && whichframe != frame) continue;
428 		getwindowframesize(frame, &swid, &shei);
429 		lowy = 0;   highy = shei - 1;
430 
431 		/* presume that there is no menu */
432 		drawlx = 0;      drawhx = swid-1;
433 		drawly = lowy;   drawhy = highy;
434 
435 		/* if there is a menu, figure it out */
436 		if ((us_tool->toolstate&MENUON) != 0)
437 		{
438 			/* see if the menu is all text */
439 			alltext = 0;
440 			var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_binding_menu_key);
441 			if (var != NOVARIABLE)
442 			{
443 				total = us_menux*us_menuy;
444 				for(i=0; i<total; i++)
445 				{
446 					us_parsebinding(((CHAR **)var->addr)[i], &commandbinding);
447 					if (*commandbinding.command == 0 || commandbinding.nodeglyph != NONODEPROTO ||
448 						commandbinding.arcglyph != NOARCPROTO)
449 					{
450 						us_freebindingparse(&commandbinding);
451 						break;
452 					}
453 					us_freebindingparse(&commandbinding);
454 				}
455 				if (i >= total) alltext = 1;
456 			}
457 			if (us_menuframe == NOWINDOWFRAME)
458 			{
459 				/* menus come out of the only editor window */
460 				switch (us_menupos)
461 				{
462 					case 0:		/* menu at top */
463 						us_menuxsz = us_menuysz = swid / us_menux;
464 						us_menulx = (swid - us_menux*us_menuxsz)/2;
465 						us_menuhx = swid - us_menulx;
466 						us_menuly = highy - us_menuysz*us_menuy;
467 						us_menuhy = highy;
468 						drawlx = 0;      drawhx = swid-1;
469 						drawly = lowy;   drawhy = us_menuly-1;
470 						break;
471 					case 1:		/* menu at bottom */
472 						us_menuxsz = us_menuysz = swid / us_menux;
473 						us_menulx = (swid - us_menux*us_menuxsz)/2;
474 						us_menuhx = swid - us_menulx;
475 						us_menuly = lowy;
476 						us_menuhy = lowy + us_menuysz * us_menuy;
477 						drawlx = 0;             drawhx = swid-1;
478 						drawly = us_menuhy+1;   drawhy = highy;
479 						break;
480 					case 2:		/* menu on left */
481 						us_menuxsz = us_menuysz = (highy-lowy) / us_menuy;
482 
483 						/* if the menu is all text, allow nonsquare menus */
484 						if (alltext != 0) us_menuxsz = ALLTEXTWIDTH / us_menux;
485 
486 						us_menulx = 0;
487 						us_menuhx = us_menuxsz * us_menux;
488 						us_menuly = ((highy-lowy) - us_menuy*us_menuysz)/2 + lowy;
489 						us_menuhy = us_menuly + us_menuy*us_menuysz;
490 						drawlx = us_menuhx+1;   drawhx = swid-1;
491 						drawly = lowy;          drawhy = highy;
492 						break;
493 					case 3:		/* menu on right */
494 						us_menuxsz = us_menuysz = (highy-lowy) / us_menuy;
495 
496 						/* if the menu is all text, allow nonsquare menus */
497 						if (alltext != 0) us_menuxsz = ALLTEXTWIDTH / us_menux;
498 
499 						us_menulx = swid - us_menuxsz * us_menux;
500 						us_menuhx = swid-1;
501 						us_menuly = ((highy-lowy) - us_menuy*us_menuysz)/2 + lowy;
502 						us_menuhy = us_menuly + us_menuy*us_menuysz;
503 						drawlx = 0;      drawhx = us_menulx-1;
504 						drawly = lowy;   drawhy = highy;
505 						break;
506 				}
507 			} else
508 			{
509 				/* floating menu window */
510 				if (frame == us_menuframe && placemenu)
511 				{
512 					getpaletteparameters(&mwid, &mhei, &pwid);
513 					switch (us_menupos)
514 					{
515 						case 0:		/* menu at top */
516 						case 1:		/* menu at bottom */
517 							us_menuxsz = us_menuysz = mwid / us_menux;
518 							if (us_menuysz * us_menuy > pwid)
519 								us_menuxsz = us_menuysz = pwid / us_menuy;
520 							us_menulx = 0;
521 							us_menuhx = us_menux * us_menuxsz;
522 							us_menuly = 0;
523 							us_menuhy = us_menuy * us_menuysz;
524 							mleft = 0;
525 							if (us_menupos == 0)
526 							{
527 								/* menu on the top */
528 								mtop = 1;
529 							} else
530 							{
531 								/* menu on the bottom */
532 								mtop = mhei - us_menuysz*us_menuy - 3;
533 							}
534 							break;
535 
536 						case 2:		/* menu on left */
537 						case 3:		/* menu on right */
538 							/* determine size of menu entries */
539 							us_menuxsz = us_menuysz = mhei / us_menuy;
540 							if (us_menuxsz * us_menux > pwid)
541 								us_menuxsz = us_menuysz = pwid / us_menux;
542 
543 							/* if the menu is all text, allow nonsquare menus */
544 							if (alltext != 0) us_menuxsz = ALLTEXTWIDTH / us_menux;
545 
546 							/* compute menu parameters */
547 							us_menuly = 0;
548 							us_menuhy = us_menuy * us_menuysz;
549 							us_menulx = 0;
550 							us_menuhx = us_menux * us_menuxsz;
551 							mtop = 0;
552 							if (us_menupos == 2)
553 							{
554 								/* menu on the left */
555 								mleft = 0;
556 							} else
557 							{
558 								/* menu on the right */
559 								mleft = mwid - us_menuxsz * us_menux - 2;
560 							}
561 							break;
562 						default:
563 							mtop = mleft = 0;
564 					}
565 					sizewindowframe(us_menuframe, us_menuhx-us_menulx, us_menuhy-us_menuly);
566 					movewindowframe(us_menuframe, mleft, mtop);
567 				}
568 			}
569 		}
570 
571 		/* now fit the windows in the remaining space */
572 		for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
573 		{
574 			/* this window must be on the right frame */
575 			if (w->frame != frame) continue;
576 
577 			/* entire window is handled simply */
578 			if (estrcmp(w->location, x_("entire")) == 0)
579 			{
580 				ulx = drawlx;              uhx = drawhx;
581 				uly = drawly;              uhy = drawhy;
582 			} else if (estrncmp(w->location, x_("top"), 3) == 0)
583 			{
584 				ulx = drawlx;              uhx = drawhx;
585 				uly = (drawhy-drawly)*(100-w->vratio)/100;
586 				uhy = drawhy;
587 			} else if (estrncmp(w->location, x_("bot"), 3) == 0)
588 			{
589 				ulx = drawlx;              uhx = drawhx;
590 				uly = drawly;              uhy = (drawhy-drawly)*w->vratio/100;
591 			} else if (estrcmp(w->location, x_("left")) == 0)
592 			{
593 				ulx = drawlx;              uhx = (drawhx-drawlx)*w->hratio/100;
594 				uly = drawly;              uhy = drawhy;
595 			} else if (estrcmp(w->location, x_("right")) == 0)
596 			{
597 				ulx = (drawhx-drawlx)*(100-w->hratio)/100;
598 				uhx = drawhx;
599 				uly = drawly;              uhy = drawhy;
600 			} else ulx = uhx = uly = uhy = 0;
601 
602 			/* subdivide for fractions of half windows */
603 			i = 3;
604 			while (w->location[i] == '-')
605 			{
606 				switch (w->location[i+1])
607 				{
608 					case 'l': uhx = (uhx - ulx)*w->hratio/100;         break;
609 					case 'r': ulx = (uhx - ulx)*(100-w->hratio)/100;   break;
610 					case 't': uly = (uhy - uly)*(100-w->vratio)/100;   break;
611 					case 'b': uhy = (uhy - uly)*w->vratio/100;         break;
612 				}
613 				i += 2;
614 			}
615 			if (estrcmp(w->location, x_("entire")) != 0)
616 			{
617 				ulx++;   uhx--;   uly++;   uhy--;
618 			}
619 
620 			/* make sure window has some size */
621 			if (ulx >= uhx) uhx = ulx + 1;
622 			if (uly >= uhy) uhy = uly + 1;
623 
624 			/* make room for border if in a mode */
625 			if ((w->state&WINDOWMODE) != 0)
626 			{
627 				ulx += WINDOWMODEBORDERSIZE;   uhx -= WINDOWMODEBORDERSIZE;
628 				uly += WINDOWMODEBORDERSIZE;   uhy -= WINDOWMODEBORDERSIZE;
629 			}
630 
631 			/* make room for sliders if a display window */
632 			if ((w->state&WINDOWTYPE) == DISPWINDOW)
633 			{
634 				uhx -= DISPLAYSLIDERSIZE;
635 				uly += DISPLAYSLIDERSIZE;
636 			}
637 			if ((w->state&WINDOWTYPE) == WAVEFORMWINDOW)
638 			{
639 				ulx += DISPLAYSLIDERSIZE;
640 				uly += DISPLAYSLIDERSIZE;
641 			}
642 
643 			/* update if the extent changed */
644 			if (w->uselx != ulx || w->usehx != uhx ||
645 				w->usely != uly || w->usehy != uhy)
646 			{
647 				/* set the window extent */
648 				w->uselx = ulx;      w->usehx = uhx;
649 				w->usely = uly;      w->usehy = uhy;
650 
651 				/* now adjust the database extents of the window */
652 				slx = w->screenlx;   shx = w->screenhx;
653 				sly = w->screenly;   shy = w->screenhy;
654 				if (scaletofit > 0)
655 				{
656 					us_squarescreen(w, NOWINDOWPART, FALSE, &slx, &shx, &sly, &shy, 0);
657 				} else if (scaletofit < 0)
658 				{
659 					newwid = (INTBIG)(((float)(uhx - ulx)) / w->scalex + 0.5);
660 					newhei = (INTBIG)(((float)(uhy - uly)) / w->scaley + 0.5);
661 					offx = newwid - (shx - slx);
662 					offy = newhei - (shy - sly);
663 					slx -= offx / 2;
664 					shx = slx + newwid;
665 					sly -= offy / 2;
666 					shy = sly + newhei;
667 				}
668 				w->screenlx = slx;   w->screenhx = shx;
669 				w->screenly = sly;   w->screenhy = shy;
670 				computewindowscale(w);
671 			}
672 		}
673 	}
674 }
675 
676 /*
677  * routine to adjust the actual drawing area of window "win" to account for
678  * the appearance or disappearance of the red simulation border
679  */
us_setwindowmode(WINDOWPART * win,INTBIG oldstate,INTBIG newstate)680 void us_setwindowmode(WINDOWPART *win, INTBIG oldstate, INTBIG newstate)
681 {
682 	if ((oldstate&WINDOWMODE) != 0)
683 	{
684 		/* was in a mode: remove border */
685 		win->uselx -= WINDOWMODEBORDERSIZE;   win->usehx += WINDOWMODEBORDERSIZE;
686 		win->usely -= WINDOWMODEBORDERSIZE;   win->usehy += WINDOWMODEBORDERSIZE;
687 	}
688 	if ((newstate&WINDOWMODE) != 0)
689 	{
690 		/* now in a mode: add border */
691 		win->uselx += WINDOWMODEBORDERSIZE;   win->usehx -= WINDOWMODEBORDERSIZE;
692 		win->usely += WINDOWMODEBORDERSIZE;   win->usehy -= WINDOWMODEBORDERSIZE;
693 	}
694 	win->state = newstate;
695 }
696 
697 /*
698  * routine to tell the names of the windows that result when the window
699  * with name "w" is split.  The strings "hwind1" and "hwind2" are filled
700  * with the names if the window is split horizontally.  The strings "vwind1"
701  * and "vwind2" are filled with the names if the window is split verticaly.
702  */
us_splitwindownames(CHAR * w,CHAR * hwind1,CHAR * hwind2,CHAR * vwind1,CHAR * vwind2)703 void us_splitwindownames(CHAR *w, CHAR *hwind1, CHAR *hwind2, CHAR *vwind1, CHAR *vwind2)
704 {
705 	REGISTER INTBIG i;
706 
707 	if (estrcmp(w, x_("entire")) == 0)
708 	{
709 		(void)estrcpy(hwind1, x_("top"));   (void)estrcpy(hwind2, x_("bottom"));
710 		(void)estrcpy(vwind1, x_("left"));  (void)estrcpy(vwind2, x_("right"));
711 		return;
712 	}
713 	if (estrcmp(w, x_("top")) == 0)
714 	{
715 		(void)estrcpy(hwind1, x_("top-l")); (void)estrcpy(hwind2, x_("top-r"));
716 		(void)estrcpy(vwind1, x_(""));      (void)estrcpy(vwind2, x_(""));
717 		return;
718 	}
719 	if (estrcmp(w, x_("bottom")) == 0)
720 	{
721 		(void)estrcpy(hwind1, x_("bot-l")); (void)estrcpy(hwind2, x_("bot-r"));
722 		(void)estrcpy(vwind1, x_(""));      (void)estrcpy(vwind2, x_(""));
723 		return;
724 	}
725 	if (estrcmp(w, x_("left")) == 0)
726 	{
727 		(void)estrcpy(vwind1, x_("top-l")); (void)estrcpy(vwind2, x_("bot-l"));
728 		(void)estrcpy(hwind1, x_(""));      (void)estrcpy(hwind2, x_(""));
729 		return;
730 	}
731 	if (estrcmp(w, x_("right")) == 0)
732 	{
733 		(void)estrcpy(vwind1, x_("top-r")); (void)estrcpy(vwind2, x_("bot-r"));
734 		(void)estrcpy(hwind1, x_(""));      (void)estrcpy(hwind2, x_(""));
735 		return;
736 	}
737 	(void)estrcpy(hwind1, w);   (void)estrcpy(hwind2, w);
738 	(void)estrcpy(vwind1, w);   (void)estrcpy(vwind2, w);
739 	i = w[estrlen(w)-1];
740 	if (i == 'l' || i == 'r')
741 	{
742 		(void)estrcat(vwind1, x_("-t"));  (void)estrcat(vwind2, x_("-b"));
743 		(void)estrcpy(hwind1, x_(""));    (void)estrcpy(hwind2, x_(""));
744 	} else
745 	{
746 		(void)estrcat(hwind1, x_("-l"));  (void)estrcat(hwind2, x_("-r"));
747 		(void)estrcpy(vwind1, x_(""));    (void)estrcpy(vwind2, x_(""));
748 	}
749 }
750 
751 /*
752  * Routine to create a new window with whatever method is available on the
753  * current machine (new window in its own frame or just a split of the current
754  * window).  If "orientation" is 1, make it a horizontal window; if 2,
755  * make it vertical.  Otherwise use any configuration.  Prints an error and
756  * returns NOWINDOWPART on failure.
757  */
us_wantnewwindow(INTBIG orientation)758 WINDOWPART *us_wantnewwindow(INTBIG orientation)
759 {
760 	REGISTER WINDOWPART *w;
761 	WINDOWFRAME *wf;
762 
763 	if (graphicshas(CANUSEFRAMES) && orientation == 0)
764 	{
765 		/* create a default window space on this frame */
766 		wf = newwindowframe(FALSE, 0);
767 		if (wf == NOWINDOWFRAME) wf = getwindowframe(FALSE);
768 		w = newwindowpart(x_("entire"), NOWINDOWPART);
769 		if (w == NOWINDOWPART)
770 		{
771 			us_abortcommand(_("Cannot create new window"));
772 			return(NOWINDOWPART);
773 		}
774 		w->frame = wf;
775 		w->buttonhandler = DEFAULTBUTTONHANDLER;
776 		w->charhandler = DEFAULTCHARHANDLER;
777 		w->changehandler = DEFAULTCHANGEHANDLER;
778 		w->termhandler = DEFAULTTERMHANDLER;
779 		w->redisphandler = DEFAULTREDISPHANDLER;
780 		(void)setvalkey((INTBIG)us_tool, VTOOL, us_current_window_key, (INTBIG)w,
781 			VWINDOWPART|VDONTSAVE);
782 
783 		/* now draw everything */
784 		us_drawmenu(0, wf);
785 		return(w);
786 	}
787 
788 	if (us_needwindow()) return(NOWINDOWPART);
789 	w = us_splitcurrentwindow(orientation, FALSE, 0, 50);
790 	return(w);
791 }
792 
793 /*
794  * routine to split the current window into two windows.  If "splitkey" is zero,
795  * nature of split is unspecified.  If "splitkey" is 1, split horizontally
796  * (only when splitting top window).  If "splitkey" is 2, split vertically.
797  * If "fillboth" is true, fill both windows with the contents of the
798  * old one.  Otherwise, leave the new current window empty.  Returns the address
799  * of the new current window, and the other half in "other" (NOWINDOWPART on error).
800  * The partitions are divided according to "percentage" of coverage.
801  */
us_splitcurrentwindow(INTBIG splitkey,BOOLEAN fillboth,WINDOWPART ** other,INTBIG percentage)802 WINDOWPART *us_splitcurrentwindow(INTBIG splitkey, BOOLEAN fillboth, WINDOWPART **other,
803 	INTBIG percentage)
804 {
805 	CHAR wind1[40], wind2[40], vwind1[40], vwind2[40];
806 	REGISTER CHAR *win1, *win2;
807 	WINDOWPART windowsave;
808 	REGISTER WINDOWPART *w2, *w3, *w, *retwin;
809 	REGISTER INTBIG curwx, curwy, horizsplit;
810 	REGISTER INTBIG x, y, l;
811 	REGISTER NODEPROTO *np;
812 
813 	/* figure out new name of windows */
814 	if (other != 0) *other = NOWINDOWPART;
815 	horizsplit = 1;
816 	us_splitwindownames(el_curwindowpart->location, wind1, wind2, vwind1, vwind2);
817 
818 	/* use the horizontal window split unless there is none */
819 	if (*wind1 == 0) win1 = vwind1; else win1 = wind1;
820 	if (*wind2 == 0) win2 = vwind2; else win2 = wind2;
821 
822 	/* special case when splitting just one window: which way to split */
823 	if (estrcmp(el_curwindowpart->location, x_("entire")) == 0)
824 	{
825 		/* see if a "horizontal" or "vertical" parameter was given */
826 		if (splitkey == 2)
827 		{
828 			/* vertical window specified explicitly */
829 			win1 = vwind1;   win2 = vwind2;
830 			horizsplit = 0;
831 		} else if (splitkey == 0)
832 		{
833 			/* make a guess about window splitting */
834 			switch (el_curwindowpart->state&WINDOWTYPE)
835 			{
836 				case EXPLORERWINDOW:
837 					win1 = vwind2;   win2 = vwind1;
838 					horizsplit = 0;
839 					break;
840 				default:
841 					np = el_curwindowpart->curnodeproto;
842 					if (np != NONODEPROTO)
843 					{
844 						curwx = el_curwindowpart->usehx - el_curwindowpart->uselx;
845 						curwy = el_curwindowpart->usehy - el_curwindowpart->usely;
846 						x = np->highx - np->lowx;
847 						y = np->highy - np->lowy;
848 						l = el_curlib->lambda[el_curtech->techindex];
849 						if (muldiv(x, curwy/2, l) + muldiv(y, curwx, l) >=
850 							muldiv(x, curwy, l) + muldiv(y, curwx/2, l))
851 						{
852 							/* vertical window makes more sense */
853 							win1 = vwind1;   win2 = vwind2;
854 							horizsplit = 0;
855 						}
856 					}
857 					break;
858 			}
859 		}
860 	} else
861 	{
862 		l = estrlen(wind1) - 1;
863 		if (wind1[l] == 'l' || wind1[l] == 'r') horizsplit = 0;
864 	}
865 
866 	/* turn off object and window highlighting */
867 	us_pushhighlight();
868 	us_clearhighlightcount();
869 	w = el_curwindowpart;
870 	copywindowpart(&windowsave, el_curwindowpart);
871 
872 	/* make two new windows in "w2" and "w3" to replace "el_curwindowpart" */
873 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_current_window_key, (INTBIG)NOWINDOWPART,
874 		VWINDOWPART|VDONTSAVE);
875 	startobjectchange((INTBIG)us_tool, VTOOL);
876 	w2 = newwindowpart(win1, w);
877 	w3 = newwindowpart(win2, w);
878 	if (w2 == NOWINDOWPART || w3 == NOWINDOWPART)
879 	{
880 		ttyputnomemory();
881 		return(NOWINDOWPART);
882 	}
883 
884 	/* make sure the split is even in the split direction */
885 	if (horizsplit != 0)
886 	{
887 		w2->vratio = percentage;
888 		w3->vratio = 100 - percentage;
889 	} else
890 	{
891 		w2->hratio = percentage;
892 		w3->hratio = 100 - percentage;
893 	}
894 
895 	/* if splitting an editor window, move the editor and explorer structure */
896 	if ((w->state&WINDOWTYPE) == TEXTWINDOW || (w->state&WINDOWTYPE) == POPTEXTWINDOW)
897 	{
898 		(void)setval((INTBIG)w3, VWINDOWPART, x_("editor"), (INTBIG)w->editor, VADDRESS);
899 		(void)setval((INTBIG)w, VWINDOWPART, x_("editor"), -1, VADDRESS);
900 	}
901 	if ((w->state&WINDOWTYPE) == EXPLORERWINDOW)
902 	{
903 		(void)setval((INTBIG)w3, VWINDOWPART, x_("expwindow"), (INTBIG)w->expwindow, VADDRESS);
904 		(void)setval((INTBIG)w, VWINDOWPART, x_("expwindow"), -1, VADDRESS);
905 	}
906 
907 	/* free the current window */
908 	killwindowpart(w);
909 
910 	/* zap the returned window if both are not to be filled */
911 	retwin = w2;
912 	if (other != 0) *other = w3;
913 	if ((windowsave.state&WINDOWTYPE) != DISPWINDOW) fillboth = FALSE;
914 	if (!fillboth)
915 	{
916 		retwin->state = (retwin->state & ~(WINDOWTYPE|GRIDON|WINDOWMODE)) | DISPWINDOW;
917 		retwin->buttonhandler = DEFAULTBUTTONHANDLER;
918 		retwin->charhandler = DEFAULTCHARHANDLER;
919 		retwin->changehandler = DEFAULTCHANGEHANDLER;
920 		retwin->termhandler = DEFAULTTERMHANDLER;
921 		retwin->redisphandler = DEFAULTREDISPHANDLER;
922 		retwin->curnodeproto = NONODEPROTO;
923 		retwin->editor = NOEDITOR;
924 		retwin->expwindow = (void *)-1;
925 	}
926 
927 	/* set the window extents */
928 	us_windowfit(w2->frame, FALSE, 1);
929 
930 	/* use former window for scaling */
931 	w = &windowsave;
932 
933 	/* windows might have got bigger: see if grid can be drawn */
934 	if ((w2->state&GRIDTOOSMALL) != 0) us_gridset(w2, GRIDON);
935 	if ((w2->state&GRIDTOOSMALL) != 0) us_gridset(w3, GRIDON);
936 
937 	endobjectchange((INTBIG)us_tool, VTOOL);
938 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_current_window_key, (INTBIG)w2,
939 		VWINDOWPART|VDONTSAVE);
940 
941 	/* restore all highlighting */
942 	us_pophighlight(FALSE);
943 	return(retwin);
944 }
945 
946 /*
947  * routine to kill a window.  Kills the current window if "thisw" is true.
948  * Kills the other window, making the current one larger, if "thisw" is false.
949  */
us_killcurrentwindow(BOOLEAN thisw)950 void us_killcurrentwindow(BOOLEAN thisw)
951 {
952 	REGISTER WINDOWPART *w1, *w2, *wnew, *w;
953 	WINDOWPART windowsave;
954 	REGISTER INTBIG windows;
955 	CHAR windcomb[40], windother[40], wind1[40], wind2[40], vwind1[40], vwind2[40];
956 	REGISTER WINDOWFRAME *wf;
957 
958 	w1 = el_curwindowpart;
959 
960 	/* if this is the only partition, see if the window frame can be deleted */
961 	if (estrcmp(w1->location, x_("entire")) == 0)
962 	{
963 		if (!graphicshas(CANHAVENOWINDOWS))
964 		{
965 			/* disallow deletion if this is the last window */
966 			windows = 0;
967 			for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
968 				if (wf->floating == 0) windows++;
969 			if (windows <= 1)
970 			{
971 				ttyputerr(_("Sorry, cannot delete the last window"));
972 				return;
973 			}
974 		}
975 		if (graphicshas(CANUSEFRAMES))
976 		{
977 			/* save highlighting and turn it off */
978 			us_pushhighlight();
979 			us_clearhighlightcount();
980 
981 			/* kill the window */
982 			startobjectchange((INTBIG)us_tool, VTOOL);
983 			us_killwindowpickanother(w1);
984 			endobjectchange((INTBIG)us_tool, VTOOL);
985 
986 			/* restore highlighting */
987 			us_pophighlight(FALSE);
988 			return;
989 		}
990 	}
991 
992 	/* figure out which other window to merge this with */
993 	if (estrcmp(w1->location, x_("top")) == 0 || estrcmp(w1->location, x_("bottom")) == 0 ||
994 		estrcmp(w1->location, x_("left")) == 0 || estrcmp(w1->location, x_("right")) == 0)
995 			(void)estrcpy(windcomb, x_("entire")); else
996 	{
997 		(void)estrcpy(windcomb, w1->location);
998 		windcomb[estrlen(windcomb)-2] = 0;
999 		if (estrcmp(windcomb, x_("bot")) == 0) (void)estrcpy(windcomb, x_("bottom"));
1000 	}
1001 
1002 	/* see what divisions this higher window typically makes */
1003 	us_splitwindownames(windcomb, wind1, wind2, vwind1, vwind2);
1004 
1005 	/* look for the other window of the typical split */
1006 	(void)estrcpy(windother, x_(""));
1007 	if (estrcmp(wind2, w1->location) == 0) (void)estrcpy(windother, wind1);
1008 	if (estrcmp(wind1, w1->location) == 0) (void)estrcpy(windother, wind2);
1009 	if (estrcmp(vwind2, w1->location) == 0) (void)estrcpy(windother, vwind1);
1010 	if (estrcmp(vwind1, w1->location) == 0) (void)estrcpy(windother, vwind2);
1011 
1012 	/* see if there is a window with that name */
1013 	for(w2 = el_topwindowpart; w2 != NOWINDOWPART; w2 = w2->nextwindowpart)
1014 		if (estrcmp(w2->location, windother) == 0) break;
1015 
1016 	/* if the other window can't be found, try one more hack */
1017 	if (w2 == NOWINDOWPART)
1018 	{
1019 		/* special case for quadrants that get split strangely */
1020 		if ((estrncmp(w1->location, x_("top-"), 4) == 0 || estrncmp(w1->location, x_("bot-"), 4) == 0) &&
1021 			estrlen(w1->location) == 5)
1022 		{
1023 			if (*w1->location == 't') (void)estrcpy(windother, x_("bot-l")); else
1024 				(void)estrcpy(windother, x_("top-l"));
1025 			windother[4] = w1->location[4];
1026 			if (windother[4] == 'l') (void)estrcpy(windcomb, x_("left")); else
1027 				(void)estrcpy(windcomb, x_("right"));
1028 			for(w2 = el_topwindowpart; w2 != NOWINDOWPART; w2 = w2->nextwindowpart)
1029 				if (estrcmp(w2->location, windother) == 0) break;
1030 		}
1031 	}
1032 	if (w2 == NOWINDOWPART)
1033 	{
1034 		us_abortcommand(_("Cannot kill the current window"));
1035 		return;
1036 	}
1037 
1038 	/* if the other window is to be killed, swap them */
1039 	if (!thisw)
1040 	{
1041 		w = w1;   w1 = w2;   w2 = w;
1042 	}
1043 
1044 	/* turn off highlighting */
1045 	us_pushhighlight();
1046 	us_clearhighlightcount();
1047 
1048 	/* create a new window to cover both */
1049 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_current_window_key, (INTBIG)NOWINDOWPART,
1050 		VWINDOWPART|VDONTSAVE);
1051 	startobjectchange((INTBIG)us_tool, VTOOL);
1052 	wnew = newwindowpart(windcomb, w2);
1053 	if (wnew == NOWINDOWPART) return;
1054 
1055 	/* save information from the old window */
1056 	copywindowpart(&windowsave, w2);
1057 
1058 	/* if merging an editor or explorer window, move the structure */
1059 	if ((w2->state&WINDOWTYPE) == TEXTWINDOW || (w2->state&WINDOWTYPE) == POPTEXTWINDOW)
1060 	{
1061 		(void)setval((INTBIG)wnew, VWINDOWPART, x_("editor"), (INTBIG)w2->editor, VADDRESS);
1062 		(void)setval((INTBIG)w2, VWINDOWPART, x_("editor"), -1, VADDRESS);
1063 	}
1064 	if ((w2->state&WINDOWTYPE) == EXPLORERWINDOW)
1065 	{
1066 		(void)setval((INTBIG)wnew, VWINDOWPART, x_("expwindow"), (INTBIG)w2->expwindow, VADDRESS);
1067 		(void)setval((INTBIG)w2, VWINDOWPART, x_("expwindow"), -1, VADDRESS);
1068 	}
1069 
1070 	/* remove old windows */
1071 	killwindowpart(w1);
1072 	killwindowpart(w2);
1073 
1074 	/* set window extents */
1075 	us_windowfit(wnew->frame, FALSE, 1);
1076 
1077 	/* use former window for scaling */
1078 	w = &windowsave;
1079 
1080 	/* window might have got bigger: see if grid can be drawn */
1081 	if ((wnew->state&GRIDTOOSMALL) != 0) us_gridset(wnew, GRIDON);
1082 
1083 	(void)setval((INTBIG)el_curlib, VLIBRARY, x_("curnodeproto"), (INTBIG)wnew->curnodeproto,
1084 		VNODEPROTO);
1085 	endobjectchange((INTBIG)us_tool, VTOOL);
1086 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_current_window_key, (INTBIG)wnew,
1087 		VWINDOWPART|VDONTSAVE);
1088 
1089 	/* restore highlighting */
1090 	us_pophighlight(FALSE);
1091 }
1092 
1093 /*
1094  * routine to determine whether the division of window "w1" into windows
1095  * "w2" and "w3" can be done with block transfer.  This requires that
1096  * the windows have identical aspect ratios in one axis
1097  */
us_windowcansplit(WINDOWPART * w1,WINDOWPART * w2,WINDOWPART * w3)1098 BOOLEAN us_windowcansplit(WINDOWPART *w1, WINDOWPART *w2, WINDOWPART *w3)
1099 {
1100 	REGISTER INTBIG new2l, new2h;
1101 
1102 	if (w2->usehx-w2->uselx == w3->usehx-w3->uselx &&
1103 		w2->screenhx-w2->screenlx == w3->screenhx-w3->screenlx)
1104 	{
1105 		/* first see if it is an obvious split */
1106 		if (w2->usehx-w2->uselx == w1->usehx-w1->uselx &&
1107 			w2->screenhx-w2->screenlx == w1->screenhx-w1->screenlx)
1108 				return(TRUE);
1109 
1110 		/* now see if it is a relative fit for changed window size */
1111 		new2l = muldiv(((w1->usehx-w1->uselx) - (w2->usehx-w2->uselx))/2,
1112 			w1->screenhx-w1->screenlx, w1->usehx-w1->uselx) + w1->screenlx;
1113 		new2h = w2->screenlx + muldiv(w2->usehx-w2->uselx,
1114 			w1->screenhx-w1->screenlx, w1->usehx-w1->uselx);
1115 		if (new2l == w2->screenlx && new2h == w2->screenhx) return(TRUE);
1116 	}
1117 	if (w2->usehy-w2->usely == w3->usehy-w3->usely &&
1118 		w2->screenhy-w2->screenly == w3->screenhy-w3->screenly)
1119 	{
1120 		/* first see if it is an obvious split */
1121 		if (w2->usehy-w2->usely == w1->usehy-w1->usely &&
1122 			w2->screenhy-w2->screenly == w1->screenhy-w1->screenly)
1123 				return(TRUE);
1124 
1125 		/* now see if it is a relative fit for changed window size */
1126 		new2l = muldiv(((w1->usehy-w1->usely) - (w2->usehy-w2->usely))/2,
1127 			w1->screenhy-w1->screenly, w1->usehy-w1->usely) + w1->screenly;
1128 		new2h = w2->screenly + muldiv(w2->usehy-w2->usely,
1129 			w1->screenhy-w1->screenly, w1->usehy-w1->usely);
1130 		if (new2l == w2->screenly && new2h == w2->screenhy) return(TRUE);
1131 	}
1132 	return(FALSE);
1133 }
1134 
1135 /*
1136  * routine to ensure that the cell "np"
1137  * is displayed somewhere on the screen.  If not, it
1138  * is displayed in the current window
1139  */
us_ensurewindow(NODEPROTO * np)1140 void us_ensurewindow(NODEPROTO *np)
1141 {
1142 	REGISTER WINDOWPART *w;
1143 	CHAR *par[1];
1144 
1145 	/* if nothing specified, quit */
1146 	if (np == NONODEPROTO) return;
1147 
1148 	/* see if that cell is in a window */
1149 	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
1150 		if (w->curnodeproto == np) break;
1151 
1152 	/* if the cell is not in a window, put it there */
1153 	if (w == NOWINDOWPART)
1154 	{
1155 		par[0] = describenodeproto(np);
1156 		us_editcell(1, par);
1157 		us_endchanges(NOWINDOWPART);
1158 	}
1159 }
1160 
1161 /*
1162  * Routine to kill window "w" and set the current window to some other.
1163  */
us_killwindowpickanother(WINDOWPART * w)1164 void us_killwindowpickanother(WINDOWPART *w)
1165 {
1166 	REGISTER NODEPROTO *np;
1167 
1168 	killwindowpart(w);
1169 
1170 	if (w != el_curwindowpart) return;
1171 
1172 	w = el_topwindowpart;
1173 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_current_window_key, (INTBIG)w,
1174 		VWINDOWPART|VDONTSAVE);
1175 	if (w != NOWINDOWPART) np = w->curnodeproto; else np = NONODEPROTO;
1176 	(void)setval((INTBIG)el_curlib, VLIBRARY, x_("curnodeproto"), (INTBIG)np, VNODEPROTO);
1177 }
1178 
1179 /*
1180  * routine to adjust the coordinate values in (x, y) from screen space to
1181  * the space of window "w"
1182  */
us_scaletowindow(INTBIG * x,INTBIG * y,WINDOWPART * w)1183 void us_scaletowindow(INTBIG *x, INTBIG *y, WINDOWPART *w)
1184 {
1185 	*x = muldiv(*x - w->uselx, w->screenhx - w->screenlx, w->usehx - w->uselx) + w->screenlx;
1186 	*y = muldiv(*y - w->usely, w->screenhy - w->screenly, w->usehy - w->usely) + w->screenly;
1187 }
1188 
1189 /******************** TEXT EDITING ********************/
1190 
1191 EDITORTABLE us_editortable[] =
1192 {
1193 	/* the point-and-click editor */
1194 	{x_("Point-and-click"),
1195 	us_editpacmakeeditor, us_editpacterminate, us_editpactotallines, us_editpacgetline,
1196 	us_editpacaddline, us_editpacreplaceline, us_editpacdeleteline, us_editpachighlightline,
1197 	us_editpacsuspendgraphics, us_editpacresumegraphics,
1198 	us_editpacwritetextfile, us_editpacreadtextfile,
1199 	us_editpaceditorterm, us_editpacshipchanges, us_editpacgotchar,
1200 	us_editpaccut, us_editpaccopy, us_editpacpaste,
1201 	us_editpacundo, us_editpacsearch, us_editpacpan},
1202 
1203 	/* the EMACS-like editor */
1204 	{x_("EMACS-like"),
1205 	us_editemacsmakeeditor, us_editemacsterminate, us_editemacstotallines, us_editemacsgetline,
1206 	us_editemacsaddline, us_editemacsreplaceline, us_editemacsdeleteline, us_editemacshighlightline,
1207 	us_editemacssuspendgraphics, us_editemacsresumegraphics,
1208 	us_editemacswritetextfile, us_editemacsreadtextfile,
1209 	us_editemacseditorterm, us_editemacsshipchanges, us_editemacsgotchar,
1210 	us_editemacscut, us_editemacscopy, us_editemacspaste,
1211 	us_editemacsundo, us_editemacssearch, us_editemacspan},
1212 
1213 	{NULL,
1214 	NULL, NULL, NULL, NULL,
1215 	NULL, NULL, NULL, NULL,
1216 	NULL, NULL,
1217 	NULL, NULL,
1218 	NULL, NULL, NULL,
1219 	NULL, NULL, NULL,
1220 	NULL, NULL, NULL}
1221 };
1222 
1223 /*
1224  * dispatch routine to describe this editor
1225  */
us_describeeditor(CHAR ** name)1226 void us_describeeditor(CHAR **name)
1227 {
1228 	*name = us_editortable[us_currenteditor].editorname;
1229 }
1230 
1231 /*
1232  * dispatch routine for creating a new editor
1233  */
us_makeeditor(WINDOWPART * oriwin,CHAR * header,INTBIG * chars,INTBIG * lines)1234 WINDOWPART *us_makeeditor(WINDOWPART *oriwin, CHAR *header, INTBIG *chars, INTBIG *lines)
1235 {
1236 	return((*us_editortable[us_currenteditor].makeeditor)(oriwin,header,chars,lines));
1237 }
1238 
1239 /*
1240  * dispatch routine to return the total number of valid lines in the edit buffer
1241  */
us_totallines(WINDOWPART * win)1242 INTBIG us_totallines(WINDOWPART *win)
1243 {
1244 	return((*us_editortable[us_currenteditor].totallines)(win));
1245 }
1246 
1247 /*
1248  * dispatch routine to get the string on line "lindex" (0 based).  A negative line
1249  * returns the current line.  Returns -1 if the index is beyond the file limit
1250  */
us_getline(WINDOWPART * win,INTBIG lindex)1251 CHAR *us_getline(WINDOWPART *win, INTBIG lindex)
1252 {
1253 	return((*us_editortable[us_currenteditor].getline)(win, lindex));
1254 }
1255 
1256 /*
1257  * dispatch routine to add line "str" to the text cell to become line "lindex"
1258  */
us_addline(WINDOWPART * win,INTBIG lindex,CHAR * str)1259 void us_addline(WINDOWPART *win, INTBIG lindex, CHAR *str)
1260 {
1261 	(*us_editortable[us_currenteditor].addline)(win, lindex, str);
1262 }
1263 
1264 /*
1265  * dispatch routine to replace the line number "lindex" with the string "str".
1266  */
us_replaceline(WINDOWPART * win,INTBIG lindex,CHAR * str)1267 void us_replaceline(WINDOWPART *win, INTBIG lindex, CHAR *str)
1268 {
1269 	(*us_editortable[us_currenteditor].replaceline)(win, lindex, str);
1270 }
1271 
1272 /*
1273  * dispatch routine to delete line number "lindex"
1274  */
us_deleteline(WINDOWPART * win,INTBIG lindex)1275 void us_deleteline(WINDOWPART *win, INTBIG lindex)
1276 {
1277 	(*us_editortable[us_currenteditor].deleteline)(win, lindex);
1278 }
1279 
1280 /*
1281  * dispatch routine to highlight lines "lindex" to "hindex" in the text window
1282  */
us_highlightline(WINDOWPART * win,INTBIG lindex,INTBIG hindex)1283 void us_highlightline(WINDOWPART *win, INTBIG lindex, INTBIG hindex)
1284 {
1285 	(*us_editortable[us_currenteditor].highlightline)(win, lindex, hindex);
1286 }
1287 
1288 /*
1289  * dispatch routine to stop the graphic display of changes (for batching)
1290  */
us_suspendgraphics(WINDOWPART * win)1291 void us_suspendgraphics(WINDOWPART *win)
1292 {
1293 	(*us_editortable[us_currenteditor].suspendgraphics)(win);
1294 }
1295 
1296 /*
1297  * dispatch routine to restart the graphic display of changes and redisplay (for batching)
1298  */
us_resumegraphics(WINDOWPART * win)1299 void us_resumegraphics(WINDOWPART *win)
1300 {
1301 	(*us_editortable[us_currenteditor].resumegraphics)(win);
1302 }
1303 
1304 /*
1305  * dispatch routine to write the text file to "file"
1306  */
us_writetextfile(WINDOWPART * win,CHAR * file)1307 void us_writetextfile(WINDOWPART *win, CHAR *file)
1308 {
1309 	(*us_editortable[us_currenteditor].writetextfile)(win, file);
1310 }
1311 
1312 /*
1313  * dispatch routine to read the text file "file"
1314  */
us_readtextfile(WINDOWPART * win,CHAR * file)1315 void us_readtextfile(WINDOWPART *win, CHAR *file)
1316 {
1317 	(*us_editortable[us_currenteditor].readtextfile)(win, file);
1318 }
1319 
1320 /*
1321  * dispatch routine to get the next character
1322  */
us_editorterm(WINDOWPART * w)1323 void us_editorterm(WINDOWPART *w)
1324 {
1325 	(*us_editortable[us_currenteditor].editorterm)(w);
1326 }
1327 
1328 /*
1329  * dispatch routine to force changes from the editor in window "w"
1330  */
us_shipchanges(WINDOWPART * w)1331 void us_shipchanges(WINDOWPART *w)
1332 {
1333 	(*us_editortable[us_currenteditor].shipchanges)(w);
1334 }
1335 
1336 /*
1337  * dispatch routine to get the next character
1338  */
us_gotchar(WINDOWPART * w,INTSML i,INTBIG special)1339 BOOLEAN us_gotchar(WINDOWPART *w, INTSML i, INTBIG special)
1340 {
1341 	return((*us_editortable[us_currenteditor].gotchar)(w, i, special));
1342 }
1343 
1344 /*
1345  * dispatch routine to cut text
1346  */
us_cuttext(WINDOWPART * w)1347 void us_cuttext(WINDOWPART *w)
1348 {
1349 	(*us_editortable[us_currenteditor].cut)(w);
1350 }
1351 
1352 /*
1353  * dispatch routine to copy text
1354  */
us_copytext(WINDOWPART * w)1355 void us_copytext(WINDOWPART *w)
1356 {
1357 	(*us_editortable[us_currenteditor].copy)(w);
1358 }
1359 
1360 /*
1361  * dispatch routine to paste text
1362  */
us_pastetext(WINDOWPART * w)1363 void us_pastetext(WINDOWPART *w)
1364 {
1365 	(*us_editortable[us_currenteditor].paste)(w);
1366 }
1367 
1368 /*
1369  * dispatch routine to undo text changes
1370  */
us_undotext(WINDOWPART * w)1371 void us_undotext(WINDOWPART *w)
1372 {
1373 	(*us_editortable[us_currenteditor].undo)(w);
1374 }
1375 
1376 /*
1377  * dispatch routine to search and/or replace text.  If "replace" is nonzero, this is
1378  * a replace.  The meaning of "bits" is as follows:
1379  *   1   search from top
1380  *   2   replace all
1381  *   4   case sensitive
1382  *   8   search upwards
1383  */
us_searchtext(WINDOWPART * w,CHAR * str,CHAR * replace,INTBIG bits)1384 void us_searchtext(WINDOWPART *w, CHAR *str, CHAR *replace, INTBIG bits)
1385 {
1386 	(*us_editortable[us_currenteditor].search)(w, str, replace, bits);
1387 }
1388 
1389 /*
1390  * dispatch routine to pan the text window by (dx, dy)
1391  */
us_pantext(WINDOWPART * w,INTBIG dx,INTBIG dy)1392 void us_pantext(WINDOWPART *w, INTBIG dx, INTBIG dy)
1393 {
1394 	(*us_editortable[us_currenteditor].pan)(w, dx, dy);
1395 }
1396 
1397 /*
1398  * support routine to allocate a new editor from the pool (if any) or memory
1399  * routine returns NOEDITOR upon error
1400  */
us_alloceditor(void)1401 EDITOR *us_alloceditor(void)
1402 {
1403 	REGISTER EDITOR *e;
1404 
1405 	e = (EDITOR *)emalloc((sizeof (EDITOR)), us_tool->cluster);
1406 	if (e == 0) return(NOEDITOR);
1407 	e->state = 0;
1408 	e->nexteditor = NOEDITOR;
1409 	e->editobjvar = NOVARIABLE;
1410 	return(e);
1411 }
1412 
1413 /*
1414  * support routine to return editor "e" to the pool of free editors
1415  */
us_freeeditor(EDITOR * e)1416 void us_freeeditor(EDITOR *e)
1417 {
1418 	if (e == 0 || e == NOEDITOR) return;
1419 	efree(e->header);
1420 	(*us_editortable[us_currenteditor].terminate)(e);
1421 	efree((CHAR *)e);
1422 }
1423 
1424 /******************** SPECIAL WINDOW HANDLERS ********************/
1425 
1426 /*
1427  * routine to accept changes in an edit window examining a variable.  If "nature" is:
1428  *  REPLACETEXTLINE  line "changed" goes from "oldline" to "newline"
1429  *  DELETETEXTLINE   line "changed deleted (was "oldline")
1430  *  INSERTTEXTLINE   line "newline" inserted before line "changed"
1431  *  REPLACEALLTEXT   "changed" lines "newline" replace all text
1432  */
us_varchanges(WINDOWPART * w,INTBIG nature,CHAR * oldline,CHAR * newline,INTBIG changed)1433 void us_varchanges(WINDOWPART *w, INTBIG nature, CHAR *oldline, CHAR *newline, INTBIG changed)
1434 {
1435 	REGISTER INTBIG j, l, save;
1436 	REGISTER BOOLEAN res;
1437 	REGISTER INTBIG newval, i, len;
1438 	REGISTER CHAR **newlist, *pt;
1439 	float newfloat;
1440 	REGISTER EDITOR *ed;
1441 	Q_UNUSED( oldline );
1442 
1443 	ed = w->editor;
1444 	if (ed->editobjvar == NOVARIABLE) return;
1445 	if ((ed->editobjvar->type&VCANTSET) != 0)
1446 	{
1447 		ttyputerr(M_("This variable cannot be changed"));
1448 		ed->editobjvar = NOVARIABLE;
1449 		return;
1450 	}
1451 	len = getlength(ed->editobjvar);
1452 
1453 	/* when replacing the entire text, reduce to individual calls */
1454 	if (nature == REPLACEALLTEXT)
1455 	{
1456 		newlist = (CHAR **)newline;
1457 		for(i=0; i<changed; i++)
1458 			us_varchanges(w, REPLACETEXTLINE, x_(""), newlist[i], i);
1459 		for(i=len-1; i>=changed; i--)
1460 			us_varchanges(w, DELETETEXTLINE, x_(""), x_(""), i);
1461 		return;
1462 	}
1463 
1464 	if (nature == DELETETEXTLINE && len == 1)
1465 	{
1466 		/* delete of last entry: instead, replace it with a null */
1467 		newline = x_("");
1468 		nature = REPLACETEXTLINE;
1469 	} else if (nature == REPLACETEXTLINE && changed >= len)
1470 	{
1471 		/* change of line beyond end: instead make an insert */
1472 		nature = INSERTTEXTLINE;
1473 	}
1474 
1475 	/* disallow deletions and insertions if the number of lines is fixed */
1476 	if (nature == DELETETEXTLINE || nature == INSERTTEXTLINE)
1477 	{
1478 		if ((ed->state&LINESFIXED) != 0) return;
1479 	}
1480 
1481 	/* get the value */
1482 	j = 0;
1483 	save = 0;
1484 	newval = 0;
1485 	if (nature == REPLACETEXTLINE || nature == INSERTTEXTLINE)
1486 	{
1487 		pt = newline;
1488 		while (*pt == ' ' || *pt == '\t') pt++;
1489 		switch (ed->editobjvar->type&VTYPE)
1490 		{
1491 			case VINTEGER:
1492 			case VSHORT:
1493 			case VBOOLEAN:
1494 			case VADDRESS:
1495 				newval = myatoi(pt);
1496 				break;
1497 			case VFRACT:
1498 				newval = atofr(pt);
1499 				break;
1500 			case VFLOAT:
1501 			case VDOUBLE:
1502 				newfloat = (float)eatof(pt);
1503 				newval = castint(newfloat);
1504 				break;
1505 			case VSTRING:
1506 				j = l = estrlen(newline);
1507 				if (estrcmp(&newline[l-3], x_(" */")) == 0)
1508 				{
1509 					for(j = l-5; j >= 0; j--)
1510 						if (estrncmp(&newline[j], x_("/* "), 3) == 0) break;
1511 					while (j > 0 && newline[j-1] == ' ') j--;
1512 					if (j < 0) j = l;
1513 				}
1514 				save = newline[j];
1515 				newline[j] = 0;
1516 				newval = (INTBIG)newline;
1517 				break;
1518 			default:
1519 				ttyputmsg(_("Cannot update this type of variable (0%o)"), ed->editobjvar->type);
1520 				break;
1521 		}
1522 	}
1523 
1524 	/* make the change */
1525 	res = TRUE;
1526 	switch (nature)
1527 	{
1528 		case REPLACETEXTLINE:
1529 			if (changed < 0 || changed >= len) return;
1530 			res = setind((INTBIG)ed->editobjaddr, ed->editobjtype, ed->editobjqual, changed, newval);
1531 			break;
1532 		case DELETETEXTLINE:
1533 			if (changed < 0 || changed >= len) return;
1534 			res = delind((INTBIG)ed->editobjaddr, ed->editobjtype, ed->editobjqual, changed);
1535 			break;
1536 		case INSERTTEXTLINE:
1537 			if (changed <= 0 || changed > len) return;
1538 			res = insind((INTBIG)ed->editobjaddr, ed->editobjtype, ed->editobjqual, changed, newval);
1539 
1540 			/* why is this next line necessary? */
1541 			ed->editobjvar = getval((INTBIG)ed->editobjaddr, ed->editobjtype, -1, ed->editobjqual);
1542 			break;
1543 	}
1544 
1545 	/* clean-up string if one was passed */
1546 	if ((nature == REPLACETEXTLINE || nature == INSERTTEXTLINE) &&
1547 		(ed->editobjvar->type&VTYPE) == VSTRING) newline[j] = (CHAR)save;
1548 
1549 	if (res)
1550 	{
1551 		ttyputerr(_("Error changing variable: ignoring further changes"));
1552 		ed->editobjvar = NOVARIABLE;
1553 	}
1554 }
1555 
1556 /*
1557  * routine to accept changes in an edit window examining a textual cell.  If "nature" is:
1558  *  REPLACETEXTLINE  line "changed" goes from "oldline" to "newline"
1559  *  DELETETEXTLINE   line "changed" deleted (was "oldline")
1560  *  INSERTTEXTLINE   line "newline" inserted before line "changed"
1561  *  REPLACEALLTEXT   "changed" lines "newline" replace all text
1562  */
us_textcellchanges(WINDOWPART * w,INTBIG nature,CHAR * oldline,CHAR * newline,INTBIG changed)1563 void us_textcellchanges(WINDOWPART *w, INTBIG nature, CHAR *oldline, CHAR *newline, INTBIG changed)
1564 {
1565 	REGISTER INTBIG len;
1566 	REGISTER BOOLEAN res;
1567 	REGISTER EDITOR *ed;
1568 	Q_UNUSED( oldline );
1569 
1570 	ed = w->editor;
1571 	if (ed->editobjvar == NOVARIABLE) return;
1572 	if ((ed->editobjvar->type&VTYPE) != VSTRING) return;
1573 	if ((ed->editobjvar->type&VCANTSET) != 0) return;
1574 	len = getlength(ed->editobjvar);
1575 
1576 	if (nature == DELETETEXTLINE && len == 1)
1577 	{
1578 		/* delete of last line: instead, replace it with a blank */
1579 		newline = x_("");
1580 		nature = REPLACETEXTLINE;
1581 	} else if (nature == REPLACETEXTLINE && changed >= len)
1582 	{
1583 		/* change of line one beyond end: instead make an insert */
1584 		nature = INSERTTEXTLINE;
1585 	}
1586 
1587 	res = TRUE;
1588 	switch (nature)
1589 	{
1590 		case REPLACETEXTLINE:
1591 			if (changed < 0 || changed >= len) return;
1592 			res = setindkey((INTBIG)ed->editobjaddr, VNODEPROTO, el_cell_message_key,
1593 				changed, (INTBIG)newline);
1594 			break;
1595 		case DELETETEXTLINE:
1596 			if (changed < 0 || changed >= len) return;
1597 			res = delindkey((INTBIG)ed->editobjaddr, VNODEPROTO, el_cell_message_key,
1598 				changed);
1599 			break;
1600 		case INSERTTEXTLINE:
1601 			res = insindkey((INTBIG)ed->editobjaddr, VNODEPROTO, el_cell_message_key,
1602 				changed, (INTBIG)newline);
1603 			break;
1604 		case REPLACEALLTEXT:
1605 			if (setvalkey((INTBIG)ed->editobjaddr, VNODEPROTO, el_cell_message_key,
1606 				(INTBIG)newline, VSTRING | VISARRAY | (changed << VLENGTHSH)) != NOVARIABLE)
1607 					res = FALSE;
1608 			break;
1609 	}
1610 
1611 	if (res)
1612 	{
1613 		ttyputerr(_("Error changing variable: ignoring further changes"));
1614 		ed->editobjvar = NOVARIABLE;
1615 	}
1616 }
1617 
1618 /*
1619  * private character handler for the text window.  This routine normally
1620  * passes all commands to the editor's character handler.  However, it
1621  * interprets M(=) which is for editing the cell on the current line
1622  */
us_celledithandler(WINDOWPART * w,INTSML ch,INTBIG special)1623 BOOLEAN us_celledithandler(WINDOWPART *w, INTSML ch, INTBIG special)
1624 {
1625 	CHAR *newpar[2], *str;
1626 	REGISTER INTBIG i;
1627 	REGISTER BOOLEAN meta;
1628 	REGISTER EDITOR *ed;
1629 	extern INTBIG us_lastemacschar;
1630 
1631 	/* the EMACS text editor must be running */
1632 	us_describeeditor(&str);
1633 	if (namesame(str, x_("emacs")) != 0) return(us_gotchar(w, ch, special));
1634 
1635 	/* see if the meta key is held down (serious black magic) */
1636 	meta = FALSE;
1637 	if ((special&ACCELERATORDOWN) != 0) meta = TRUE;
1638 	if ((us_lastemacschar&2) != 0) meta = TRUE;
1639 
1640 	/* pass character on to the editor if not M(=) */
1641 	if (!meta || ch != '=') return(us_gotchar(w, ch, special));
1642 
1643 	/* M(=) typed: parse current line to edit named cell */
1644 	ed = w->editor;
1645 	(void)allocstring(&str, us_getline(w, ed->curline), el_tempcluster);
1646 
1647 	/* first drop everything past the first space character */
1648 	for(i=0; str[i] != 0; i++) if (str[i] == ' ') break;
1649 	if (str[i] != 0) str[i] = 0;
1650 
1651 	if (str[0] == 0) ttyputerr(_("No cell specified on this line")); else
1652 	{
1653 		/* issue the "editcell" command */
1654 		newpar[0] = x_("editcell");
1655 		newpar[1] = str;
1656 		telltool(us_tool, 2, newpar);
1657 		setactivity(_("Cell Selection"));
1658 	}
1659 
1660 	/* clean up */
1661 	efree(str);
1662 	return(FALSE);
1663 }
1664 
1665 /******************** COMMAND SUPPORT ********************/
1666 
us_demandxy(INTBIG * x,INTBIG * y)1667 BOOLEAN us_demandxy(INTBIG *x, INTBIG *y)
1668 {
1669 	BOOLEAN ret;
1670 
1671 	ret = getxy(x, y);
1672 	if (ret) ttyputmsg(_("Cursor must be in an editing window"));
1673 	return(ret);
1674 }
1675 
1676 static INTBIG us_curx, us_cury;
1677 
1678 /*
1679  * routine to get the co-ordinates of the cursor into the reference parameters
1680  * "x" and "y".  If "GOTXY" is set in the global variable "us_state" then
1681  * this has already been done.  The routine returns true if there is not a
1682  * valid cursor position.
1683  */
getxy(INTBIG * x,INTBIG * y)1684 BOOLEAN getxy(INTBIG *x, INTBIG *y)
1685 {
1686 	INTBIG gx, gy;
1687 	REGISTER BOOLEAN ret;
1688 
1689 	if ((us_state&GOTXY) == 0)
1690 	{
1691 		readtablet(&gx, &gy);
1692 		ret = us_setxy(gx, gy);
1693 	} else ret = FALSE;
1694 	*x = us_curx;
1695 	*y = us_cury;
1696 	return(ret);
1697 }
1698 
1699 /*
1700  * routine to take the values (realx, realy) from the tablet and store
1701  * them in the variables (us_curx, us_cury) which are in design-space
1702  * co-ordinates.  "GOTXY" in the global variable "us_state" is set to indicate
1703  * that the co-ordinates are valid.  The current window is set according
1704  * to the cursor position.  The routine returns true if the position
1705  * is not in a window.
1706  */
us_setxy(INTBIG x,INTBIG y)1707 BOOLEAN us_setxy(INTBIG x, INTBIG y)
1708 {
1709 	REGISTER WINDOWPART *w;
1710 	REGISTER WINDOWFRAME *wf;
1711 
1712 	us_curx = x;   us_cury = y;
1713 
1714 	/* figure out which window it is in */
1715 	wf = getwindowframe(TRUE);
1716 	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
1717 	{
1718 		if (w->frame != wf) continue;
1719 		if (x >= w->uselx && x <= w->usehx && y >= w->usely && y <= w->usehy)
1720 		{
1721 			/* make this window the current one */
1722 			if (w != el_curwindowpart)
1723 			{
1724 				(void)setvalkey((INTBIG)us_tool, VTOOL, us_current_window_key, (INTBIG)w,
1725 					VWINDOWPART|VDONTSAVE);
1726 				(void)setval((INTBIG)el_curlib, VLIBRARY, x_("curnodeproto"),
1727 					(INTBIG)w->curnodeproto, VNODEPROTO);
1728 			}
1729 			us_scaletowindow(&us_curx, &us_cury, w);
1730 			xform(us_curx, us_cury, &us_curx, &us_cury, w->intocell);
1731 			us_state |= GOTXY;
1732 			return(FALSE);
1733 		}
1734 	}
1735 	return(TRUE);
1736 }
1737 
1738 /*
1739  * routine to force the parameters "xcur" and "ycur" to align to the
1740  * nearest "alignment" units
1741  */
gridalign(INTBIG * xcur,INTBIG * ycur,INTBIG alignmentdivisor,NODEPROTO * cell)1742 void gridalign(INTBIG *xcur, INTBIG *ycur, INTBIG alignmentdivisor, NODEPROTO *cell)
1743 {
1744 	INTBIG otheralign;
1745 	REGISTER INTBIG val, alignment;
1746 	REGISTER TECHNOLOGY *tech;
1747 
1748 	tech = NOTECHNOLOGY;
1749 	if (cell != NONODEPROTO) tech = cell->tech;
1750 	if (tech == NOTECHNOLOGY) tech = el_curtech;
1751 	alignment = muldiv(us_alignment_ratio, el_curlib->lambda[tech->techindex], WHOLE) /
1752 		alignmentdivisor;
1753 	val = us_alignvalue(*xcur, alignment, &otheralign);
1754 	if (abs(*xcur-val) < abs(*xcur-otheralign)) *xcur = val; else
1755 		*xcur = otheralign;
1756 	val = us_alignvalue(*ycur, alignment, &otheralign);
1757 	if (abs(*ycur-val) < abs(*ycur-otheralign)) *ycur = val; else
1758 		*ycur = otheralign;
1759 }
1760 
1761 /*
1762  * routine to return "value", aligned to the nearest "alignment" units.
1763  * The next closest alignment value (if "value" is not on the grid)
1764  * is returned in "otheralign".
1765  */
us_alignvalue(INTBIG value,INTBIG alignment,INTBIG * otheralign)1766 INTBIG us_alignvalue(INTBIG value, INTBIG alignment, INTBIG *otheralign)
1767 {
1768 	REGISTER INTBIG i, v1, v2;
1769 	REGISTER INTBIG sign;
1770 
1771 	/* determine the sign of the value */
1772 	if (value < 0) { sign = -1; value = -value; } else sign = 1;
1773 
1774 	/* compute the two aligned values */
1775 	if (alignment == 0) v1 = value; else
1776 		v1 = value / alignment * alignment;
1777 	if (v1 == value) v2 = value; else v2 = v1 + alignment;
1778 	v1 *= sign;   v2 *= sign;
1779 
1780 	/* make sure "v1" is the closest aligned value */
1781 	if (abs(v1-value) > abs(v2-value)) { i = v1;   v1 = v2;   v2 = i; }
1782 
1783 	*otheralign = v2;
1784 	return(v1);
1785 }
1786 
1787 /* routine to ensure that a current window exists */
us_needwindow(void)1788 BOOLEAN us_needwindow(void)
1789 {
1790 	if (el_curwindowpart != NOWINDOWPART) return(FALSE);
1791 	us_abortcommand(_("No current window"));
1792 	return(TRUE);
1793 }
1794 
1795 /* routine to ensure that a cell exists in the current window */
us_needcell(void)1796 NODEPROTO *us_needcell(void)
1797 {
1798 	REGISTER NODEPROTO *np;
1799 
1800 	np = getcurcell();
1801 	if (np != NONODEPROTO) return(np);
1802 	if (el_curwindowpart == NOWINDOWPART)
1803 	{
1804 		us_abortcommand(_("No current window (select one with Cells/Edit Cell)"));
1805 	} else
1806 	{
1807 		if ((us_tool->toolstate&NODETAILS) != 0)
1808 			us_abortcommand(_("No cell in this window (select one with Cells/Edit Cell)")); else
1809 				us_abortcommand(_("No cell in this window (select one with '-editcell')"));
1810 	}
1811 	return(NONODEPROTO);
1812 }
1813 
1814 /*
1815  * Routine to ensure that node "item" can be modified in cell "cell".
1816  * If "item" is NONODEINST, check that cell "cell" can be modified at all.
1817  * Returns true if the edit cannot be done.
1818  */
us_cantedit(NODEPROTO * cell,NODEINST * item,BOOLEAN giveerror)1819 BOOLEAN us_cantedit(NODEPROTO *cell, NODEINST *item, BOOLEAN giveerror)
1820 {
1821 	REGISTER INTBIG count;
1822 	extern COMCOMP us_noyesalwaysp;
1823 	CHAR *pars[5];
1824 	REGISTER void *infstr;
1825 
1826 	/* if an instance is specified, check it */
1827 	if (item != NONODEINST)
1828 	{
1829 		if ((item->userbits&NILOCKED) != 0)
1830 		{
1831 			if (!giveerror) return(TRUE);
1832 			infstr = initinfstr();
1833 			formatinfstr(infstr, _("Changes to locked node %s are disallowed.  Change anyway?"),
1834 				describenodeinst(item));
1835 			count = ttygetparam(returninfstr(infstr), &us_noyesalwaysp, 5, pars);
1836 			if (count <= 0 || namesamen(pars[0], x_("no"), estrlen(pars[0])) == 0) return(TRUE);
1837 			if (namesamen(pars[0], x_("always"), estrlen(pars[0])) == 0)
1838 				(void)setval((INTBIG)item, VNODEINST, x_("userbits"),
1839 					item->userbits & ~NILOCKED, VINTEGER);
1840 		}
1841 		if (item->proto->primindex != 0)
1842 		{
1843 			/* see if a primitive is locked */
1844 			if ((item->proto->userbits&LOCKEDPRIM) != 0 &&
1845 				(us_useroptions&NOPRIMCHANGES) != 0)
1846 			{
1847 				if (!giveerror) return(TRUE);
1848 				infstr = initinfstr();
1849 				formatinfstr(infstr, _("Changes to locked primitives (such as %s) are disallowed.  Change anyway?"),
1850 					describenodeinst(item));
1851 				count = ttygetparam(returninfstr(infstr), &us_noyesalwaysp, 5, pars);
1852 				if (count <= 0 || namesamen(pars[0], x_("no"), estrlen(pars[0])) == 0) return(TRUE);
1853 				if (namesamen(pars[0], x_("always"), estrlen(pars[0])) == 0)
1854 					(void)setvalkey((INTBIG)us_tool, VTOOL, us_optionflagskey,
1855 						 us_useroptions & ~NOPRIMCHANGES, VINTEGER);
1856 			}
1857 		} else
1858 		{
1859 			/* see if this type of cell is locked */
1860 			if ((cell->userbits&NPILOCKED) != 0)
1861 			{
1862 				if (!giveerror) return(TRUE);
1863 				infstr = initinfstr();
1864 				formatinfstr(infstr, _("Instances in cell %s are locked.  You cannot move %s.  Change anyway?"),
1865 					describenodeproto(cell), describenodeinst(item));
1866 				count = ttygetparam(returninfstr(infstr), &us_noyesalwaysp, 5, pars);
1867 				if (count <= 0 || namesamen(pars[0], x_("no"), estrlen(pars[0])) == 0) return(TRUE);
1868 				if (namesamen(pars[0], x_("always"), estrlen(pars[0])) == 0)
1869 					setval((INTBIG)cell, VNODEPROTO, x_("userbits"),
1870 						cell->userbits & ~NPILOCKED, VINTEGER);
1871 			}
1872 		}
1873 	}
1874 
1875 	/* check for general changes to the cell */
1876 	if ((cell->userbits&NPLOCKED) != 0)
1877 	{
1878 		if (!giveerror) return(TRUE);
1879 		infstr = initinfstr();
1880 		formatinfstr(infstr, _("Changes to cell %s are locked.  Change anyway?"),
1881 			describenodeproto(cell));
1882 		count = ttygetparam(returninfstr(infstr), &us_noyesalwaysp, 5, pars);
1883 		if (count <= 0 || namesamen(pars[0], x_("no"), estrlen(pars[0])) == 0) return(TRUE);
1884 		if (namesamen(pars[0], x_("always"), estrlen(pars[0])) == 0)
1885 			setval((INTBIG)cell, VNODEPROTO, x_("userbits"), cell->userbits & ~NPLOCKED, VINTEGER);
1886 	}
1887 	return(FALSE);
1888 }
1889 
1890 /*
1891  * routine to determine the proper position of the cursor given that
1892  * it must adjust to the nearest "angle" tenth-degree radial coming out of
1893  * the point (tx,ty) and that it is currently at (nx, ny).  The
1894  * adjusted point is placed into (fx, fy) and the proper radial starting
1895  * point in "poly" is placed in (tx, ty).
1896  */
us_getslide(INTBIG angle,INTBIG tx,INTBIG ty,INTBIG nx,INTBIG ny,INTBIG * fx,INTBIG * fy)1897 void us_getslide(INTBIG angle, INTBIG tx, INTBIG ty, INTBIG nx, INTBIG ny, INTBIG *fx,
1898 	INTBIG *fy)
1899 {
1900 	REGISTER INTBIG ang;
1901 	INTBIG ix, iy;
1902 
1903 	/* if angle is unconstrained, use the exact cursor position */
1904 	if (angle <= 0)
1905 	{
1906 		*fx = nx;   *fy = ny;
1907 		return;
1908 	}
1909 
1910 	/* check all permissable angles */
1911 	for(ang = 0; ang < 3600; ang += angle)
1912 	{
1913 		/* get close point to (nx,ny) on "ang" tenth-degree radial from (tx,ty) */
1914 		(void)intersect(tx, ty, ang, nx, ny, (ang+900)%3600, &ix, &iy);
1915 
1916 		/* accumulate the intersection closest to the cursor */
1917 		if (ang != 0 && abs(*fx-nx) + abs(*fy-ny) < abs(ix-nx) + abs(iy-ny)) continue;
1918 		*fx = ix;   *fy = iy;
1919 	}
1920 }
1921 
1922 /*
1923  * routine to convert command interpreter letter "letter" to the full
1924  * variable name on the user tool object
1925  */
us_commandvarname(INTSML letter)1926 CHAR *us_commandvarname(INTSML letter)
1927 {
1928 	static CHAR varname[20];
1929 
1930 	if (isupper(letter))
1931 	{
1932 		(void)estrcpy(varname, x_("USER_local_capX"));
1933 		varname[14] = tolower(letter);
1934 	} else
1935 	{
1936 		(void)estrcpy(varname, x_("USER_local_X"));
1937 		varname[11] = (CHAR)letter;
1938 	}
1939 	return(varname);
1940 }
1941 
1942 /*
1943  * routine to parse the variable path in "str" and return the object address
1944  * and type on which this variable resides along with the variable name.
1945  * The object address and type are placed in "objaddr" and "objtype";
1946  * the variable name is placed in "varname".  "comvar" is set to true
1947  * if the variable is a command-interpreter variable (as opposed to a
1948  * database variable).  If an array index specification is given (a "[]")
1949  * then the index value is returned in "aindex" (otherwise the value is set
1950  * to -1).  The routine returns true if the variable path is invalid.
1951  */
us_getvar(CHAR * str,INTBIG * objaddr,INTBIG * objtype,CHAR ** varname,BOOLEAN * comvar,INTBIG * aindex)1952 BOOLEAN us_getvar(CHAR *str, INTBIG *objaddr, INTBIG *objtype, CHAR **varname,
1953 	BOOLEAN *comvar, INTBIG *aindex)
1954 {
1955 	REGISTER INTBIG i;
1956 	static CHAR fullvarname[50];
1957 
1958 	/* see if an array index is specified */
1959 	*aindex = -1;
1960 	i = estrlen(str);
1961 	if (str[i-1] == ']') for(i--; i >= 0; i--) if (str[i] == '[')
1962 	{
1963 		*aindex = myatoi(&str[i+1]);
1964 		break;
1965 	}
1966 
1967 	/* see if this is a command interpreter variable */
1968 	*comvar = FALSE;
1969 	if (str[1] == 0 || str[1] == '[')
1970 	{
1971 		if (str[0] >= 'a' && str[0] <= 'z') *comvar = TRUE;
1972 		if (str[0] >= 'A' && str[0] <= 'Z') *comvar = TRUE;
1973 	}
1974 
1975 	/* replace the actual name for command interpreter variables */
1976 	if (*comvar)
1977 	{
1978 		(void)esnprintf(fullvarname, 50, x_("tool:user.%s"), us_commandvarname(str[0]));
1979 		str = fullvarname;
1980 	}
1981 
1982 	/* pick apart the variable path */
1983 	return(us_evaluatevariable(str, objaddr, objtype, varname));
1984 }
1985 
1986 #define LINELIMIT 300
1987 
1988 /*
1989  * Routine to dump the copyright information to file "f".  Each line of the copyright
1990  * is prefixed by "prefix" and postfixed by "postfix".
1991  */
us_emitcopyright(FILE * f,CHAR * prefix,CHAR * postfix)1992 void us_emitcopyright(FILE *f, CHAR *prefix, CHAR *postfix)
1993 {
1994 	REGISTER VARIABLE *var;
1995 	REGISTER FILE *io;
1996 	CHAR *dummy, buffer[LINELIMIT];
1997 
1998 	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING, us_copyright_file_key);
1999 	if (var == NOVARIABLE) return;
2000 	io = xopen((CHAR *)var->addr, el_filetypetext, el_libdir, &dummy);
2001 	if (io == 0) return;
2002 	for(;;)
2003 	{
2004 		if (xfgets(buffer, LINELIMIT, io)) break;
2005 		fprintf(f, x_("%s%s%s\n"), prefix, buffer, postfix);
2006 	}
2007 	xclose(io);
2008 }
2009 
2010 /******************** QUICK-KEY BINDING ********************/
2011 
2012 /*
2013  * Routine to find the entry in the key binding variable ("USER_binding_keys")
2014  * that corresponds to key "key", special code "special".  Returns negative if
2015  * the key is not in the variable.  If the key is found, the binding string
2016  * is returned in "binding".
2017  */
us_findboundkey(INTSML key,INTBIG special,CHAR ** binding)2018 INTBIG us_findboundkey(INTSML key, INTBIG special, CHAR **binding)
2019 {
2020 	REGISTER INTBIG i, len;
2021 	INTSML boundkey;
2022 	INTBIG boundspecial;
2023 	REGISTER VARIABLE *var;
2024 	REGISTER CHAR *thisbinding, *pt;
2025 
2026 	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_binding_keys_key);
2027 	if (var == NOVARIABLE) return(-1);
2028 	len = getlength(var);
2029 	pt = NOSTRING;
2030 	for(i=0; i<len; i++)
2031 	{
2032 		thisbinding = ((CHAR **)var->addr)[i];
2033 		pt = us_getboundkey(thisbinding, &boundkey, &boundspecial);
2034 		if (pt == 0) continue;
2035 		if (us_samekey(key, special, boundkey, boundspecial)) break;
2036 	}
2037 	if (i >= len) return(-1);
2038 	*binding = pt;
2039 	return(i);
2040 }
2041 
2042 /*
2043  * Routine to set the binding of key "key", special code "special" to the string
2044  * "binding".  If "quietly" is true, do this without change control.
2045  */
us_setkeybinding(CHAR * binding,INTSML key,INTBIG special,BOOLEAN quietly)2046 void us_setkeybinding(CHAR *binding, INTSML key, INTBIG special, BOOLEAN quietly)
2047 {
2048 	CHAR *pt, *justone[1], **bigger, *str;
2049 	REGISTER INTBIG i, j, len, inserted;
2050 	INTSML boundkey;
2051 	INTBIG boundspecial;
2052 	REGISTER VARIABLE *var;
2053 
2054 	if ((special&ACCELERATORDOWN) != 0) key = toupper(key);
2055 	i = us_findboundkey(key, special, &pt);
2056 	if (i >= 0)
2057 	{
2058 		if (quietly) nextchangequiet();
2059 		(void)setindkey((INTBIG)us_tool, VTOOL, us_binding_keys_key, i, (INTBIG)binding);
2060 		return;
2061 	}
2062 
2063 	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_binding_keys_key);
2064 	if (var == NOVARIABLE)
2065 	{
2066 		justone[0] = binding;
2067 		if (quietly) nextchangequiet();
2068 		(void)setvalkey((INTBIG)us_tool, VTOOL, us_binding_keys_key, (INTBIG)justone,
2069 			VSTRING|VDONTSAVE|VISARRAY|(1<<VLENGTHSH));
2070 	} else
2071 	{
2072 		len = getlength(var);
2073 		bigger = (CHAR **)emalloc((len+1) * (sizeof (CHAR *)), el_tempcluster);
2074 		if (bigger == 0) return;
2075 		j = 0;
2076 		inserted = 0;
2077 		for(i=0; i<len; i++)
2078 		{
2079 			pt = ((CHAR **)var->addr)[i];
2080 			str = us_getboundkey(pt, &boundkey, &boundspecial);
2081 			if (str == 0) continue;
2082 			if (namesame(str, x_("command=")) == 0) continue;
2083 			if (inserted == 0)
2084 			{
2085 				if (boundspecial > special || (boundspecial == special && boundkey > key))
2086 				{
2087 					(void)allocstring(&bigger[j++], binding, el_tempcluster);
2088 					inserted = 1;
2089 				}
2090 			}
2091 			(void)allocstring(&bigger[j++], pt, el_tempcluster);
2092 		}
2093 		if (inserted == 0)
2094 			(void)allocstring(&bigger[j++], binding, el_tempcluster);
2095 		if (quietly) nextchangequiet();
2096 		(void)setvalkey((INTBIG)us_tool, VTOOL, us_binding_keys_key, (INTBIG)bigger,
2097 			VSTRING|VDONTSAVE|VISARRAY|(j<<VLENGTHSH));
2098 		for(i=0; i<j; i++) efree((CHAR *)bigger[i]);
2099 		efree((CHAR *)bigger);
2100 	}
2101 }
2102 
2103 /*
2104  * Routine to describe key "boundkey"/"boundspecial".  Formats it according to
2105  * "readable":
2106  *  -1 for end of menu ("/A")
2107  *   0 for beginning of menu storage ("A/")
2108  *   1 for display ("Ctrl-A")
2109  */
us_describeboundkey(INTSML boundkey,INTBIG boundspecial,INTBIG readable)2110 CHAR *us_describeboundkey(INTSML boundkey, INTBIG boundspecial, INTBIG readable)
2111 {
2112 	CHAR *acceleratorstring, *acceleratorprefix, *shiftprefix, *accprefix;
2113 	REGISTER void *infstr;
2114 
2115 	getacceleratorstrings(&acceleratorstring, &acceleratorprefix);
2116 	if (readable > 0)
2117 	{
2118 		accprefix = acceleratorprefix;
2119 		shiftprefix = x_("S-");
2120 	} else
2121 	{
2122 		accprefix = x_("m-");
2123 		shiftprefix = x_("S");
2124 	}
2125 	infstr = initinfstr();
2126 	if ((boundspecial&SPECIALKEYDOWN) != 0)
2127 	{
2128 		if (readable < 0) addtoinfstr(infstr, '/');
2129 		switch ((boundspecial&SPECIALKEY)>>SPECIALKEYSH)
2130 		{
2131 			case SPECIALKEYF1:  addstringtoinfstr(infstr, x_("F1"));    break;
2132 			case SPECIALKEYF2:  addstringtoinfstr(infstr, x_("F2"));    break;
2133 			case SPECIALKEYF3:  addstringtoinfstr(infstr, x_("F3"));    break;
2134 			case SPECIALKEYF4:  addstringtoinfstr(infstr, x_("F4"));    break;
2135 			case SPECIALKEYF5:  addstringtoinfstr(infstr, x_("F5"));    break;
2136 			case SPECIALKEYF6:  addstringtoinfstr(infstr, x_("F6"));    break;
2137 			case SPECIALKEYF7:  addstringtoinfstr(infstr, x_("F7"));    break;
2138 			case SPECIALKEYF8:  addstringtoinfstr(infstr, x_("F8"));    break;
2139 			case SPECIALKEYF9:  addstringtoinfstr(infstr, x_("F9"));    break;
2140 			case SPECIALKEYF10: addstringtoinfstr(infstr, x_("F10"));   break;
2141 			case SPECIALKEYF11: addstringtoinfstr(infstr, x_("F11"));   break;
2142 			case SPECIALKEYF12: addstringtoinfstr(infstr, x_("F12"));   break;
2143 			case SPECIALKEYARROWL:
2144 				if ((boundspecial&ACCELERATORDOWN) != 0)
2145 					addstringtoinfstr(infstr, accprefix);
2146 				if ((boundspecial&SHIFTDOWN) != 0)
2147 					addstringtoinfstr(infstr, shiftprefix);
2148 				addstringtoinfstr(infstr, x_("LEFT"));
2149 				break;
2150 			case SPECIALKEYARROWR:
2151 				if ((boundspecial&ACCELERATORDOWN) != 0)
2152 					addstringtoinfstr(infstr, accprefix);
2153 				if ((boundspecial&SHIFTDOWN) != 0)
2154 					addstringtoinfstr(infstr, shiftprefix);
2155 				addstringtoinfstr(infstr, x_("RIGHT"));
2156 				break;
2157 			case SPECIALKEYARROWU:
2158 				if ((boundspecial&ACCELERATORDOWN) != 0)
2159 					addstringtoinfstr(infstr, accprefix);
2160 				if ((boundspecial&SHIFTDOWN) != 0)
2161 					addstringtoinfstr(infstr, shiftprefix);
2162 				addstringtoinfstr(infstr, x_("UP"));
2163 				break;
2164 			case SPECIALKEYARROWD:
2165 				if ((boundspecial&ACCELERATORDOWN) != 0)
2166 					addstringtoinfstr(infstr, accprefix);
2167 				if ((boundspecial&SHIFTDOWN) != 0)
2168 					addstringtoinfstr(infstr, shiftprefix);
2169 				addstringtoinfstr(infstr, x_("DOWN"));
2170 				break;
2171 		}
2172 		if (readable == 0) addtoinfstr(infstr, '/');
2173 	} else
2174 	{
2175 		if ((boundspecial&ACCELERATORDOWN) != 0)
2176 		{
2177 			if (readable < 0) addtoinfstr(infstr, '/');
2178 			if (readable > 0) addstringtoinfstr(infstr, accprefix);
2179 			if (boundkey > 0 && boundkey < 033) formatinfstr(infstr, x_("^%c"), boundkey + 0100); else
2180 				addtoinfstr(infstr, (CHAR)boundkey);
2181 			if (readable == 0) addtoinfstr(infstr, '/');
2182 		} else
2183 		{
2184 			if (readable < 0) addtoinfstr(infstr, '\\');
2185 			if (boundkey > 0 && boundkey < 033) formatinfstr(infstr, x_("^%c"), boundkey + 0100); else
2186 				if (boundkey == 0177) addstringtoinfstr(infstr, x_("DEL")); else
2187 					addtoinfstr(infstr, (CHAR)boundkey);
2188 			if (readable == 0) addtoinfstr(infstr, '\\');
2189 		}
2190 	}
2191 	return(returninfstr(infstr));
2192 }
2193 
2194 /*
2195  * Returns true if the keys "key1"/"special1" is the same as "key2"/"special2"
2196  */
us_samekey(INTSML key1,INTBIG special1,INTSML key2,INTBIG special2)2197 BOOLEAN us_samekey(INTSML key1, INTBIG special1, INTSML key2, INTBIG special2)
2198 {
2199 	if (special1 != special2) return(FALSE);
2200 	if ((special1&SPECIALKEYDOWN) == 0)
2201 	{
2202 		if ((special1&ACCELERATORDOWN) != 0)
2203 		{
2204 			key1 = toupper(key1);
2205 			key2 = toupper(key2);
2206 		}
2207 		if (key1 != key2) return(FALSE);
2208 	}
2209 	return(TRUE);
2210 }
2211 
2212 /*
2213  * Routine to parse the key binding string in "origbinding" (an entry in "USER_binding_keys")
2214  * Extracts the key information from the start of the string and returns it in
2215  * "boundkey" and "boundspecial".  Then returns the rest of the string.
2216  * Returns zero on error.
2217  */
us_getboundkey(CHAR * origbinding,INTSML * boundkey,INTBIG * boundspecial)2218 CHAR *us_getboundkey(CHAR *origbinding, INTSML *boundkey, INTBIG *boundspecial)
2219 {
2220 	REGISTER CHAR *pt, save, *word;
2221 	REGISTER INTBIG offset, len;
2222 	CHAR binding[200], *acceleratorstring, *acceleratorprefix;
2223 
2224 	estrcpy(binding, origbinding);
2225 	*boundspecial = 0;
2226 	if (binding[0] != 0 && binding[1] == 0)
2227 	{
2228 		*boundkey = binding[0];
2229 		return(x_(""));
2230 	}
2231 	if (*binding == '/' || *binding == '\\')
2232 	{
2233 		pt = binding;
2234 		save = *pt;
2235 		word = binding + 1;
2236 	} else
2237 	{
2238 		for(pt = binding; *pt != 0; pt++)
2239 			if (*pt == '/' || *pt == '\\') break;
2240 		save = *pt;
2241 		*pt = 0;
2242 		word = binding;
2243 	}
2244 
2245 	getacceleratorstrings(&acceleratorstring, &acceleratorprefix);
2246 	len = estrlen(acceleratorprefix);
2247 	if (namesamen(word, acceleratorprefix, len) == 0)
2248 	{
2249 		word += len;
2250 		*boundspecial |= ACCELERATORDOWN;
2251 	} else if (word[0] == 'm' && word[1] == '-')
2252 	{
2253 		word += 2;
2254 		*boundspecial |= ACCELERATORDOWN;
2255 	}
2256 
2257 	/* handle single keystrokes */
2258 	if (word[1] == 0)
2259 	{
2260 		*boundkey = word[0];
2261 		if (save == '/') *boundspecial |= ACCELERATORDOWN;
2262 	} else
2263 	{
2264 		/* look for special names */
2265 		if (word[0] == '^')
2266 		{
2267 			if (word[1] == '^')
2268 			{
2269 				*boundkey = '^';
2270 			} else if (isdigit(word[1]) != 0)
2271 			{
2272 				*boundkey = (INTSML)myatoi(&word[1]);
2273 			} else
2274 			{
2275 				*boundkey = toupper(word[1]) - 0100;
2276 			}
2277 		} else
2278 		{
2279 			if (namesame(word, x_("del")) == 0) *boundkey = 0177; else
2280 			if (namesame(word, x_("left")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYARROWL<<SPECIALKEYSH); else
2281 			if (namesame(word, x_("sleft")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYARROWL<<SPECIALKEYSH)|SHIFTDOWN; else
2282 			if (namesame(word, x_("right")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYARROWR<<SPECIALKEYSH); else
2283 			if (namesame(word, x_("sright")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYARROWR<<SPECIALKEYSH)|SHIFTDOWN; else
2284 			if (namesame(word, x_("up")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYARROWU<<SPECIALKEYSH); else
2285 			if (namesame(word, x_("sup")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYARROWU<<SPECIALKEYSH)|SHIFTDOWN; else
2286 			if (namesame(word, x_("down")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYARROWD<<SPECIALKEYSH); else
2287 			if (namesame(word, x_("sdown")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYARROWD<<SPECIALKEYSH)|SHIFTDOWN; else
2288 			if (namesame(word, x_("f1")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYF1<<SPECIALKEYSH); else
2289 			if (namesame(word, x_("f2")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYF2<<SPECIALKEYSH); else
2290 			if (namesame(word, x_("f3")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYF3<<SPECIALKEYSH); else
2291 			if (namesame(word, x_("f4")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYF4<<SPECIALKEYSH); else
2292 			if (namesame(word, x_("f5")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYF5<<SPECIALKEYSH); else
2293 			if (namesame(word, x_("f6")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYF6<<SPECIALKEYSH); else
2294 			if (namesame(word, x_("f7")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYF7<<SPECIALKEYSH); else
2295 			if (namesame(word, x_("f8")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYF8<<SPECIALKEYSH); else
2296 			if (namesame(word, x_("f9")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYF9<<SPECIALKEYSH); else
2297 			if (namesame(word, x_("f10")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYF10<<SPECIALKEYSH); else
2298 			if (namesame(word, x_("f11")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYF11<<SPECIALKEYSH); else
2299 			if (namesame(word, x_("f12")) == 0) *boundspecial |= SPECIALKEYDOWN|(SPECIALKEYF12<<SPECIALKEYSH);
2300 		}
2301 	}
2302 	*pt++ = save;
2303 	offset = pt - binding;
2304 	return(origbinding+offset);
2305 }
2306 
2307 /*
2308  * Routine to recursively replace all mention of popup menu "oldpm" with "newpm".
2309  */
us_recursivelyreplacemenu(POPUPMENU * pm,POPUPMENU * oldpm,POPUPMENU * newpm)2310 void us_recursivelyreplacemenu(POPUPMENU *pm, POPUPMENU *oldpm, POPUPMENU *newpm)
2311 {
2312 	REGISTER INTBIG i;
2313 	REGISTER POPUPMENUITEM *mi;
2314 	REGISTER USERCOM *uc;
2315 	REGISTER VARIABLE *var;
2316 	CHAR varname[200], **strings;
2317 
2318 	for(i=0; i<pm->total; i++)
2319 	{
2320 		mi = &pm->list[i];
2321 		uc = mi->response;
2322 		if (uc->menu == NOPOPUPMENU) continue;
2323 
2324 		if (uc->menu == oldpm)
2325 		{
2326 			esnprintf(varname, 200, x_("USER_binding_popup_%s"), pm->name);
2327 			var = getval((INTBIG)us_tool, VTOOL, -1, varname);
2328 			strings = (CHAR **)var->addr;
2329 			setind((INTBIG)us_tool, VTOOL, varname, i+1, (INTBIG)strings[i+1]);
2330 		}
2331 		us_recursivelyreplacemenu(uc->menu, oldpm, newpm);
2332 	}
2333 }
2334 
2335 /*
2336  * Routine to adjust the quick keys according to the variable "var".
2337  */
us_adjustquickkeys(VARIABLE * var,BOOLEAN warnofchanges)2338 void us_adjustquickkeys(VARIABLE *var, BOOLEAN warnofchanges)
2339 {
2340 	REGISTER INTBIG len, i, j, clen, elen;
2341 	INTSML key;
2342 	INTBIG special;
2343 	REGISTER CHAR *keybinding, *menuname, *menucommand, *pt, **quickkeylist,
2344 		*expected;
2345 	REGISTER POPUPMENU *pm;
2346 	REGISTER POPUPMENUITEM *mi;
2347 	REGISTER void *infstr;
2348 
2349 	/* first scan all existing menus and make sure they are still in the quick key list */
2350 	len = getlength(var);
2351 	quickkeylist = (CHAR **)var->addr;
2352 	for(i=0; i<us_pulldownmenucount; i++)
2353 		us_scanquickkeys(us_pulldowns[i], quickkeylist, len, &warnofchanges);
2354 
2355 	/* next scan the list of quick keys and make sure they are attached to menus */
2356 	for(i=0; i<len; i++)
2357 	{
2358 		keybinding = quickkeylist[i];
2359 		menuname = us_getboundkey(keybinding, &key, &special);
2360 		for(pt = menuname; *pt != 0 && *pt != '/'; pt++) ;
2361 		if (*pt == 0) continue;
2362 		*pt = 0;
2363 		pm = us_getpopupmenu(menuname);
2364 		*pt = '/';
2365 		if (pm == NOPOPUPMENU) continue;
2366 		menucommand = pt + 1;
2367 		mi = NOPOPUPMENUITEM;
2368 		for(j=0; j<pm->total; j++)
2369 		{
2370 			mi = &pm->list[j];
2371 			if (namesame(us_removeampersand(mi->attribute), menucommand) == 0) break;
2372 		}
2373 		if (j >= pm->total) continue;
2374 
2375 		/* see if this menu item has the quick key */
2376 		expected = us_describeboundkey(key, special, -1);
2377 		elen = estrlen(expected);
2378 		clen = estrlen(mi->attribute);
2379 		if (mi->attribute[clen-1] == '<') clen--;
2380 		if (clen-elen > 0 &&
2381 			namesamen(&mi->attribute[clen-elen], expected, elen) == 0) continue;
2382 
2383 		/* not there: see if there is another key bound */
2384 		infstr = initinfstr();
2385 		for(pt = mi->attribute; *pt != 0; pt++)
2386 		{
2387 			if (*pt == '/' || *pt == '\\' || *pt == '<') break;
2388 			addtoinfstr(infstr, *pt);
2389 		}
2390 		addstringtoinfstr(infstr, us_describeboundkey(key, special, -1));
2391 		clen = estrlen(mi->attribute) - 1;
2392 		if (mi->attribute[clen] == '<') addstringtoinfstr(infstr, x_("<"));
2393 		pt = returninfstr(infstr);
2394 		(void)reallocstring(&mi->attribute, pt, us_tool->cluster);
2395 		nativemenurename(pm, j);
2396 		us_adjustpopupmenu(pm, j);
2397 
2398 		/* make it the Meta key */
2399 		infstr = initinfstr();
2400 		addstringtoinfstr(infstr, us_describeboundkey(key, special, 0));
2401 		addstringtoinfstr(infstr, x_("command="));
2402 		addstringtoinfstr(infstr, mi->response->comname);
2403 		us_appendargs(infstr, mi->response);
2404 		us_setkeybinding(returninfstr(infstr), key, special, TRUE);
2405 
2406 		if (warnofchanges)
2407 		{
2408 			warnofchanges = FALSE;
2409 			ttyputmsg(_("Warning: key bindings have been changed by this library"));
2410 			ttyputmsg(_("  (for example, the '%s' key is now bound to '%s')"),
2411 				us_describeboundkey(key, special, 1), us_removeampersand(mi->attribute));
2412 		}
2413 	}
2414 }
2415 
2416 /*
2417  * Helper routine for "us_quickkeydlog" to recursively examine menu "pm" and
2418  * load the quick keys tables.
2419  */
us_scanquickkeys(POPUPMENU * pm,CHAR ** quickkeylist,INTBIG quickkeycount,BOOLEAN * warnofchanges)2420 void us_scanquickkeys(POPUPMENU *pm, CHAR **quickkeylist, INTBIG quickkeycount, BOOLEAN *warnofchanges)
2421 {
2422 	REGISTER INTBIG j, i, checked, len;
2423 	INTBIG special;
2424 	REGISTER POPUPMENUITEM *mi;
2425 	REGISTER USERCOM *uc;
2426 	REGISTER POPUPMENU *thispm;
2427 	CHAR *pt, menuline[200], menukey[50];
2428 	REGISTER CHAR *mname, *menuname;
2429 	INTSML key;
2430 
2431 	for(i=0; i<pm->total; i++)
2432 	{
2433 		mi = &pm->list[i];
2434 		uc = mi->response;
2435 		if (uc->menu != NOPOPUPMENU)
2436 		{
2437 			us_scanquickkeys(uc->menu, quickkeylist, quickkeycount, warnofchanges);
2438 			continue;
2439 		}
2440 		if (uc->active < 0 && *mi->attribute == 0) continue;
2441 
2442 		/* see if this item has a quick key */
2443 		estrcpy(menuline, mi->attribute);
2444 		j = estrlen(menuline) - 1;
2445 		checked = 0;
2446 		if (menuline[j] == '<')
2447 		{
2448 			menuline[j] = 0;
2449 			checked = 1;
2450 		}
2451 		for(mname = menuline; *mname != 0; mname++)
2452 			if (*mname == '/' || *mname == '\\') break;
2453 		if (*mname == 0) continue;
2454 		estrcpy(menukey, &mname[1]);
2455 		mname[1] = 0;
2456 		estrcat(menukey, mname);
2457 		len = estrlen(menukey);
2458 
2459 		/* item has a quick key: see if it is in the list */
2460 		for(j=0; j<quickkeycount; j++)
2461 		{
2462 			if (estrncmp(quickkeylist[j], menukey, len) != 0) continue;
2463 			menuname = us_getboundkey(quickkeylist[j], &key, &special);
2464 			for(pt = menuname; *pt != 0 && *pt != '/'; pt++) ;
2465 			if (*pt == 0) continue;
2466 			*pt = 0;
2467 			thispm = us_getpopupmenu(menuname);
2468 			*pt = '/';
2469 			if (thispm != pm) continue;
2470 			if (estrncmp(quickkeylist[j], menukey, len) != 0) continue;
2471 			if (namesame(&pt[1], us_removeampersand(mi->attribute)) == 0) break;
2472 		}
2473 		if (j < quickkeycount) continue;
2474 
2475 		/* remove the Meta key */
2476 		(void)us_getboundkey(menukey, &key, &special);
2477 		us_setkeybinding(x_(""), key, special, TRUE);
2478 
2479 		/* this menu entry is not in the quick key list: remove its quick key */
2480 		for(mname = mi->attribute; *mname != 0; mname++)
2481 			if (*mname == '/' || *mname == '\\') break;
2482 		if (*mname != 0)
2483 		{
2484 			if (checked != 0) *mname++ = '<';
2485 			*mname = 0;
2486 		}
2487 		nativemenurename(pm, i);
2488 		us_adjustpopupmenu(pm, i);
2489 		if (*warnofchanges)
2490 		{
2491 			*warnofchanges = FALSE;
2492 			ttyputmsg(_("Warning: key bindings have been changed by this library"));
2493 			pt = us_removeampersand(mi->attribute);
2494 			if (*pt == '>') pt++;
2495 			ttyputmsg(_("  (for example, the '%s' command is no longer attached to key '%s')"),
2496 				pt, us_describeboundkey(key, special, 1));
2497 		}
2498 	}
2499 }
2500 
2501 /*
2502  * Helper routine to remove special characters from menu item "name".
2503  */
us_removeampersand(CHAR * name)2504 CHAR *us_removeampersand(CHAR *name)
2505 {
2506 	REGISTER CHAR *pt;
2507 	REGISTER INTBIG len;
2508 	REGISTER void *infstr;
2509 
2510 	len = estrlen(name);
2511 	if (name[len-1] == '<' && name[0] == '>') name++;
2512 	infstr = initinfstr();
2513 	for(pt = name; *pt != 0; pt++)
2514 	{
2515 		if (*pt == '/' || *pt == '\\') break;
2516 		if (*pt == '<' || *pt == '&') continue;
2517 		addtoinfstr(infstr, *pt);
2518 	}
2519 	return(returninfstr(infstr));
2520 }
2521 
2522 /*
2523  * Routine to search all of the pulldown menus and find the entry named "name".
2524  * Returns the variable key for that menu and the item index.
2525  */
us_findnamedmenu(CHAR * name,INTBIG * key,INTBIG * item)2526 void us_findnamedmenu(CHAR *name, INTBIG *key, INTBIG *item)
2527 {
2528 	REGISTER POPUPMENU *pm;
2529 	REGISTER INTBIG i;
2530 	REGISTER void *infstr;
2531 
2532 	for(pm = us_firstpopupmenu; pm != NOPOPUPMENU; pm = pm->nextpopupmenu)
2533 	{
2534 		for(i=0; i<pm->total; i++)
2535 			if (us_matchpurecommand(pm->list[i].attribute, name)) break;
2536 		if (i < pm->total)
2537 		{
2538 			infstr = initinfstr();
2539 			formatinfstr(infstr, x_("USER_binding_popup_%s"), pm->name);
2540 			*key = makekey(returninfstr(infstr));
2541 			*item = i+1;
2542 			break;
2543 		}
2544 	}
2545 }
2546 
2547 /*
2548  * Routine to take item "menuitem" of the popup at variable "menukey" and substitute "command"
2549  * for the text there (preserving quick-key bindings).
2550  */
us_plugincommand(INTBIG menukey,INTBIG menuitem,CHAR * command)2551 CHAR *us_plugincommand(INTBIG menukey, INTBIG menuitem, CHAR *command)
2552 {
2553 	REGISTER VARIABLE *var;
2554 	REGISTER CHAR *pt;
2555 	REGISTER INTBIG i;
2556 	REGISTER void *infstr;
2557 
2558 	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, menukey);
2559 	if (var == NOVARIABLE) return(x_(""));
2560 	if (menuitem >= getlength(var)) return(x_(""));
2561 	pt = ((CHAR **)var->addr)[menuitem];
2562 
2563 	/* look for "message=" and substitute the result */
2564 	infstr = initinfstr();
2565 	for(i=0; pt[i] != 0; i++)
2566 	{
2567 		if (namesamen(&pt[i], x_("message=\""), 9) == 0) break;
2568 		addtoinfstr(infstr, pt[i]);
2569 	}
2570 	if (pt[i] != 0)
2571 	{
2572 		/* do the substitution */
2573 		formatinfstr(infstr, x_("message=\"%s\""), command);
2574 		pt += 9;
2575 		while (*pt != 0 && *pt != '"') pt++;
2576 		if (*pt == '"') pt++;
2577 		addstringtoinfstr(infstr, pt);
2578 	}
2579 	return(returninfstr(infstr));
2580 }
2581 
2582 /*
2583  * Routine to compare command "command" from the pulldown menus with "purecommand" that doesn't
2584  * have any menu control information.  Returns TRUE if they are the same.
2585  */
us_matchpurecommand(CHAR * command,CHAR * purecommand)2586 BOOLEAN us_matchpurecommand(CHAR *command, CHAR *purecommand)
2587 {
2588 	CHAR c, pc;
2589 
2590 	for(;;)
2591 	{
2592 		if (*command == '&') command++;
2593 		c = *command++;
2594 		if (c == '/' || c == '\\') c = 0;
2595 		pc = *purecommand++;
2596 		if (c != pc) return(FALSE);
2597 		if (c == 0) break;
2598 	}
2599 	return(TRUE);
2600 }
2601 
2602 /*
2603  * routine to determine whether the command bound to key "key" is the
2604  * last instance of the "telltool user" command that is bound to a key.
2605  * This is important to know because if the last "telltool user" is unbound,
2606  * there is no way to execute any long commands!
2607  */
us_islasteval(INTSML key,INTBIG special)2608 BOOLEAN us_islasteval(INTSML key, INTBIG special)
2609 {
2610 	REGISTER INTBIG i, j, keytotal, foundanother;
2611 	REGISTER BOOLEAN retval;
2612 	INTBIG boundspecial;
2613 	INTSML boundkey;
2614 	REGISTER VARIABLE *var;
2615 	CHAR *pt;
2616 	COMMANDBINDING commandbindingthis, commandbindingother;
2617 
2618 	/* get the command on this key */
2619 	retval = FALSE;
2620 	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_binding_keys_key);
2621 	if (var == NOVARIABLE) return(FALSE);
2622 	i = us_findboundkey(key, special, &pt);
2623 	if (i < 0) return(FALSE);
2624 	us_parsebinding(pt, &commandbindingthis);
2625 	if (*commandbindingthis.command != 0)
2626 	{
2627 		/* see if it is "telltool user" */
2628 		if (namesame(commandbindingthis.command, x_("telltool user")) == 0)
2629 		{
2630 			/* this is "telltool user"...check all other keys for this command */
2631 			keytotal = getlength(var);
2632 			foundanother = 0;
2633 			for(j=0; j<keytotal; j++)
2634 			{
2635 				pt = us_getboundkey(((CHAR **)var->addr)[j], &boundkey, &boundspecial);
2636 				if (us_samekey(key, special, boundkey, boundspecial)) continue;
2637 				us_parsebinding(pt, &commandbindingother);
2638 				if (*commandbindingother.command != 0 &&
2639 					namesame(commandbindingother.command, x_("telltool user")) == 0) foundanother++;
2640 				us_freebindingparse(&commandbindingother);
2641 				if (foundanother != 0) break;
2642 			}
2643 			if (foundanother == 0) retval = TRUE;
2644 		}
2645 	}
2646 	us_freebindingparse(&commandbindingthis);
2647 	return(retval);
2648 }
2649 
2650 /*
2651  * routine to parse the binding string in "st" and fill in the binding structure
2652  * in "commandbinding".
2653  */
us_parsebinding(CHAR * st,COMMANDBINDING * commandbinding)2654 void us_parsebinding(CHAR *st, COMMANDBINDING *commandbinding)
2655 {
2656 	CHAR *pt;
2657 	REGISTER CHAR *str;
2658 	REGISTER void *infstr;
2659 
2660 	commandbinding->nodeglyph = NONODEPROTO;
2661 	commandbinding->arcglyph = NOARCPROTO;
2662 	commandbinding->backgroundcolor = 0;
2663 	commandbinding->menumessagesize = TXTSETPOINTS(20);
2664 	commandbinding->menumessage = 0;
2665 	commandbinding->popup = NOPOPUPMENU;
2666 	commandbinding->inputpopup = FALSE;
2667 	commandbinding->command = 0;
2668 
2669 	pt = st;
2670 	for(;;)
2671 	{
2672 		str = getkeyword(&pt, x_("= "));
2673 		if (str == NOSTRING || *str == 0) break;
2674 		(void)tonextchar(&pt);
2675 
2676 		if (namesame(str, x_("node")) == 0)
2677 		{
2678 			str = getkeyword(&pt, x_(" "));
2679 			if (str == NOSTRING) break;
2680 			commandbinding->nodeglyph = getnodeproto(str);
2681 			continue;
2682 		}
2683 		if (namesame(str, x_("arc")) == 0)
2684 		{
2685 			str = getkeyword(&pt, x_(" "));
2686 			if (str == NOSTRING) break;
2687 			commandbinding->arcglyph = getarcproto(str);
2688 			continue;
2689 		}
2690 		if (namesame(str, x_("background")) == 0)
2691 		{
2692 			str = getkeyword(&pt, x_(" "));
2693 			if (str == NOSTRING) break;
2694 			commandbinding->backgroundcolor = myatoi(str);
2695 			continue;
2696 		}
2697 		if (namesame(str, x_("popup")) == 0)
2698 		{
2699 			str = getkeyword(&pt, x_(" "));
2700 			if (str == NOSTRING) break;
2701 			commandbinding->popup = us_getpopupmenu(str);
2702 			continue;
2703 		}
2704 		if (namesame(str, x_("inputpopup")) == 0)
2705 		{
2706 			str = getkeyword(&pt, x_(" "));
2707 			if (str == NOSTRING) break;
2708 			commandbinding->popup = us_getpopupmenu(str);
2709 			commandbinding->inputpopup = TRUE;
2710 			continue;
2711 		}
2712 		if (namesame(str, x_("message")) == 0)
2713 		{
2714 			(void)tonextchar(&pt);
2715 			infstr = initinfstr();
2716 			for(str = pt; *str != 0; str++)
2717 			{
2718 				if (*str == '"') break;
2719 				if (*str == '^') str++;
2720 				addtoinfstr(infstr, *str);
2721 			}
2722 			pt = str+1;
2723 			(void)allocstring(&commandbinding->menumessage, returninfstr(infstr), us_tool->cluster);
2724 			continue;
2725 		}
2726 		if (namesame(str, x_("messagesize")) == 0)
2727 		{
2728 			str = getkeyword(&pt, x_(" "));
2729 			if (str == NOSTRING) break;
2730 			commandbinding->menumessagesize = myatoi(str);
2731 		}
2732 		if (namesame(str, x_("command")) == 0) break;
2733 	}
2734 	(void)allocstring(&commandbinding->command, pt, us_tool->cluster);
2735 }
2736 
2737 /*
2738  * routine to free any allocated things in the "commandbinding" structure that was filled-in
2739  * by "us_parsebinding()".
2740  */
us_freebindingparse(COMMANDBINDING * commandbinding)2741 void us_freebindingparse(COMMANDBINDING *commandbinding)
2742 {
2743 	if (commandbinding->menumessage != 0) efree(commandbinding->menumessage);
2744 	commandbinding->menumessage = 0;
2745 	if (commandbinding->command != 0) efree(commandbinding->command);
2746 	commandbinding->command = 0;
2747 }
2748 
2749 /*
2750  * routine to set the trace information in the "size" coordinate pairs in
2751  * "newlist" onto the node "ni".
2752  */
us_settrace(NODEINST * ni,INTBIG * newlist,INTBIG size)2753 void us_settrace(NODEINST *ni, INTBIG *newlist, INTBIG size)
2754 {
2755 	REGISTER INTBIG lx, hx, ly, hy, x, y, i;
2756 	INTBIG lxo, hxo, lyo, hyo;
2757 
2758 	/* get the extent of the data */
2759 	lx = hx = newlist[0];   ly = hy = newlist[1];
2760 	for(i=1; i<size; i++)
2761 	{
2762 		x = newlist[i*2];
2763 		y = newlist[i*2+1];
2764 		lx = mini(lx, x);   hx = maxi(hx, x);
2765 		ly = mini(ly, y);   hy = maxi(hy, y);
2766 	}
2767 
2768 	/* make these co-ordinates relative to the center */
2769 	x = (hx+lx) / 2;   y = (hy+ly) / 2;
2770 	for(i=0; i<size; i++)
2771 	{
2772 		newlist[i*2] = newlist[i*2] - x;
2773 		newlist[i*2+1] = newlist[i*2+1] - y;
2774 	}
2775 
2776 	/* adjust size for node size offset */
2777 	nodesizeoffset(ni, &lxo, &lyo, &hxo, &hyo);
2778 	lx -= lxo;   hx += hxo;
2779 	ly -= lyo;   hy += hyo;
2780 
2781 	/* erase the node instance */
2782 	startobjectchange((INTBIG)ni, VNODEINST);
2783 
2784 	/* change the trace data */
2785 	(void)setvalkey((INTBIG)ni, VNODEINST, el_trace_key, (INTBIG)newlist,
2786 		VINTEGER|VISARRAY|((size*2)<<VLENGTHSH));
2787 
2788 	/* scale the node (which redraws too) */
2789 	if (ni->proto->primindex != 0 && (lx != ni->lowx || hx != ni->highx || ly != ni->lowy ||
2790 		hy != ni->highy || ni->rotation != 0 || ni->transpose != 0))
2791 			modifynodeinst(ni, lx-ni->lowx, ly-ni->lowy, hx-ni->highx, hy-ni->highy,
2792 				-ni->rotation, ni->transpose);
2793 
2794 	/* redisplay */
2795 	endobjectchange((INTBIG)ni, VNODEINST);
2796 
2797 	/* restore original data */
2798 	for(i=0; i<size; i++)
2799 	{
2800 		newlist[i*2] = newlist[i*2] + x;
2801 		newlist[i*2+1] = newlist[i*2+1] + y;
2802 	}
2803 }
2804 
2805 /*
2806  * routine to scale the trace information on node "ni" given that it will change
2807  * in size to "nlx"->"nhx" and "nly"->"nhy".
2808  */
us_scaletraceinfo(NODEINST * ni,INTBIG nlx,INTBIG nhx,INTBIG nly,INTBIG nhy)2809 void us_scaletraceinfo(NODEINST *ni, INTBIG nlx, INTBIG nhx, INTBIG nly, INTBIG nhy)
2810 {
2811 	REGISTER VARIABLE *var;
2812 	REGISTER INTBIG *newlist, oldx, oldy, denx, deny, len, i;
2813 
2814 	/* stop now if no trace information */
2815 	var = gettrace(ni);
2816 	if (var == NOVARIABLE) return;
2817 
2818 	/* get new array for new trace */
2819 	len = getlength(var);
2820 	newlist = (INTBIG *)emalloc(len * SIZEOFINTBIG, el_tempcluster);
2821 	if (newlist == 0) return;
2822 
2823 	/* copy the data and scale it */
2824 	denx = ni->highx - ni->lowx;
2825 	if (denx == 0) denx = nhx - nlx;
2826 	deny = ni->highy - ni->lowy;
2827 	if (deny == 0) deny = nhy - nly;
2828 	for(i=0; i<len; i += 2)
2829 	{
2830 		oldx = ((INTBIG *)var->addr)[i];
2831 		oldy = ((INTBIG *)var->addr)[i+1];
2832 		oldx = muldiv(oldx, nhx - nlx, denx);
2833 		oldy = muldiv(oldy, nhy - nly, deny);
2834 		newlist[i] = oldx;   newlist[i+1] = oldy;
2835 	}
2836 
2837 	/* store the new list */
2838 	(void)setvalkey((INTBIG)ni, VNODEINST, el_trace_key, (INTBIG)newlist,
2839 		VINTEGER|VISARRAY|(len<<VLENGTHSH));
2840 	efree((CHAR *)newlist);
2841 }
2842 
2843 /*
2844  * Routine to fillet the two highlighted objects
2845  */
us_dofillet(void)2846 void us_dofillet(void)
2847 {
2848 	REGISTER VARIABLE *var;
2849 	HIGHLIGHT thishigh, otherhigh;
2850 	REGISTER NODEINST *ni1, *ni2, *swapni;
2851 	double startoffset, endangle, srot, erot, newangle, dx, dy;
2852 	INTBIG ix, iy, ix1, iy1, ix2, iy2;
2853 	REGISTER BOOLEAN on1, on2;
2854 	REGISTER INTBIG ang1, ang2, size1, size2, arc1, arc2, swapsize, icount, newrot;
2855 	REGISTER INTBIG *newlist1, *newlist2, *line1xs, *line1ys, *line1xe, *line1ye,
2856 		*line2xs, *line2ys, *line2xe, *line2ye, x, y, i, *swaplist;
2857 	XARRAY trans;
2858 
2859 	/* must be exactly two nodes selected */
2860 	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_highlightedkey);
2861 	if (var == NOVARIABLE)
2862 	{
2863 		us_abortcommand(_("Must select two nodes before filleting"));
2864 		return;
2865 	}
2866 	if (getlength(var) != 2)
2867 	{
2868 		us_abortcommand(_("Must select two nodes before filleting"));
2869 		return;
2870 	}
2871 
2872 	if (us_makehighlight(((CHAR **)var->addr)[0], &thishigh) ||
2873 		us_makehighlight(((CHAR **)var->addr)[1], &otherhigh)) return;
2874 
2875 	/* get the two objects */
2876 	if ((thishigh.status&HIGHTYPE) != HIGHFROM || (otherhigh.status&HIGHTYPE) != HIGHFROM)
2877 	{
2878 		us_abortcommand(_("Must select two nodes before filleting"));
2879 		return;
2880 	}
2881 
2882 	if (!thishigh.fromgeom->entryisnode || !otherhigh.fromgeom->entryisnode)
2883 	{
2884 		us_abortcommand(_("Must select two nodes before filleting"));
2885 		return;
2886 	}
2887 
2888 	/* get description of first node */
2889 	ni1 = thishigh.fromgeom->entryaddr.ni;
2890 	if (ni1->proto == art_circleprim || ni1->proto == art_thickcircleprim)
2891 	{
2892 		getarcdegrees(ni1, &startoffset, &endangle);
2893 		if (startoffset == 0.0 && endangle == 0.0)
2894 		{
2895 			us_abortcommand(_("Must select arcs, not circles before filleting"));
2896 			return;
2897 		}
2898 		newlist1 = emalloc((6*SIZEOFINTBIG), el_tempcluster);
2899 		if (newlist1 == 0) return;
2900 		newlist1[0] = (ni1->lowx + ni1->highx) / 2;
2901 		newlist1[1] = (ni1->lowy + ni1->highy) / 2;
2902 		getarcendpoints(ni1, startoffset, endangle, &newlist1[2], &newlist1[3],
2903 			&newlist1[4], &newlist1[5]);
2904 		size1 = 0;
2905 		arc1 = 1;
2906 	} else if (ni1->proto == art_openedpolygonprim || ni1->proto == art_openeddottedpolygonprim ||
2907 		ni1->proto == art_openeddashedpolygonprim || ni1->proto == art_openedthickerpolygonprim ||
2908 		ni1->proto == art_closedpolygonprim)
2909 	{
2910 		var = gettrace(ni1);
2911 		if (var == NOVARIABLE)
2912 		{
2913 			us_abortcommand(_("Must select nodes with outline information before filleting"));
2914 			return;
2915 		}
2916 
2917 		/* transform the traces */
2918 		size1 = getlength(var) / 2;
2919 		newlist1 = emalloc((size1*2*SIZEOFINTBIG), el_tempcluster);
2920 		if (newlist1 == 0) return;
2921 		makerot(ni1, trans);
2922 		x = (ni1->highx + ni1->lowx) / 2;
2923 		y = (ni1->highy + ni1->lowy) / 2;
2924 		for(i=0; i<size1; i++)
2925 			xform(((INTBIG *)var->addr)[i*2]+x, ((INTBIG *)var->addr)[i*2+1]+y, &newlist1[i*2],
2926 				&newlist1[i*2+1], trans);
2927 		arc1 = 0;
2928 	} else
2929 	{
2930 		us_abortcommand(_("Node %s cannot be filleted"), describenodeinst(ni1));
2931 		return;
2932 	}
2933 
2934 	/* get description of second node */
2935 	ni2 = otherhigh.fromgeom->entryaddr.ni;
2936 	if (ni2->proto == art_circleprim || ni2->proto == art_thickcircleprim)
2937 	{
2938 		getarcdegrees(ni2, &startoffset, &endangle);
2939 		if (startoffset == 0.0 && endangle == 0.0)
2940 		{
2941 			us_abortcommand(_("Must select arcs, not circles before filleting"));
2942 			return;
2943 		}
2944 		newlist2 = emalloc((6*SIZEOFINTBIG), el_tempcluster);
2945 		if (newlist2 == 0) return;
2946 		newlist2[0] = (ni2->lowx + ni2->highx) / 2;
2947 		newlist2[1] = (ni2->lowy + ni2->highy) / 2;
2948 		getarcendpoints(ni2, startoffset, endangle, &newlist2[2], &newlist2[3],
2949 			&newlist2[4], &newlist2[5]);
2950 		size2 = 0;
2951 		x = y = 0;
2952 		arc2 = 1;
2953 	} else if (ni2->proto == art_openedpolygonprim || ni2->proto == art_openeddottedpolygonprim ||
2954 		ni2->proto == art_openeddashedpolygonprim || ni2->proto == art_openedthickerpolygonprim ||
2955 		ni2->proto == art_closedpolygonprim)
2956 	{
2957 		var = gettrace(ni2);
2958 		if (var == NOVARIABLE)
2959 		{
2960 			us_abortcommand(_("Must select nodes with outline information before filleting"));
2961 			return;
2962 		}
2963 
2964 		/* transform the traces */
2965 		size2 = getlength(var) / 2;
2966 		newlist2 = emalloc((size2*2*SIZEOFINTBIG), el_tempcluster);
2967 		if (newlist2 == 0) return;
2968 		makerot(ni2, trans);
2969 		x = (ni2->highx + ni2->lowx) / 2;
2970 		y = (ni2->highy + ni2->lowy) / 2;
2971 		for(i=0; i<size2; i++)
2972 			xform(((INTBIG *)var->addr)[i*2]+x, ((INTBIG *)var->addr)[i*2+1]+y, &newlist2[i*2],
2973 				&newlist2[i*2+1], trans);
2974 		arc2 = 0;
2975 	} else
2976 	{
2977 		us_abortcommand(_("Node %s cannot be filleted"), describenodeinst(ni2));
2978 		return;
2979 	}
2980 
2981 	/* handle different types of filleting */
2982 	if (arc1 != 0 && arc2 != 0)
2983 	{
2984 		/* cannot handle arc-to-arc filleting */
2985 		us_abortcommand(_("Cannot fillet two curves"));
2986 	} else if (arc1 == 0 && arc2 == 0)
2987 	{
2988 		/* handle line-to-line filleting: find out which endpoints are closest */
2989 		if (computedistance(x,y, newlist1[0],newlist1[1]) <
2990 			computedistance(x,y, newlist1[size1*2-2],newlist1[size1*2-1]))
2991 		{
2992 			line1xs = &newlist1[0];
2993 			line1ys = &newlist1[1];
2994 			line1xe = &newlist1[2];
2995 			line1ye = &newlist1[3];
2996 		} else
2997 		{
2998 			line1xs = &newlist1[size1*2-2];
2999 			line1ys = &newlist1[size1*2-1];
3000 			line1xe = &newlist1[size1*2-4];
3001 			line1ye = &newlist1[size1*2-3];
3002 		}
3003 		if (computedistance(*line1xs,*line1ys, newlist2[0],newlist2[1]) <
3004 			computedistance(*line1xs,*line1ys, newlist2[size2*2-2],newlist2[size2*2-1]))
3005 		{
3006 			line2xs = &newlist2[0];
3007 			line2ys = &newlist2[1];
3008 			line2xe = &newlist2[2];
3009 			line2ye = &newlist2[3];
3010 		} else
3011 		{
3012 			line2xs = &newlist2[size2*2-2];
3013 			line2ys = &newlist2[size2*2-1];
3014 			line2xe = &newlist2[size2*2-4];
3015 			line2ye = &newlist2[size2*2-3];
3016 		}
3017 
3018 		/* compute intersection point */
3019 		ang1 = figureangle(*line1xs, *line1ys, *line1xe, *line1ye);
3020 		ang2 = figureangle(*line2xs, *line2ys, *line2xe, *line2ye);
3021 		if (intersect(*line1xs, *line1ys, ang1, *line2xs, *line2ys, ang2, &ix, &iy) != 0)
3022 			us_abortcommand(_("Lines do not intersect")); else
3023 		{
3024 			*line1xs = ix;   *line1ys = iy;
3025 			*line2xs = ix;   *line2ys = iy;
3026 			us_pushhighlight();
3027 			us_clearhighlightcount();
3028 			us_settrace(ni1, newlist1, size1);
3029 			us_settrace(ni2, newlist2, size2);
3030 			us_pophighlight(TRUE);
3031 		}
3032 	} else
3033 	{
3034 		/* handle arc-to-line filleting */
3035 		if (arc1 == 0)
3036 		{
3037 			swaplist = newlist1;   newlist1 = newlist2;   newlist2 = swaplist;
3038 			swapsize = size1;      size1 = size2;         size2 = swapsize;
3039 			swapni = ni1;          ni1 = ni2;             ni2 = swapni;
3040 		}
3041 
3042 		/* "newlist1" describes the arc, "newlist2" describes the line */
3043 		if (computedistance(newlist1[0],newlist1[1], newlist2[0],newlist2[1]) <
3044 			computedistance(newlist1[0],newlist1[1], newlist2[size2*2-2],newlist2[size2*2-1]))
3045 		{
3046 			line2xs = &newlist2[0];
3047 			line2ys = &newlist2[1];
3048 			line2xe = &newlist2[2];
3049 			line2ye = &newlist2[3];
3050 		} else
3051 		{
3052 			line2xs = &newlist2[size2*2-2];
3053 			line2ys = &newlist2[size2*2-1];
3054 			line2xe = &newlist2[size2*2-4];
3055 			line2ye = &newlist2[size2*2-3];
3056 		}
3057 		icount = circlelineintersection(newlist1[0],newlist1[1], newlist1[2],newlist1[3],
3058 			*line2xs, *line2ys, *line2xe, *line2ye, &ix1, &iy1, &ix2, &iy2, 0);
3059 		if (icount == 0)
3060 		{
3061 			us_abortcommand(_("Line does not intersect arc: cannot fillet"));
3062 		} else
3063 		{
3064 			if (icount == 2)
3065 			{
3066 				on1 = us_pointonexparc(newlist1[0],newlist1[1], newlist1[2],newlist1[3],
3067 					newlist1[4],newlist1[5], ix1, iy1);
3068 				on2 = us_pointonexparc(newlist1[0],newlist1[1], newlist1[2],newlist1[3],
3069 					newlist1[4],newlist1[5], ix2, iy2);
3070 				if (!on1 && on2)
3071 				{
3072 					icount = 1;
3073 					ix1 = ix2;   iy1 = iy2;
3074 				} else if (on1 && !on2)
3075 				{
3076 					icount = 1;
3077 				}
3078 			}
3079 			if (icount == 2)
3080 			{
3081 				x = (*line2xs + *line2xe) / 2;
3082 				y = (*line2ys + *line2ye) / 2;
3083 				if (computedistance(ix1,iy1, x,y) > computedistance(ix2,iy2, x,y))
3084 				{
3085 					ix1 = ix2;   iy1 = iy2;
3086 				}
3087 			}
3088 
3089 			/* make them fillet at (ix1,iy1) */
3090 			us_pushhighlight();
3091 			us_clearhighlightcount();
3092 
3093 			/* adjust the arc (node ni1) */
3094 			dx = (double)(newlist1[2]-newlist1[0]);   dy = (double)(newlist1[3]-newlist1[1]);
3095 			if (dx == 0.0 && dy == 0.0)
3096 			{
3097 				us_abortcommand(_("Domain error during fillet"));
3098 				return;
3099 			}
3100 			srot = atan2(dy, dx);
3101 			if (srot < 0.0) srot += EPI*2.0;
3102 
3103 			dx = (double)(newlist1[4]-newlist1[0]);   dy = (double)(newlist1[5]-newlist1[1]);
3104 			if (dx == 0.0 && dy == 0.0)
3105 			{
3106 				us_abortcommand(_("Domain error during fillet"));
3107 				return;
3108 			}
3109 			erot = atan2(dy, dx);
3110 			if (erot < 0.0) erot += EPI*2.0;
3111 
3112 			dx = (double)(ix1-newlist1[0]);   dy = (double)(iy1-newlist1[1]);
3113 			if (dx == 0.0 && dy == 0.0)
3114 			{
3115 				us_abortcommand(_("Domain error during fillet"));
3116 				return;
3117 			}
3118 			newangle = atan2(dy, dx);
3119 			if (newangle < 0.0) newangle += EPI*2.0;
3120 			if (computedistance(ix1,iy1, newlist1[2],newlist1[3]) <
3121 				computedistance(ix1,iy1, newlist1[4],newlist1[5])) srot = newangle; else
3122 					erot = newangle;
3123 			erot -= srot;
3124 			if (erot < 0.0) erot += EPI*2.0;
3125 			newrot = rounddouble(srot * 1800.0 / EPI);
3126 			srot -= ((double)newrot) * EPI / 1800.0;
3127 			startobjectchange((INTBIG)ni1, VNODEINST);
3128 			modifynodeinst(ni1, 0, 0, 0, 0, newrot - ni1->rotation, 0);
3129 			setarcdegrees(ni1, srot, erot);
3130 			endobjectchange((INTBIG)ni1, VNODEINST);
3131 
3132 			/* adjust the line (node ni2) */
3133 			*line2xs = ix1;   *line2ys = iy1;
3134 			us_settrace(ni2, newlist2, size2);
3135 
3136 			/* restore highlighting */
3137 			us_pophighlight(TRUE);
3138 		}
3139 	}
3140 	efree((CHAR *)newlist1);
3141 	efree((CHAR *)newlist2);
3142 }
3143 
3144 /*
3145  * Houtine to convert the text in "msg" to bits on the display.
3146  */
us_layouttext(CHAR * layer,INTBIG tsize,INTBIG scale,INTBIG font,INTBIG italic,INTBIG bold,INTBIG underline,INTBIG separation,CHAR * msg)3147 void us_layouttext(CHAR *layer, INTBIG tsize, INTBIG scale, INTBIG font, INTBIG italic,
3148 	INTBIG bold, INTBIG underline, INTBIG separation, CHAR *msg)
3149 {
3150 	REGISTER NODEPROTO *np;
3151 	REGISTER INTBIG x, y;
3152 	REGISTER NODEINST *ni, **nodelist;
3153 	REGISTER BOOLEAN err;
3154 	REGISTER INTBIG cx, cy, lambda, gridsize, i;
3155 	INTBIG wid, hei, bx, by, count;
3156 	UINTBIG descript[TEXTDESCRIPTSIZE];
3157 	HIGHLIGHT high;
3158 	UCHAR1 **rowstart, *data;
3159 	REGISTER void *merge=0;
3160 	LISTINTBIG *linodes;
3161 	static POLYGON *poly = NOPOLYGON;
3162 
3163 	np = us_needcell();
3164 	if (np == NONODEPROTO) return;
3165 
3166 	/* convert the text to bits */
3167 	TDCLEAR(descript);
3168 	if (tsize < 4) tsize = 4;
3169 	if (tsize > TXTMAXPOINTS) tsize = TXTMAXPOINTS;
3170 	TDSETSIZE(descript, TXTSETPOINTS(tsize));
3171 	TDSETFACE(descript, font);
3172 	if (italic != 0) TDSETITALIC(descript, VTITALIC);
3173 	if (bold != 0) TDSETBOLD(descript, VTBOLD);
3174 	if (underline != 0) TDSETUNDERLINE(descript, VTUNDERLINE);
3175 	screensettextinfo(el_curwindowpart, NOTECHNOLOGY, descript);
3176 	err = gettextbits(el_curwindowpart, msg, &wid, &hei, &rowstart);
3177 	if (err)
3178 	{
3179 		us_abortcommand(_("Sorry, this system cannot layout text"));
3180 		return;
3181 	}
3182 
3183 	/* determine the primitive to use for the layout */
3184 	for(us_layouttextprim = el_curtech->firstnodeproto; us_layouttextprim != NONODEPROTO;
3185 		us_layouttextprim = us_layouttextprim->nextnodeproto)
3186 			if (namesame(us_layouttextprim->protoname, layer) == 0) break;
3187 	if (us_layouttextprim == NONODEPROTO)
3188 	{
3189 		us_abortcommand(_("Cannot find '%s' node"), layer);
3190 		return;
3191 	}
3192 
3193 	lambda = el_curlib->lambda[el_curtech->techindex];
3194 	separation = separation * lambda / 2;
3195 	gridsize = lambda * scale;
3196 	bx = (el_curwindowpart->screenlx+el_curwindowpart->screenhx -
3197 		wid * gridsize) / 2;
3198 	by = (el_curwindowpart->screenly+el_curwindowpart->screenhy -
3199 		hei * gridsize) / 2;
3200 	gridalign(&bx, &by, 1, np);
3201 	(void)needstaticpolygon(&poly, 4, us_tool->cluster);
3202 	if (separation == 0) merge = mergenew(us_tool->cluster); else
3203 		linodes = newintlistobj(us_tool->cluster);
3204 
3205 	for(y=0; y<hei; y++)
3206 	{
3207 		cy = by - y * gridsize;
3208 		data = rowstart[y];
3209 		for(x=0; x<wid; x++)
3210 		{
3211 			cx = bx + x * gridsize;
3212 			if (*data != 0)
3213 			{
3214 				poly->xv[0] = cx;
3215 				poly->yv[0] = cy;
3216 				poly->xv[1] = cx + gridsize;
3217 				poly->yv[1] = cy + gridsize;
3218 				poly->style = FILLEDRECT;
3219 				poly->count = 2;
3220 				if (separation == 0) mergeaddpolygon(merge, 0, el_curtech, poly); else
3221 				{
3222 					/* place the node now */
3223 					ni = newnodeinst(us_layouttextprim, poly->xv[0]+separation,
3224 						poly->xv[1]-separation, poly->yv[0]+separation,
3225 						poly->yv[1]-separation, 0, 0, np);
3226 					if (ni != NONODEINST)
3227 						addtointlistobj(linodes, (INTBIG)ni, FALSE);
3228 				}
3229 			}
3230 			data++;
3231 		}
3232 	}
3233 	us_clearhighlightcount();
3234 	if (separation == 0)
3235 	{
3236 		mergeextract(merge, us_layouttextpolygon);
3237 		mergedelete(merge);
3238 	} else
3239 	{
3240 		nodelist = (NODEINST **)getintlistobj(linodes, &count);
3241 		for(i=0; i<count; i++)
3242 		{
3243 			ni = nodelist[i];
3244 			endobjectchange((INTBIG)ni, VNODEINST);
3245 			high.status = HIGHFROM;
3246 			high.fromgeom = ni->geom;
3247 			high.fromport = NOPORTPROTO;
3248 			high.frompoint = 0;
3249 			high.cell = np;
3250 			us_addhighlight(&high);
3251 		}
3252 	}
3253 }
3254 
3255 /*
3256  * Helper routine for "us_layouttext" to process a polygon and convert it to layout.
3257  */
us_layouttextpolygon(INTBIG layer,TECHNOLOGY * tech,INTBIG * x,INTBIG * y,INTBIG count)3258 void us_layouttextpolygon(INTBIG layer, TECHNOLOGY *tech, INTBIG *x, INTBIG *y, INTBIG count)
3259 {
3260 	REGISTER NODEPROTO *cell;
3261 	REGISTER NODEINST *ni;
3262 	REGISTER INTBIG lx, hx, ly, hy, cx, cy, *newlist;
3263 	REGISTER INTBIG i;
3264 	HIGHLIGHT high;
3265 	Q_UNUSED( layer );
3266 	Q_UNUSED( tech );
3267 
3268 	cell = us_needcell();
3269 	if (cell == NONODEPROTO) return;
3270 	lx = hx = x[0];
3271 	ly = hy = y[0];
3272 	for(i=1; i<count; i++)
3273 	{
3274 		if (x[i] < lx) lx = x[i];
3275 		if (x[i] > hx) hx = x[i];
3276 		if (y[i] < ly) ly = y[i];
3277 		if (y[i] > hy) hy = y[i];
3278 	}
3279 	cx = (lx+hx) / 2;   cy = (ly+hy) / 2;
3280 	ni = newnodeinst(us_layouttextprim, lx, hx, ly, hy, 0, 0, cell);
3281 	newlist = (INTBIG *)emalloc(count * 2 * SIZEOFINTBIG, el_tempcluster);
3282 	if (newlist == 0) return;
3283 	for(i=0; i<count; i++)
3284 	{
3285 		newlist[i*2] = x[i] - cx;
3286 		newlist[i*2+1] = y[i] - cy;
3287 	}
3288 	(void)setvalkey((INTBIG)ni, VNODEINST, el_trace_key, (INTBIG)newlist,
3289 		VINTEGER|VISARRAY|((count*2)<<VLENGTHSH));
3290 	endobjectchange((INTBIG)ni, VNODEINST);
3291 	high.status = HIGHFROM;
3292 	high.fromgeom = ni->geom;
3293 	high.fromport = NOPORTPROTO;
3294 	high.frompoint = 0;
3295 	high.cell = cell;
3296 	us_addhighlight(&high);
3297 }
3298 
3299 /*
3300  * Routine to determine whether the point (x,y) is on the arc centered at (cx,cy), starting
3301  * at (sx,sy), and ending at (ex,ey).  Returns true if on the arc.
3302  */
us_pointonexparc(INTBIG cx,INTBIG cy,INTBIG sx,INTBIG sy,INTBIG ex,INTBIG ey,INTBIG x,INTBIG y)3303 BOOLEAN us_pointonexparc(INTBIG cx, INTBIG cy, INTBIG sx, INTBIG sy, INTBIG ex, INTBIG ey, INTBIG x, INTBIG y)
3304 {
3305 	REGISTER INTBIG as, ae, a;
3306 
3307 	as = figureangle(cx, cy, sx, sy);
3308 	ae = figureangle(cx, cy, ex, ey);
3309 	a = figureangle(cx, cy, x, y);
3310 
3311 	if (ae > as)
3312 	{
3313 		if (a >= as && a <= ae) return(TRUE);
3314 	} else
3315 	{
3316 		if (a >= as || a <= ae) return(TRUE);
3317 	}
3318 	return(FALSE);
3319 }
3320 
3321 /*
3322  * routine to recursively check sub-cell revision times
3323  * P. Attfield
3324  */
us_check_cell_date(NODEPROTO * np,UINTBIG rev_time)3325 void us_check_cell_date(NODEPROTO *np, UINTBIG rev_time)
3326 {
3327 	REGISTER NODEPROTO *np2;
3328 	REGISTER NODEINST *ni;
3329 
3330 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
3331 	{
3332 		np2 = ni->proto;
3333 		if (np2->primindex != 0) continue; /* ignore if primitive */
3334 
3335 		/* ignore recursive references (showing icon in contents) */
3336 		if (isiconof(np2, np)) continue;
3337 		if (np2->temp1 != 0) continue; /* ignore if already seen */
3338 		us_check_cell_date(np2, rev_time); /* recurse */
3339 	}
3340 
3341 	/* check this cell */
3342 	np->temp1++; /* flag that we have seen this one */
3343 	if (np->revisiondate <= rev_time) return;
3344 
3345 	/* possible error in hierarchy */
3346 	ttyputerr(_("WARNING: sub-cell '%s' has been edited"), describenodeproto(np));
3347 	ttyputmsg(_("         since the last revision to the current cell"));
3348 }
3349 
3350 /*
3351  * routine to switch to library "lib"
3352  */
us_switchtolibrary(LIBRARY * lib)3353 void us_switchtolibrary(LIBRARY *lib)
3354 {
3355 	CHAR *newpar[2];
3356 	REGISTER INTBIG oldlam;
3357 	REGISTER WINDOWPART *w;
3358 
3359 	/* select the new library */
3360 	us_clearhighlightcount();
3361 	oldlam = el_curlib->lambda[el_curtech->techindex];
3362 	selectlibrary(lib, TRUE);
3363 	us_setlambda(NOWINDOWFRAME);
3364 	if ((us_curnodeproto == NONODEPROTO || us_curnodeproto->primindex == 0) &&
3365 		(us_state&NONPERSISTENTCURNODE) == 0)
3366 			us_setnodeproto(el_curtech->firstnodeproto);
3367 	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
3368 		us_setcellname(w);
3369 
3370 	/* if nothing displayed and new library has top cell, show it */
3371 	if (el_curwindowpart == NOWINDOWPART ||
3372 		el_curwindowpart->curnodeproto == NONODEPROTO)
3373 	{
3374 		if (el_curlib->curnodeproto != NONODEPROTO)
3375 		{
3376 			newpar[0] = describenodeproto(el_curlib->curnodeproto);
3377 			us_editcell(1, newpar);
3378 		}
3379 	}
3380 
3381 	/* redo the explorer window (if it is up) */
3382 	us_redoexplorerwindow();
3383 }
3384 
3385 /*
3386  * Routine to replace all cross-library references that point into
3387  * "oldlib" with equivalent ones that point to "newlib".  This is called
3388  * when a library is re-read from disk ("oldlib" is the former one).
3389  */
us_replacelibraryreferences(LIBRARY * oldlib,LIBRARY * newlib)3390 void us_replacelibraryreferences(LIBRARY *oldlib, LIBRARY *newlib)
3391 {
3392 	REGISTER LIBRARY *lib;
3393 	REGISTER NODEPROTO *np, *newnp;
3394 	REGISTER NODEINST *ni, *nextni, *newni;
3395 	REGISTER ARCINST *ai;
3396 	REGISTER PORTARCINST *pi;
3397 	REGISTER PORTEXPINST *pe;
3398 
3399 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
3400 	{
3401 		if (lib == oldlib || lib == newlib) continue;
3402 
3403 		/* look for cross-library references to "oldlib" */
3404 		us_correctxlibref(&lib->firstvar, &lib->numvar, oldlib, newlib);
3405 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
3406 		{
3407 			us_correctxlibref(&np->firstvar, &np->numvar, oldlib, newlib);
3408 			for(ni = np->firstnodeinst; ni != NONODEINST; ni = nextni)
3409 			{
3410 				nextni = ni->nextnodeinst;
3411 
3412 				/* correct variables */
3413 				us_correctxlibref(&ni->firstvar, &ni->numvar, oldlib, newlib);
3414 				us_correctxlibref(&ni->geom->firstvar, &ni->geom->numvar, oldlib, newlib);
3415 				for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
3416 					us_correctxlibref(&pi->firstvar, &pi->numvar, oldlib, newlib);
3417 				for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
3418 					us_correctxlibref(&pe->firstvar, &pe->numvar, oldlib, newlib);
3419 
3420 				if (ni->proto->primindex != 0) continue;
3421 				if (ni->proto->lib == oldlib)
3422 				{
3423 					/* find the equivalent name in the new library */
3424 					newnp = us_findcellinotherlib(ni->proto, newlib);
3425 					if (newnp == NONODEPROTO)
3426 					{
3427 						ttyputerr(_("Error: cell %s{%s};%ld no longer present in library %s"),
3428 							ni->proto->protoname, ni->proto->cellview->sviewname,
3429 								ni->proto->version, newlib->libname);
3430 						continue;
3431 					}
3432 					newni = replacenodeinst(ni, newnp, FALSE, TRUE);
3433 					if (newni == NONODEINST)
3434 					{
3435 						ttyputerr(_("Error: node %s{%s};%ld could not be replaced with equivalent in library %s"),
3436 							ni->proto->protoname, ni->proto->cellview->sviewname,
3437 								ni->proto->version, newlib->libname);
3438 						continue;
3439 					}
3440 				}
3441 			}
3442 			for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
3443 			{
3444 				us_correctxlibref(&ai->firstvar, &ai->numvar, oldlib, newlib);
3445 				us_correctxlibref(&ai->geom->firstvar, &ai->geom->numvar, oldlib, newlib);
3446 			}
3447 		}
3448 	}
3449 }
3450 
3451 /*
3452  * Helper routine to search "numvar" variables in "firstvar" and replace references
3453  * to anything in library "oldlib" to point to an equivalent in "newlib".
3454  */
us_correctxlibref(VARIABLE ** firstvar,INTSML * numvar,LIBRARY * oldlib,LIBRARY * newlib)3455 void us_correctxlibref(VARIABLE **firstvar, INTSML *numvar, LIBRARY *oldlib, LIBRARY *newlib)
3456 {
3457 	REGISTER INTBIG i, j, checkcell;
3458 	REGISTER VARIABLE *var, *svar, *dvar;
3459 	REGISTER NODEPROTO *np, *newnp;
3460 	REGISTER NODEINST *ni;
3461 	REGISTER ARCINST *ai;
3462 	REGISTER PORTPROTO *pp;
3463 	REGISTER PORTARCINST *pi;
3464 	REGISTER PORTEXPINST *pe;
3465 	REGISTER LIBRARY *lib;
3466 	REGISTER NETWORK *net;
3467 	REGISTER GEOM *geom;
3468 
3469 	for(i=0; i < *numvar; i++)
3470 	{
3471 		var = &(*firstvar)[i];
3472 		if ((var->type&VISARRAY) != 0) continue;
3473 		checkcell = 0;
3474 		np = NONODEPROTO;
3475 		switch (var->type&VTYPE)
3476 		{
3477 			case VNODEINST:
3478 				ni = (NODEINST *)var->addr;
3479 				np = ni->parent;
3480 				checkcell = 1;
3481 				break;
3482 			case VNODEPROTO:
3483 				np = (NODEPROTO *)var->addr;
3484 				if (np->primindex != 0) break;
3485 				if (np->lib != oldlib) break;
3486 				newnp = us_findcellinotherlib(np, newlib);
3487 				var->addr = (INTBIG)newnp;
3488 				break;
3489 			case VPORTARCINST:
3490 				pi = (PORTARCINST *)var->addr;
3491 				np = pi->conarcinst->parent;
3492 				checkcell = 1;
3493 				break;
3494 			case VPORTEXPINST:
3495 				pe = (PORTEXPINST *)var->addr;
3496 				np = pe->exportproto->parent;
3497 				checkcell = 1;
3498 				break;
3499 			case VPORTPROTO:
3500 				pp = (PORTPROTO *)var->addr;
3501 				np = pp->parent;
3502 				checkcell = 1;
3503 				break;
3504 			case VARCINST:
3505 				ai = (ARCINST *)var->addr;
3506 				np = ai->parent;
3507 				checkcell = 1;
3508 				break;
3509 			case VGEOM:
3510 				geom = (GEOM *)var->addr;
3511 				np = geomparent(geom);
3512 				checkcell = 1;
3513 				break;
3514 			case VLIBRARY:
3515 				lib = (LIBRARY *)var->addr;
3516 				if (lib != oldlib) break;
3517 				var->addr = (INTBIG)newlib;
3518 				break;
3519 			case VNETWORK:
3520 				net = (NETWORK *)var->addr;
3521 				np = net->parent;
3522 				checkcell = 1;
3523 				break;
3524 		}
3525 		if (checkcell != 0)
3526 		{
3527 			if (np->primindex != 0) continue;
3528 			if (np->lib != oldlib) continue;
3529 
3530 			/* cannot correct the reference: delete the variable */
3531 			for(j=i+1; j < *numvar; j++)
3532 			{
3533 				svar = &(*firstvar)[j];
3534 				dvar = &(*firstvar)[j-1];
3535 				*dvar = *svar;
3536 			}
3537 			(*numvar)--;
3538 			i--;
3539 		}
3540 	}
3541 }
3542 
3543 /*
3544  * Helper routine to find the equivalent to cell "cell" in library "lib".
3545  */
us_findcellinotherlib(NODEPROTO * cell,LIBRARY * lib)3546 NODEPROTO *us_findcellinotherlib(NODEPROTO *cell, LIBRARY *lib)
3547 {
3548 	REGISTER NODEPROTO *newnp;
3549 
3550 	for(newnp = lib->firstnodeproto; newnp != NONODEPROTO; newnp = newnp->nextnodeproto)
3551 	{
3552 		if (namesame(cell->protoname, newnp->protoname) != 0) continue;
3553 		if (cell->cellview != newnp->cellview) continue;
3554 		if (cell->version != newnp->version) continue;
3555 		break;
3556 	}
3557 	return(newnp);
3558 }
3559 
3560 /******************** TEXT OBJECTS ********************/
3561 
3562 /*
3563  * routine to recompute the text descriptor in "descript" to change the
3564  * grab-point according to the location of (xcur, ycur), given that the
3565  * text centers at (xc, yc) and is "xw" by "yw" in size.
3566  */
us_figuregrabpoint(UINTBIG * descript,INTBIG xcur,INTBIG ycur,INTBIG xc,INTBIG yc,INTBIG xw,INTBIG yw)3567 void us_figuregrabpoint(UINTBIG *descript, INTBIG xcur, INTBIG ycur, INTBIG xc,
3568 	INTBIG yc, INTBIG xw, INTBIG yw)
3569 {
3570 	if (xcur < xc - xw/2)
3571 	{
3572 		/* grab-point is on the bottom left */
3573 		if (ycur < yc - yw/2)
3574 		{
3575 			TDSETPOS(descript, VTPOSUPRIGHT);
3576 			return;
3577 		}
3578 
3579 		/* grab-point is on the top left */
3580 		if (ycur > yc + yw/2)
3581 		{
3582 			TDSETPOS(descript, VTPOSDOWNRIGHT);
3583 			return;
3584 		}
3585 
3586 		/* grab-point is on the left */
3587 		TDSETPOS(descript, VTPOSRIGHT);
3588 		return;
3589 	}
3590 
3591 	if (xcur > xc + xw/2)
3592 	{
3593 		/* grab-point is on the bottom right */
3594 		if (ycur < yc - yw/2)
3595 		{
3596 			TDSETPOS(descript, VTPOSUPLEFT);
3597 			return;
3598 		}
3599 
3600 		/* grab-point is on the top right */
3601 		if (ycur > yc + yw/2)
3602 		{
3603 			TDSETPOS(descript, VTPOSDOWNLEFT);
3604 			return;
3605 		}
3606 
3607 		/* grab-point is on the right */
3608 		TDSETPOS(descript, VTPOSLEFT);
3609 		return;
3610 	}
3611 
3612 	/* grab-point is on the bottom */
3613 	if (ycur < yc - yw/2)
3614 	{
3615 		TDSETPOS(descript, VTPOSUP);
3616 		return;
3617 	}
3618 
3619 	/* grab-point is on the top */
3620 	if (ycur > yc + yw/2)
3621 	{
3622 		TDSETPOS(descript, VTPOSDOWN);
3623 		return;
3624 	}
3625 
3626 	/* grab-point is in the center: check for VERY center */
3627 	if (ycur >= yc - yw/6 && ycur <= yc + yw/6 && xcur >= xc - xw/6 &&
3628 		xcur <= xc + xw/6)
3629 	{
3630 		TDSETPOS(descript, VTPOSBOXED);
3631 		return;
3632 	}
3633 
3634 	/* grab-point is simply centered */
3635 	TDSETPOS(descript, VTPOSCENT);
3636 }
3637 
3638 /*
3639  * routine to return the new text descriptor field, given that the old is in
3640  * "formerdesc".  The instructions for changing this variable are in "count"
3641  * and "par", where "count" where the first two values are the X and Y offset,
3642  * and the third value is the text position.  Returns -1 on error.
3643  */
us_figurevariableplace(UINTBIG * formerdesc,INTBIG count,CHAR * par[])3644 void us_figurevariableplace(UINTBIG *formerdesc, INTBIG count, CHAR *par[])
3645 {
3646 	INTBIG xval, yval;
3647 	REGISTER INTBIG grab;
3648 
3649 	if (count >= 2)
3650 	{
3651 		xval = atofr(par[0]) * 4 / WHOLE;
3652 		yval = atofr(par[1]) * 4 / WHOLE;
3653 		us_setdescriptoffset(formerdesc, xval, yval);
3654 	}
3655 	if (count >= 3)
3656 	{
3657 		grab = us_gettextposition(par[2]);
3658 		TDSETPOS(formerdesc, grab);
3659 	}
3660 }
3661 
3662 /*
3663  * routine to change the X and Y offset factors in the text descriptor
3664  * "formerdesc" to "xval" and "yval".  Returns the new text descriptor.
3665  */
us_setdescriptoffset(UINTBIG * formerdesc,INTBIG xval,INTBIG yval)3666 void us_setdescriptoffset(UINTBIG *formerdesc, INTBIG xval, INTBIG yval)
3667 {
3668 	REGISTER INTBIG oldx, oldy;
3669 
3670 	/* make sure the range is proper */
3671 	oldx = xval;   oldy = yval;
3672 	propervaroffset(&xval, &yval);
3673 	if (xval != oldx || yval != oldy)
3674 		ttyputmsg(_("Text offset adjusted"));
3675 
3676 	TDSETOFF(formerdesc, xval, yval);
3677 }
3678 
3679 /*
3680  * routine to rotate the text descriptor in "descript" to account for
3681  * the rotation of the object on which it resides: "geom".  The new
3682  * text descriptor is returned.
3683  */
us_rotatedescript(GEOM * geom,UINTBIG * descript)3684 void us_rotatedescript(GEOM *geom, UINTBIG *descript)
3685 {
3686 	us_rotatedescriptArb(geom, descript, FALSE);
3687 }
3688 
3689 /*
3690  * routine to undo the rotation of the text descriptor in "descript" to account for
3691  * the rotation of the object on which it resides: "geom".  The new
3692  * text descriptor is returned.
3693  */
us_rotatedescriptI(GEOM * geom,UINTBIG * descript)3694 void us_rotatedescriptI(GEOM *geom, UINTBIG *descript)
3695 {
3696 	us_rotatedescriptArb(geom, descript, TRUE);
3697 }
3698 
3699 /*
3700  * routine to rotate the text descriptor in "descript" to account for
3701  * the rotation of the object on which it resides: "geom".  Inverts the
3702  * sense of the rotation if "invert" is true.  The new
3703  * text descriptor is returned.
3704  */
us_rotatedescriptArb(GEOM * geom,UINTBIG * descript,BOOLEAN invert)3705 void us_rotatedescriptArb(GEOM *geom, UINTBIG *descript, BOOLEAN invert)
3706 {
3707 	REGISTER INTBIG style;
3708 	REGISTER NODEINST *ni;
3709 	XARRAY trans;
3710 
3711 	/* arcs do not rotate */
3712 	if (!geom->entryisnode) return;
3713 
3714 	switch (TDGETPOS(descript))
3715 	{
3716 		case VTPOSCENT:
3717 		case VTPOSBOXED:     return;
3718 		case VTPOSUP:        style = TEXTBOT;       break;
3719 		case VTPOSDOWN:      style = TEXTTOP;       break;
3720 		case VTPOSLEFT:      style = TEXTRIGHT;     break;
3721 		case VTPOSRIGHT:     style = TEXTLEFT;      break;
3722 		case VTPOSUPLEFT:    style = TEXTBOTRIGHT;  break;
3723 		case VTPOSUPRIGHT:   style = TEXTBOTLEFT;   break;
3724 		case VTPOSDOWNLEFT:  style = TEXTTOPRIGHT;  break;
3725 		case VTPOSDOWNRIGHT: style = TEXTTOPLEFT;   break;
3726 		default:             return;
3727 	}
3728 	ni = geom->entryaddr.ni;
3729 	if (invert)
3730 	{
3731 		if (ni->transpose == 0) makeangle((3600 - ni->rotation)%3600, 0, trans); else
3732 			makeangle(ni->rotation, ni->transpose, trans);
3733 	} else
3734 	{
3735 		makeangle(ni->rotation, ni->transpose, trans);
3736 	}
3737 	style = rotatelabel(style, TDGETROTATION(descript), trans);
3738 	switch (style)
3739 	{
3740 		case TEXTBOT:       TDSETPOS(descript, VTPOSUP);          break;
3741 		case TEXTTOP:       TDSETPOS(descript, VTPOSDOWN);        break;
3742 		case TEXTRIGHT:     TDSETPOS(descript, VTPOSLEFT);        break;
3743 		case TEXTLEFT:      TDSETPOS(descript, VTPOSRIGHT);       break;
3744 		case TEXTBOTRIGHT:  TDSETPOS(descript, VTPOSUPLEFT);      break;
3745 		case TEXTBOTLEFT:   TDSETPOS(descript, VTPOSUPRIGHT);     break;
3746 		case TEXTTOPRIGHT:  TDSETPOS(descript, VTPOSDOWNLEFT);    break;
3747 		case TEXTTOPLEFT:   TDSETPOS(descript, VTPOSDOWNRIGHT);   break;
3748 	}
3749 }
3750 
3751 /*
3752  * routine to adjust the displayable text on node "ni" to account for new
3753  * size/rotation factors in the node.  This is only done for invisible pins
3754  * in the generic technology, where the displayable text must track the node.
3755  */
us_adjustdisplayabletext(NODEINST * ni)3756 void us_adjustdisplayabletext(NODEINST *ni)
3757 {
3758 	REGISTER INTBIG i;
3759 	REGISTER INTBIG halfx, halfy, lambda;
3760 	UINTBIG descript[TEXTDESCRIPTSIZE];
3761 	REGISTER VARIABLE *var;
3762 
3763 	/* make sure this is the invisible pin */
3764 	if (ni->proto != gen_invispinprim) return;
3765 
3766 	/* search for displayable text */
3767 	for(i=0; i<ni->numvar; i++)
3768 	{
3769 		var = &ni->firstvar[i];
3770 		if ((var->type&VDISPLAY) == 0) continue;
3771 
3772 		/* compute the proper display offset */
3773 		TDCOPY(descript, var->textdescript);
3774 		lambda = lambdaofnode(ni);
3775 		halfx = (ni->highx - ni->lowx) * 2 / lambda;
3776 		halfy = (ni->highy - ni->lowy) * 2 / lambda;
3777 		switch (TDGETPOS(descript))
3778 		{
3779 			case VTPOSCENT:
3780 			case VTPOSBOXED:
3781 				us_setdescriptoffset(descript, 0, 0);
3782 				break;
3783 			case VTPOSUP:
3784 				us_setdescriptoffset(descript, 0, -halfy);
3785 				break;
3786 			case VTPOSDOWN:
3787 				us_setdescriptoffset(descript, 0, halfy);
3788 				break;
3789 			case VTPOSLEFT:
3790 				us_setdescriptoffset(descript, halfx, 0);
3791 				break;
3792 			case VTPOSRIGHT:
3793 				us_setdescriptoffset(descript, -halfx, 0);
3794 				break;
3795 			case VTPOSUPLEFT:
3796 				us_setdescriptoffset(descript, halfx, -halfy);
3797 				break;
3798 			case VTPOSUPRIGHT:
3799 				us_setdescriptoffset(descript, -halfx, -halfy);
3800 				break;
3801 			case VTPOSDOWNLEFT:
3802 				us_setdescriptoffset(descript, halfx, halfy);
3803 				break;
3804 			case VTPOSDOWNRIGHT:
3805 				us_setdescriptoffset(descript, -halfx, halfy);
3806 				break;
3807 		}
3808 		modifydescript((INTBIG)ni, VNODEINST, var, descript);
3809 	}
3810 }
3811 
3812 /*
3813  * routine to parse the "grab-point" specification in "pp" and return the
3814  * code.  Prints an error message on error.
3815  */
us_gettextposition(CHAR * pp)3816 INTBIG us_gettextposition(CHAR *pp)
3817 {
3818 	REGISTER INTBIG l;
3819 
3820 	l = estrlen(pp);
3821 	if (namesamen(pp, x_("centered"), l) == 0 && l >= 1) return(VTPOSCENT);
3822 	if (namesamen(pp, x_("boxed"), l) == 0 && l >= 1) return(VTPOSBOXED);
3823 	if (namesame(pp, x_("up")) == 0) return(VTPOSUP);
3824 	if (namesame(pp, x_("down")) == 0) return(VTPOSDOWN);
3825 	if (namesamen(pp, x_("left"), l) == 0 && l >= 1) return(VTPOSLEFT);
3826 	if (namesamen(pp, x_("right"), l) == 0 && l >= 1) return(VTPOSRIGHT);
3827 	if (namesamen(pp, x_("up-left"), l) == 0 && l >= 4) return(VTPOSUPLEFT);
3828 	if (namesamen(pp, x_("up-right"), l) == 0 && l >= 4) return(VTPOSUPRIGHT);
3829 	if (namesamen(pp, x_("down-left"), l) == 0 && l >= 6) return(VTPOSDOWNLEFT);
3830 	if (namesamen(pp, x_("down-right"), l) == 0 && l >= 6) return(VTPOSDOWNRIGHT);
3831 	us_abortcommand(_("Unrecognized grab-point: %s"), pp);
3832 	return(VTPOSCENT);
3833 }
3834 
3835 /*
3836  * routine to parse the "text size" specification in "pp" and return the
3837  * code.  Prints an error message and returns -1 on error.
3838  */
us_gettextsize(CHAR * pp,INTBIG old)3839 INTBIG us_gettextsize(CHAR *pp, INTBIG old)
3840 {
3841 	REGISTER INTBIG i, l;
3842 
3843 	l = estrlen(pp);
3844 	if (tolower(pp[l-1]) == 'p')
3845 	{
3846 		l = eatoi(pp);
3847 		if (l <= 0) l = 8;
3848 		if (l > TXTMAXPOINTS) l = TXTMAXPOINTS;
3849 		return(TXTSETPOINTS(l));
3850 	}
3851 	if (tolower(pp[l-1]) == 'l')
3852 	{
3853 		l = atofr(pp) * 4 / WHOLE;
3854 		if (l <= 0) l = 4;
3855 		if (l > TXTMAXQLAMBDA) l = TXTMAXQLAMBDA;
3856 		return(TXTSETQLAMBDA(l));
3857 	}
3858 	if (namesamen(pp, x_("up"), l) == 0 && l >= 1)
3859 	{
3860 		i = TXTGETPOINTS(old);
3861 		if (i > 0 && i < TXTMAXPOINTS) old = TXTSETPOINTS(i+1); else
3862 		{
3863 			i = TXTGETQLAMBDA(old);
3864 			if (i > 0 && i < TXTMAXQLAMBDA) old = TXTSETQLAMBDA(i+1);
3865 		}
3866 		return(old);
3867 	} else if (namesamen(pp, x_("down"), l) == 0 && l >= 1)
3868 	{
3869 		i = TXTGETPOINTS(old);
3870 		if (i > 1) old = TXTSETPOINTS(i-1); else
3871 		{
3872 			i = TXTGETQLAMBDA(old);
3873 			if (i > 1) old = TXTSETQLAMBDA(i-1);
3874 		}
3875 		return(old);
3876 	}
3877 	us_abortcommand(_("Unrecognized text size: %s"), pp);
3878 	return(-1);
3879 }
3880 
3881 static NODEINST  *us_searchcircuittextni;
3882 static ARCINST   *us_searchcircuittextai;
3883 static PORTPROTO *us_searchcircuittextpp;
3884 static INTBIG     us_searchcircuittextvarnum;	/* number of the variable on this text */
3885 static INTBIG     us_searchcircuittextline;		/* line number (in multiline text) */
3886 static INTBIG     us_searchcircuittextstart;	/* starting character position */
3887 static INTBIG     us_searchcircuittextend;		/* ending character position */
3888 static BOOLEAN    us_searchcircuittextfound;	/* true if text is selected */
3889 
3890 /*
3891  * Routine to initialize text searching in edit window "win".
3892  */
us_initsearchcircuittext(WINDOWPART * win)3893 void us_initsearchcircuittext(WINDOWPART *win)
3894 {
3895 	REGISTER NODEPROTO *np;
3896 
3897 	np = win->curnodeproto;
3898 	if (np == NONODEPROTO) return;
3899 	us_searchcircuittextni = np->firstnodeinst;
3900 	us_searchcircuittextai = np->firstarcinst;
3901 	us_searchcircuittextpp = np->firstportproto;
3902 	us_searchcircuittextvarnum = 0;
3903 	us_searchcircuittextfound = FALSE;
3904 }
3905 
us_advancecircuittext(CHAR * search,INTBIG bits)3906 void us_advancecircuittext(CHAR *search, INTBIG bits)
3907 {
3908 	REGISTER NODEINST *ni;
3909 	REGISTER ARCINST *ai;
3910 	REGISTER PORTPROTO *pp;
3911 	REGISTER CHAR *pt;
3912 	REGISTER INTBIG len;
3913 	REGISTER VARIABLE *var;
3914 	HIGHLIGHT newhigh;
3915 
3916 	/* look for a node with this string */
3917 	while (us_searchcircuittextni != NONODEINST)
3918 	{
3919 		ni = us_searchcircuittextni;
3920 		while (us_searchcircuittextvarnum < ni->numvar)
3921 		{
3922 			var = &ni->firstvar[us_searchcircuittextvarnum];
3923 			us_searchcircuittextvarnum++;
3924 			if ((var->type&VDISPLAY) == 0) continue;
3925 			if ((var->type&VTYPE) != VSTRING) continue;
3926 			if ((var->type&VISARRAY) == 0)
3927 			{
3928 				pt = (CHAR *)var->addr;
3929 				us_searchcircuittextstart = us_stringinstring(pt, search, bits);
3930 				if (us_searchcircuittextstart < 0) continue;
3931 				us_searchcircuittextend = us_searchcircuittextstart + estrlen(search);
3932 
3933 				/* show the text */
3934 				us_clearhighlightcount();
3935 				newhigh.status = HIGHTEXT;
3936 				newhigh.cell = ni->parent;
3937 				newhigh.fromgeom = ni->geom;
3938 				newhigh.fromport = NOPORTPROTO;
3939 				newhigh.frompoint = 0;
3940 				newhigh.fromvar = var;
3941 				newhigh.fromvarnoeval = NOVARIABLE;
3942 				us_addhighlight(&newhigh);
3943 				us_showallhighlight();
3944 				us_endchanges(NOWINDOWPART);
3945 				us_searchcircuittextfound = TRUE;
3946 				return;
3947 			} else
3948 			{
3949 				len = getlength(var);
3950 				for(us_searchcircuittextline=0; us_searchcircuittextline<len; us_searchcircuittextline++)
3951 				{
3952 					pt = ((CHAR **)var->addr)[us_searchcircuittextline];
3953 					us_searchcircuittextstart = us_stringinstring(pt, search, bits);
3954 					if (us_searchcircuittextstart < 0) continue;
3955 					us_searchcircuittextend = us_searchcircuittextstart + estrlen(search);
3956 
3957 					/* show the text */
3958 					us_clearhighlightcount();
3959 					newhigh.status = HIGHTEXT;
3960 					newhigh.cell = ni->parent;
3961 					newhigh.fromgeom = ni->geom;
3962 					newhigh.fromport = NOPORTPROTO;
3963 					newhigh.frompoint = 0;
3964 					newhigh.fromvar = var;
3965 					newhigh.fromvarnoeval = NOVARIABLE;
3966 					us_addhighlight(&newhigh);
3967 					us_showallhighlight();
3968 					us_endchanges(NOWINDOWPART);
3969 					us_searchcircuittextfound = TRUE;
3970 					return;
3971 				}
3972 			}
3973 		}
3974 		us_searchcircuittextni = ni->nextnodeinst;
3975 		us_searchcircuittextvarnum = 0;
3976 	}
3977 
3978 	/* look for an arc with this string */
3979 	while (us_searchcircuittextai != NOARCINST)
3980 	{
3981 		ai = us_searchcircuittextai;
3982 		while (us_searchcircuittextvarnum < ai->numvar)
3983 		{
3984 			var = &ai->firstvar[us_searchcircuittextvarnum];
3985 			us_searchcircuittextvarnum++;
3986 			if ((var->type&VDISPLAY) == 0) continue;
3987 			if ((var->type&VTYPE) != VSTRING) continue;
3988 			if ((var->type&VISARRAY) == 0)
3989 			{
3990 				pt = (CHAR *)var->addr;
3991 				us_searchcircuittextstart = us_stringinstring(pt, search, bits);
3992 				if (us_searchcircuittextstart < 0) continue;
3993 				us_searchcircuittextend = us_searchcircuittextstart + estrlen(search);
3994 
3995 				/* show the text */
3996 				us_clearhighlightcount();
3997 				newhigh.status = HIGHTEXT;
3998 				newhigh.cell = ai->parent;
3999 				newhigh.fromgeom = ai->geom;
4000 				newhigh.fromport = NOPORTPROTO;
4001 				newhigh.frompoint = 0;
4002 				newhigh.fromvar = var;
4003 				newhigh.fromvarnoeval = NOVARIABLE;
4004 				us_addhighlight(&newhigh);
4005 				us_showallhighlight();
4006 				us_endchanges(NOWINDOWPART);
4007 				us_searchcircuittextfound = TRUE;
4008 				return;
4009 			} else
4010 			{
4011 				len = getlength(var);
4012 				for(us_searchcircuittextline=0; us_searchcircuittextline<len; us_searchcircuittextline++)
4013 				{
4014 					pt = ((CHAR **)var->addr)[us_searchcircuittextline];
4015 					us_searchcircuittextstart = us_stringinstring(pt, search, bits);
4016 					if (us_searchcircuittextstart < 0) continue;
4017 					us_searchcircuittextend = us_searchcircuittextstart + estrlen(search);
4018 
4019 					/* show the text */
4020 					us_clearhighlightcount();
4021 					newhigh.status = HIGHTEXT;
4022 					newhigh.cell = ai->parent;
4023 					newhigh.fromgeom = ai->geom;
4024 					newhigh.fromport = NOPORTPROTO;
4025 					newhigh.frompoint = 0;
4026 					newhigh.fromvar = var;
4027 					newhigh.fromvarnoeval = NOVARIABLE;
4028 					us_addhighlight(&newhigh);
4029 					us_showallhighlight();
4030 					us_endchanges(NOWINDOWPART);
4031 					us_searchcircuittextfound = TRUE;
4032 					return;
4033 				}
4034 			}
4035 		}
4036 		us_searchcircuittextai = ai->nextarcinst;
4037 		us_searchcircuittextvarnum = 0;
4038 	}
4039 
4040 	/* look for an export with this string */
4041 	while (us_searchcircuittextpp != NOPORTPROTO)
4042 	{
4043 		pp = us_searchcircuittextpp;
4044 		us_searchcircuittextpp = pp->nextportproto;
4045 		us_searchcircuittextstart = us_stringinstring(pp->protoname, search, bits);
4046 		if (us_searchcircuittextstart < 0) continue;
4047 		us_searchcircuittextend = us_searchcircuittextstart + estrlen(search);
4048 
4049 		/* show the text */
4050 		us_clearhighlightcount();
4051 		newhigh.status = HIGHTEXT;
4052 		newhigh.cell = pp->parent;
4053 		newhigh.fromgeom = pp->subnodeinst->geom;
4054 		newhigh.fromport = pp;
4055 		newhigh.frompoint = 0;
4056 		newhigh.fromvar = NOVARIABLE;
4057 		newhigh.fromvarnoeval = NOVARIABLE;
4058 		us_addhighlight(&newhigh);
4059 		us_showallhighlight();
4060 		us_endchanges(NOWINDOWPART);
4061 		us_searchcircuittextfound = TRUE;
4062 		return;
4063 	}
4064 	us_searchcircuittextfound = FALSE;
4065 }
4066 
4067 /*
4068  * Routine to look for the substring "search" inside of the larger string "string".
4069  * if "bits" has 4 in it, the search is case sensitive.  Returns the character
4070  * position in "string" of where "search" is found (-1 if not found).
4071  */
us_stringinstring(CHAR * string,CHAR * search,INTBIG bits)4072 INTBIG us_stringinstring(CHAR *string, CHAR *search, INTBIG bits)
4073 {
4074 	REGISTER INTBIG stringlen, searchlen, searchwid, i;
4075 
4076 	stringlen = estrlen(string);
4077 	searchlen = estrlen(search);
4078 	searchwid = stringlen - searchlen;
4079 	if (searchwid < 0) return(-1);
4080 	if ((bits&4) == 0)
4081 	{
4082 		for(i=0; i<=searchwid; i++)
4083 			if (namesamen(&string[i], search, searchlen) == 0) return(i);
4084 	} else
4085 	{
4086 		for(i=0; i<=searchwid; i++)
4087 			if (estrncmp(&string[i], search, searchlen) == 0) return(i);
4088 	}
4089 	return(-1);
4090 }
4091 
4092 /*
4093  * Routine to find the string "search" in the circuit in window "win".  "bits" is:
4094  *  2  replace all with "replaceall"
4095  *  4  case sensitive
4096  *  8  find reverse
4097  */
us_searchcircuittext(WINDOWPART * win,CHAR * search,CHAR * replaceall,INTBIG bits)4098 void us_searchcircuittext(WINDOWPART *win, CHAR *search, CHAR *replaceall, INTBIG bits)
4099 {
4100 	REGISTER NODEPROTO *np;
4101 	REGISTER INTBIG total;
4102 
4103 	np = win->curnodeproto;
4104 	if (np == NONODEPROTO) return;
4105 
4106 	if ((bits&2) != 0)
4107 	{
4108 		total = 0;
4109 		for(;;)
4110 		{
4111 			/* advance to the next circuit text */
4112 			us_advancecircuittext(search, bits);
4113 			if (!us_searchcircuittextfound) break;
4114 			us_replacecircuittext(win, replaceall);
4115 			total++;
4116 		}
4117 		if (total == 0) ttybeep(SOUNDBEEP, TRUE); else
4118 			ttyputmsg(_("Replaced %ld times"), total);
4119 		return;
4120 	}
4121 	us_advancecircuittext(search, bits);
4122 	if (!us_searchcircuittextfound) ttybeep(SOUNDBEEP, TRUE);
4123 }
4124 
4125 /*
4126  * Routine to replace the text last selected with "us_searchcircuittext()" with
4127  * the string "replace".
4128  */
us_replacecircuittext(WINDOWPART * win,CHAR * replace)4129 void us_replacecircuittext(WINDOWPART *win, CHAR *replace)
4130 {
4131 	REGISTER HIGHLIGHT *high;
4132 	REGISTER INTBIG i, len, addr, type;
4133 	REGISTER VARIABLE *var;
4134 	REGISTER NODEINST *ni;
4135 	REGISTER PORTPROTO *pp;
4136 	REGISTER CHAR *pt;
4137 	CHAR *newname;
4138 	REGISTER void *infstr;
4139 	Q_UNUSED( win );
4140 
4141 	if (!us_searchcircuittextfound) return;
4142 	high = us_getonehighlight();
4143 	if (high->status != HIGHTEXT) return;
4144 	if (high->fromgeom->entryisnode && high->fromvar == NOVARIABLE)
4145 	{
4146 		/* export text */
4147 		ni = high->fromgeom->entryaddr.ni;
4148 		pp = high->fromport;
4149 		infstr = initinfstr();
4150 		for(i=0; i<us_searchcircuittextstart; i++)
4151 			addtoinfstr(infstr, pp->protoname[i]);
4152 		addstringtoinfstr(infstr, replace);
4153 		len = estrlen(pp->protoname);
4154 		for(i=us_searchcircuittextend; i<len; i++)
4155 			addtoinfstr(infstr, pp->protoname[i]);
4156 		us_pushhighlight();
4157 		us_clearhighlightcount();
4158 		us_renameport(pp, returninfstr(infstr));
4159 		us_pophighlight(FALSE);
4160 		us_endchanges(NOWINDOWPART);
4161 		return;
4162 	}
4163 
4164 	/* rename variable */
4165 	var = high->fromvar;
4166 	if (var == NOVARIABLE) return;
4167 	addr = (INTBIG)high->fromgeom->entryaddr.blind;
4168 	if (high->fromgeom->entryisnode) type = VNODEINST; else
4169 		type = VARCINST;
4170 	us_pushhighlight();
4171 	us_clearhighlightcount();
4172 	startobjectchange(addr, type);
4173 	if ((var->type&VISARRAY) != 0)
4174 	{
4175 		pt = ((CHAR **)var->addr)[us_searchcircuittextline];
4176 		infstr = initinfstr();
4177 		for(i=0; i<us_searchcircuittextstart; i++)
4178 			addtoinfstr(infstr, pt[i]);
4179 		addstringtoinfstr(infstr, replace);
4180 		len = estrlen(pt);
4181 		for(i=us_searchcircuittextend; i<len; i++)
4182 			addtoinfstr(infstr, pt[i]);
4183 		allocstring(&newname, returninfstr(infstr), el_tempcluster);
4184 		(void)setindkey(addr, type, var->key, us_searchcircuittextline,
4185 			(INTBIG)newname);
4186 		efree(newname);
4187 	} else
4188 	{
4189 		pt = (CHAR *)var->addr;
4190 		infstr = initinfstr();
4191 		for(i=0; i<us_searchcircuittextstart; i++)
4192 			addtoinfstr(infstr, pt[i]);
4193 		addstringtoinfstr(infstr, replace);
4194 		len = estrlen(pt);
4195 		for(i=us_searchcircuittextend; i<len; i++)
4196 			addtoinfstr(infstr, pt[i]);
4197 		allocstring(&newname, returninfstr(infstr), el_tempcluster);
4198 		(void)setvalkey(addr, type, var->key, (INTBIG)newname,
4199 			var->type);
4200 		efree(newname);
4201 	}
4202 	endobjectchange(addr, type);
4203 	us_pophighlight(FALSE);
4204 	us_endchanges(NOWINDOWPART);
4205 }
4206 
4207 /************************ IN-PLACE VARIABLE EDITING ************************/
4208 
4209 static INTBIG      us_editvarstartline, us_editvarstartchar;
4210 static INTBIG      us_editvarlabellen;
4211 static INTBIG      us_editvarendline, us_editvarendchar;
4212 static INTBIG      us_editvarclickline, us_editvarclickchar;
4213 static VARIABLE   *us_editvariable;
4214 static INTBIG      us_editvarlength;
4215 static INTBIG      us_editvarlineheight;
4216 static INTBIG      us_editvarobjtype;
4217 static INTBIG      us_editvarobjaddr;
4218 static BOOLEAN     us_editvariabledoubleclick;
4219 static CHAR      **us_editvarlines;
4220 static CHAR       *us_editvaroneline[1];
4221 static CHAR       *us_editvarvarname = 0;
4222 static TECHNOLOGY *us_editvartech;
4223 
4224 static void    us_editvariabletexthighlight(void);
4225 static BOOLEAN us_editvariabletexteachdown(INTBIG x, INTBIG y);
4226 static BOOLEAN us_editvariabletextfindpos(INTBIG x, INTBIG y, INTBIG *line, INTBIG *chr);
4227 static void    us_editvariableforcefullwords(INTBIG *startline, INTBIG *startchar, INTBIG *endline, INTBIG *endchar);
4228 static BOOLEAN us_editvariabletexthandlebutton(INTBIG x, INTBIG y, INTBIG but);
4229 static BOOLEAN us_editvariabletexthandlechar(INTSML chr, INTBIG special);
4230 static void    us_editvariabletextreplacetext(CHAR *replace);
4231 
us_editvariabletext(VARIABLE * var,INTBIG objtype,INTBIG objaddr,CHAR * varname)4232 void us_editvariabletext(VARIABLE *var, INTBIG objtype, INTBIG objaddr, CHAR *varname)
4233 {
4234 	INTBIG tsx, tsy, newtype, newvalue, units;
4235 	UINTBIG textdescript[TEXTDESCRIPTSIZE];
4236 
4237 	/* preserve information in globals */
4238 	us_editvariable = var;
4239 	us_editvarobjtype = objtype & VTYPE;
4240 	us_editvarobjaddr = objaddr;
4241 	if (us_editvarvarname == 0) (void)allocstring(&us_editvarvarname, varname, us_tool->cluster); else
4242 		(void)reallocstring(&us_editvarvarname, varname, us_tool->cluster);
4243 	us_editvarlabellen = 0;
4244 	if ((us_editvariable->type&VISARRAY) == 0)
4245 	{
4246 		us_editvarlength = 1;
4247 		(void)allocstring(&us_editvaroneline[0], describedisplayedvariable(var, -1, -1), us_tool->cluster);
4248 		if (TDGETDISPPART(var->textdescript) != VTDISPLAYVALUE)
4249 		{
4250 			TDCOPY(textdescript, var->textdescript);
4251 			TDSETDISPPART(var->textdescript, VTDISPLAYVALUE);
4252 			us_editvarlabellen = estrlen(us_editvaroneline[0]) - estrlen(describedisplayedvariable(var, -1, -1));
4253 			TDCOPY(var->textdescript, textdescript);
4254 		}
4255 		us_editvarlines = us_editvaroneline;
4256 	} else
4257 	{
4258 		us_editvarlength = getlength(var);
4259 		us_editvarlines = (CHAR **)var->addr;
4260 	}
4261 	switch (objtype)
4262 	{
4263 		case VNODEINST:
4264 			us_editvartech = ((NODEINST *)objaddr)->parent->tech;
4265 			break;
4266 		case VARCINST:
4267 			us_editvartech = ((ARCINST *)objaddr)->parent->tech;
4268 			break;
4269 		case VPORTPROTO:
4270 			us_editvartech = ((PORTPROTO *)objaddr)->subnodeinst->parent->tech;
4271 			break;
4272 		case VNODEPROTO:
4273 			us_editvartech = ((NODEPROTO *)objaddr)->tech;
4274 			break;
4275 	}
4276 
4277 	/* flush graphics */
4278 	us_endchanges(NOWINDOWPART);
4279 
4280 	/* determine height of a line of text */
4281 	screensettextinfo(el_curwindowpart, us_editvartech, us_editvariable->textdescript);
4282 	screengettextsize(el_curwindowpart, x_("Xy"), &tsx, &tsy);
4283 	us_editvarlineheight = tsy;
4284 
4285 	/* set highlighting to cover all text */
4286 	us_editvarstartline = 0;
4287 	us_editvarstartchar = us_editvarlabellen;
4288 	us_editvarendline = us_editvarlength-1;
4289 	us_editvarendchar = estrlen(us_editvarlines[us_editvarendline]);
4290 	us_editvariabletexthighlight();
4291 
4292 	/* loop while editing text */
4293 	modalloop(us_editvariabletexthandlechar, us_editvariabletexthandlebutton, IBEAMCURSOR);
4294 
4295 	/* remove highlighting */
4296 	us_editvariabletexthighlight();
4297 
4298 	/* recalculate the proper type now that editing is done */
4299 	if ((us_editvariable->type&VISARRAY) == 0)
4300 	{
4301 		units = TDGETUNITS(us_editvariable->textdescript);
4302 		getsimpletype(&us_editvarlines[0][us_editvarlabellen], &newtype, &newvalue, units);
4303 		newtype = (us_editvariable->type & ~VTYPE) | newtype;
4304 		if (us_editvarobjtype != VPORTPROTO)
4305 			startobjectchange(us_editvarobjaddr, us_editvarobjtype); else
4306 				startobjectchange((INTBIG)(((PORTPROTO *)us_editvarobjaddr)->subnodeinst), VNODEINST);
4307 		if (us_editvarobjtype == VNODEPROTO)
4308 			us_undrawcellvariable(us_editvariable, (NODEPROTO *)us_editvarobjaddr);
4309 		var = setval(us_editvarobjaddr, us_editvarobjtype,
4310 			us_editvarvarname, newvalue, newtype);
4311 		if (var != NOVARIABLE) us_editvariable = var;
4312 		if (us_editvarobjtype == VNODEPROTO)
4313 			us_drawcellvariable(us_editvariable, (NODEPROTO *)us_editvarobjaddr);
4314 		if (us_editvarobjtype != VPORTPROTO)
4315 			endobjectchange(us_editvarobjaddr, us_editvarobjtype); else
4316 				endobjectchange((INTBIG)(((PORTPROTO *)us_editvarobjaddr)->subnodeinst), VNODEINST);
4317 		us_endchanges(NOWINDOWPART);
4318 	}
4319 }
4320 
us_editvariabletexthandlebutton(INTBIG x,INTBIG y,INTBIG but)4321 static BOOLEAN us_editvariabletexthandlebutton(INTBIG x, INTBIG y, INTBIG but)
4322 {
4323 	INTBIG line, chr;
4324 
4325 	us_editvariabledoubleclick = doublebutton(but);
4326 	if (!us_editvariabletextfindpos(x, y, &line, &chr)) return(TRUE);
4327 	us_editvariabletexthighlight();
4328 	if (line == 0 && chr < us_editvarlabellen) chr = us_editvarlabellen;
4329 	if (shiftbutton(but))
4330 	{
4331 		if (line < us_editvarstartline || (line == us_editvarstartline &&
4332 			chr < us_editvarstartchar))
4333 		{
4334 			us_editvarstartline = line;
4335 			us_editvarstartchar = chr;
4336 		} else if (line > us_editvarendline || (line == us_editvarendline &&
4337 			chr > us_editvarendchar))
4338 		{
4339 			us_editvarendline = line;
4340 			us_editvarendchar = chr;
4341 		}
4342 	} else
4343 	{
4344 		us_editvarstartline = line;
4345 		us_editvarstartchar = chr;
4346 		us_editvarendline = line;
4347 		us_editvarendchar = chr;
4348 	}
4349 	us_editvarclickline = line;
4350 	us_editvarclickchar = chr;
4351 	if (us_editvariabledoubleclick)
4352 		us_editvariableforcefullwords(&us_editvarstartline, &us_editvarstartchar,
4353 			&us_editvarendline, &us_editvarendchar);
4354 	us_editvariabletexthighlight();
4355 
4356 	trackcursor(FALSE, us_ignoreup, us_nullvoid, us_editvariabletexteachdown, us_stoponchar,
4357 			us_nullvoid, TRACKNORMAL);
4358 	return(FALSE);
4359 }
4360 
us_editvariabletexthandlechar(INTSML chr,INTBIG special)4361 BOOLEAN us_editvariabletexthandlechar(INTSML chr, INTBIG special)
4362 {
4363 	CHAR replace[2], *pt;
4364 	REGISTER INTBIG startchar, endchar, i, j;
4365 	REGISTER void *infstr;
4366 
4367 	if ((special&SPECIALKEYDOWN) != 0)
4368 	{
4369 		switch ((special&SPECIALKEY)>>SPECIALKEYSH)
4370 		{
4371 			case SPECIALKEYARROWL:
4372 				us_editvariabletexthighlight();
4373 				if (us_editvarstartline == us_editvarendline &&
4374 					us_editvarstartchar == us_editvarendchar)
4375 				{
4376 					if (us_editvarstartchar > 0)
4377 					{
4378 						if (us_editvarstartline != 0 || us_editvarstartchar > us_editvarlabellen)
4379 							us_editvarstartchar--;
4380 					} else
4381 					{
4382 						if (us_editvarstartline > 0)
4383 						{
4384 							us_editvarstartline--;
4385 							us_editvarstartchar = estrlen(us_editvarlines[us_editvarstartline]);
4386 						}
4387 					}
4388 				}
4389 				us_editvarendline = us_editvarstartline;
4390 				us_editvarendchar = us_editvarstartchar;
4391 				us_editvariabletexthighlight();
4392 				return(FALSE);
4393 			case SPECIALKEYARROWR:
4394 				us_editvariabletexthighlight();
4395 				if (us_editvarstartline == us_editvarendline &&
4396 					us_editvarstartchar == us_editvarendchar)
4397 				{
4398 					if (us_editvarendchar < (INTBIG)estrlen(us_editvarlines[us_editvarendline]))
4399 						us_editvarendchar++; else
4400 					{
4401 						if (us_editvarendline < us_editvarlength-1)
4402 						{
4403 							us_editvarendline++;
4404 							us_editvarendchar = 0;
4405 						}
4406 					}
4407 				}
4408 				us_editvarstartline = us_editvarendline;
4409 				us_editvarstartchar = us_editvarendchar;
4410 				us_editvariabletexthighlight();
4411 				return(FALSE);
4412 			case SPECIALKEYARROWU:
4413 				us_editvariabletexthighlight();
4414 				if (us_editvarstartline > 0)
4415 				{
4416 					us_editvarstartline--;
4417 					if (us_editvarstartchar > (INTBIG)estrlen(us_editvarlines[us_editvarstartline]))
4418 						us_editvarstartchar = estrlen(us_editvarlines[us_editvarstartline]);
4419 				}
4420 				us_editvarendline = us_editvarstartline;
4421 				us_editvarendchar = us_editvarstartchar;
4422 				us_editvariabletexthighlight();
4423 				return(FALSE);
4424 			case SPECIALKEYARROWD:
4425 				us_editvariabletexthighlight();
4426 				if (us_editvarendline < us_editvarlength-1)
4427 				{
4428 					us_editvarendline++;
4429 					if (us_editvarendchar > (INTBIG)estrlen(us_editvarlines[us_editvarendline]))
4430 						us_editvarendchar = estrlen(us_editvarlines[us_editvarendline]);
4431 				}
4432 				us_editvarstartline = us_editvarendline;
4433 				us_editvarstartchar = us_editvarendchar;
4434 				us_editvariabletexthighlight();
4435 				return(FALSE);
4436 		}
4437 	}
4438 
4439 	/* handle paste */
4440 	if ((special&ACCELERATORDOWN) != 0)
4441 	{
4442 		if (chr == 'v' || chr == 'V')
4443 		{
4444 			pt = getcutbuffer();
4445 			us_editvariabletexthighlight();
4446 			us_editvariabletextreplacetext(pt);
4447 			if ((us_editvariable->type&VISARRAY) == 0)
4448 			{
4449 				(void)reallocstring(&us_editvaroneline[0], describedisplayedvariable(us_editvariable, -1, -1),
4450 					us_tool->cluster);
4451 				us_editvarlines = us_editvaroneline;
4452 			} else
4453 			{
4454 				us_editvarlength = getlength(us_editvariable);
4455 				us_editvarlines = (CHAR **)us_editvariable->addr;
4456 			}
4457 			us_editvariabletexthighlight();
4458 			return(FALSE);
4459 		}
4460 
4461 		/* handle copy/cut */
4462 		if (chr == 'c' || chr == 'C' ||
4463 			chr == 'x' || chr == 'X')
4464 		{
4465 			infstr = initinfstr();
4466 			for(i=us_editvarstartline; i<=us_editvarendline; i++)
4467 			{
4468 				if (i > us_editvarstartline) addtoinfstr(infstr, '\n');
4469 				startchar = 0;
4470 				endchar = estrlen(us_editvarlines[i]);
4471 				if (i == us_editvarstartline) startchar = us_editvarstartchar;
4472 				if (i == us_editvarendline) endchar = us_editvarendchar;
4473 				for(j=startchar; j<endchar; j++)
4474 					addtoinfstr(infstr, us_editvarlines[i][j]);
4475 			}
4476 			setcutbuffer(returninfstr(infstr));
4477 			if (chr == 'c' || chr == 'C') return(FALSE);
4478 			chr = 0;
4479 		}
4480 	}
4481 
4482 	/* delete what is selected and insert what was typed */
4483 	if (chr == '\n' || chr == '\r')
4484 	{
4485 		/* cannot insert second line if text is not an array */
4486 		if ((us_editvariable->type&VISARRAY) == 0) return(TRUE);
4487 	}
4488 	us_editvariabletexthighlight();
4489 	if (chr == DELETEKEY || chr == BACKSPACEKEY)
4490 	{
4491 		chr = 0;
4492 		if (us_editvarstartline == us_editvarendline &&
4493 			us_editvarstartchar == us_editvarendchar)
4494 		{
4495 			if (us_editvarstartchar > 0)
4496 			{
4497 				if (us_editvarstartline != 0 || us_editvarstartchar > us_editvarlabellen)
4498 					us_editvarstartchar--;
4499 			} else
4500 			{
4501 				if (us_editvarstartline > 0)
4502 				{
4503 					us_editvarstartline--;
4504 					us_editvarstartchar = estrlen(us_editvarlines[us_editvarstartline]);
4505 				}
4506 			}
4507 		}
4508 	}
4509 	replace[0] = (CHAR)chr;
4510 	replace[1] = 0;
4511 	us_editvariabletextreplacetext(replace);
4512 	if ((us_editvariable->type&VISARRAY) == 0)
4513 	{
4514 		(void)reallocstring(&us_editvaroneline[0], describedisplayedvariable(us_editvariable, -1, -1),
4515 			us_tool->cluster);
4516 		us_editvarlines = us_editvaroneline;
4517 	} else
4518 	{
4519 		us_editvarlength = getlength(us_editvariable);
4520 		us_editvarlines = (CHAR **)us_editvariable->addr;
4521 	}
4522 	us_editvariabletexthighlight();
4523 	return(FALSE);
4524 }
4525 
us_editvariabletextreplacetext(CHAR * replace)4526 void us_editvariabletextreplacetext(CHAR *replace)
4527 {
4528 	void *stringarray;
4529 	REGISTER INTBIG i, newline, newchar;
4530 	CHAR **newtext;
4531 	REGISTER VARIABLE *var;
4532 	INTBIG newtype, newvalue, count;
4533 	REGISTER void *infstr;
4534 
4535 	stringarray = newstringarray(el_tempcluster);
4536 
4537 	/* add all lines before the start of selection */
4538 	newline = 0;
4539 	for(i=0; i<us_editvarstartline; i++)
4540 	{
4541 		if (i != 0) addtostringarray(stringarray, us_editvarlines[i]); else
4542 			addtostringarray(stringarray, &us_editvarlines[i][us_editvarlabellen]);
4543 		newline++;
4544 	}
4545 
4546 	/* build the line with the selection start */
4547 	if (newline == 0) newchar = us_editvarlabellen; else newchar = 0;
4548 	infstr = initinfstr();
4549 	for(i=newchar; i<us_editvarstartchar; i++)
4550 	{
4551 		addtoinfstr(infstr, us_editvarlines[us_editvarstartline][i]);
4552 		newchar++;
4553 	}
4554 
4555 	/* now add the replacement text */
4556 	for(i=0; i<(INTBIG)estrlen(replace); i++)
4557 	{
4558 		if (replace[i] == '\n' || replace[i] == '\r')
4559 		{
4560 			addtostringarray(stringarray, returninfstr(infstr));
4561 			infstr = initinfstr();
4562 			newline++;
4563 			newchar = 0;
4564 		} else
4565 		{
4566 			addtoinfstr(infstr, replace[i]);
4567 			newchar++;
4568 		}
4569 	}
4570 
4571 	/* now add the line with the selection end */
4572 	for(i=us_editvarendchar; i<(INTBIG)estrlen(us_editvarlines[us_editvarendline]); i++)
4573 		addtoinfstr(infstr, us_editvarlines[us_editvarendline][i]);
4574 	addtostringarray(stringarray, returninfstr(infstr));
4575 
4576 	/* add all lines after the end of selection */
4577 	for(i=us_editvarendline+1; i<us_editvarlength; i++)
4578 		addtostringarray(stringarray, us_editvarlines[i]);
4579 
4580 	/* get the new text and put it on the object */
4581 	newtext = getstringarray(stringarray, &count);
4582 	if (us_editvarobjtype != VPORTPROTO)
4583 		startobjectchange(us_editvarobjaddr, us_editvarobjtype); else
4584 			startobjectchange((INTBIG)(((PORTPROTO *)us_editvarobjaddr)->subnodeinst), VNODEINST);
4585 	if (us_editvarobjtype == VNODEPROTO)
4586 		us_undrawcellvariable(us_editvariable, (NODEPROTO *)us_editvarobjaddr);
4587 	if ((us_editvariable->type&VISARRAY) == 0)
4588 	{
4589 		/* presume a string for now: recompute type when done editing */
4590 		newvalue = (INTBIG)newtext[0];
4591 		newtype = (us_editvariable->type & ~VTYPE) | VSTRING;
4592 		var = setval(us_editvarobjaddr, us_editvarobjtype,
4593 			us_editvarvarname, newvalue, newtype);
4594 	} else
4595 	{
4596 		newtype = (us_editvariable->type & ~VLENGTH) | (count << VLENGTHSH);
4597 		var = setval(us_editvarobjaddr, us_editvarobjtype,
4598 			us_editvarvarname, (INTBIG)newtext, newtype);
4599 	}
4600 	if (var != NOVARIABLE) us_editvariable = var;
4601 	if (us_editvarobjtype == VNODEPROTO)
4602 		us_drawcellvariable(us_editvariable, (NODEPROTO *)us_editvarobjaddr);
4603 	if (us_editvarobjtype != VPORTPROTO)
4604 		endobjectchange(us_editvarobjaddr, us_editvarobjtype); else
4605 			endobjectchange((INTBIG)(((PORTPROTO *)us_editvarobjaddr)->subnodeinst), VNODEINST);
4606 	us_endchanges(NOWINDOWPART);
4607 	killstringarray(stringarray);
4608 
4609 	/* set the new selection point */
4610 	us_editvarstartline = us_editvarendline = newline;
4611 	us_editvarstartchar = us_editvarendchar = newchar;
4612 }
4613 
us_editvariabletexteachdown(INTBIG x,INTBIG y)4614 BOOLEAN us_editvariabletexteachdown(INTBIG x, INTBIG y)
4615 {
4616 	INTBIG line, chr, startline, startchar, endline, endchar;
4617 
4618 	if (!us_editvariabletextfindpos(x, y, &line, &chr)) return(FALSE);
4619 	if (line == 0 && chr < us_editvarlabellen) chr = us_editvarlabellen;
4620 	startline = us_editvarstartline;
4621 	startchar = us_editvarstartchar;
4622 	endline = us_editvarendline;
4623 	endchar = us_editvarendchar;
4624 	if (line > us_editvarclickline || (line == us_editvarclickline && chr > us_editvarclickchar))
4625 	{
4626 		startline = us_editvarclickline;
4627 		startchar = us_editvarclickchar;
4628 		endline = line;
4629 		endchar = chr;
4630 		if (us_editvariabledoubleclick)
4631 			us_editvariableforcefullwords(&startline, &startchar, &endline, &endchar);
4632 	}
4633 	if (line < us_editvarclickline || (line == us_editvarclickline && chr < us_editvarclickchar))
4634 	{
4635 		startline = line;
4636 		startchar = chr;
4637 		endline = us_editvarclickline;
4638 		endchar = us_editvarclickchar;
4639 		if (us_editvariabledoubleclick)
4640 			us_editvariableforcefullwords(&startline, &startchar, &endline, &endchar);
4641 	}
4642 	if (startline != us_editvarstartline || startchar != us_editvarstartchar ||
4643 		endline != us_editvarendline || endchar != us_editvarendchar)
4644 	{
4645 		us_editvariabletexthighlight();
4646 		us_editvarstartline = startline;
4647 		us_editvarstartchar = startchar;
4648 		us_editvarendline = endline;
4649 		us_editvarendchar = endchar;
4650 		us_editvariabletexthighlight();
4651 	}
4652 	return(FALSE);
4653 }
4654 
us_editvariableforcefullwords(INTBIG * startline,INTBIG * startchar,INTBIG * endline,INTBIG * endchar)4655 void us_editvariableforcefullwords(INTBIG *startline, INTBIG *startchar, INTBIG *endline, INTBIG *endchar)
4656 {
4657 	CHAR *pt;
4658 	INTBIG len;
4659 
4660 	pt = us_editvarlines[*startline];
4661 	while (*startchar > 0 && isalnum(pt[*startchar - 1]))
4662 		(*startchar)--;
4663 
4664 	pt = us_editvarlines[*endline];
4665 	len = estrlen(pt);
4666 	while (*endchar < len && isalnum(pt[*endchar]))
4667 		(*endchar)++;
4668 }
4669 
us_editvariabletextfindpos(INTBIG xp,INTBIG yp,INTBIG * line,INTBIG * chr)4670 BOOLEAN us_editvariabletextfindpos(INTBIG xp, INTBIG yp, INTBIG *line, INTBIG *chr)
4671 {
4672 	REGISTER INTBIG i, j, screenlx, screenhx, screenly, screenhy;
4673 	CHAR save;
4674 	INTBIG x, y;
4675 	INTBIG tsx, tsy;
4676 	REGISTER INTBIG lasttsx, charwid;
4677 
4678 	/* determine text size */
4679 	screensettextinfo(el_curwindowpart, us_editvartech, us_editvariable->textdescript);
4680 	for(i = 0; i < us_editvarlength; i++)
4681 	{
4682 		getdisparrayvarlinepos(us_editvarobjaddr, us_editvarobjtype, us_editvartech,
4683 			el_curwindowpart, us_editvariable, i, &x, &y, TRUE);
4684 		screenlx = applyxscale(el_curwindowpart, x - el_curwindowpart->screenlx) +
4685 			el_curwindowpart->uselx;
4686 		screenly = applyyscale(el_curwindowpart, y - el_curwindowpart->screenly) +
4687 			el_curwindowpart->usely;
4688 		screengettextsize(el_curwindowpart, us_editvarlines[i], &tsx, &tsy);
4689 		screenhx = screenlx + tsx;
4690 		screenhy = screenly + us_editvarlineheight;
4691 		if (yp < screenly || yp > screenhy) continue;
4692 		if (xp < screenlx-us_editvarlineheight ||
4693 			xp > screenhx+us_editvarlineheight) continue;
4694 		*line = i;
4695 		lasttsx = 0;
4696 		for(j=1; j<=(INTBIG)estrlen(us_editvarlines[i]); j++)
4697 		{
4698 			save = us_editvarlines[i][j];
4699 			us_editvarlines[i][j] = 0;
4700 			screengettextsize(el_curwindowpart, us_editvarlines[i], &tsx, &tsy);
4701 			charwid = tsx - lasttsx;
4702 			lasttsx = tsx;
4703 			us_editvarlines[i][j] = save;
4704 			if (xp < screenlx + tsx - charwid/2) break;
4705 		}
4706 		*chr = j-1;
4707 		return(TRUE);
4708 	}
4709 	return(FALSE);
4710 }
4711 
us_editvariabletexthighlight(void)4712 void us_editvariabletexthighlight(void)
4713 {
4714 	REGISTER INTBIG i, j, startch;
4715 	CHAR save;
4716 	INTBIG x, y, screenlx, screenhx, screenly, screenhy;
4717 	INTBIG tsx, tsy;
4718 
4719 	/* determine text size */
4720 	screensettextinfo(el_curwindowpart, us_editvartech, us_editvariable->textdescript);
4721 	for(i = us_editvarstartline; i <= us_editvarendline; i++)
4722 	{
4723 		getdisparrayvarlinepos(us_editvarobjaddr, us_editvarobjtype, us_editvartech,
4724 			el_curwindowpart, us_editvariable, i, &x, &y, TRUE);
4725 		screenlx = applyxscale(el_curwindowpart, x - el_curwindowpart->screenlx) +
4726 			el_curwindowpart->uselx;
4727 		screenly = applyyscale(el_curwindowpart, y - el_curwindowpart->screenly) +
4728 			el_curwindowpart->usely;
4729 		startch = 0;
4730 		if (i == us_editvarstartline && us_editvarstartchar != 0)
4731 		{
4732 			save = us_editvarlines[i][us_editvarstartchar];
4733 			us_editvarlines[i][us_editvarstartchar] = 0;
4734 			screengettextsize(el_curwindowpart, us_editvarlines[i], &tsx, &tsy);
4735 			screenlx += tsx;
4736 			us_editvarlines[i][us_editvarstartchar] = save;
4737 			startch = us_editvarstartchar;
4738 		}
4739 		if (i == us_editvarendline) j = us_editvarendchar; else
4740 			j = estrlen(us_editvarlines[i]);
4741 		save = us_editvarlines[i][j];
4742 		us_editvarlines[i][j] = 0;
4743 		screengettextsize(el_curwindowpart, &us_editvarlines[i][startch], &tsx, &tsy);
4744 		screenhx = screenlx + tsx;
4745 		us_editvarlines[i][j] = save;
4746 		screenhy = screenly + us_editvarlineheight;
4747 		if (screenlx <= el_curwindowpart->uselx) screenlx = el_curwindowpart->uselx+1;
4748 		if (screenhx > el_curwindowpart->usehx) screenhx = el_curwindowpart->usehx;
4749 		if (screenly < el_curwindowpart->usely) screenly = el_curwindowpart->usely;
4750 		if (screenhy > el_curwindowpart->usehy) screenhy = el_curwindowpart->usehy;
4751 		if ((el_curwindowpart->state&INPLACEEDIT) != 0)
4752 			xformbox(&screenlx, &screenhx, &screenly, &screenhy, el_curwindowpart->outofcell);
4753 		screeninvertbox(el_curwindowpart, screenlx-1, screenhx-1,
4754 			screenly, screenhy-1);
4755 	}
4756 }
4757 
4758 /******************** USER-BROADCAST CHANGES ********************/
4759 
4760 /*
4761  * routine to allocate a new ubchange from the pool (if any) or memory,
4762  * fill in the "cell", "change", "x", and "y" fields, and link it to the
4763  * global list.  Returns true on error.
4764  */
us_newubchange(INTBIG change,void * object,void * parameter)4765 BOOLEAN us_newubchange(INTBIG change, void *object, void *parameter)
4766 {
4767 	REGISTER UBCHANGE *d;
4768 
4769 	if (us_ubchangefree == NOUBCHANGE)
4770 	{
4771 		d = (UBCHANGE *)emalloc((sizeof (UBCHANGE)), us_tool->cluster);
4772 		if (d == 0) return(TRUE);
4773 	} else
4774 	{
4775 		/* take ubchange from free list */
4776 		d = us_ubchangefree;
4777 		us_ubchangefree = (UBCHANGE *)d->nextubchange;
4778 	}
4779 	d->object = object;
4780 	d->parameter = parameter;
4781 	d->change = change;
4782 	d->nextubchange = us_ubchanges;
4783 	us_ubchanges = d;
4784 	return(FALSE);
4785 }
4786 
4787 /*
4788  * routine to return ubchange "d" to the pool of free ubchanges
4789  */
us_freeubchange(UBCHANGE * d)4790 void us_freeubchange(UBCHANGE *d)
4791 {
4792 	d->nextubchange = us_ubchangefree;
4793 	us_ubchangefree = d;
4794 }
4795 
4796 /*
4797  * routine to remove all queued user broadcast changes to cell "np"
4798  * because it was deleted
4799  */
us_removeubchange(NODEPROTO * np)4800 void us_removeubchange(NODEPROTO *np)
4801 {
4802 	REGISTER UBCHANGE *d, *lastd, *nextd;
4803 	REGISTER NODEPROTO *thisnp;
4804 
4805 	lastd = NOUBCHANGE;
4806 	for(d = us_ubchanges; d != NOUBCHANGE; d = nextd)
4807 	{
4808 		nextd = d->nextubchange;
4809 		if (d->change == UBNEWFC) thisnp = ((NODEINST *)d->object)->parent; else
4810 			if (d->change == UBKILLFM) thisnp = (NODEPROTO *)d->object; else
4811 				thisnp = NONODEPROTO;
4812 		if (thisnp == np)
4813 		{
4814 			if (lastd == NOUBCHANGE) us_ubchanges = nextd; else
4815 				lastd->nextubchange = nextd;
4816 			us_freeubchange(d);
4817 			continue;
4818 		}
4819 		lastd = d;
4820 	}
4821 }
4822 
4823 /*
4824  * routine to remove variable "FACET_message" from cell "np".
4825  */
us_delcellmessage(NODEPROTO * np)4826 void us_delcellmessage(NODEPROTO *np)
4827 {
4828 	(void)us_newubchange(UBKILLFM, np, 0);
4829 }
4830 
4831 /*
4832  * routine to add a cell-center to cell "np"
4833  */
us_addcellcenter(NODEINST * ni)4834 void us_addcellcenter(NODEINST *ni)
4835 {
4836 	(void)us_newubchange(UBNEWFC, ni, 0);
4837 }
4838 
4839 /*
4840  * Routine to queue a check of the SPICE parts.
4841  */
us_checkspiceparts(void)4842 void us_checkspiceparts(void)
4843 {
4844 	(void)us_newubchange(UBSPICEPARTS, 0, 0);
4845 }
4846 
4847 /*
4848  * Routine to queue a deletion of a technology-edit layer cell.
4849  */
us_deltecedlayercell(NODEPROTO * np)4850 void us_deltecedlayercell(NODEPROTO *np)
4851 {
4852 	(void)us_newubchange(UBTECEDDELLAYER, np, 0);
4853 }
4854 
4855 /*
4856  * Routine to queue a deletion of a technology-edit node cell.
4857  */
us_deltecednodecell(NODEPROTO * np)4858 void us_deltecednodecell(NODEPROTO *np)
4859 {
4860 	(void)us_newubchange(UBTECEDDELNODE, np, 0);
4861 }
4862 
4863 /*
4864  * Routine to queue a rename of a technology-edit cell.
4865  */
us_renametecedcell(NODEPROTO * np,CHAR * oldname)4866 void us_renametecedcell(NODEPROTO *np, CHAR *oldname)
4867 {
4868 	(void)us_newubchange(UBTECEDRENAME, np, oldname);
4869 }
4870 
4871 /*
4872  * Routine to queue a turn-on of a tool.
4873  */
us_toolturnedon(TOOL * tool)4874 void us_toolturnedon(TOOL *tool)
4875 {
4876 	(void)us_newubchange(UBTOOLISON, tool, 0);
4877 }
4878 
4879 /*
4880  * routine to implement all user broadcast changes queued during the last broadcast
4881  */
us_doubchanges(void)4882 void us_doubchanges(void)
4883 {
4884 	REGISTER UBCHANGE *d, *nextd;
4885 	REGISTER WINDOWPART *w;
4886 	REGISTER NODEPROTO *np;
4887 	REGISTER NODEINST *ni, *oni;
4888 	REGISTER EDITOR *ed;
4889 	REGISTER INTBIG bit;
4890 	REGISTER LIBRARY *lib;
4891 	REGISTER VARIABLE *var;
4892 	REGISTER TOOL *tool;
4893 	REGISTER CHAR *partsname;
4894 	CHAR *par[1];
4895 	REGISTER void *infstr;
4896 
4897 	for(d = us_ubchanges; d != NOUBCHANGE; d = nextd)
4898 	{
4899 		nextd = d->nextubchange;
4900 
4901 		switch (d->change)
4902 		{
4903 			case UBKILLFM:	/* remove cell_message */
4904 				np = (NODEPROTO *)d->object;
4905 				for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
4906 				{
4907 					if (w->curnodeproto != np) continue;
4908 					if ((w->state&WINDOWTYPE) != TEXTWINDOW) continue;
4909 
4910 					/* see if the window still has a valid variable */
4911 					ed = w->editor;
4912 					if (ed->editobjvar == NOVARIABLE) continue;
4913 					var = getval((INTBIG)ed->editobjaddr, ed->editobjtype, -1, ed->editobjqual);
4914 					if (var == NOVARIABLE)
4915 					{
4916 						(void)newwindowpart(w->location, w);
4917 						killwindowpart(w);
4918 					}
4919 				}
4920 				break;
4921 			case UBNEWFC:	/* added cell-center */
4922 				ni = (NODEINST *)d->object;
4923 				np = ni->parent;
4924 				for(oni = np->firstnodeinst; oni != NONODEINST; oni = oni->nextnodeinst)
4925 				{
4926 					if (oni == ni) continue;
4927 					if (oni->proto == gen_cellcenterprim)
4928 					{
4929 						ttyputerr(_("Can only be one cell-center in a cell: new one deleted"));
4930 						us_clearhighlightcount();
4931 						startobjectchange((INTBIG)ni, VNODEINST);
4932 						(void)killnodeinst(ni);
4933 						us_setnodeprotocenter(oni->lowx, oni->lowy, np);
4934 						break;
4935 					}
4936 				}
4937 				if (oni == NONODEINST)
4938 					us_setnodeprotocenter(ni->lowx, ni->lowy, np);
4939 				break;
4940 			case UBSPICEPARTS:		/* check for new spice parts */
4941 				var = getvalkey((INTBIG)sim_tool, VTOOL, VSTRING, sim_spice_partskey);
4942 				if (var == NOVARIABLE) break;
4943 				partsname = (CHAR *)var->addr;
4944 				if (namesame(partsname, sim_spice_parts) == 0) break;
4945 				(void)reallocstring(&sim_spice_parts, partsname, sim_tool->cluster);
4946 
4947 				/* invoke the command file */
4948 				infstr = initinfstr();
4949 				addstringtoinfstr(infstr, el_libdir);
4950 				addstringtoinfstr(infstr, sim_spice_parts);
4951 				par[0] = returninfstr(infstr);
4952 				us_commandfile(1, par);
4953 				break;
4954 			case UBTECEDDELLAYER:		/* tell technology edit that layer cell was deleted */
4955 				us_teceddeletelayercell((NODEPROTO *)d->object);
4956 				break;
4957 			case UBTECEDDELNODE:		/* tell technology edit that node cell was deleted */
4958 				us_teceddeletenodecell((NODEPROTO *)d->object);
4959 				break;
4960 			case UBTECEDRENAME:			/* tell technology edit that cell was renamed */
4961 				np = (NODEPROTO *)d->object;
4962 				us_tecedrenamecell((CHAR *)d->parameter, np->protoname);
4963 				break;
4964 			case UBTOOLISON:
4965 				/* look through all cells and update those that need it */
4966 				tool = (TOOL *)d->object;
4967 				bit = 1 << tool->toolindex;
4968 				for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
4969 					for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
4970 				{
4971 					if ((np->adirty & bit) != 0)
4972 					{
4973 						if (tool->examinenodeproto != 0)
4974 							(*tool->examinenodeproto)(np);
4975 					}
4976 					np->adirty &= ~bit;
4977 				}
4978 				break;
4979 		}
4980 
4981 		/* cleanup */
4982 		us_freeubchange(d);
4983 	}
4984 	us_ubchanges = NOUBCHANGE;
4985 }
4986 
4987 /******************** COLOR ********************/
4988 
4989 /*
4990  * setup the color map for the graphics of technology "tech".  "style" is:
4991  *  COLORSEXISTING  continue existing colors
4992  *  COLORSDEFAULT   use default opaque colors
4993  *  COLORSBLACK     use black background colors
4994  *  COLORSWHITE     use white background colors
4995  * A set of transparent colors is obtained from technology "tech", combined
4996  * with the appropriate opaque colors, and set into the proper
4997  *  variables on the "user" tool (and subsequently displayed).
4998  * The 256 entries are organized thusly:
4999  * Bit 0 is for highlighting; bit 1 is an escape for
5000  * opaque colors, the next 5 bits are the transparent colors (if the opaque
5001  * escape is off) or the opaque value (if the bit is set).
5002  * The last bit is for the grid, although it may not appear if there are 128 entries.
5003  * This routine uses the database variable "USER_color_map" on the
5004  * technologies.
5005  */
us_getcolormap(TECHNOLOGY * tech,INTBIG style,BOOLEAN broadcast)5006 void us_getcolormap(TECHNOLOGY *tech, INTBIG style, BOOLEAN broadcast)
5007 {
5008 	static TECH_COLORMAP colmap[38] =
5009 	{
5010 		{255,255,255}, /*   4(0004) WHITE:   white                           */
5011 		{  0,  0,  0}, /*  12(0014) BLACK:   black                           */
5012 		{255,  0,  0}, /*  20(0024) RED:     red                             */
5013 		{  0,  0,255}, /*  28(0034) BLUE:    blue                            */
5014 		{  0,255,  0}, /*  36(0044) GREEN:   green                           */
5015 		{  0,255,255}, /*  44(0054) CYAN:    cyan                            */
5016 		{255,  0,255}, /*  52(0064) MAGENTA: magenta                         */
5017 		{255,255,  0}, /*  60(0074) YELLOW:  yellow                          */
5018 		{  0,  0,  0}, /*  68(0104) CELLTXT: cell and port names             */
5019 		{  0,  0,  0}, /*  76(0114) CELLOUT: cell outline                    */
5020 		{  0,  0,  0}, /*  84(0124) WINBOR:  window border color             */
5021 		{  0,255,  0}, /*  92(0134) HWINBOR: highlighted window border color */
5022 		{  0,  0,  0}, /* 100(0144) MENBOR:  menu border color               */
5023 		{255,255,255}, /* 108(0154) HMENBOR: highlighted menu border color   */
5024 		{  0,  0,  0}, /* 116(0164) MENTXT:  menu text color                 */
5025 		{  0,  0,  0}, /* 124(0174) MENGLY:  menu glyph color                */
5026 		{  0,  0,  0}, /* 132(0204) CURSOR:  cursor color                    */
5027 		{180,180,180}, /* 140(0214) GRAY:    gray                            */
5028 		{255,190,  6}, /* 148(0224) ORANGE:  orange                          */
5029 		{186,  0,255}, /* 156(0234) PURPLE:  purple                          */
5030 		{139, 99, 46}, /* 164(0244) BROWN:   brown                           */
5031 		{230,230,230}, /* 172(0254) LGRAY:   light gray                      */
5032 		{100,100,100}, /* 180(0264) DGRAY:   dark gray                       */
5033 		{255,150,150}, /* 188(0274) LRED:    light red                       */
5034 		{159, 80, 80}, /* 196(0304) DRED:    dark red                        */
5035 		{175,255,175}, /* 204(0314) LGREEN:  light green                     */
5036 		{ 89,159, 85}, /* 212(0324) DGREEN:  dark green                      */
5037 		{150,150,255}, /* 220(0334) LBLUE:   light blue                      */
5038 		{  2, 15,159}, /* 228(0344) DBLUE:   dark blue                       */
5039 		{  0,  0,  0}, /* 236(0354)          unassigned                      */
5040 		{  0,  0,  0}, /* 244(0364)          unassigned                      */
5041 		{  0,  0,  0}, /* 252(0374)          unassigned                      */
5042 		{  0,  0,  0}, /*                    grid                            */
5043 		{255,255,255}, /*                    highlight                       */
5044 		{255,  0,  0}, /*                    black background highlight      */
5045 		{255,  0,  0}, /*                    white background highlight      */
5046 		{255,255,255}, /*                    black background cursor         */
5047 		{  0,  0,  0}  /*                    white background cursor         */
5048 	};
5049 	static TECH_COLORMAP default_colmap[32] =
5050 	{                  /*     transpar4 transpar3 transpar2 transpar1 transpar0 */
5051 		{200,200,200}, /* 0:                                                    */
5052 		{  0,  0,200}, /* 1:                                          transpar0 */
5053 		{220,  0,120}, /* 2:                                transpar1           */
5054 		{ 80,  0,160}, /* 3:                                transpar1+transpar0 */
5055 		{ 70,250, 70}, /* 4:                      transpar2                     */
5056 		{  0,140,140}, /* 5:                      transpar2+          transpar0 */
5057 		{180,130,  0}, /* 6:                      transpar2+transpar1           */
5058 		{ 55, 70,140}, /* 7:                      transpar2+transpar1+transpar0 */
5059 		{250,250,  0}, /* 8:            transpar3                               */
5060 		{ 85,105,160}, /* 9:            transpar3+                    transpar0 */
5061 		{190, 80,100}, /* 10:           transpar3+          transpar1           */
5062 		{ 70, 50,150}, /* 11:           transpar3+          transpar1+transpar0 */
5063 		{ 80,210,  0}, /* 12:           transpar3+transpar2                     */
5064 		{ 50,105,130}, /* 13:           transpar3+transpar2+          transpar0 */
5065 		{170,110,  0}, /* 14:           transpar3+transpar2+transpar1           */
5066 		{ 60, 60,130}, /* 15:           transpar3+transpar2+transpar1+transpar0 */
5067 		{180,180,180}, /* 16: transpar4+                                        */
5068 		{  0,  0,180}, /* 17: transpar4+                              transpar0 */
5069 		{200,  0,100}, /* 18: transpar4+                    transpar1           */
5070 		{ 60,  0,140}, /* 19: transpar4+                    transpar1+transpar0 */
5071 		{ 50,230, 50}, /* 20: transpar4+          transpar2                     */
5072 		{  0,120,120}, /* 21: transpar4+          transpar2+          transpar0 */
5073 		{160,110,  0}, /* 22: transpar4+          transpar2+transpar1           */
5074 		{ 35, 50,120}, /* 23: transpar4+          transpar2+transpar1+transpar0 */
5075 		{230,230,  0}, /* 24: transpar4+transpar3                               */
5076 		{ 65, 85,140}, /* 25: transpar4+transpar3+                    transpar0 */
5077 		{170, 60, 80}, /* 26: transpar4+transpar3+          transpar1           */
5078 		{ 50, 30,130}, /* 27: transpar4+transpar3+          transpar1+transpar0 */
5079 		{ 60,190,  0}, /* 28: transpar4+transpar3+transpar2                     */
5080 		{ 30, 85,110}, /* 29: transpar4+transpar3+transpar2+          transpar0 */
5081 		{150, 90,  0}, /* 30: transpar4+transpar3+transpar2+transpar1           */
5082 		{ 40, 40,110}, /* 31: transpar4+transpar3+transpar2+transpar1+transpar0 */
5083 	};
5084 
5085 	TECH_COLORMAP *mapptr, *thisptr;
5086 	REGISTER INTBIG i;
5087 	REGISTER VARIABLE *var, *rvar, *gvar, *bvar;
5088 	static INTBIG USER_color_map_key = 0;
5089 	INTBIG red[256], green[256], blue[256];
5090 	extern GRAPHICS us_gbox;
5091 
5092 	/* get the technology's color information */
5093 	if (USER_color_map_key == 0) USER_color_map_key = makekey(x_("USER_color_map"));
5094 	var = getvalkey((INTBIG)tech, VTECHNOLOGY, VCHAR|VISARRAY, USER_color_map_key);
5095 	if (var != NOVARIABLE) mapptr = (TECH_COLORMAP *)var->addr; else mapptr = 0;
5096 
5097 	/* get existing color information */
5098 	rvar = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_red_key);
5099 	gvar = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_green_key);
5100 	bvar = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_blue_key);
5101 
5102 	/* must have some colors */
5103 	if (rvar == NOVARIABLE && gvar == NOVARIABLE && bvar == NOVARIABLE && var == NOVARIABLE)
5104 	{
5105 		mapptr = default_colmap;
5106 	}
5107 
5108 	if (style == COLORSEXISTING)
5109 	{
5110 		/* not resetting, get the old color values */
5111 		for(i=0; i<256; i++)
5112 		{
5113 			red[i] = ((INTBIG *)rvar->addr)[i];
5114 			green[i] = ((INTBIG *)gvar->addr)[i];
5115 			blue[i] = ((INTBIG *)bvar->addr)[i];
5116 			if ((i&(LAYERH|LAYERG|LAYEROE)) != 0) continue;
5117 			if (mapptr == 0) continue;
5118 			if (i == 0) continue;
5119 			thisptr = &mapptr[i>>2];
5120 			red[i]  = thisptr->red;   green[i]   = thisptr->green;
5121 			blue[i] = thisptr->blue;
5122 		}
5123 	} else
5124 	{
5125 #if SIMTOOL
5126 		/* update simulation window colors */
5127 		switch (style)
5128 		{
5129 			case COLORSWHITE:
5130 			case COLORSDEFAULT:
5131 				sim_window_setdisplaycolor(OFF_STRENGTH, BLUE);
5132 				sim_window_setdisplaycolor(NODE_STRENGTH, GREEN);
5133 				sim_window_setdisplaycolor(GATE_STRENGTH, MAGENTA);
5134 				sim_window_setdisplaycolor(VDD_STRENGTH, BLACK);
5135 				sim_window_setdisplaycolor(LOGIC_LOW, BLUE);
5136 				sim_window_setdisplaycolor(LOGIC_HIGH, MAGENTA);
5137 				sim_window_setdisplaycolor(LOGIC_X, BLACK);
5138 				sim_window_setdisplaycolor(LOGIC_Z, LGRAY);
5139 				break;
5140 			case COLORSBLACK:
5141 				sim_window_setdisplaycolor(OFF_STRENGTH, GREEN);
5142 				sim_window_setdisplaycolor(NODE_STRENGTH, CYAN);
5143 				sim_window_setdisplaycolor(GATE_STRENGTH, MAGENTA);
5144 				sim_window_setdisplaycolor(VDD_STRENGTH, LRED);
5145 				sim_window_setdisplaycolor(LOGIC_LOW, GREEN);
5146 				sim_window_setdisplaycolor(LOGIC_HIGH, MAGENTA);
5147 				sim_window_setdisplaycolor(LOGIC_X, LRED);
5148 				sim_window_setdisplaycolor(LOGIC_Z, DGRAY);
5149 				break;
5150 		}
5151 #endif
5152 
5153 		/* resetting: load entirely new color map */
5154 		for(i=0; i<256; i++)
5155 		{
5156 			if ((i&LAYERH) != 0)
5157 			{
5158 				switch (style)
5159 				{
5160 					case COLORSDEFAULT: thisptr = &colmap[33];   break;	/* white */
5161 					case COLORSBLACK:   thisptr = &colmap[34];   break;	/* red */
5162 					case COLORSWHITE:   thisptr = &colmap[35];   break;	/* red */
5163 				}
5164 			} else if ((i&LAYERG) != 0)
5165 			{
5166 				switch (style)
5167 				{
5168 					case COLORSDEFAULT: thisptr = &colmap[32];   break;	/* black */
5169 					case COLORSBLACK:   thisptr = &colmap[33];   break;	/* white */
5170 					case COLORSWHITE:   thisptr = &colmap[32];   break;	/* black */
5171 				}
5172 			} else if ((i&LAYEROE) != 0)
5173 			{
5174 				thisptr = &colmap[i>>2];
5175 
5176 				if (i == HMENBOR) switch (style)
5177 				{
5178 					case COLORSBLACK: thisptr = &colmap[2];   break;	/* red */
5179 					case COLORSWHITE: thisptr = &colmap[2];   break;	/* red */
5180 				}
5181 				if (i == CURSOR) switch (style)
5182 				{
5183 					case COLORSDEFAULT: thisptr = &colmap[16];   break;	/* default */
5184 					case COLORSBLACK:   thisptr = &colmap[36];   break;	/* white */
5185 					case COLORSWHITE:   thisptr = &colmap[37];   break;	/* black */
5186 				}
5187 
5188 				/* reverse black and white when using black background */
5189 				if (style == COLORSBLACK)
5190 				{
5191 					switch (i)
5192 					{
5193 						case CELLTXT:
5194 						case CELLOUT:
5195 						case WINBOR:
5196 						case MENBOR:
5197 						case MENTXT:
5198 						case MENGLY:
5199 						case BLACK:    thisptr = &colmap[33];   break;		/* white */
5200 						case WHITE:    thisptr = &colmap[37];   break;		/* black */
5201 					}
5202 				}
5203 			} else
5204 			{
5205 				if (rvar != NOVARIABLE) red[i] = ((INTBIG *)rvar->addr)[i];
5206 				if (gvar != NOVARIABLE) green[i] = ((INTBIG *)gvar->addr)[i];
5207 				if (bvar != NOVARIABLE) blue[i] = ((INTBIG *)bvar->addr)[i];
5208 				if (mapptr != 0) thisptr = &mapptr[i>>2]; else thisptr = 0;
5209 				if (i == ALLOFF) switch (style)
5210 				{
5211 					case COLORSDEFAULT: thisptr = &default_colmap[0]; break;	/* default */
5212 					case COLORSBLACK:   thisptr = &colmap[32];        break;	/* black */
5213 					case COLORSWHITE:   thisptr = &colmap[33];        break;	/* white */
5214 				}
5215 				if (thisptr == 0) continue;
5216 			}
5217 			red[i]  = thisptr->red;   green[i]   = thisptr->green;
5218 			blue[i] = thisptr->blue;
5219 		}
5220 
5221 		/* also set the grid color appropriately if it doesn't have its own bitplane */
5222 		if (el_maplength < 256)
5223 		{
5224 			switch (style)
5225 			{
5226 				case COLORSDEFAULT: us_gbox.col = BLACK;   break;	/* black */
5227 				case COLORSBLACK:   us_gbox.col = WHITE;   break;	/* white */
5228 				case COLORSWHITE:   us_gbox.col = BLACK;   break;	/* black */
5229 			}
5230 		}
5231 	}
5232 
5233 	/* set the color map */
5234 	if (broadcast)
5235 		startobjectchange((INTBIG)us_tool, VTOOL);
5236 	if (!broadcast) nextchangequiet();
5237 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_colormap_red_key, (INTBIG)red,
5238 		VINTEGER|VISARRAY|(256<<VLENGTHSH));
5239 	if (!broadcast) nextchangequiet();
5240 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_colormap_green_key, (INTBIG)green,
5241 		VINTEGER|VISARRAY|(256<<VLENGTHSH));
5242 	if (!broadcast) nextchangequiet();
5243 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_colormap_blue_key, (INTBIG)blue,
5244 		VINTEGER|VISARRAY|(256<<VLENGTHSH));
5245 	if (broadcast)
5246 		endobjectchange((INTBIG)us_tool, VTOOL);
5247 }
5248 
5249 /*
5250  * routine to load entry "entry" of the global color map entries with the value
5251  * (red, green, blue), letter "letter".  Handles highlight and grid layers
5252  * right if "spread" is true.
5253  */
us_setcolorentry(INTBIG entry1,INTBIG red,INTBIG green,INTBIG blue,INTBIG letter,BOOLEAN spread)5254 void us_setcolorentry(INTBIG entry1, INTBIG red, INTBIG green, INTBIG blue, INTBIG letter,
5255 	BOOLEAN spread)
5256 {
5257 	REGISTER INTBIG j;
5258 	Q_UNUSED( letter );
5259 
5260 	startobjectchange((INTBIG)us_tool, VTOOL);
5261 	(void)setindkey((INTBIG)us_tool, VTOOL, us_colormap_red_key, entry1, red);
5262 	(void)setindkey((INTBIG)us_tool, VTOOL, us_colormap_green_key, entry1, green);
5263 	(void)setindkey((INTBIG)us_tool, VTOOL, us_colormap_blue_key, entry1, blue);
5264 
5265 	/* place in other entries if special */
5266 	if ((entry1&LAYERH) == LAYERH && spread)
5267 	{
5268 		/* set all highlight colors */
5269 		for(j=0; j<256; j++) if ((j&LAYERH) == LAYERH)
5270 		{
5271 			(void)setindkey((INTBIG)us_tool, VTOOL, us_colormap_red_key, j, red);
5272 			(void)setindkey((INTBIG)us_tool, VTOOL, us_colormap_green_key, j, green);
5273 			(void)setindkey((INTBIG)us_tool, VTOOL, us_colormap_blue_key, j, blue);
5274 		}
5275 	} else if ((entry1&(LAYERG|LAYERH)) == LAYERG && spread)
5276 	{
5277 		/* set all grid colors */
5278 		for(j=0; j<256; j++) if ((j&(LAYERG|LAYERH)) == LAYERG)
5279 		{
5280 			(void)setindkey((INTBIG)us_tool, VTOOL, us_colormap_red_key, j, red);
5281 			(void)setindkey((INTBIG)us_tool, VTOOL, us_colormap_green_key, j, green);
5282 			(void)setindkey((INTBIG)us_tool, VTOOL, us_colormap_blue_key, j, blue);
5283 		}
5284 	}
5285 	endobjectchange((INTBIG)us_tool, VTOOL);
5286 }
5287 
5288 /*
5289  * routine to convert a red/green/blue color in (ir,ig,ib) to a hue/saturation/
5290  * intensity color in (h,s,i)
5291  */
us_rgbtohsv(INTBIG ir,INTBIG ig,INTBIG ib,float * h,float * s,float * i)5292 void us_rgbtohsv(INTBIG ir, INTBIG ig, INTBIG ib, float *h, float *s, float *i)
5293 {
5294 	float x, r, g, b, rdot, gdot, bdot;
5295 
5296 	r = ir / 255.0f;
5297 	g = ig / 255.0f;
5298 	b = ib / 255.0f;
5299 
5300 	/* "i" is maximum of "r", "g", and "b" */
5301 	if (r > g) *i = r; else *i = g;
5302 	if (b > *i) *i = b;
5303 
5304 	/* "x" is minimum of "r", "g", and "b" */
5305 	if (r < g) x = r; else x = g;
5306 	if (b < x) x = b;
5307 
5308 	/* "saturation" is (i-x)/i */
5309 	if (*i == 0.0) *s = 0.0; else *s = (*i - x) / *i;
5310 
5311 	if (*s == 0.0) *h = 0.0; else
5312 	{
5313 		rdot = (*i - r) / (*i - x);
5314 		gdot = (*i - g) / (*i - x);
5315 		bdot = (*i - b) / (*i - x);
5316 		if (b == x && r == *i) *h = (1.0f - gdot) / 6.0f; else
5317 		if (b == x && g == *i) *h = (1.0f + rdot) / 6.0f; else
5318 		if (r == x && g == *i) *h = (3.0f - bdot) / 6.0f; else
5319 		if (r == x && b == *i) *h = (3.0f + gdot) / 6.0f; else
5320 		if (g == x && b == *i) *h = (5.0f - rdot) / 6.0f; else
5321 		if (g == x && r == *i) *h = (5.0f + bdot) / 6.0f; else
5322 			ttyputmsg(_("Cannot convert (%ld,%ld,%ld), for x=%g i=%g s=%g"), ir, ig, ib, x, *i, *s);
5323 	}
5324 }
5325 
5326 /*
5327  * routine to convert a hue/saturation/intensity color in (h,s,v) to a red/
5328  * green/blue color in (r,g,b)
5329  */
us_hsvtorgb(float h,float s,float v,INTBIG * r,INTBIG * g,INTBIG * b)5330 void us_hsvtorgb(float h, float s, float v, INTBIG *r, INTBIG *g, INTBIG *b)
5331 {
5332 	REGISTER INTBIG i;
5333 	REGISTER float f, m, n, k;
5334 
5335 	h = h * 6.0f;
5336 	i = (INTBIG)h;
5337 	f = h - (float)i;
5338 	m = v * (1.0f - s);
5339 	n = v * (1.0f - s * f);
5340 	k = v * (1.0f - s * (1.0f - f));
5341 	switch (i)
5342 	{
5343 		case 0:
5344 			*r = (INTBIG)(v*255.0); *g = (INTBIG)(k*255.0); *b = (INTBIG)(m*255.0);
5345 			break;
5346 		case 1:
5347 			*r = (INTBIG)(n*255.0); *g = (INTBIG)(v*255.0); *b = (INTBIG)(m*255.0);
5348 			break;
5349 		case 2:
5350 			*r = (INTBIG)(m*255.0); *g = (INTBIG)(v*255.0); *b = (INTBIG)(k*255.0);
5351 			break;
5352 		case 3:
5353 			*r = (INTBIG)(m*255.0); *g = (INTBIG)(n*255.0); *b = (INTBIG)(v*255.0);
5354 			break;
5355 		case 4:
5356 			*r = (INTBIG)(k*255.0); *g = (INTBIG)(m*255.0); *b = (INTBIG)(v*255.0);
5357 			break;
5358 		case 5:
5359 			*r = (INTBIG)(v*255.0); *g = (INTBIG)(m*255.0); *b = (INTBIG)(n*255.0);
5360 			break;
5361 	}
5362 	if (*r < 0 || *r > 255 || *g < 0 || *g > 255 || *b < 0 || *b > 255)
5363 		ttyputmsg(x_("(%g,%g,%g) -> (%ld,%ld,%ld) (i=%ld)"),h, s, v, *r, *g, *b, i);
5364 }
5365 
5366 /*
5367  * Routine to return an array of integers that describes the print colors for technology "tech".
5368  * The array has 5 entries for each layer in the technology.  The first 3 are the red/green/blue
5369  * color.  The 4th entry is the opacity and the 5th entry is the foreground factor.
5370  *
5371  * Opacity indicates how much a layer should obscure lower levels.  It is
5372  * a fractional integer (where 1.0 is WHOLE).
5373  * Set it to WHOLE if the layer should be opaque, or a smaller fraction if lower
5374  * levels should partially show through.
5375  *
5376  * Foreground should be 1 if the layer should show up beneath partially
5377  * transparent layers.  It is set to 0 for layers like wells and selects
5378  * that should not show through.
5379  *
5380  * The R, G, and B coordinates are on a scale of 0 to 255 from dark to light.
5381  * White layers (R=G=B=255) will not be visible.
5382  */
us_getprintcolors(TECHNOLOGY * tech)5383 INTBIG *us_getprintcolors(TECHNOLOGY *tech)
5384 {
5385 	REGISTER VARIABLE *var, *varred, *vargreen, *varblue;
5386 	REGISTER INTBIG i, fun, col, tot;
5387 	float opacity;
5388 
5389 	/* make sure the array is there */
5390 	if (us_printcolordata != 0) efree((CHAR *)us_printcolordata);
5391 	us_printcolordata = (INTBIG *)emalloc(tech->layercount*5*SIZEOFINTBIG, io_tool->cluster);
5392 	if (us_printcolordata == 0) return(0);
5393 
5394 	/* presume no print colors and create them from the display colors */
5395 	varred = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_red_key);
5396 	vargreen = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_green_key);
5397 	varblue = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_blue_key);
5398 	if (varred == NOVARIABLE || vargreen == NOVARIABLE || varblue == NOVARIABLE) return(0);
5399 	for(i=0; i<tech->layercount; i++)
5400 	{
5401 		fun = layerfunction(tech, i);
5402 		if ((fun&LFPSEUDO) != 0) continue;
5403 		col = tech->layers[i]->col;
5404 		us_printcolordata[i*5] = ((INTBIG *)varred->addr)[col];
5405 		us_printcolordata[i*5+1] = ((INTBIG *)vargreen->addr)[col];
5406 		us_printcolordata[i*5+2] = ((INTBIG *)varblue->addr)[col];
5407 		opacity = 1.0f;
5408 		switch (fun&LFTYPE)
5409 		{
5410 			case LFMETAL12:   opacity *= 0.9f;
5411 			case LFMETAL11:   opacity *= 0.9f;
5412 			case LFMETAL10:   opacity *= 0.9f;
5413 			case LFMETAL9:    opacity *= 0.9f;
5414 			case LFMETAL8:    opacity *= 0.9f;
5415 			case LFMETAL7:    opacity *= 0.9f;
5416 			case LFMETAL6:    opacity *= 0.9f;
5417 			case LFMETAL5:    opacity *= 0.9f;
5418 			case LFMETAL4:    opacity *= 0.9f;
5419 			case LFMETAL3:    opacity *= 0.9f;
5420 			case LFMETAL2:    opacity *= 0.9f;
5421 			case LFMETAL1:    opacity *= 0.9f;
5422 		}
5423 		us_printcolordata[i*5+3] = (INTBIG)(opacity*WHOLE);
5424 		if ((fun&LFTYPE) == LFIMPLANT || (fun&LFTYPE) == LFSUBSTRATE ||
5425 			(fun&LFTYPE) == LFWELL) us_printcolordata[i*5+4] = 0; else
5426 				us_printcolordata[i*5+4] = 1;
5427 	}
5428 
5429 	/* if there are print colors, use them */
5430 	var = getval((INTBIG)tech, VTECHNOLOGY, VINTEGER|VISARRAY, x_("USER_print_colors"));
5431 	if (var != NOVARIABLE)
5432 	{
5433 		tot = getlength(var);
5434 		if (tot > tech->layercount*5) tot = tech->layercount*5;
5435 		for(i=0; i<tot; i++) us_printcolordata[i] = ((INTBIG *)var->addr)[i];
5436 	}
5437 	return(us_printcolordata);
5438 }
5439 
5440 /******************** MISCELLANEOUS ********************/
5441 
5442 /*
5443  * Routine to determine the offset of the "length" and "width" attributes on
5444  * node "ni" given that it uses a text descriptor of "descript".
5445  */
us_getlenwidoffset(NODEINST * ni,UINTBIG * descript,INTBIG * xoff,INTBIG * yoff)5446 void us_getlenwidoffset(NODEINST *ni, UINTBIG *descript, INTBIG *xoff, INTBIG *yoff)
5447 {
5448 	REGISTER INTBIG i;
5449 
5450 	*xoff = *yoff = 0;
5451 	i = TXTGETQLAMBDA(TDGETSIZE(descript));
5452 	if (i > 4) i /= 2; else i = 2;
5453 	switch (ni->rotation)
5454 	{
5455 		case 0:
5456 			if (ni->transpose == 0) *yoff = i; else
5457 				*xoff = -i;
5458 			break;
5459 		case 900:
5460 			if (ni->transpose == 0) *xoff = i; else
5461 				*yoff = i;
5462 			break;
5463 		case 1800:
5464 			if (ni->transpose == 0) *yoff = -i; else
5465 				*xoff = i;
5466 			break;
5467 		case 2700:
5468 			if (ni->transpose == 0) *xoff = -i; else
5469 				*yoff = -i;
5470 			break;
5471 	}
5472 }
5473 
5474 /*
5475  * Routine to return the placement angle to use for node "np".
5476  */
us_getplacementangle(NODEPROTO * np)5477 INTBIG us_getplacementangle(NODEPROTO *np)
5478 {
5479 	REGISTER VARIABLE *var;
5480 
5481 	var = getvalkey((INTBIG)np, VNODEPROTO, VINTEGER, us_placement_angle_key);
5482 	if (var != NOVARIABLE) return(var->addr);
5483 	var = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER, us_placement_angle_key);
5484 	if (var != NOVARIABLE) return(var->addr);
5485 	return(0);
5486 }
5487 
5488 /*
5489  * Routine to adjust the coordinates (cx,cy) to account for the fact that they are from primitive
5490  * "prim" and in cell "cell".  If this is not a primitive, or if the technology of the primitive
5491  * matches the technology of the cell, then do nothing.  Otherwise, adjust the size of the coordinates
5492  * so that the technology/lambda of the primitive is adjusted to that of the cell.
5493  */
us_adjustfornodeincell(NODEPROTO * prim,NODEPROTO * cell,INTBIG * cx,INTBIG * cy)5494 void us_adjustfornodeincell(NODEPROTO *prim, NODEPROTO *cell, INTBIG *cx, INTBIG *cy)
5495 {
5496 	REGISTER LIBRARY *lib;
5497 	REGISTER INTBIG celllambda, primlambda;
5498 
5499 	if (prim->primindex == 0) return;
5500 	if (cell == NONODEPROTO) return;
5501 	if (prim->tech == cell->tech) return;
5502 	if (cell->tech == gen_tech) return;
5503 
5504 	lib = cell->lib;
5505 	celllambda = lib->lambda[cell->tech->techindex],
5506 	primlambda = lib->lambda[prim->tech->techindex],
5507 	*cx = muldiv(*cx, celllambda, primlambda);
5508 	*cy = muldiv(*cy, celllambda, primlambda);
5509 }
5510 
5511 /*
5512  * Routine to get the "displayed" location of node "ni" and return it in
5513  * (xpos, ypos).
5514  */
us_getnodedisplayposition(NODEINST * ni,INTBIG * xpos,INTBIG * ypos)5515 void us_getnodedisplayposition(NODEINST *ni, INTBIG *xpos, INTBIG *ypos)
5516 {
5517 	REGISTER NODEPROTO *np;
5518 	INTBIG cox, coy, plx, ply, phx, phy;
5519 	REGISTER INTBIG dx, dy;
5520 	REGISTER VARIABLE *var;
5521 	XARRAY trans;
5522 
5523 	np = ni->proto;
5524 	if ((us_useroptions&CENTEREDPRIMITIVES) == 0)
5525 	{
5526 		corneroffset(ni, np, ni->rotation, ni->transpose, &cox, &coy, FALSE);
5527 		*xpos = ni->lowx+cox;
5528 		*ypos = ni->lowy+coy;
5529 	} else
5530 	{
5531 		var = getvalkey((INTBIG)np, VNODEPROTO, VINTEGER|VISARRAY, el_prototype_center_key);
5532 		if (var != NOVARIABLE)
5533 		{
5534 			dx = ((INTBIG *)var->addr)[0] + (ni->lowx+ni->highx)/2 -
5535 				(np->lowx+np->highx)/2;
5536 			dy = ((INTBIG *)var->addr)[1] + (ni->lowy+ni->highy)/2 -
5537 				(np->lowy+np->highy)/2;
5538 			makerot(ni, trans);
5539 			xform(dx, dy, &cox, &coy, trans);
5540 			*xpos = cox;
5541 			*ypos = coy;
5542 		} else
5543 		{
5544 			nodesizeoffset(ni, &plx, &ply, &phx, &phy);
5545 			makerot(ni, trans);
5546 			dx = (ni->lowx+plx+ni->highx-phx)/2;
5547 			dy = (ni->lowy+ply+ni->highy-phy)/2;
5548 			xform(dx, dy, xpos, ypos, trans);
5549 		}
5550 	}
5551 }
5552 
5553 /*
5554  * routine to put cell center (x, y) on cell "np".
5555  */
us_setnodeprotocenter(INTBIG x,INTBIG y,NODEPROTO * np)5556 void us_setnodeprotocenter(INTBIG x, INTBIG y, NODEPROTO *np)
5557 {
5558 	INTBIG position[2];
5559 
5560 	position[0] = x;   position[1] = y;
5561 	nextchangequiet();
5562 	(void)setvalkey((INTBIG)np, VNODEPROTO, el_prototype_center_key,
5563 		(INTBIG)position, VINTEGER|VISARRAY|(2<<VLENGTHSH));
5564 }
5565 
5566 /*
5567  * routine to remove cell center from cell "np".
5568  */
us_delnodeprotocenter(NODEPROTO * np)5569 void us_delnodeprotocenter(NODEPROTO *np)
5570 {
5571 	nextchangequiet();
5572 	(void)delvalkey((INTBIG)np, VNODEPROTO, el_prototype_center_key);
5573 }
5574 
5575 /*
5576  * Routine called when any of the "essential bounds" nodes are created, destroyed, or moved
5577  * in cell "cell".
5578  */
us_setessentialbounds(NODEPROTO * cell)5579 void us_setessentialbounds(NODEPROTO *cell)
5580 {
5581 	REGISTER NODEINST *ni;
5582 	REGISTER INTBIG lx, hx, ly, hy, x, y, essentialcount;
5583 	REGISTER VARIABLE *var;
5584 	INTBIG bounds[4];
5585 
5586 	essentialcount = 0;
5587 	lx = hx = ly = hy = 0;
5588 	for(ni = cell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
5589 	{
5590 		if (ni->proto != gen_essentialprim) continue;
5591 		x = (ni->lowx + ni->highx) / 2;
5592 		y = (ni->lowy + ni->highy) / 2;
5593 		if (essentialcount == 0)
5594 		{
5595 			lx = hx = x;
5596 			ly = hy = y;
5597 		} else
5598 		{
5599 			if (x < lx) lx = x;
5600 			if (x > hx) hx = x;
5601 			if (y < ly) ly = y;
5602 			if (y > hy) hy = y;
5603 		}
5604 		essentialcount++;
5605 	}
5606 	if (essentialcount > 2)
5607 	{
5608 		/* make sure they are all on the bounds */
5609 		for(ni = cell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
5610 		{
5611 			if (ni->proto != gen_essentialprim) continue;
5612 			x = (ni->lowx + ni->highx) / 2;
5613 			y = (ni->lowy + ni->highy) / 2;
5614 			if ((x == lx || x == hx) && (y == ly || y == hy)) continue;
5615 			ttyputerr(_("Warning: Essential bounds markers inconsistent in cell %s"),
5616 				describenodeproto(cell));
5617 			break;
5618 		}
5619 	}
5620 
5621 	/* set the essential bounds */
5622 	if (essentialcount <= 1)
5623 	{
5624 		/* delete the bounds */
5625 		var = getvalkey((INTBIG)cell, VNODEPROTO, VINTEGER|VISARRAY, el_essential_bounds_key);
5626 		if (var != NOVARIABLE)
5627 		{
5628 			nextchangequiet();
5629 			delvalkey((INTBIG)cell, VNODEPROTO, el_essential_bounds_key);
5630 		}
5631 	} else
5632 	{
5633 		bounds[0] = lx;   bounds[1] = hx;
5634 		bounds[2] = ly;   bounds[3] = hy;
5635 		nextchangequiet();
5636 		setvalkey((INTBIG)cell, VNODEPROTO, el_essential_bounds_key, (INTBIG)bounds,
5637 			VINTEGER|VISARRAY|(4<<VLENGTHSH));
5638 	}
5639 }
5640 
us_getlowleft(NODEINST * ni,INTBIG * x,INTBIG * y)5641 void us_getlowleft(NODEINST *ni, INTBIG *x, INTBIG *y)
5642 {
5643 	INTBIG lx, ly, hx, hy;
5644 	XARRAY trans;
5645 	static POLYGON *poly = NOPOLYGON;
5646 
5647 	(void)needstaticpolygon(&poly, 4, us_tool->cluster);
5648 
5649 	nodesizeoffset(ni, &lx, &ly, &hx, &hy);
5650 	maketruerectpoly(ni->lowx+lx, ni->highx-hx, ni->lowy+ly, ni->highy-hy, poly);
5651 	if (ni->rotation != 0 || ni->transpose != 0)
5652 	{
5653 		makerot(ni, trans);
5654 		xformpoly(poly, trans);
5655 	}
5656 	getbbox(poly, x, &hx, y, &hy);
5657 }
5658 
5659 /*
5660  * routine to modify the text descriptor in the highlighted object "high"
5661  */
us_modifytextdescript(HIGHLIGHT * high,UINTBIG * descript)5662 void us_modifytextdescript(HIGHLIGHT *high, UINTBIG *descript)
5663 {
5664 	REGISTER VARIABLE *var;
5665 
5666 	if (high->fromvar != NOVARIABLE)
5667 	{
5668 		var = high->fromvar;
5669 		if (high->fromvarnoeval != NOVARIABLE) var = high->fromvarnoeval;
5670 		if (TDDIFF(descript, var->textdescript))
5671 		{
5672 			if (high->fromport != NOPORTPROTO)
5673 			{
5674 				modifydescript((INTBIG)high->fromport, VPORTPROTO, var, descript);
5675 			} else if (high->fromgeom == NOGEOM)
5676 			{
5677 				modifydescript((INTBIG)high->cell, VNODEPROTO, var, descript);
5678 			} else
5679 			{
5680 				modifydescript((INTBIG)high->fromgeom->entryaddr.blind,
5681 					high->fromgeom->entryisnode ? VNODEINST : VARCINST, var, descript);
5682 			}
5683 		}
5684 		return;
5685 	}
5686 	if (high->fromport != NOPORTPROTO)
5687 	{
5688 		(void)setind((INTBIG)high->fromport, VPORTPROTO, x_("textdescript"), 0, descript[0]);
5689 		(void)setind((INTBIG)high->fromport, VPORTPROTO, x_("textdescript"), 1, descript[1]);
5690 		return;
5691 	}
5692 	if (high->fromgeom->entryisnode)
5693 	{
5694 		(void)setind((INTBIG)high->fromgeom->entryaddr.ni, VNODEINST,
5695 			x_("textdescript"), 0, descript[0]);
5696 		(void)setind((INTBIG)high->fromgeom->entryaddr.ni, VNODEINST,
5697 			x_("textdescript"), 1, descript[1]);
5698 		return;
5699 	}
5700 }
5701 
5702 /*
5703  * Routine to adjust the popup menu stored on the user interface object to correspond
5704  * to changes made to entry "pindex" of memory-structure popup "pm".
5705  */
us_adjustpopupmenu(POPUPMENU * pm,INTBIG pindex)5706 void us_adjustpopupmenu(POPUPMENU *pm, INTBIG pindex)
5707 {
5708 	CHAR **lines, *popupname;
5709 	CHAR *pt;
5710 	VARIABLE *var;
5711 	COMMANDBINDING commandbinding;
5712 	REGISTER void *infstr;
5713 
5714 	infstr = initinfstr();
5715 	addstringtoinfstr(infstr, x_("USER_binding_popup_"));
5716 	addstringtoinfstr(infstr, pm->name);
5717 	popupname = returninfstr(infstr);
5718 	var = getval((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, popupname);
5719 	if (var != NOVARIABLE)
5720 	{
5721 		lines = (CHAR **)var->addr;
5722 		us_parsebinding(lines[pindex+1], &commandbinding);
5723 		infstr = initinfstr();
5724 		for(pt = lines[pindex+1]; *pt != 0; pt++)
5725 		{
5726 			if (estrncmp(pt, x_("message=\""), 9) == 0)
5727 			{
5728 				addstringtoinfstr(infstr, x_("message=\""));
5729 				pt += 9;
5730 				while (*pt != 0 && *pt != '"') pt++;
5731 				addstringtoinfstr(infstr, pm->list[pindex].attribute);
5732 			}
5733 			addtoinfstr(infstr, *pt);
5734 		}
5735 		nextchangequiet();
5736 		(void)setind((INTBIG)us_tool, VTOOL, popupname, pindex+1, (INTBIG)returninfstr(infstr));
5737 		us_freebindingparse(&commandbinding);
5738 	}
5739 }
5740 
5741 /*
5742  * Routine to set the 9 entries of an XARRAY on a C object using change control.
5743  * This must be done as individual index changes because "setval()" cannot handle
5744  * arrays on fixed objects (objects in the C structures).  The array is attribute
5745  * "name" on object "addr" of type "type".  It is filled from "array".
5746  */
us_setxarray(INTBIG addr,INTBIG type,CHAR * name,XARRAY array)5747 void us_setxarray(INTBIG addr, INTBIG type, CHAR *name, XARRAY array)
5748 {
5749 	REGISTER INTBIG i, x, y;
5750 
5751 	i = 0;
5752 	for(y=0; y<3; y++)
5753 	{
5754 		for(x=0; x<3; x++)
5755 		{
5756 			(void)setind(addr, type, name, i, array[y][x]);
5757 			i++;
5758 		}
5759 	}
5760 }
5761 
5762 /*
5763  * Routine to return true if "name" is legal for objects of type "type".
5764  */
us_validname(CHAR * name,INTBIG type)5765 BOOLEAN us_validname(CHAR *name, INTBIG type)
5766 {
5767 	REGISTER CHAR *pt;
5768 
5769 	for(pt = name; *pt != 0; pt++)
5770 	{
5771 		if (*pt == ' ' || *pt == '\t')
5772 		{
5773 			ttyputerr(_("Name cannot have embedded spaces"));
5774 			return(FALSE);
5775 		}
5776 		if (*pt < ' ' || *pt >= 0177)
5777 		{
5778 			ttyputerr(_("Name has unprintable characters"));
5779 			return(FALSE);
5780 		}
5781 		if (type == VNODEPROTO)
5782 		{
5783 			if (*pt == ':' || *pt == ';' || *pt == '{')
5784 			{
5785 				ttyputerr(_("Name cannot have '%c' in it"), *pt);
5786 				return(FALSE);
5787 			}
5788 		}
5789 		if (type == VARCPROTO || type == VTECHNOLOGY || type == VLIBRARY)
5790 		{
5791 			if (*pt == ':')
5792 			{
5793 				ttyputerr(_("Name cannot have '%c' in it"), *pt);
5794 				return(FALSE);
5795 			}
5796 		}
5797 	}
5798 	return(TRUE);
5799 }
5800 
5801 /*
5802  * Helper routine to determine the proper "address" field to use from variable "var".
5803  * Normally, it is simply "var->addr", but if it is a string with the "++" or "--"
5804  * sequence in it, then it auto-increments/decrements a numeric value, and so the
5805  * "++"/"--" are removed, and the original variable (which resides on "addr"/"type")
5806  * is modified.
5807  */
us_inheritaddress(INTBIG addr,INTBIG type,VARIABLE * var)5808 INTBIG us_inheritaddress(INTBIG addr, INTBIG type, VARIABLE *var)
5809 {
5810 	REGISTER CHAR *str;
5811 	CHAR line[30];
5812 	REGISTER INTBIG i, j, len, incrpoint, retval, curval;
5813 	REGISTER void *infstr;
5814 
5815 	/* if it isn't a string, just return its address */
5816 	if ((var->type&(VCODE1|VCODE1)) == 0 && (var->type & VTYPE) != VSTRING) return(var->addr);
5817 	if ((var->type & VISARRAY) != 0) return(var->addr);
5818 
5819 	str = (CHAR *)var->addr;
5820 	len = estrlen(str);
5821 	for(i=0; i<len; i++)
5822 	{
5823 		if (str[i] == '+' && str[i+1] == '+') break;
5824 		if (str[i] == '-' && str[i+1] == '-') break;
5825 	}
5826 	if (i >= len) return(var->addr);
5827 
5828 	/* construct the proper inherited string and increment the variable */
5829 	incrpoint = 0;
5830 	infstr = initinfstr();
5831 	for(i=0; i<len; i++)
5832 	{
5833 		if (str[i] == '+' && str[i+1] == '+')
5834 		{
5835 			incrpoint = i;
5836 			i++;
5837 			continue;
5838 		}
5839 		if (str[i] == '-' && str[i+1] == '-')
5840 		{
5841 			incrpoint = i;
5842 			i++;
5843 			continue;
5844 		}
5845 		addtoinfstr(infstr, str[i]);
5846 	}
5847 
5848 	/* get the new value */
5849 	retval = (INTBIG)returninfstr(infstr);
5850 
5851 	/* increment the variable */
5852 	for(i = incrpoint-1; i>0; i--)
5853 		if (!isdigit(str[i])) break;
5854 	i++;
5855 	str[incrpoint] = 0;
5856 	curval = myatoi(&str[i]);
5857 	str[incrpoint] = str[incrpoint+1];
5858 	if (str[incrpoint] == '+') curval++; else curval--;
5859 	infstr = initinfstr();
5860 	for(j=0; j<i; j++)
5861 		addtoinfstr(infstr, str[j]);
5862 	esnprintf(line, 30, x_("%ld"), curval);
5863 	addstringtoinfstr(infstr, line);
5864 	addstringtoinfstr(infstr, &str[incrpoint]);
5865 	(void)setval(addr, type, makename(var->key), (INTBIG)returninfstr(infstr), var->type);
5866 
5867 	return(retval);
5868 }
5869 
5870 /*
5871  * Routine to update the location of parameters on node "ni".
5872  * Returns TRUE if adjustments were made.
5873  */
us_adjustparameterlocations(NODEINST * ni)5874 BOOLEAN us_adjustparameterlocations(NODEINST *ni)
5875 {
5876 	REGISTER INTBIG i, xo, yo;
5877 	REGISTER UINTBIG pos, size, face, rot, it, bo, un;
5878 	REGISTER BOOLEAN changed;
5879 	REGISTER VARIABLE *npvar, *nivar, *iconvar;
5880 	REGISTER NODEPROTO *np, *cnp;
5881 	REGISTER NODEINST *icon;
5882 
5883 	/* get the location from the example icon */
5884 	np = ni->proto;
5885 	cnp = contentsview(np);
5886 	if (cnp == NONODEPROTO) return(FALSE);
5887 
5888 	/* look for an example of the icon in the contents */
5889 	for(icon = cnp->firstnodeinst; icon != NONODEINST; icon = icon->nextnodeinst)
5890 		if (icon->proto == np) break;
5891 	if (icon == NONODEINST) return(FALSE);
5892 
5893 	/* now adjust the positions to match the icon */
5894 	changed = FALSE;
5895 	for(i=0; i<cnp->numvar; i++)
5896 	{
5897 		/* find a parameter */
5898 		npvar = &cnp->firstvar[i];
5899 		if (TDGETINHERIT(npvar->textdescript) == 0) continue;
5900 
5901 		/* find the parameter on this instance */
5902 		nivar = getvalkeynoeval((INTBIG)ni, VNODEINST, -1, npvar->key);
5903 		if (nivar == NOVARIABLE) continue;
5904 
5905 		/* see what the icon's position is */
5906 		iconvar = getvalkeynoeval((INTBIG)icon, VNODEINST, -1, npvar->key);
5907 		if (iconvar == NOVARIABLE) continue;
5908 
5909 		/* see if the offsets are the same */
5910 		xo = TDGETXOFF(iconvar->textdescript);
5911 		yo = TDGETYOFF(iconvar->textdescript);
5912 		pos = TDGETPOS(iconvar->textdescript);
5913 		size = TDGETSIZE(iconvar->textdescript);
5914 		face = TDGETFACE(iconvar->textdescript);
5915 		rot = TDGETROTATION(iconvar->textdescript);
5916 		it = TDGETITALIC(iconvar->textdescript);
5917 		bo = TDGETBOLD(iconvar->textdescript);
5918 		un = TDGETUNDERLINE(iconvar->textdescript);
5919 		if (xo == TDGETXOFF(nivar->textdescript) && yo == TDGETYOFF(nivar->textdescript) &&
5920 			pos == TDGETPOS(nivar->textdescript) && size == TDGETSIZE(nivar->textdescript) &&
5921 			face == TDGETFACE(nivar->textdescript) && rot == TDGETROTATION(nivar->textdescript) &&
5922 			it == TDGETITALIC(nivar->textdescript) && bo == TDGETBOLD(nivar->textdescript) &&
5923 			un == TDGETUNDERLINE(nivar->textdescript)) continue;
5924 		if (!changed)
5925 			startobjectchange((INTBIG)ni, VNODEINST);
5926 		TDSETOFF(nivar->textdescript, xo, yo);
5927 		TDSETPOS(nivar->textdescript, pos);
5928 		TDSETSIZE(nivar->textdescript, size);
5929 		TDSETFACE(nivar->textdescript, face);
5930 		TDSETROTATION(nivar->textdescript, rot);
5931 		TDSETITALIC(nivar->textdescript, it);
5932 		TDSETBOLD(nivar->textdescript, bo);
5933 		TDSETUNDERLINE(nivar->textdescript, un);
5934 		changed = TRUE;
5935 	}
5936 	if (changed) endobjectchange((INTBIG)ni, VNODEINST);
5937 	return(changed);
5938 }
5939 
5940 /*
5941  * Routine to inherit all prototype attributes down to instance "ni".
5942  */
us_inheritattributes(NODEINST * ni)5943 void us_inheritattributes(NODEINST *ni)
5944 {
5945 	REGISTER NODEPROTO *np, *cnp;
5946 	REGISTER NODEINST *icon;
5947 	REGISTER INTBIG i, j;
5948 	REGISTER VARIABLE *var, *ovar;
5949 	REGISTER BOOLEAN found, first;
5950 	REGISTER PORTPROTO *pp, *cpp;
5951 
5952 	/* ignore primitives */
5953 	np = ni->proto;
5954 	if (np->primindex != 0) return;
5955 
5956 	/* first inherit directly from this node's prototype */
5957 	for(i=0; i<np->numvar; i++)
5958 	{
5959 		var = &np->firstvar[i];
5960 		if (TDGETINHERIT(var->textdescript) == 0) continue;
5961 		us_inheritcellattribute(var, ni, np, NONODEINST);
5962 	}
5963 
5964 	/* inherit directly from each port's prototype */
5965 	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
5966 		us_inheritexportattributes(pp, ni, np);
5967 
5968 	/* if this node is an icon, also inherit from the contents prototype */
5969 	cnp = contentsview(np);
5970 	if (cnp != NONODEPROTO)
5971 	{
5972 		/* look for an example of the icon in the contents */
5973 		for(icon = cnp->firstnodeinst; icon != NONODEINST; icon = icon->nextnodeinst)
5974 			if (icon->proto == np) break;
5975 
5976 		for(i=0; i<cnp->numvar; i++)
5977 		{
5978 			var = &cnp->firstvar[i];
5979 			if (TDGETINHERIT(var->textdescript) == 0) continue;
5980 			us_inheritcellattribute(var, ni, cnp, icon);
5981 		}
5982 		for(cpp = cnp->firstportproto; cpp != NOPORTPROTO; cpp = cpp->nextportproto)
5983 			us_inheritexportattributes(cpp, ni, cnp);
5984 	}
5985 
5986 	/* now delete parameters that are not in the prototype */
5987 	cnp = contentsview(np);
5988 	if (cnp == NONODEPROTO) cnp = np;
5989 	found = TRUE;
5990 	first = TRUE;
5991 	while (found)
5992 	{
5993 		found = FALSE;
5994 		for(i=0; i<ni->numvar; i++)
5995 		{
5996 			var = &ni->firstvar[i];
5997 			if (TDGETISPARAM(var->textdescript) == 0) continue;
5998 			for(j=0; j<cnp->numvar; j++)
5999 			{
6000 				ovar = &cnp->firstvar[j];
6001 				if (TDGETISPARAM(ovar->textdescript) == 0) continue;
6002 				if (ovar->key == var->key) break;
6003 			}
6004 			if (j >= cnp->numvar)
6005 			{
6006 				if (first) startobjectchange((INTBIG)ni, VNODEINST);
6007 				first = FALSE;
6008 				delvalkey((INTBIG)ni, VNODEINST, var->key);
6009 				found = TRUE;
6010 				break;
6011 			}
6012 		}
6013 	}
6014 	if (!first) endobjectchange((INTBIG)ni, VNODEINST);
6015 }
6016 
6017 /*
6018  * Routine to add all inheritable export variables from export "pp" on cell "np"
6019  * to instance "ni".
6020  */
us_inheritexportattributes(PORTPROTO * pp,NODEINST * ni,NODEPROTO * np)6021 void us_inheritexportattributes(PORTPROTO *pp, NODEINST *ni, NODEPROTO *np)
6022 {
6023 	REGISTER INTSML saverot, savetrn;
6024 	REGISTER INTBIG i, dx, dy, lambda, style=0;
6025 	INTBIG x, y;
6026 	UINTBIG descript[TEXTDESCRIPTSIZE];
6027 	REGISTER VARIABLE *var, *newvar;
6028 	REGISTER CHAR *pt, *attrname;
6029 	XARRAY trans;
6030 	REGISTER void *infstr;
6031 	Q_UNUSED( np );
6032 
6033 	for(i=0; i<pp->numvar; i++)
6034 	{
6035 		var = &pp->firstvar[i];
6036 		if (TDGETINHERIT(var->textdescript) == 0) continue;
6037 		infstr = initinfstr();
6038 		addstringtoinfstr(infstr, x_("ATTRP_"));
6039 		addstringtoinfstr(infstr, pp->protoname);
6040 		addstringtoinfstr(infstr, x_("_"));
6041 		pt = makename(var->key);
6042 		addstringtoinfstr(infstr, &pt[5]);
6043 		attrname = returninfstr(infstr);
6044 
6045 		/* see if the attribute is already there */
6046 		newvar = getval((INTBIG)ni, VNODEINST, -1, attrname);
6047 		if (newvar != NOVARIABLE) continue;
6048 
6049 		/* set the attribute */
6050 		startobjectchange((INTBIG)ni, VNODEINST);
6051 		newvar = setval((INTBIG)ni, VNODEINST, attrname,
6052 			us_inheritaddress((INTBIG)pp, VPORTPROTO, var), var->type);
6053 		if (newvar != NOVARIABLE)
6054 		{
6055 			lambda = lambdaofnode(ni);
6056 			TDCOPY(descript, var->textdescript);
6057 			dx = TDGETXOFF(descript);
6058 			dx = dx * lambda / 4;
6059 			dy = TDGETYOFF(descript);
6060 			dy = dy * lambda / 4;
6061 
6062 			saverot = pp->subnodeinst->rotation;
6063 			savetrn = pp->subnodeinst->transpose;
6064 			pp->subnodeinst->rotation = pp->subnodeinst->transpose = 0;
6065 			portposition(pp->subnodeinst, pp->subportproto, &x, &y);
6066 			pp->subnodeinst->rotation = saverot;
6067 			pp->subnodeinst->transpose = savetrn;
6068 			x += dx;   y += dy;
6069 			makerot(pp->subnodeinst, trans);
6070 			xform(x, y, &x, &y, trans);
6071 			maketrans(ni, trans);
6072 			xform(x, y, &x, &y, trans);
6073 			makerot(ni, trans);
6074 			xform(x, y, &x, &y, trans);
6075 			x = x - (ni->lowx + ni->highx) / 2;
6076 			y = y - (ni->lowy + ni->highy) / 2;
6077 			switch (TDGETPOS(descript))
6078 			{
6079 				case VTPOSCENT:      style = TEXTCENT;      break;
6080 				case VTPOSBOXED:     style = TEXTBOX;       break;
6081 				case VTPOSUP:        style = TEXTBOT;       break;
6082 				case VTPOSDOWN:      style = TEXTTOP;       break;
6083 				case VTPOSLEFT:      style = TEXTRIGHT;     break;
6084 				case VTPOSRIGHT:     style = TEXTLEFT;      break;
6085 				case VTPOSUPLEFT:    style = TEXTBOTRIGHT;  break;
6086 				case VTPOSUPRIGHT:   style = TEXTBOTLEFT;   break;
6087 				case VTPOSDOWNLEFT:  style = TEXTTOPRIGHT;  break;
6088 				case VTPOSDOWNRIGHT: style = TEXTTOPLEFT;   break;
6089 			}
6090 			makerot(pp->subnodeinst, trans);
6091 			style = rotatelabel(style, TDGETROTATION(descript), trans);
6092 			switch (style)
6093 			{
6094 				case TEXTCENT:     TDSETPOS(descript, VTPOSCENT);      break;
6095 				case TEXTBOX:      TDSETPOS(descript, VTPOSBOXED);     break;
6096 				case TEXTBOT:      TDSETPOS(descript, VTPOSUP);        break;
6097 				case TEXTTOP:      TDSETPOS(descript, VTPOSDOWN);      break;
6098 				case TEXTRIGHT:    TDSETPOS(descript, VTPOSLEFT);      break;
6099 				case TEXTLEFT:     TDSETPOS(descript, VTPOSRIGHT);     break;
6100 				case TEXTBOTRIGHT: TDSETPOS(descript, VTPOSUPLEFT);    break;
6101 				case TEXTBOTLEFT:  TDSETPOS(descript, VTPOSUPRIGHT);   break;
6102 				case TEXTTOPRIGHT: TDSETPOS(descript, VTPOSDOWNLEFT);  break;
6103 				case TEXTTOPLEFT:  TDSETPOS(descript, VTPOSDOWNRIGHT); break;
6104 			}
6105 			x = x * 4 / lambda;
6106 			y = y * 4 / lambda;
6107 			TDSETOFF(descript, x, y);
6108 			TDSETINHERIT(descript, 0);
6109 			TDCOPY(newvar->textdescript, descript);
6110 		}
6111 		endobjectchange((INTBIG)ni, VNODEINST);
6112 	}
6113 }
6114 
6115 /*
6116  * Routine to add inheritable variable "var" from cell "np" to instance "ni".
6117  * If "icon" is not NONODEINST, use the position of the variable from it.
6118  */
us_inheritcellattribute(VARIABLE * var,NODEINST * ni,NODEPROTO * np,NODEINST * icon)6119 void us_inheritcellattribute(VARIABLE *var, NODEINST *ni, NODEPROTO *np, NODEINST *icon)
6120 {
6121 	REGISTER VARIABLE *newvar, *ivar, *posvar;
6122 	REGISTER INTBIG xc, yc, lambda, i, newtype;
6123 
6124 	/* see if the attribute is already there */
6125 	newvar = getvalkey((INTBIG)ni, VNODEINST, -1, var->key);
6126 	if (newvar != NOVARIABLE)
6127 	{
6128 		/* make sure visibility is OK */
6129 		if (TDGETINTERIOR(var->textdescript) == 0)
6130 		{
6131 			/* parameter should be visible: make it so */
6132 			if ((newvar->type&VDISPLAY) == 0)
6133 			{
6134 				startobjectchange((INTBIG)ni, VNODEINST);
6135 				newvar->type |= VDISPLAY;
6136 				endobjectchange((INTBIG)ni, VNODEINST);
6137 			}
6138 		} else
6139 		{
6140 			/* parameter not normally visible: make it invisible if it has the default value */
6141 			if ((newvar->type&VDISPLAY) != 0)
6142 			{
6143 				if (estrcmp(describevariable(var, -1, -1), describevariable(newvar, -1, -1)) == 0)
6144 				{
6145 					startobjectchange((INTBIG)ni, VNODEINST);
6146 					newvar->type &= ~VDISPLAY;
6147 					endobjectchange((INTBIG)ni, VNODEINST);
6148 				}
6149 			}
6150 		}
6151 		return;
6152 	}
6153 
6154 	/* determine offset of the attribute on the instance */
6155 	posvar = var;
6156 	if (icon != NONODEINST)
6157 	{
6158 		ivar = NOVARIABLE;
6159 		for(i=0; i<icon->numvar; i++)
6160 		{
6161 			ivar = &icon->firstvar[i];
6162 			if (ivar->key == var->key) break;
6163 		}
6164 		if (i < icon->numvar) posvar = ivar;
6165 	}
6166 
6167 	lambda = np->lib->lambda[np->tech->techindex];
6168 	xc = TDGETXOFF(posvar->textdescript) * lambda / 4;
6169 	if (posvar == var) xc -= (np->lowx + np->highx) / 2;
6170 	yc = TDGETYOFF(posvar->textdescript) * lambda / 4;
6171 	if (posvar == var) yc -= (np->lowy + np->highy) / 2;
6172 	lambda = lambdaofnode(ni);
6173 	xc = xc * 4 / lambda;
6174 	yc = yc * 4 / lambda;
6175 
6176 	/* set the attribute */
6177 	startobjectchange((INTBIG)ni, VNODEINST);
6178 	newtype = (var->type & ~(VDISPLAY|VTYPE|VCODE1|VCODE2)) |
6179 		(posvar->type & (VDISPLAY|VTYPE|VCODE1|VCODE2));
6180 	if (TDGETINTERIOR(var->textdescript) != 0) newtype &= ~VDISPLAY;
6181 	newvar = setvalkey((INTBIG)ni, VNODEINST, var->key,
6182 		us_inheritaddress((INTBIG)np, VNODEPROTO, posvar), newtype);
6183 	if (newvar != NOVARIABLE)
6184 	{
6185 		defaulttextsize(3, newvar->textdescript);
6186 		TDCOPY(newvar->textdescript, posvar->textdescript);
6187 		TDSETINHERIT(newvar->textdescript, 0);
6188 		TDSETOFF(newvar->textdescript, xc, yc);
6189 		if (TDGETISPARAM(var->textdescript) != 0)
6190 		{
6191 			TDSETINTERIOR(newvar->textdescript, 0);
6192 			i = TDGETDISPPART(newvar->textdescript);
6193 			if (i == VTDISPLAYNAMEVALINH || i == VTDISPLAYNAMEVALINHALL)
6194 				TDSETDISPPART(newvar->textdescript, VTDISPLAYNAMEVALUE);
6195 		}
6196 	}
6197 	endobjectchange((INTBIG)ni, VNODEINST);
6198 }
6199 
6200 /*
6201  * Routine to add a parameter attribute to node "ni".  The variable key is "key",
6202  * the new value is "addr", and the type is "type".
6203  */
us_addparameter(NODEINST * ni,INTBIG key,INTBIG addr,INTBIG type,UINTBIG * descript)6204 void us_addparameter(NODEINST *ni, INTBIG key, INTBIG addr, INTBIG type, UINTBIG *descript)
6205 {
6206 	REGISTER VARIABLE *var;
6207 	UINTBIG textdescript[TEXTDESCRIPTSIZE];
6208 
6209 	us_findexistingparameter(ni, key, textdescript);
6210 	startobjectchange((INTBIG)ni, VNODEINST);
6211 	var = setvalkey((INTBIG)ni, VNODEINST, key, addr, type|VDISPLAY);
6212 	if (var != NOVARIABLE)
6213 	{
6214 		TDCOPY(var->textdescript, textdescript);
6215 		TDSETISPARAM(var->textdescript, VTISPARAMETER);
6216 		TDSETINTERIOR(var->textdescript, 0);
6217 		TDSETDISPPART(var->textdescript, VTDISPLAYNAMEVALUE);
6218 		if (descript != 0) TDSETUNITS(var->textdescript, TDGETUNITS(descript));
6219 	}
6220 	endobjectchange((INTBIG)ni, VNODEINST);
6221 }
6222 
6223 /*
6224  * Routine to find the formal parameter that corresponds to the actual parameter
6225  * "var" on node "ni".  Returns NOVARIABLE if not a parameter or cannot be found.
6226  */
us_findparametersource(VARIABLE * var,NODEINST * ni)6227 VARIABLE *us_findparametersource(VARIABLE *var, NODEINST *ni)
6228 {
6229 	REGISTER NODEPROTO *np, *cnp;
6230 	REGISTER INTBIG k;
6231 	REGISTER VARIABLE *nvar;
6232 
6233 	/* find this parameter in the cell */
6234 	np = ni->proto;
6235 	cnp = contentsview(np);
6236 	if (cnp != NONODEPROTO) np = cnp;
6237 	for(k=0; k<np->numvar; k++)
6238 	{
6239 		nvar = &np->firstvar[k];
6240 		if (namesame(makename(var->key), makename(nvar->key)) == 0) return(nvar);
6241 	}
6242 	return(NOVARIABLE);
6243 }
6244 
6245 /*
6246  * Routine to determine the location of parameter "key" on node "ni".
6247  * Returns the variable
6248  * The parameter offset is stored in (xoff, yoff).
6249  */
us_findexistingparameter(NODEINST * ni,INTBIG key,UINTBIG * textdescript)6250 void us_findexistingparameter(NODEINST *ni, INTBIG key, UINTBIG *textdescript)
6251 {
6252 	REGISTER NODEINST *icon;
6253 	REGISTER NODEPROTO *np, *cnp;
6254 	REGISTER INTBIG i, numvar, count, xsum, yval, lowy, highy, xoff, yoff;
6255 	REGISTER VARIABLE *firstvar, *var;
6256 
6257 	/* special case if this is a parameter */
6258 	np = ni->proto;
6259 	cnp = contentsview(np);
6260 	if (cnp != NONODEPROTO)
6261 	{
6262 		/* look for an example of the icon in the contents */
6263 		for(icon = cnp->firstnodeinst; icon != NONODEINST; icon = icon->nextnodeinst)
6264 			if (icon->proto == np) break;
6265 		if (icon != NONODEINST)
6266 		{
6267 			var = NOVARIABLE;
6268 			for(i=0; i<icon->numvar; i++)
6269 			{
6270 				var = &icon->firstvar[i];
6271 				if (var->key == key) break;
6272 			}
6273 			if (i < icon->numvar)
6274 			{
6275 				TDCOPY(textdescript, var->textdescript);
6276 				return;
6277 			}
6278 		}
6279 	}
6280 
6281 	numvar = ni->numvar;
6282 	firstvar = ni->firstvar;
6283 	count = xsum = 0;
6284 	lowy = highy = 0;
6285 	for(i=0; i<numvar; i++)
6286 	{
6287 		var = &firstvar[i];
6288 		if (TDGETISPARAM(var->textdescript) == 0) continue;
6289 		xsum += TDGETXOFF(var->textdescript);
6290 		yval = TDGETYOFF(var->textdescript);
6291 		if (count == 0) lowy = highy = yval; else
6292 		{
6293 			if (yval < lowy) lowy = yval;
6294 			if (yval > highy) highy = yval;
6295 		}
6296 		count++;
6297 	}
6298 	defaulttextsize(3, textdescript);
6299 	if (count == 0) xoff = yoff = 0; else
6300 	{
6301 		xoff = xsum / count;
6302 		if (count == 1) yoff = lowy - 4; else
6303 			yoff = lowy - (highy - lowy) / (count-1);
6304 	}
6305 	TDSETOFF(textdescript, xoff, yoff);
6306 }
6307 
6308 /*
6309  * Routine to determine the location of a new parameter in cell "np".
6310  * The parameter offset is stored in (xoff, yoff).
6311  */
us_getnewparameterpos(NODEPROTO * np,INTBIG * xoff,INTBIG * yoff)6312 void us_getnewparameterpos(NODEPROTO *np, INTBIG *xoff, INTBIG *yoff)
6313 {
6314 	REGISTER INTBIG i, numvar, count, xsum, yval, lowy, highy;
6315 	REGISTER VARIABLE *firstvar, *var;
6316 
6317 	numvar = np->numvar;
6318 	firstvar = np->firstvar;
6319 	count = xsum = 0;
6320 	lowy = highy = 0;
6321 	for(i=0; i<numvar; i++)
6322 	{
6323 		var = &firstvar[i];
6324 		if (TDGETISPARAM(var->textdescript) == 0) continue;
6325 		xsum += TDGETXOFF(var->textdescript);
6326 		yval = TDGETYOFF(var->textdescript);
6327 		if (count == 0) lowy = highy = yval; else
6328 		{
6329 			if (yval < lowy) lowy = yval;
6330 			if (yval > highy) highy = yval;
6331 		}
6332 		count++;
6333 	}
6334 	if (count == 0) *xoff = *yoff = 0; else
6335 	{
6336 		*xoff = xsum / count;
6337 		if (count == 1) *yoff = lowy - 4; else
6338 			*yoff = lowy - (highy - lowy) / (count-1);
6339 	}
6340 }
6341 
6342 static INTBIG      us_coveragepolyscrunched;
6343 static INTBIG      us_coveragejobsize;
6344 static INTBIG     *us_coveragelayers;
6345 static INTBIG      us_coveragelayercount;
6346 static TECHNOLOGY *us_coveragetech;
6347 static void       *us_coveragedialog;
6348 static float      *us_coveragearea;
6349 
6350 void us_gathercoveragegeometry(NODEPROTO *np, XARRAY trans, void **merge);
6351 void us_getcoveragegeometry(INTBIG layer, TECHNOLOGY *tech, INTBIG *x, INTBIG *y, INTBIG count);
6352 
6353 /*
6354  * Routine to compute the percentage of coverage for the polysilicon and metal layers.
6355  */
us_showlayercoverage(NODEPROTO * cell)6356 void us_showlayercoverage(NODEPROTO *cell)
6357 {
6358 	REGISTER LIBRARY *lib;
6359 	REGISTER NODEPROTO *np;
6360 	REGISTER VARIABLE *var;
6361 	REGISTER INTBIG i, fun, lambda, percentcoverage;
6362 	REGISTER float totalarea, coverageratio;
6363 	REGISTER void **submerge, **polymerge;
6364 
6365 	/* initialize for analysis */
6366 	lambda = lambdaofcell(cell);
6367 	us_coveragetech = cell->tech;
6368 
6369 	/* determine which layers are being collected */
6370 	us_coveragelayercount = 0;
6371 	for(i=0; i<us_coveragetech->layercount; i++)
6372 	{
6373 		fun = layerfunction(us_coveragetech, i);
6374 		if ((fun&LFPSEUDO) != 0) continue;
6375 		if (!layerismetal(fun) && !layerispoly(fun)) continue;
6376 		us_coveragelayercount++;
6377 	}
6378 	if (us_coveragelayercount == 0)
6379 	{
6380 		ttyputerr(_("No metal or polysilicon layers in this technology"));
6381 		return;
6382 	}
6383 	us_coveragelayers = (INTBIG *)emalloc(us_coveragelayercount * SIZEOFINTBIG, us_tool->cluster);
6384 	if (us_coveragelayers == 0) return;
6385 	us_coveragelayercount = 0;
6386 	for(i=0; i<us_coveragetech->layercount; i++)
6387 	{
6388 		fun = layerfunction(us_coveragetech, i);
6389 		if ((fun&LFPSEUDO) != 0) continue;
6390 		if (!layerismetal(fun) && !layerispoly(fun)) continue;
6391 		us_coveragelayers[us_coveragelayercount++] = i;
6392 	}
6393 
6394 	/* show the progress dialog */
6395 	us_coveragedialog = DiaInitProgress(_("Merging geometry..."), 0);
6396 	if (us_coveragedialog == 0)
6397 	{
6398 		termerrorlogging(TRUE);
6399 		return;
6400 	}
6401 	DiaSetProgress(us_coveragedialog, 0, 1);
6402 
6403 	/* reset merging information */
6404 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
6405 	{
6406 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
6407 		{
6408 			np->temp1 = 0;
6409 
6410 			/* if this cell has parameters, force its polygons to be examined for every instance */
6411 			for(i=0; i<np->numvar; i++)
6412 			{
6413 				var = &np->firstvar[i];
6414 				if (TDGETISPARAM(var->textdescript) != 0) break;
6415 			}
6416 			if (i < np->numvar) np->temp1 = -1;
6417 		}
6418 	}
6419 
6420 	/* run through the work and count the number of polygons */
6421 	us_coveragepolyscrunched = 0;
6422 	us_gathercoveragegeometry(cell, el_matid, 0);
6423 	us_coveragejobsize = us_coveragepolyscrunched;
6424 
6425 	/* now gather all of the geometry into the polygon merging system */
6426 	polymerge = (void **)emalloc(us_coveragelayercount * (sizeof (void *)), us_tool->cluster);
6427 	if (polymerge == 0) return;
6428 	for(i=0; i<us_coveragelayercount; i++)
6429 		polymerge[i] = mergenew(us_tool->cluster);
6430 	us_coveragepolyscrunched = 0;
6431 	us_gathercoveragegeometry(cell, el_matid, polymerge);
6432 
6433 	/* extract the information */
6434 	us_coveragearea = (float *)emalloc(us_coveragelayercount * (sizeof (float)), us_tool->cluster);
6435 	if (us_coveragearea == 0) return;
6436 	for(i=0; i<us_coveragelayercount; i++)
6437 	{
6438 		us_coveragearea[i] = 0.0;
6439 		mergeextract(polymerge[i], us_getcoveragegeometry);
6440 	}
6441 
6442 	/* show the results */
6443 	totalarea = (float)(cell->highx - cell->lowx);
6444 	totalarea *= (float)(cell->highy - cell->lowy);
6445 	ttyputmsg(x_("Cell is %g square lambda"), totalarea/(float)lambda/(float)lambda);
6446 	for(i=0; i<us_coveragelayercount; i++)
6447 	{
6448 		if (us_coveragearea[i] == 0.0) continue;
6449 		if (totalarea == 0.0) coverageratio = 0.0; else
6450 			coverageratio = us_coveragearea[i] / totalarea;
6451 		percentcoverage = (INTBIG)(coverageratio * 100.0 + 0.5);
6452 
6453 		ttyputmsg(x_("Layer %s covers %g square lambda (%ld%%)"),
6454 			layername(us_coveragetech, us_coveragelayers[i]),
6455 			us_coveragearea[i]/(float)lambda/(float)lambda, percentcoverage);
6456 	}
6457 
6458 	/* delete merge information */
6459 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
6460 	{
6461 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
6462 		{
6463 			if (np->temp1 == 0 || np->temp1 == -1) continue;
6464 			submerge = (void **)np->temp1;
6465 			for(i=0; i<us_coveragelayercount; i++)
6466 				mergedelete(submerge[i]);
6467 			efree((CHAR *)submerge);
6468 		}
6469 	}
6470 	for(i=0; i<us_coveragelayercount; i++)
6471 		mergedelete(polymerge[i]);
6472 	efree((CHAR *)polymerge);
6473 
6474 	/* clean up */
6475 	efree((CHAR *)us_coveragelayers);
6476 	DiaDoneProgress(us_coveragedialog);
6477 }
6478 
6479 /*
6480  * Recursive helper routine to gather all well areas in cell "np"
6481  * (transformed by "trans").  If "merge" is nonzero, merge geometry into it.
6482  * If "gathernet" is true, follow networks, looking for special primitives and nets.
6483  */
us_gathercoveragegeometry(NODEPROTO * np,XARRAY trans,void ** merge)6484 void us_gathercoveragegeometry(NODEPROTO *np, XARRAY trans, void **merge)
6485 {
6486 	REGISTER NODEINST *ni;
6487 	REGISTER NODEPROTO *subnp;
6488 	REGISTER ARCINST *ai;
6489 	REGISTER GEOM *geom;
6490 	REGISTER INTBIG i, j, tot, updatepct, sea;
6491 	REGISTER void **submerge;
6492 	static INTBIG checkstop = 0;
6493 	XARRAY localrot, localtran, final, subrot;
6494 	static POLYGON *poly = NOPOLYGON;
6495 
6496 	(void)needstaticpolygon(&poly, 4, us_tool->cluster);
6497 
6498 	/* do a spatial search so that polygon merging grows sensibly */
6499 	updatepct = 0;
6500 	sea = initsearch(np->lowx, np->highx, np->lowy, np->highy, np);
6501 	for(;;)
6502 	{
6503 		geom = nextobject(sea);
6504 		if (geom == NOGEOM) break;
6505 		checkstop++;
6506 		if ((checkstop%10) == 0)
6507 		{
6508 			if (stopping(STOPREASONERC)) { termsearch(sea);   return; }
6509 		}
6510 		if (!geom->entryisnode)
6511 		{
6512 			ai = geom->entryaddr.ai;
6513 			tot = arcpolys(ai, NOWINDOWPART);
6514 			us_coveragepolyscrunched += tot;
6515 			if (merge != 0)
6516 			{
6517 				if (us_coveragejobsize > 0)
6518 				{
6519 					if (updatepct++ > 25)
6520 					{
6521 						DiaSetProgress(us_coveragedialog, us_coveragepolyscrunched, us_coveragejobsize);
6522 						updatepct = 0;
6523 					}
6524 				}
6525 				for(i=0; i<tot; i++)
6526 				{
6527 					shapearcpoly(ai, i, poly);
6528 					if (poly->tech != us_coveragetech) continue;
6529 					for(j=0; j<us_coveragelayercount; j++)
6530 					{
6531 						if (poly->layer == us_coveragelayers[j])
6532 						{
6533 							xformpoly(poly, trans);
6534 							mergeaddpolygon(merge[j], poly->layer, poly->tech, poly);
6535 						}
6536 					}
6537 				}
6538 			}
6539 			continue;
6540 		}
6541 
6542 		ni = geom->entryaddr.ni;
6543 		subnp = ni->proto;
6544 		makerot(ni, localrot);
6545 		transmult(localrot, trans, final);
6546 		if (subnp->primindex == 0)
6547 		{
6548 			maketrans(ni, localtran);
6549 			transmult(localtran, final, subrot);
6550 
6551 			if (merge != 0)
6552 			{
6553 				if (subnp->temp1 == -1)
6554 				{
6555 					/* parameterized cell: examine the contents recursively */
6556 					us_gathercoveragegeometry(subnp, subrot, merge);
6557 				} else
6558 				{
6559 					submerge = (void **)subnp->temp1;
6560 					if (submerge == 0)
6561 					{
6562 						/* gather the subcell's merged information */
6563 						submerge = (void **)emalloc(us_coveragelayercount * (sizeof (void *)),
6564 							us_tool->cluster);
6565 						if (submerge == 0) return;
6566 						for(i=0; i<us_coveragelayercount; i++)
6567 							submerge[i] = mergenew(us_tool->cluster);
6568 						us_gathercoveragegeometry(subnp, el_matid, submerge);
6569 						subnp->temp1 = (INTBIG)submerge;
6570 					}
6571 
6572 					for(i=0; i<us_coveragelayercount; i++)
6573 						mergeaddmerge(merge[i], submerge[i], subrot);
6574 				}
6575 			}
6576 		} else
6577 		{
6578 			tot = nodepolys(ni, 0, NOWINDOWPART);
6579 			us_coveragepolyscrunched += tot;
6580 			if (merge != 0)
6581 			{
6582 				if (us_coveragejobsize > 0)
6583 				{
6584 					if (updatepct++ > 25)
6585 					{
6586 						DiaSetProgress(us_coveragedialog, us_coveragepolyscrunched, us_coveragejobsize);
6587 						updatepct = 0;
6588 					}
6589 				}
6590 				for(i=0; i<tot; i++)
6591 				{
6592 					shapenodepoly(ni, i, poly);
6593 					if (poly->tech != us_coveragetech) continue;
6594 					for(j=0; j<us_coveragelayercount; j++)
6595 					{
6596 						if (poly->layer == us_coveragelayers[j])
6597 						{
6598 							xformpoly(poly, final);
6599 							mergeaddpolygon(merge[j], poly->layer, poly->tech, poly);
6600 						}
6601 					}
6602 				}
6603 			}
6604 		}
6605 	}
6606 }
6607 
6608 /*
6609  * Coroutine of the polygon merging package that is given a merged polygon with
6610  * "count" points in (x,y) with area "area".
6611  */
us_getcoveragegeometry(INTBIG layer,TECHNOLOGY * tech,INTBIG * x,INTBIG * y,INTBIG count)6612 void us_getcoveragegeometry(INTBIG layer, TECHNOLOGY *tech, INTBIG *x, INTBIG *y, INTBIG count)
6613 {
6614 	REGISTER INTBIG i;
6615 	Q_UNUSED( tech );
6616 
6617 	for(i=0; i<us_coveragelayercount; i++)
6618 	{
6619 		if (layer != us_coveragelayers[i]) continue;
6620 		us_coveragearea[i] += areapoints(count, x, y);
6621 		break;
6622 	}
6623 }
6624