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