1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: usrmenu.c
6  * User interface tool: menu display 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 "usr.h"
35 #include "efunction.h"
36 #include "tecart.h"
37 #include "tecschem.h"
38 
39 extern GRAPHICS us_ebox, us_nmbox, us_box;
40 
41 /* for drawing text (nothing changes) */
42 GRAPHICS us_menufigs = {LAYERA, MENGLY, SOLIDC, SOLIDC,
43 	{0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF}, NOVARIABLE, 0};
44 
45 /* for drawing menu shadows */
46 static GRAPHICS us_shadowdesc = {LAYERA, MENGLY, PATTERNED, PATTERNED,
47 	{0x5555,0xAAAA,0x5555,0xAAAA,0x5555,0xAAAA,0x5555,0xAAAA,
48 	0x5555,0xAAAA,0x5555,0xAAAA,0x5555,0xAAAA,0x5555,0xAAAA}, NOVARIABLE, 0};
49 
50 /* for drawing menu text (nothing changes) */
51 GRAPHICS us_menutext = {LAYERA, MENTXT, SOLIDC, SOLIDC,
52 	{0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF}, NOVARIABLE, 0};
53 
54 #define MENUARCLENGTH   8	/* length of arcs in menu entries */
55 #define MENUSHADOW      2	/* size of shadow around popup menus */
56 
57 /* prototypes for local routines */
58 static INTBIG us_groupfunction(NODEPROTO*);
59 #ifndef USEQT
60 static void us_pmfirst(void);
61 static void us_pmdone(void);
62 static BOOLEAN us_pmeachchar(INTBIG, INTBIG, INTSML);
63 static BOOLEAN us_pmeachdown(INTBIG, INTBIG);
64 static void us_pminvertline(void);
65 #endif
66 
67 /****************************** MENU DISPLAY ******************************/
68 
69 /*
70  * routine to draw the entire display screen.  If "scaletofit" is positive,
71  * each window will be rescaled to fit properly.  If "scaletofit" is negative,
72  * each window will be adjusted to fit (but not shrunk back from edges).
73  */
us_drawmenu(INTBIG scaletofit,WINDOWFRAME * mw)74 void us_drawmenu(INTBIG scaletofit, WINDOWFRAME *mw)
75 {
76 	REGISTER INTBIG x, y;
77 	BOOLEAN placemenu;
78 	REGISTER WINDOWPART *w;
79 	REGISTER VARIABLE *var;
80 
81 	/* re-draw windows */
82 	if (scaletofit != 0) placemenu = TRUE; else
83 		placemenu = FALSE;
84 	us_windowfit(mw, placemenu, scaletofit);
85 	us_erasescreen(mw);
86 	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
87 	{
88 		/* draw window border and its contents */
89 		if (mw != NOWINDOWFRAME && mw != w->frame) continue;
90 		us_drawwindow(w, el_colwinbor);
91 		if (w->redisphandler != 0) (*w->redisphandler)(w);
92 	}
93 	if (el_curwindowpart != NOWINDOWPART) us_highlightwindow(el_curwindowpart, TRUE);
94 
95 	/* draw menus if requested */
96 	if ((us_tool->toolstate&MENUON) != 0)
97 	{
98 #ifdef USEQT
99 		if (us_menuframe == NOWINDOWFRAME || (mw != NOWINDOWFRAME && us_menuframe != mw)) return;
100 #else
101 		if (mw != NOWINDOWFRAME && us_menuframe != NOWINDOWFRAME &&
102 			us_menuframe != mw) return;
103 #endif
104 
105 		/* draw the menu entries */
106 		var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_binding_menu_key);
107 		if (var == NOVARIABLE) return;
108 
109 		if (us_menupos <= 1)
110 		{
111 			/* horizontal menus */
112 			for(y=0; y<us_menuy; y++) for(x=0; x<us_menux; x++)
113 				us_drawmenuentry(x, y, ((CHAR **)var->addr)[y * us_menux + x]);
114 		} else
115 		{
116 			/* vertical menus */
117 			for(x=0; x<us_menux; x++) for(y=0; y<us_menuy; y++)
118 				us_drawmenuentry(x, y, ((CHAR **)var->addr)[x * us_menuy + y]);
119 		}
120 
121 		/* highlight any special entry (shouldn't update status display too) */
122 		us_menuhnx = us_menuhax = -1;
123 		us_showcurrentnodeproto();
124 		us_showcurrentarcproto();
125 	}
126 }
127 
us_drawmenuentry(INTBIG x,INTBIG y,CHAR * str)128 void us_drawmenuentry(INTBIG x, INTBIG y, CHAR *str)
129 {
130 	REGISTER INTBIG i, scaleu, scaled, x1, y1, lowx, lowy, highx, highy, xl,
131 		yl, xh, yh, ofun, fun, lambda, bits, wid, twid;
132 	REGISTER NODEINST *ni;
133 	NODEINST node;
134 	REGISTER ARCINST *ai;
135 	ARCINST arc;
136 	REGISTER NODEPROTO *tnp;
137 	REGISTER ARCPROTO *tap;
138 	REGISTER TECHNOLOGY *tech;
139 	VARIABLE colorvar[2];
140 	WINDOWPART ww;
141 	XARRAY trans;
142 	CHAR line[100];
143 	INTBIG swid, shei, pangle;
144 	INTBIG pxs, pys;
145 	static POLYGON *poly = NOPOLYGON;
146 	WINDOWFRAME *menuframe;
147 	COMMANDBINDING commandbinding;
148 
149 	/* get polygon */
150 	(void)needstaticpolygon(&poly, 4, us_tool->cluster);
151 
152 	/* setup the fake variable for coloring artwork objects in the menu */
153 	colorvar[0].key = art_colorkey;
154 	colorvar[0].type = VINTEGER;
155 	colorvar[0].addr = el_colmengly;
156 
157 	if (stopping(STOPREASONDISPLAY)) return;
158 	if ((us_tool->toolstate&MENUON) == 0) return;
159 
160 	/* parse the menu entry string */
161 	us_parsebinding(str, &commandbinding);
162 
163 	/* set drawing bounds to address the world */
164 	if (us_menuframe != NOWINDOWFRAME) menuframe = us_menuframe; else
165 		menuframe = el_curwindowpart->frame;
166 	getwindowframesize(menuframe, &swid, &shei);
167 	ww.screenlx = ww.screenly = ww.uselx = ww.usely = 0;
168 	ww.screenhx = ww.usehx = swid-1;   ww.screenhy = ww.usehy = shei-1;
169 	ww.frame = menuframe;
170 	ww.curnodeproto = NONODEPROTO;
171 	ww.state = DISPWINDOW;
172 	computewindowscale(&ww);
173 
174 	/* draw border around menu entry */
175 	xl = x * us_menuxsz + us_menulx;   xh = xl + us_menuxsz;
176 	yl = y * us_menuysz + us_menuly;   yh = yl + us_menuysz;
177 	maketruerectpoly(xl, xh, yl, yh, poly);
178 	poly->desc = &us_ebox;
179 	poly->style = FILLEDRECT;
180 	us_showpoly(poly, &ww);
181 
182 	if (commandbinding.nodeglyph != NONODEPROTO || commandbinding.arcglyph != NOARCPROTO)
183 	{
184 		/* graphics will be drawn: draw an outline of background color */
185 		poly->desc = &us_box;
186 		us_box.col = commandbinding.backgroundcolor;
187 		poly->style = CLOSEDRECT;
188 		if (commandbinding.arcglyph != NOARCPROTO &&
189 			commandbinding.arcglyph == us_curarcproto)
190 		{
191 			maketruerectpoly(xl+2, xh-2, yl+2, yh-2, poly);
192 			us_showpoly(poly, &ww);
193 			maketruerectpoly(xl+3, xh-3, yl+3, yh-3, poly);
194 			us_showpoly(poly, &ww);
195 			maketruerectpoly(xl+4, xh-4, yl+4, yh-4, poly);
196 			us_showpoly(poly, &ww);
197 		}
198 		maketruerectpoly(xl+1, xh-1, yl+1, yh-1, poly);
199 	} else
200 	{
201 		/* text will be drawn: fill the entry with background color */
202 		poly->desc = &us_box;
203 		us_box.col = commandbinding.backgroundcolor;
204 		poly->style = FILLEDRECT;
205 	}
206 	us_showpoly(poly, &ww);
207 
208 	maketruerectpoly(xl, xh, yl, yh, poly);
209 	poly->desc = &us_nmbox;
210 	us_nmbox.col = el_colmenbor;
211 	poly->style = CLOSEDRECT;
212 	us_showpoly(poly, &ww);
213 
214 	/* stop now if there is no entry in this menu square */
215 	if (*commandbinding.command == 0)
216 	{
217 		us_freebindingparse(&commandbinding);
218 		return;
219 	}
220 
221 	/* draw the internal glyph if there is one */
222 	if (commandbinding.nodeglyph != NONODEPROTO || commandbinding.arcglyph != NOARCPROTO)
223 	{
224 		if (commandbinding.nodeglyph != NONODEPROTO)
225 		{
226 			/* see if an alternate icon should be displayed */
227 			tnp = iconview(commandbinding.nodeglyph);
228 			if (tnp != NONODEPROTO) commandbinding.nodeglyph = tnp;
229 
230 			/* build the dummy nodeinst for menu display */
231 			tech = commandbinding.nodeglyph->tech;
232 			if (tech == NOTECHNOLOGY) tech = el_curtech;
233 			lambda = el_curlib->lambda[tech->techindex];
234 			ni = &node;   initdummynode(ni);
235 			ni->proto = commandbinding.nodeglyph;
236 			pangle = us_getplacementangle(ni->proto);
237 			ni->rotation = (INTSML)(pangle % 3600);
238 			ni->transpose = (INTSML)(pangle / 3600);
239 			defaultnodesize(commandbinding.nodeglyph, &pxs, &pys);
240 			ni->userbits |= NEXPAND;
241 			lowx = ni->lowx  = -pxs / 2;
242 			highx = ni->highx = ni->lowx + pxs;
243 			lowy = ni->lowy  = -pys / 2;
244 			highy = ni->highy = ni->lowy + pys;
245 
246 			/*
247 			 * Special hack: the Schematic Transistors do not fill their bounding box
248 			 * but stops 1 lambda short of the top.  To center it in the menu square,
249 			 * that amount is rotated and the node is shifted thusly.
250 			 */
251 			if (ni->proto == sch_transistorprim || ni->proto == sch_transistor4prim)
252 			{
253 				makerot(ni, trans);
254 				xform(0, el_curlib->lambda[sch_tech->techindex]/2, &pxs, &pys, trans);
255 				ni->lowx += pxs;
256 				ni->highx += pxs;
257 				ni->lowy += pys;
258 				ni->highy += pys;
259 			}
260 
261 			/* fake the variable "ART_color" to be the glyph color */
262 			ni->firstvar = &colorvar[0];
263 			ni->numvar = 1;
264 			if (commandbinding.menumessage != 0)
265 			{
266 				/* add node specialization bits */
267 				bits = myatoi(commandbinding.menumessage);
268 				ni->userbits |= bits;
269 			}
270 		}
271 		if (commandbinding.arcglyph != NOARCPROTO)
272 		{
273 			/* build dummy display arcinst for menu */
274 			tech = commandbinding.arcglyph->tech;
275 			lambda = el_curlib->lambda[tech->techindex];
276 			ai = &arc;   initdummyarc(ai);
277 			ai->proto = commandbinding.arcglyph;
278 			ai->length = MENUARCLENGTH * lambda;
279 			ai->width = defaultarcwidth(commandbinding.arcglyph);
280 			if (ai->width > ai->length)
281 				ai->length = ai->width * 2;
282 			ai->end[0].xpos = -ai->length/2;
283 			ai->end[0].ypos = ai->end[1].ypos = 0;
284 			ai->end[1].xpos = ai->length/2;
285 			lowx = -(ai->width+ai->length)/2;  highx = -lowx;
286 			lowy = -ai->width / 2;             highy = -lowy;
287 
288 			/* fake the variable "ART_color" to be the glyph color */
289 			ai->firstvar = &colorvar[0];
290 			ai->numvar = 1;
291 		}
292 
293 		/* temporarily make everything visible */
294 		for(i=0; i<tech->layercount; i++)
295 		{
296 			tech->layers[i]->colstyle &= ~INVTEMP;
297 			if ((tech->layers[i]->colstyle&INVISIBLE) == 0) continue;
298 			tech->layers[i]->colstyle |= INVTEMP;
299 			tech->layers[i]->colstyle &= ~INVISIBLE;
300 		}
301 
302 		/* compute the scale for graphics */
303 		scaleu = scaled = maxi(highx-lowx, highy-lowy);
304 		if (scaleu == 0) scaleu = scaled = 1;
305 #define INSET 2
306 		if (commandbinding.nodeglyph != NONODEPROTO)
307 		{
308 			/* make all nodes of the same class be the same scale */
309 			ofun = us_groupfunction(commandbinding.nodeglyph);
310 			for(tnp = tech->firstnodeproto; tnp != NONODEPROTO; tnp = tnp->nextnodeproto)
311 			{
312 				fun = us_groupfunction(tnp);
313 				if (fun != ofun) continue;
314 				defaultnodesize(tnp, &pxs, &pys);
315 				scaled = maxi(scaled, maxi(pxs, pys));
316 			}
317 
318 			/* for pins, make them the same scale as the arcs */
319 			if (ofun == NPPIN)
320 			{
321 				for(tap = tech->firstarcproto; tap != NOARCPROTO; tap = tap->nextarcproto)
322 					scaled = maxi(scaled, MENUARCLENGTH*lambda + defaultarcwidth(tap));
323 			}
324 		} else if (commandbinding.arcglyph != NOARCPROTO)
325 		{
326 			/* make all arcs be the same scale */
327 			twid = defaultarcwidth(commandbinding.arcglyph);
328 			for(tap = tech->firstarcproto; tap != NOARCPROTO; tap = tap->nextarcproto)
329 			{
330 				wid = defaultarcwidth(tap);
331 				if (twid <= MENUARCLENGTH*lambda && wid <= MENUARCLENGTH*lambda)
332 					scaled = maxi(scaled, MENUARCLENGTH*lambda + wid);
333 				if (twid > MENUARCLENGTH*lambda && wid > MENUARCLENGTH*lambda)
334 					scaled = maxi(scaled, wid*3);
335 			}
336 
337 			/* and make them at least the same scale as the pins */
338 			for(tnp = tech->firstnodeproto; tnp != NONODEPROTO; tnp = tnp->nextnodeproto)
339 			{
340 				fun = us_groupfunction(tnp);
341 				if (fun != NPPIN) continue;
342 				scaled = maxi(scaled, maxi(tnp->highx-tnp->lowx, tnp->highy-tnp->lowy));
343 			}
344 		}
345 
346 		/* modify the window to transform nodeinst into the menu space */
347 		ww.uselx = xl+INSET;   ww.usehx = xh-INSET;
348 		ww.usely = yl+INSET;   ww.usehy = yh-INSET;
349 		ww.screenlx = muldiv(lowx, scaled, scaleu) - 1;
350 		ww.screenly = muldiv(lowy, scaled, scaleu) - 1;
351 		ww.screenhx = muldiv(highx, scaled, scaleu) + 1;
352 		ww.screenhy = muldiv(highy, scaled, scaleu) + 1;
353 		x1 = ww.screenhx - ww.screenlx;
354 		y1 = ww.screenhy - ww.screenly;
355 		if (x1 > y1)
356 		{
357 			ww.screenly -= (x1-y1) / 2;
358 			ww.screenhy += (x1-y1) / 2;
359 		} else
360 		{
361 			ww.screenlx -= (y1-x1) / 2;
362 			ww.screenhx += (y1-x1) / 2;
363 		}
364 		computewindowscale(&ww);
365 
366 		/* draw the object into the menu */
367 		if (commandbinding.nodeglyph != NONODEPROTO)
368 		{
369 			begintraversehierarchy();
370 			makerot(ni, trans);
371 			if (us_drawnodeinst(ni, LAYERA, trans, 1, &ww) == 2)
372 				(void)us_drawnodeinst(ni, LAYERA, trans, 2, &ww);
373 			endtraversehierarchy();
374 		} else if (commandbinding.arcglyph != NOARCPROTO)
375 		{
376 			if (us_drawarcinst(ai, LAYERA, el_matid, 1, &ww) == 2)
377 				(void)us_drawarcinst(ai, LAYERA, el_matid, 2, &ww);
378 		}
379 
380 		/* restore visibilities */
381 		for(i=0; i<tech->layercount; i++)
382 			if ((tech->layers[i]->colstyle&INVTEMP) != 0)
383 				tech->layers[i]->colstyle |= INVISIBLE;
384 	} else
385 	{
386 		/* no glyph: use text */
387 		makerectpoly(xl,xh, yl,yh, poly);
388 		poly->desc = &us_menutext;
389 		us_menutext.col = el_colmentxt;
390 		poly->style = TEXTBOX;
391 		TDCLEAR(poly->textdescript);
392 		TDSETSIZE(poly->textdescript, commandbinding.menumessagesize);
393 		poly->tech = el_curtech;
394 		if (commandbinding.menumessage != 0)
395 			poly->string = commandbinding.menumessage; else
396 		{
397 			(void)estrncpy(line, commandbinding.command, 99);
398 			poly->string = line;
399 			if (islower(line[0])) line[0] = toupper(line[0]);
400 		}
401 		us_showpoly(poly, &ww);
402 	}
403 	us_freebindingparse(&commandbinding);
404 }
405 
406 /*
407  * routine to return the function of nodeproto "np", grouped according to its
408  * general function (i.e. all transistors return the same value).
409  */
us_groupfunction(NODEPROTO * np)410 INTBIG us_groupfunction(NODEPROTO *np)
411 {
412 	REGISTER INTBIG fun;
413 
414 	fun = (np->userbits&NFUNCTION) >> NFUNCTIONSH;
415 	switch (fun)
416 	{
417 		/* combine all transistors */
418 		case NPTRANMOS:   case NPTRA4NMOS:
419 		case NPTRADMOS:   case NPTRA4DMOS:
420 		case NPTRAPMOS:   case NPTRA4PMOS:
421 		case NPTRANPN:    case NPTRA4NPN:
422 		case NPTRAPNP:    case NPTRA4PNP:
423 		case NPTRANJFET:  case NPTRA4NJFET:
424 		case NPTRAPJFET:  case NPTRA4PJFET:
425 		case NPTRADMES:   case NPTRA4DMES:
426 		case NPTRAEMES:   case NPTRA4EMES:
427 		case NPTRANSREF:   fun = NPTRANS;       break;
428 
429 		/* combine all analog parts */
430 		case NPRESIST:
431 		case NPCAPAC:
432 		case NPECAPAC:
433 		case NPDIODE:
434 		case NPDIODEZ:     fun = NPINDUCT;      break;
435 
436 		/* combine all two-port parts */
437 		case NPCCVS:
438 		case NPCCCS:
439 		case NPVCVS:
440 		case NPVCCS:
441 		case NPTLINE:      fun = NPTLINE;       break;
442 
443 		/* combine all transistor parts */
444 		case NPBASE:
445 		case NPEMIT:       fun = NPCOLLECT;     break;
446 
447 		/* combine all logic parts */
448 		case NPBUFFER:
449 		case NPGATEAND:
450 		case NPGATEOR:
451 		case NPMUX:        fun = NPGATEXOR;     break;
452 
453 		/* combine power and ground */
454 		case NPCONPOWER:   fun = NPCONGROUND;   break;
455 
456 		/* combine all SPICE parts */
457 		case NPMETER:      fun = NPSOURCE;      break;
458 
459 		/* combine all bias parts */
460 		case NPSUBSTRATE:  fun = NPWELL;        break;
461 	}
462 	return(fun);
463 }
464 
465 /******************** MENU COMPUTATION ********************/
466 
467 /*
468  * routine to change the "getproto" commands in the menu to
469  * properly reflect the primitive nodes and arcs in the current
470  * technology
471  */
us_setmenunodearcs(void)472 void us_setmenunodearcs(void)
473 {
474 	REGISTER ARCPROTO *ap;
475 	REGISTER NODEPROTO *np, *pinnp;
476 	REGISTER INTBIG didinst, i;
477 	REGISTER BOOLEAN isgetproto;
478 	REGISTER VARIABLE *var;
479 	REGISTER void *infstr;
480 	CHAR si[20], sj[20], *par[MAXPARS+6];
481 	COMMANDBINDING commandbinding;
482 	extern COMCOMP us_userp;
483 
484 	/* bind menu entries for arcs */
485 	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_binding_menu_key);
486 	if (var == NOVARIABLE) return;
487 	ap = el_curtech->firstarcproto;
488 	np = el_curtech->firstnodeproto;
489 	didinst = 0;
490 	for(i=0; i<us_menuy*us_menux; i++)
491 	{
492 		us_parsebinding(((CHAR **)var->addr)[i], &commandbinding);
493 
494 		/* make sure the menu item is a "getproto" command */
495 		isgetproto = FALSE;
496 		if (commandbinding.command != 0)
497 		{
498 			if (namesamen(commandbinding.command, x_("getproto"), 8) == 0 ||
499 				namesamen(commandbinding.command, x_("pmgetproto"), 10) == 0 ||
500 				namesamen(commandbinding.command, x_("rem getproto"), 12) == 0 ||
501 				namesamen(commandbinding.command, x_("rem pmgetproto"), 14) == 0)
502 			{
503 				isgetproto = TRUE;
504 			}
505 		}
506 		if (isgetproto)
507 		{
508 			/* make sure the command is getting a primitive in a technology */
509 			if (commandbinding.nodeglyph == NONODEPROTO ||
510 				commandbinding.nodeglyph->primindex != 0)
511 			{
512 				/* load up the strings for the menu indices */
513 				if (us_menupos <= 1)
514 				{
515 					(void)esnprintf(si, 20, x_("%ld"), i%us_menux);
516 					(void)esnprintf(sj, 20, x_("%ld"), i/us_menux);
517 				} else
518 				{
519 					(void)esnprintf(si, 20, x_("%ld"), i/us_menuy);
520 					(void)esnprintf(sj, 20, x_("%ld"), i%us_menuy);
521 				}
522 				par[0] = x_("set");          par[1] = x_("menu");
523 				par[2] = x_("background");   par[3] = x_("blue");
524 				par[4] = sj;             par[5] = si;
525 				par[6] = x_("getproto");
526 
527 				/* found a "getproto" command: now change it */
528 				if (ap != NOARCPROTO)
529 				{
530 					for(;;)
531 					{
532 						if (ap == NOARCPROTO) break;
533 						if ((ap->userbits&ANOTUSED) == 0)
534 						{
535 							pinnp = getpinproto(ap);
536 							if (pinnp == NONODEPROTO) break;
537 							if ((pinnp->userbits & NNOTUSED) == 0) break;
538 						}
539 						ap = ap->nextarcproto;
540 					}
541 					if (ap != NOARCPROTO)
542 					{
543 						par[3] = x_("red");
544 						par[7] = x_("arc");   par[8] = describearcproto(ap);
545 						us_bind(9, par);
546 						ap = ap->nextarcproto;
547 						us_freebindingparse(&commandbinding);
548 						continue;
549 					}
550 				}
551 
552 				/* insert the instance popup between the arcs and the nodes */
553 				if (didinst == 0)
554 				{
555 					didinst = 1;
556 					if (parse(x_("pmgetproto"), &us_userp, FALSE) >= 0)
557 					{
558 						par[2] = x_("message");
559 						par[3] = _("Inst.");
560 						par[6] = x_("pmgetproto");
561 						par[7] = x_("instance");
562 						us_bind(8, par);
563 						us_freebindingparse(&commandbinding);
564 						continue;
565 					}
566 				}
567 
568 				/* done with arcs, do the nodes */
569 				if (np != NONODEPROTO)
570 				{
571 					while (np != NONODEPROTO && (np->userbits & NNOTUSED) != 0)
572 						np = np->nextnodeproto;
573 					if (np != NONODEPROTO &&
574 						((np->userbits&NFUNCTION)>>NFUNCTIONSH) != NPNODE)
575 					{
576 						par[4] = x_("glyph");
577 						par[5] = describenodeproto(np);
578 						par[6] = sj;             par[7] = si;
579 						par[8] = x_("rem");
580 						par[9] = x_("getproto");
581 						par[10] = x_("node");
582 						infstr = initinfstr();
583 						formatinfstr(infstr, "%s:%s", np->tech->techname, nldescribenodeproto(np));
584 						par[11] = returninfstr(infstr);
585 						us_bind(12, par);
586 						np = np->nextnodeproto;
587 						us_freebindingparse(&commandbinding);
588 						continue;
589 					}
590 				}
591 
592 				/* no nodes or arcs left: make it a blank entry */
593 				par[6] = x_("-");
594 				us_bind(7, par);
595 			}
596 		}
597 		us_freebindingparse(&commandbinding);
598 	}
599 }
600 
601 /*
602  * routine to set the number of entries in the menu to "x" by "y", and to
603  * place the menu on the proper side of the screen according to "position":
604  * 0: top,  1: bottom,  2: left,  3: right.  If "allgetproto" is true,
605  * set every entry to the appropriate "getproto" command.
606  */
us_setmenusize(INTBIG x,INTBIG y,INTBIG position,BOOLEAN allgetproto)607 void us_setmenusize(INTBIG x, INTBIG y, INTBIG position, BOOLEAN allgetproto)
608 {
609 	REGISTER INTBIG i, oldlen;
610 	REGISTER CHAR **temp, *last;
611 	REGISTER VARIABLE *oldvar;
612 	COMMANDBINDING commandbinding;
613 
614 	/* validate orientation of menu */
615 	if (position <= 1)
616 	{
617 		/* menu runs horizontally across the top or bottom */
618 		if (y > x) { i = x;  x = y;  y = i; }
619 	} else
620 	{
621 		/* menu runs vertically down the left or right */
622 		if (y < x) { i = x;  x = y;  y = i; }
623 	}
624 	if (us_menux == x && us_menuy == y && us_menupos == position && !allgetproto)
625 		return;
626 
627 	/* set new size, position */
628 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_menu_x_key, x, VINTEGER|VDONTSAVE);
629 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_menu_y_key, y, VINTEGER|VDONTSAVE);
630 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_menu_position_key, position, VINTEGER|VDONTSAVE);
631 
632 	/* rebuild the binding array size */
633 	temp = (CHAR **)emalloc(x*y * (sizeof (CHAR *)), el_tempcluster);
634 	if (temp == 0) return;
635 	if (allgetproto)
636 	{
637 		for(i=0; i<x*y; i++) temp[i] = x_("command=getproto");
638 		oldlen = 0;
639 		last = 0;
640 	} else
641 	{
642 		for(i=0; i<x*y; i++) temp[i] = x_("");
643 		oldvar = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_binding_menu_key);
644 		if (oldvar == NOVARIABLE) oldlen = 0; else oldlen = getlength(oldvar);
645 		last = NOSTRING;
646 		for(i=0; i<mini(oldlen, x*y); i++)
647 		{
648 			last = ((CHAR **)oldvar->addr)[i];
649 			(void)allocstring(&temp[i], last, el_tempcluster);
650 		}
651 
652 		/* if old menu ends with a "getproto", extend their range */
653 		if (last != NOSTRING)
654 		{
655 			us_parsebinding(last, &commandbinding);
656 			if (i < x*y && *commandbinding.command != 0 &&
657 				namesamen(commandbinding.command, x_("getproto"), 8) == 0)
658 			{
659 				for(; i<x*y; i++) temp[i] = x_("command=getproto");
660 			} else last = NOSTRING;
661 			us_freebindingparse(&commandbinding);
662 		}
663 	}
664 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_binding_menu_key, (INTBIG)temp,
665 		VSTRING|VISARRAY|VDONTSAVE|((x*y)<<VLENGTHSH));
666 	for(i=0; i<mini(oldlen, x*y); i++) efree(temp[i]);
667 	efree((CHAR *)temp);
668 	if (last != NOSTRING) us_setmenunodearcs();
669 }
670 
671 /******************** POPUP MENUS ********************/
672 
673 #ifdef USEQT
674 
675 /*
676  * routine to display the popup menu "menu" and allow picking.  The menu
677  * terminates when any button is clicked (a down-click is necessary if "*waitfordown"
678  * is true).  If "header" is true, display a header.  If "top" and "left" are not
679  * negative, they are used as the coordinates of the upper-left of the menu.  If
680  * "quitline" is nonzero, then exit the menu when the cursor moves over that line
681  * (1: left side, 2: right side, 3: pulldown menu extent, 4: popup menu extent).
682  * Returns the address of the item in the menu that is selected and sets "menu" to
683  * the menu (if a submenu in a hierarchy was used).  Returns NOPOPUPMENUITEM
684  * if no entry was selected.  Returns zero if this function cannot be done.  If the
685  * mouse is still down (because it crossed the quit line) then "waitfordown" is set
686  * true.  Also sets the "changed" items to nonzero for any entries that are modified.
687  */
us_popupmenu(POPUPMENU ** cmenu,BOOLEAN * waitfordown,BOOLEAN header,INTBIG left,INTBIG top,INTBIG quitline)688 POPUPMENUITEM *us_popupmenu(POPUPMENU **cmenu, BOOLEAN *waitfordown, BOOLEAN header,
689 	INTBIG left, INTBIG top, INTBIG quitline)
690 {
691 	INTBIG i = nativepopupmenu(cmenu, header, left, top);
692 	if (i < 0) return(NOPOPUPMENUITEM);
693 	return(&(*cmenu)->list[i]);
694 }
695 
696 #else /* USEQT */
697 
698 #define ATTVALGAP 20		/* distance between attribute and value columns */
699 
700 /* structure for popup menus */
701 typedef struct
702 {
703 	WINDOWPART     popupwindow;		/* window in which popup resides */
704 	INTBIG         hline;			/* currently highlighted line in popup menu (-1 if none) */
705 	INTBIG         posx, posy;		/* position of lower-left corner of popup menu */
706 	INTBIG         sizex, sizey;	/* size of popup menu */
707 	INTBIG         txthei;			/* height of a line of the popup menu */
708 	INTBIG         yshift;			/* shift up amount when menu bumps bottom */
709 	INTBIG         attlen, vallen;	/* width of attribute and value fields in popup menu */
710 	INTBIG         curpos;			/* current character position in value field */
711 	BOOLEAN        header;			/* true if a header is to be displayed in popup menu */
712 	INTBIG         quitline;		/* nonzero if menu should abort when it crosses a line */
713 	INTBIG         inarea;			/* global flag for being inside an area */
714 	POPUPMENUITEM *retval;			/* return item */
715 	POPUPMENU     *retmenu;			/* menu of returned item */
716 	POPUPMENU     *current;			/* current popup menu structure */
717 } POPUPINST;
718 
719 static POPUPINST us_pmcur;
720 
721 /*
722  * routine to display the popup menu "menu" and allow picking.  The menu
723  * terminates when any button is clicked (a down-click is necessary if "*waitfordown"
724  * is true).  If "header" is true, display a header.  If "top" and "left" are not
725  * negative, they are used as the coordinates of the upper-left of the menu.  If
726  * "quitline" is nonzero, then exit the menu when the cursor moves over that line
727  * (1: left side, 2: right side, 3: pulldown menu extent, 4: popup menu extent).
728  * Returns the address of the item in the menu that is selected and sets "menu" to
729  * the menu (if a submenu in a hierarchy was used).  Returns NOPOPUPMENUITEM
730  * if no entry was selected.  Returns zero if this function cannot be done.  If the
731  * mouse is still down (because it crossed the quit line) then "waitfordown" is set
732  * true.  Also sets the "changed" items to nonzero for any entries that are modified.
733  */
us_popupmenu(POPUPMENU ** cmenu,BOOLEAN * waitfordown,BOOLEAN header,INTBIG left,INTBIG top,INTBIG quitline)734 POPUPMENUITEM *us_popupmenu(POPUPMENU **cmenu, BOOLEAN *waitfordown, BOOLEAN header,
735 	INTBIG left, INTBIG top, INTBIG quitline)
736 {
737 	REGISTER INTBIG i, yp, startfont;
738 	BOOLEAN waitdown;
739 	REGISTER CHAR *savepos, *pt;
740 	REGISTER INTBIG textscale, save_pix;
741 	UINTBIG descript[TEXTDESCRIPTSIZE];
742 	INTBIG x, y, chwid, swid, shei;
743 	REGISTER POPUPMENU *menu;
744 	POPUPMENU *whichmenu;
745 	REGISTER POPUPMENUITEM *mi;
746 	REGISTER WINDOWFRAME *frame;
747 
748 	/* see if this is a simple popup that could be handled by the OS */
749 	menu = *cmenu;
750 	for(i=0; i<menu->total; i++)
751 	{
752 		mi = &menu->list[i];
753 		if (mi->response != NOUSERCOM && mi->response->menu != NOPOPUPMENU)
754 			continue;
755 		if (mi->value != 0) break;
756 	}
757 	if (i >= menu->total)
758 	{
759 		/* no type-in entries: see if OS can draw this simple popup menu */
760 		if (graphicshas(CANSHOWPOPUP))
761 		{
762 			whichmenu = menu;
763 			i = nativepopupmenu(&whichmenu, header, left, top);
764 			if (i < 0) return(NOPOPUPMENUITEM);
765 			return(&whichmenu->list[i]);
766 		}
767 	}
768 
769 	/* must draw the popup by hand */
770 	if (us_needwindow()) return(0);
771 
772 	/* determine width of attribute and value halves in menu */
773 	waitdown = *waitfordown;
774 	*waitfordown = FALSE;
775 	us_pmcur.header = header;
776 	us_pmcur.current = menu;
777 	us_pmcur.inarea = 0;
778 	us_pmcur.quitline = quitline;
779 	us_pmcur.retval = NOPOPUPMENUITEM;
780 	us_pmcur.retmenu = menu;
781 	us_pmcur.yshift = 0;
782 
783 	/* get size of current window */
784 	frame = getwindowframe(FALSE);
785 	getwindowframesize(frame, &swid, &shei);
786 	us_pmcur.popupwindow.screenlx = us_pmcur.popupwindow.screenly = 0;
787 	us_pmcur.popupwindow.uselx = us_pmcur.popupwindow.usely = 0;
788 	us_pmcur.popupwindow.screenhx = us_pmcur.popupwindow.usehx = swid-1;
789 	us_pmcur.popupwindow.screenhy = us_pmcur.popupwindow.usehy = shei-1;
790 	us_pmcur.popupwindow.frame = frame;
791 	us_pmcur.popupwindow.state = DISPWINDOW;
792 	computewindowscale(&us_pmcur.popupwindow);
793 
794 	/* loop through fonts to find one that fits the menu */
795 #ifdef	sun
796 	startfont = 20;
797 #else
798 	startfont = 14;
799 #endif
800 	for(textscale = startfont; textscale >= 4; textscale--)
801 	{
802 		us_pmcur.attlen = us_pmcur.vallen = 0;
803 		TDCLEAR(descript);
804 		if (textscale == startfont) TDSETSIZE(descript, TXTMENU); else
805 			TDSETSIZE(descript, textscale);
806 		screensettextinfo(&us_pmcur.popupwindow, NOTECHNOLOGY, descript);
807 		screengettextsize(&us_pmcur.popupwindow, x_("0"), &chwid, &y);
808 		us_pmcur.txthei = y+2;
809 		for(i=0; i<us_pmcur.current->total; i++)
810 		{
811 			mi = &us_pmcur.current->list[i];
812 			mi->changed = FALSE;
813 			pt = us_stripampersand(mi->attribute);
814 			screengettextsize(&us_pmcur.popupwindow, pt, &x, &y);
815 			if (x > us_pmcur.attlen) us_pmcur.attlen = x;
816 			if (mi->value == 0) continue;
817 			screengettextsize(&us_pmcur.popupwindow, mi->value, &x, &y);
818 			if (x > us_pmcur.vallen) us_pmcur.vallen = x;
819 			if (mi->maxlen*chwid > us_pmcur.vallen) us_pmcur.vallen = mi->maxlen*chwid;
820 		}
821 
822 		/* make sure the header will fit in the menu */
823 		if (header)
824 		{
825 			pt = us_stripampersand(us_pmcur.current->header);
826 			screengettextsize(&us_pmcur.popupwindow, pt, &x, &y);
827 			if (us_pmcur.vallen == 0)
828 			{
829 				if (x > us_pmcur.attlen) us_pmcur.attlen = x;
830 			} else
831 			{
832 				if (x > us_pmcur.attlen+us_pmcur.vallen+ATTVALGAP)
833 				{
834 					i = x - (us_pmcur.attlen+us_pmcur.vallen+ATTVALGAP);
835 					us_pmcur.attlen += i/2;
836 					us_pmcur.vallen = x - us_pmcur.attlen - ATTVALGAP;
837 				}
838 			}
839 		}
840 
841 		/* determine the actual size of the menu */
842 		us_pmcur.sizex = us_pmcur.attlen + 4;
843 		if (us_pmcur.vallen != 0) us_pmcur.sizex += us_pmcur.vallen + ATTVALGAP;
844 		us_pmcur.sizey = us_pmcur.current->total * us_pmcur.txthei + 3;
845 		if (header) us_pmcur.sizey += us_pmcur.txthei;
846 
847 		/* if the menu fits, quit */
848 		if (us_pmcur.sizex+MENUSHADOW <= swid &&
849 			us_pmcur.sizey+MENUSHADOW <= shei) break;
850 	}
851 
852 	/* no text size will fit */
853 	if (textscale < 4) return(0);
854 
855 	/* determine the location of the menu */
856 	readtablet(&x, &y);
857 	if (top < 0 || left < 0)
858 	{
859 		us_pmcur.posx = x-us_pmcur.sizex/2;
860 		us_pmcur.posy = y-us_pmcur.sizey/2;
861 	} else
862 	{
863 		if (quitline == 2) us_pmcur.posx = left - us_pmcur.sizex; else
864 			us_pmcur.posx = left;
865 		us_pmcur.posy = top-us_pmcur.sizey;
866 	}
867 	if (us_pmcur.posx < 0) us_pmcur.posx = 0;
868 	if (us_pmcur.posy <= MENUSHADOW)
869 	{
870 		us_pmcur.yshift = MENUSHADOW+1-us_pmcur.posy;
871 		us_pmcur.posy = MENUSHADOW+1;
872 	}
873 	if (us_pmcur.posx+us_pmcur.sizex+MENUSHADOW >= swid)
874 		us_pmcur.posx = swid - us_pmcur.sizex-MENUSHADOW-1;
875 	if (us_pmcur.posy+us_pmcur.sizey > shei)
876 		us_pmcur.posy = shei - us_pmcur.sizey;
877 
878 	/* save the display under the menu */
879 	save_pix = screensavebox(&us_pmcur.popupwindow, us_pmcur.posx,
880 		us_pmcur.posx+us_pmcur.sizex+MENUSHADOW,
881 		us_pmcur.posy-MENUSHADOW-1, us_pmcur.posy+us_pmcur.sizey-1);
882 
883 	/* write the menu on the screen */
884 	us_menufigs.col = el_colmengly;
885 	us_menutext.col = el_colmentxt;
886 	screendrawbox(&us_pmcur.popupwindow, us_pmcur.posx,
887 		us_pmcur.posx+us_pmcur.sizex-1,
888 		us_pmcur.posy, us_pmcur.posy+us_pmcur.sizey-1, &us_ebox);
889 	screendrawline(&us_pmcur.popupwindow, us_pmcur.posx, us_pmcur.posy,
890 		us_pmcur.posx+us_pmcur.sizex-1, us_pmcur.posy, &us_menufigs, 0);
891 	screendrawline(&us_pmcur.popupwindow, us_pmcur.posx+us_pmcur.sizex-1,
892 		us_pmcur.posy, us_pmcur.posx+us_pmcur.sizex-1,
893 		us_pmcur.posy+us_pmcur.sizey-1, &us_menufigs, 0);
894 	screendrawline(&us_pmcur.popupwindow, us_pmcur.posx+us_pmcur.sizex-1,
895 		us_pmcur.posy+us_pmcur.sizey-1, us_pmcur.posx,
896 			us_pmcur.posy+us_pmcur.sizey-1, &us_menufigs, 0);
897 	screendrawline(&us_pmcur.popupwindow, us_pmcur.posx,
898 		us_pmcur.posy+us_pmcur.sizey-1,
899 		us_pmcur.posx, us_pmcur.posy, &us_menufigs, 0);
900 
901 	/* now draw a shadow */
902 	us_shadowdesc.col = el_colmengly;
903 	screendrawbox(&us_pmcur.popupwindow, us_pmcur.posx+MENUSHADOW,
904 		us_pmcur.posx+us_pmcur.sizex, us_pmcur.posy-MENUSHADOW-1,
905 		us_pmcur.posy-1, &us_shadowdesc);
906 	if (quitline != 2)
907 		screendrawbox(&us_pmcur.popupwindow, us_pmcur.posx+us_pmcur.sizex,
908 			us_pmcur.posx+us_pmcur.sizex+MENUSHADOW, us_pmcur.posy-MENUSHADOW-1,
909 			us_pmcur.posy+us_pmcur.sizey-MENUSHADOW, &us_shadowdesc);
910 
911 	/* write the header */
912 	if (header)
913 	{
914 		screendrawline(&us_pmcur.popupwindow, us_pmcur.posx,
915 			us_pmcur.posy+us_pmcur.sizey-us_pmcur.txthei-2,
916 			us_pmcur.posx+us_pmcur.sizex-1,
917 			us_pmcur.posy+us_pmcur.sizey-us_pmcur.txthei-2,
918 			&us_menufigs, 0);
919 		pt = us_stripampersand(us_pmcur.current->header);
920 		screendrawtext(&us_pmcur.popupwindow, us_pmcur.posx+2,
921 			us_pmcur.posy+us_pmcur.sizey-us_pmcur.txthei-1,
922 			pt, &us_menutext);
923 		screeninvertbox(&us_pmcur.popupwindow, us_pmcur.posx+1,
924 			us_pmcur.posx+us_pmcur.sizex-2,
925 			us_pmcur.posy+us_pmcur.sizey-us_pmcur.txthei-1,
926 			us_pmcur.posy+us_pmcur.sizey-2);
927 		yp = us_pmcur.posy+us_pmcur.sizey-us_pmcur.txthei*2-2;
928 	} else yp = us_pmcur.posy+us_pmcur.sizey-us_pmcur.txthei-2;
929 
930 	/* write the rest of the text in the menu */
931 	for(i=0; i<us_pmcur.current->total; i++)
932 	{
933 		mi = &us_pmcur.current->list[i];
934 		if (*mi->attribute == 0 && mi->value == 0)
935 		{
936 			/* no entry: draw a dotted line */
937 			screendrawline(&us_pmcur.popupwindow, us_pmcur.posx, yp+us_pmcur.txthei/2,
938 				us_pmcur.posx+us_pmcur.sizex-1, yp+us_pmcur.txthei/2,
939 				&us_menufigs, 1);
940 		} else
941 		{
942 			/* draw a text entry */
943 			savepos = &mi->attribute[estrlen(mi->attribute)-1];
944 			if (mi->attribute[0] != '>' && *savepos == '<') *savepos = 0; else
945 				savepos = 0;
946 			pt = us_stripampersand(mi->attribute);
947 			screendrawtext(&us_pmcur.popupwindow, us_pmcur.posx+2, yp, pt,
948 				&us_menutext);
949 			if (savepos != 0) *savepos = '<';
950 			if (mi->value != 0)
951 			{
952 				screendrawtext(&us_pmcur.popupwindow,
953 					us_pmcur.posx+us_pmcur.attlen+ATTVALGAP+2,
954 					yp, mi->value, &us_menutext);
955 			}
956 		}
957 		yp -= us_pmcur.txthei;
958 	}
959 
960 	/* set the initial state of highlighted lines */
961 	if (x < us_pmcur.posx || x >= us_pmcur.posx+us_pmcur.sizex ||
962 		y < us_pmcur.posy || y >= us_pmcur.posy+us_pmcur.sizey-us_pmcur.txthei-2)
963 			us_pmcur.hline = -1; else
964 	{
965 		us_pmcur.hline = us_pmcur.current->total - (y - us_pmcur.posy) / us_pmcur.txthei - 1;
966 		us_pmcur.retval = &us_pmcur.current->list[us_pmcur.hline];
967 		us_pminvertline();
968 		us_pmcur.curpos = -1;
969 	}
970 
971 	/* do command completion while handling input to menu */
972 	trackcursor(waitdown, us_pmeachdown, us_pmfirst, us_pmeachdown, us_pmeachchar,
973 		us_pmdone, (quitline == 0 ? TRACKSELECTING : TRACKHSELECTING));
974 
975 	/* restore what was under the menu */
976 	screenrestorebox(save_pix, 1);
977 
978 	/* report the menu entry selected */
979 	if (us_pmcur.inarea < 0) *waitfordown = TRUE;
980 	if (us_pmcur.retval != NOPOPUPMENUITEM) *cmenu = us_pmcur.retmenu;
981 	return(us_pmcur.retval);
982 }
983 
us_pmfirst(void)984 void us_pmfirst(void) {}
us_pmdone(void)985 void us_pmdone(void) {}
986 
us_pmeachchar(INTBIG x,INTBIG y,INTSML chr)987 BOOLEAN us_pmeachchar(INTBIG x, INTBIG y, INTSML chr)
988 {
989 	REGISTER POPUPMENUITEM *mi;
990 	REGISTER INTBIG i;
991 	INTBIG xs, ys;
992 
993 	/* set the cursor properly if it moved */
994 	(void)us_pmeachdown(x, y);
995 
996 	/* quit now if carriage-return is typed */
997 	if (chr == '\n' || chr == '\r') return(TRUE);
998 
999 	/* make sure there is a valid line */
1000 	if (us_pmcur.hline == -1) return(FALSE);
1001 	mi = &us_pmcur.current->list[us_pmcur.hline];
1002 	if (mi->value == 0 || mi->maxlen < 0) return(FALSE);
1003 
1004 	/* handle deletion of a character */
1005 	if (chr == us_erasech)
1006 	{
1007 		if (us_pmcur.curpos <= 0) return(FALSE);
1008 		us_pmcur.curpos--;
1009 		mi->value[us_pmcur.curpos] = 0;
1010 		screengettextsize(&us_pmcur.popupwindow, mi->value, &xs, &ys);
1011 		us_pminvertline();
1012 		screendrawbox(&us_pmcur.popupwindow,
1013 			us_pmcur.posx+2+us_pmcur.attlen+ATTVALGAP+xs,
1014 			us_pmcur.posx+us_pmcur.sizex-1,
1015 			us_pmcur.posy+us_pmcur.sizey-us_pmcur.txthei*(us_pmcur.hline+2)-2,
1016 			us_pmcur.posy+us_pmcur.sizey-us_pmcur.txthei*(us_pmcur.hline+1)-3,
1017 			&us_ebox);
1018 		us_pminvertline();
1019 		return(FALSE);
1020 	}
1021 
1022 	/* handle deletion of the entire line */
1023 	if (chr == us_killch)
1024 	{
1025 		if (us_pmcur.curpos <= 0) return(FALSE);
1026 		us_pmcur.curpos = 0;
1027 		mi->value[us_pmcur.curpos] = 0;
1028 		us_pminvertline();
1029 		screendrawbox(&us_pmcur.popupwindow,
1030 			us_pmcur.posx+2+us_pmcur.attlen+ATTVALGAP,
1031 			us_pmcur.posx+us_pmcur.sizex-1,
1032 			us_pmcur.posy+us_pmcur.sizey-us_pmcur.txthei*(us_pmcur.hline+2)-2,
1033 			us_pmcur.posy+us_pmcur.sizey-us_pmcur.txthei*(us_pmcur.hline+1)-3,
1034 			&us_ebox);
1035 		us_pminvertline();
1036 		return(FALSE);
1037 	}
1038 
1039 	/* see if the new character will fit */
1040 	if (us_pmcur.curpos >= us_pmcur.vallen || us_pmcur.curpos >= mi->maxlen) return(FALSE);
1041 
1042 	/* do not allow unprintable characters */
1043 	if (chr < 040) return(FALSE);
1044 
1045 	/* on the first character, blank the line */
1046 	if (us_pmcur.curpos < 0)
1047 	{
1048 		us_pmcur.curpos = 0;
1049 		mi->changed = TRUE;
1050 		us_pminvertline();
1051 		screendrawbox(&us_pmcur.popupwindow,
1052 			us_pmcur.posx+2+us_pmcur.attlen+ATTVALGAP,
1053 			us_pmcur.posx+us_pmcur.sizex-1,
1054 			us_pmcur.posy+us_pmcur.sizey-us_pmcur.txthei*(us_pmcur.hline+2)-2,
1055 			us_pmcur.posy+us_pmcur.sizey-us_pmcur.txthei*(us_pmcur.hline+1)-3,
1056 			&us_ebox);
1057 		us_pminvertline();
1058 		mi->value[0] = 0;
1059 	}
1060 
1061 	/* add a letter to a value field */
1062 	screengettextsize(&us_pmcur.popupwindow, mi->value, &xs, &ys);
1063 	i = us_pmcur.posx+2+us_pmcur.attlen+ATTVALGAP+xs;
1064 	mi->value[us_pmcur.curpos] = (CHAR)chr;
1065 	us_pmcur.curpos++;
1066 	mi->value[us_pmcur.curpos] = 0;
1067 	us_pminvertline();
1068 	screendrawtext(&us_pmcur.popupwindow, i,
1069 		us_pmcur.posy+us_pmcur.sizey-us_pmcur.txthei*(us_pmcur.hline+2)-2,
1070 			&mi->value[us_pmcur.curpos-1], &us_menutext);
1071 	us_pminvertline();
1072 	return(FALSE);
1073 }
1074 
1075 /*
1076  * helper routine for popup menus to handle cursor movement
1077  */
us_pmeachdown(INTBIG x,INTBIG y)1078 BOOLEAN us_pmeachdown(INTBIG x, INTBIG y)
1079 {
1080 	REGISTER INTBIG line, maxy, i, lx;
1081 	INTBIG sx, sy, swid, shei, pwid;
1082 	REGISTER POPUPMENUITEM *pm;
1083 	POPUPMENU *cmenu;
1084 	REGISTER USERCOM *uc;
1085 	POPUPINST savepmcur;
1086 	BOOLEAN but;
1087 
1088 	/* determine top coordinate of menu */
1089 	maxy = us_pmcur.posy+us_pmcur.sizey-2;
1090 	if (us_pmcur.header) maxy -= us_pmcur.txthei;
1091 
1092 	getpaletteparameters(&swid, &shei, &pwid);
1093 
1094 	for(;;)
1095 	{
1096 		/* compute current line in menu */
1097 		if (x < us_pmcur.posx || x >= us_pmcur.posx+us_pmcur.sizex ||
1098 			y < us_pmcur.posy || y > maxy) line = -1; else
1099 		{
1100 			line = us_pmcur.current->total - (y - us_pmcur.posy) / us_pmcur.txthei - 1;
1101 			us_pmcur.inarea = 1;
1102 		}
1103 		if (line >= us_pmcur.current->total || line < 0) line = -1; else
1104 		{
1105 			pm = &us_pmcur.current->list[line];
1106 			if (*pm->attribute == 0 && pm->value == 0) line = -1;
1107 		}
1108 		if (line != us_pmcur.hline)
1109 		{
1110 			if (us_pmcur.hline != -1)
1111 			{
1112 				us_pminvertline();
1113 				us_pmcur.hline = -1;
1114 			}
1115 			us_pmcur.hline = line;
1116 			if (us_pmcur.hline == -1) us_pmcur.retval = NOPOPUPMENUITEM; else
1117 			{
1118 				us_pmcur.retval = &us_pmcur.current->list[us_pmcur.hline];
1119 				us_pmcur.retmenu = us_pmcur.current;
1120 				us_pminvertline();
1121 				us_pmcur.curpos = -1;
1122 
1123 				/* new menu entry: see if it is hierarchical */
1124 				uc = us_pmcur.retval->response;
1125 				if (uc != NOUSERCOM && uc->menu != NOPOPUPMENU)
1126 				{
1127 					savepmcur = us_pmcur;
1128 					but = FALSE;
1129 					i = 1;
1130 					lx = us_pmcur.posx+us_pmcur.sizex;
1131 					if (us_pmcur.posx > us_pmcur.popupwindow.usehx-lx)
1132 					{
1133 						i = 2;
1134 						lx = us_pmcur.posx;
1135 						if (us_pmcur.posx < us_pmcur.sizex)
1136 						{
1137 							i = us_pmcur.quitline;
1138 							lx = x-2;
1139 						}
1140 					}
1141 					cmenu = uc->menu;
1142 					pm = us_popupmenu(&cmenu, &but, FALSE, lx,
1143 						us_pmcur.posy+(us_pmcur.current->total-line)*us_pmcur.txthei, i);
1144 					us_pmcur = savepmcur;
1145 					us_pmcur.retval = pm;
1146 					us_pmcur.retmenu = cmenu;
1147 					if (!but) return(TRUE);
1148 					readtablet(&sx, &sy);
1149 					x = sx;   y = sy;
1150 					continue;
1151 				}
1152 			}
1153 		}
1154 		break;
1155 	}
1156 
1157 	/* if exiting over quit line, stop */
1158 	switch (us_pmcur.quitline)
1159 	{
1160 		case 1:			/* quit if off left side */
1161 			if (y < shei)
1162 			{
1163 				if (x >= us_pmcur.posx) break;
1164 				if (y <= maxy-us_pmcur.yshift+2 &&
1165 					y >= maxy-us_pmcur.yshift-us_pmcur.txthei-2) break;
1166 			}
1167 			us_pmcur.inarea = -1;
1168 			return(TRUE);
1169 		case 2:			/* quit if off right side */
1170 			if (y < shei)
1171 			{
1172 				if (x <= us_pmcur.posx+us_pmcur.sizex) break;
1173 				if (y <= maxy-us_pmcur.yshift+2 &&
1174 					y >= maxy-us_pmcur.yshift-us_pmcur.txthei-2) break;
1175 			}
1176 			us_pmcur.inarea = -1;
1177 			return(TRUE);
1178 		case 3:			/* quit if out of this pulldown menu's menubar location */
1179 			if (y <= maxy) break;
1180 			lx = x;
1181 			for(i=0; i<us_pulldownmenucount; i++)
1182 			{
1183 				if (us_pmcur.current == us_pulldowns[i]) break;
1184 				lx = us_pulldownmenupos[i];
1185 			}
1186 			if (x >= lx && (x <= us_pulldownmenupos[i] || i == us_pulldownmenucount-1))
1187 				break;
1188 			us_pmcur.inarea = -1;
1189 			return(TRUE);
1190 		case 4:			/* quit if out of this popup menu's location */
1191 			if (x >= us_pmcur.posx && x <= us_pmcur.posx+us_pmcur.sizex &&
1192 				y >= us_pmcur.posy && y <= us_pmcur.posy+us_pmcur.sizey) break;
1193 			us_pmcur.inarea = -1;
1194 			return(TRUE);
1195 	}
1196 	return(FALSE);
1197 }
1198 
1199 /*
1200  * routine to invert the text line at "us_pmcur.hline"
1201  */
us_pminvertline(void)1202 void us_pminvertline(void)
1203 {
1204 	INTBIG yp;
1205 
1206 	yp = us_pmcur.posy+us_pmcur.sizey-us_pmcur.txthei*(us_pmcur.hline+1)-2;
1207 	if (us_pmcur.header) yp -= us_pmcur.txthei;
1208 	screeninvertbox(&us_pmcur.popupwindow, us_pmcur.posx+1,
1209 		us_pmcur.posx+us_pmcur.sizex-2, yp, yp+us_pmcur.txthei-1);
1210 }
1211 #endif /* USEQT */
1212 
1213 /*
1214  * Routine to remove the "&" from the string "msg" and return the new string.
1215  * This is used in menu entries, which use the "&" character to mark the
1216  * location of the mnemonic for the entry.
1217  */
us_stripampersand(CHAR * msg)1218 CHAR *us_stripampersand(CHAR *msg)
1219 {
1220 	static CHAR buf[200];
1221 	REGISTER CHAR *pt;
1222 
1223 	pt = buf;
1224 	while (*msg != 0)
1225 	{
1226 		if (*msg != '&') *pt++ = *msg;
1227 		msg++;
1228 	}
1229 	*pt = 0;
1230 	return(buf);
1231 }
1232 
1233 /*
1234  * routine to scan popupmenu "pm" for command-key equivalents and set them
1235  */
us_scanforkeyequiv(POPUPMENU * pm)1236 void us_scanforkeyequiv(POPUPMENU *pm)
1237 {
1238 	REGISTER POPUPMENUITEM *mi;
1239 	REGISTER INTBIG j;
1240 
1241 	for(j=0; j<pm->total; j++)
1242 	{
1243 		mi = &pm->list[j];
1244 		if (mi->response == NOUSERCOM) continue;
1245 		if (mi->response->active < 0) continue;
1246 		if (mi->response->menu != NOPOPUPMENU)
1247 		{
1248 			us_scanforkeyequiv(mi->response->menu);
1249 			continue;
1250 		}
1251 		us_setkeyequiv(pm, j+1);
1252 	}
1253 }
1254 
us_setkeyequiv(POPUPMENU * pm,INTBIG i)1255 void us_setkeyequiv(POPUPMENU *pm, INTBIG i)
1256 {
1257 	REGISTER INTBIG len, special;
1258 	REGISTER INTSML key;
1259 	REGISTER CHAR *pt, lastchar;
1260 	REGISTER POPUPMENUITEM *mi;
1261 	REGISTER void *infstr;
1262 
1263 	/* make sure it is a valid menu entry */
1264 	mi = &pm->list[i-1];
1265 	if (mi->response == NOUSERCOM) return;
1266 	if (mi->response->active < 0) return;
1267 	if (mi->response->menu != NOPOPUPMENU) return;
1268 
1269 	/* remove ending "<" for checkmarks */
1270 	len = estrlen(mi->attribute);
1271 	lastchar = 0;
1272 	if (mi->attribute[len-1] == '<')
1273 	{
1274 		lastchar = mi->attribute[len-1];
1275 		mi->attribute[len-1] = 0;
1276 	}
1277 
1278 	/* see if it has a "/" or "\" in it */
1279 	for(pt = mi->attribute; *pt != 0 && *pt != '/' && *pt != '\\'; pt++) ;
1280 	if (*pt == 0) return;
1281 
1282 	/* make it the Meta key */
1283 	key = pt[1];
1284 	if (key != 0)
1285 	{
1286 		if (*pt == '/')
1287 		{
1288 			special = ACCELERATORDOWN;
1289 			key = tolower(key);
1290 		} else special = 0;
1291 		infstr = initinfstr();
1292 		formatinfstr(infstr, x_("%c%c"), key, *pt);
1293 		addstringtoinfstr(infstr, x_("command="));
1294 		addstringtoinfstr(infstr, mi->response->comname);
1295 		us_appendargs(infstr, mi->response);
1296 		us_setkeybinding(returninfstr(infstr), key, special, FALSE);
1297 	}
1298 
1299 	/* restore ending "<" checkmark */
1300 	len = estrlen(mi->attribute);
1301 	mi->attribute[len] = lastchar;
1302 }
1303 
1304 /*
1305  * Routine to examine popup menu "pm" and ensure that no two letters have the same
1306  * "&"-invocation key.
1307  */
us_validatemenu(POPUPMENU * pm)1308 void us_validatemenu(POPUPMENU *pm)
1309 {
1310 	REGISTER INTBIG i, j, index;
1311 	REGISTER POPUPMENUITEM *mi;
1312 	REGISTER USERCOM *uc;
1313 	REGISTER CHAR *pt;
1314 	CHAR tagged[256];
1315 
1316 	for(i=0; i<256; i++) tagged[i] = 0;
1317 	for(j=0; j < pm->total; j++)
1318 	{
1319 		mi = &pm->list[j];
1320 		uc = mi->response;
1321 		if (uc->active < 0) continue;
1322 
1323 		if (uc->menu != NOPOPUPMENU)
1324 		{
1325 			us_validatemenu(uc->menu);
1326 		}
1327 		for(pt = mi->attribute; *pt != 0; pt++) if (*pt == '&') break;
1328 		if (*pt == 0) continue;
1329 		index = tolower(pt[1]) & 0xFF;
1330 		if (tagged[index] != 0)
1331 		{
1332 			ttyputerr(_("Warning: two entries share letter '%c' ('%s' and '%s')"),
1333 				index, mi->attribute, pm->list[tagged[index]-1].attribute);
1334 		}
1335 		tagged[index] = (CHAR)(j+1);
1336 	}
1337 }
1338