1 /*
2  * xvpopup.c - pop up "Are you sure?  Yes/No/Maybe" sort of dialog box
3  *
4  * callable functions:
5  *
6  *   CenterMapWindow(win,x,y)  -  maps and centers a window around the mouse
7  *   PopUp(str,...)        -  maps, sets up popW
8  *   ErrPopUp(str,str)     -  maps, sets up popW
9  *   GetStrPopUp(...)      -  opens a 1-line, editable text popup window
10  *   GrabPopUp(*hide,*del) -  opens 'grab' popup dialog
11  *   PadPopUp()            -  opens 'grab' popup dialog
12  *   ClosePopUp()          -  closes pop-up or alert window, if open
13  *   OpenAlert(str)        -  maps a button-less window
14  *   CloseAlert()          -  closes a button-less window
15  *   PUCheckEvent(event)   -  called by event handler
16  */
17 
18 #include "copyright.h"
19 
20 #include "xv.h"
21 
22 #define OMIT_ICON_BITS
23 #include "bits/icon"   /* icon_bits[] not used, but icon_width/height are */
24 
25 #define PUWIDE 480
26 #define PUHIGH 170
27 
28 #define PAD_PUWIDE 480
29 #define PAD_PUHIGH 215
30 
31 #define BUTTH   24
32 
33 static int  doPopUp       PARM((const char *, const char **, int, int, const char *));
34 static void attachPUD     PARM((void));
35 static void TextRect      PARM((Window, const char *, int, int, int, int, u_long));
36 static void createPUD     PARM((void));
37 static void drawPUD       PARM((int, int, int, int));
38 static void drawPadOMStr  PARM((void));
39 static void clickPUD      PARM((int, int));
40 static void doGetStrKey   PARM((int));
41 static int  doGSKey       PARM((int));
42 static void changedGSBuf  PARM((void));
43 static void drawGSBuf     PARM((void));
44 static void buildPadLists PARM((void));
45 static void build1PadList PARM((const char *, const char **, const char **, int *,
46 				const char **, const char **, int));
47 
48 
49 /* values 'popUp' can take */
50 #define ISPOPUP  1
51 #define ISALERT  2
52 #define ISGETSTR 3
53 #define ISGRAB   4
54 #define ISPAD    5
55 
56 #define DELAYSTR "Delay:"
57 #define SECSTR   "seconds"
58 #define HIDESTR  "Hide XV windows"
59 
60 /* local variables */
61 static Window      popW;
62 static int         nbts, selected, popUp=0, firsttime=1;
63 static int         puwide = PUWIDE;
64 static int         puhigh = PUHIGH;
65 static BUTT       *bts;
66 static const char *text;
67 static char        accel[8];
68 
69 static char       *gsBuf;       /* stuff needed for GetStrPopUp() handling */
70 static const char *gsFilter;
71 static int         gsBufLen, gsAllow, gsCurPos, gsStPos, gsEnPos;
72 static int         gsx, gsy, gsw, gsh;
73 
74 /* stuff for GrabPopUp */
75 static CBUTT ahideCB;
76 
77 
78 /*** stuff for PadPopUp ***/
79 static char   padSbuf[256], padBbuf[256], padLbuf[256], padBuf[256];
80 static char  *padInst, padSinst[200], padBinst[200], padLinst[200];
81 static MBUTT  padDfltMB, padMthdMB;
82 static BUTT   padDButt, padOMButt;
83 static int    padHaveDooDads = 0;
84 static int    padMode, padOMode;
85 static DIAL   padWDial, padHDial, padODial;
86 
87 static int         padMthdLen=3;
88 static const char *padMthdNames[] = { "Solid Fill", "Run 'bggen'", "Load Image" };
89 
90 static int         padColDefLen = 9;
91 static const char *padColDefNames[] = { "black", "red",  "yellow", "green",
92 					"cyan",  "blue", "magenta", "white",
93 					"50% gray" };
94 
95 static const char *padColDefVals[]  = { "black", "red", "yellow", "green",
96 					"cyan",  "blue", "magenta", "white",
97 					"gray50" };
98 
99 static int         padBgDefLen = 8;
100 static const char *padBgDefNames[] = { "Black->White",
101 				       "Blue Gradient",
102 				       "RGB Rainbow",
103 				       "Full Rainbow",
104 				       "Color Assortment",
105 				       "Green Tiles",
106 				       "Red Balls",
107 				       "Red+Yellow Diamonds" };
108 
109 static const char *padBgDefVals[] = { "black white",
110 				      "100 100 255  50 50 150",
111 				      "red green blue",
112 				      "black red yellow green blue purple black",
113 				      "black white red black yellow white green black cyan white blue black magenta white red yellow green cyan blue magenta red",
114 				      "green black -r 30 -G 32x32",
115 				      "red black -r 45 -G 32x32",
116 				      "red yellow -r 45 -G 32x32" };
117 
118 
119 /* this should match with PAD_O* defs in xv.h */
120 static const char *padOMStr[] = { "RGB", "Int.", "Hue", "Sat." };
121 
122 #define PAD_MAXDEFLEN 10
123 static int         padColLen = 0;
124 static const char *padColNames [PAD_MAXDEFLEN];
125 static const char *padColVals  [PAD_MAXDEFLEN];
126 static int         padBgLen = 0;
127 static const char *padBgNames  [PAD_MAXDEFLEN];
128 static const char *padBgVals   [PAD_MAXDEFLEN];
129 static int         padLoadLen = 0;
130 static const char *padLoadNames[PAD_MAXDEFLEN];
131 static const char *padLoadVals [PAD_MAXDEFLEN];
132 
133 
134 /***************************************************/
CenterMapWindow(win,dx,dy,w,h)135 void CenterMapWindow(win, dx, dy, w, h)
136      Window win;
137      int    dx, dy, w, h;
138 {
139   XSizeHints hints;
140   Window       rW,cW;
141   int          rx,ry,x,y,wx,wy;
142   unsigned int mask;
143 
144 
145   if (!XQueryPointer(theDisp,rootW,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
146     /* couldn't query mouse.  just center on screen */
147     wx = (dispWIDE-w)/2;   wy = (dispHIGH-h)/2;
148   }
149   else {
150     wx = x - dx;
151     wy = y - dy;
152     if (wx<0) wx = 0;
153     if (wy<0) wy = 0;
154     if (wx + w > dispWIDE) wx = dispWIDE - w;
155     if (wy + h > dispHIGH) wy = dispHIGH - h;
156   }
157 
158 
159   if (winCtrPosKludge) {
160     wx -= (p_offx + ch_offx);
161     wy -= (p_offy + ch_offy);
162   }
163   else {
164     wx -= (ch_offx);
165     wy -= (ch_offy);
166   }
167 
168   if (!XGetNormalHints(theDisp, win, &hints)) hints.flags = 0;
169   hints.width  = hints.min_width  = hints.max_width  = w;
170   hints.height = hints.min_height = hints.max_height = h;
171   hints.x = wx;  hints.y = wy;
172   hints.flags  |= (USSize | PMinSize | PMaxSize | USPosition);
173   XSetNormalHints(theDisp, win, &hints);
174 
175   XMoveWindow(theDisp, win, wx, wy);
176   XMapRaised(theDisp, win);
177 }
178 
179 
180 /***************************************************/
PopUp(txt,labels,n)181 int PopUp(txt, labels, n)
182      const char *txt;
183      const char *labels[];
184      int         n;
185 {
186   return doPopUp(txt, labels, n, ISPOPUP, "xv confirm");
187 }
188 
189 
190 /***************************************************/
doPopUp(txt,labels,n,poptyp,wname)191 static int doPopUp(txt, labels, n, poptyp, wname)
192      const char *txt;
193      const char *labels[];
194      int         n, poptyp;
195      const char *wname;
196 {
197   int    i;
198   XEvent event;
199 
200   if (firsttime) createPUD();
201 
202   if (poptyp != ISPAD) { puwide = PUWIDE;      puhigh = PUHIGH;     }
203                   else { puwide = PAD_PUWIDE;  puhigh = PAD_PUHIGH; }
204 
205 
206   /* attach controls to popW, now that it exists */
207   if      (poptyp==ISGRAB) ahideCB.win = popW;
208   else if (poptyp == ISPAD) {
209 
210     if (!padHaveDooDads) {
211       DCreate(&padWDial, popW, 16,      puhigh-16-100-1,75,100,
212 	      1.0, 2048.0, (double)pWIDE, 1.0, 10.0,
213 	      infofg, infobg, hicol, locol, "Width", NULL);
214       DCreate(&padHDial, popW, 16+1+75, puhigh-16-100-1,75,100,
215 	      1.0, 2048.0, (double)pHIGH, 1.0, 10.0,
216 	      infofg, infobg, hicol, locol, "Height", NULL);
217 
218       DCreate(&padODial, popW, 16+1+75+75+9, puhigh-16-100-1,75,100,
219 	      0.0, 100.0, 100.0, 1.0, 10.0,
220 	      infofg, infobg, hicol, locol, "Opaque", NULL);
221 
222       MBCreate(&padMthdMB, popW, 100-2+44, 10, 140, 19, NULL,
223 	       padMthdNames, padMthdLen, infofg, infobg, hicol, locol);
224       padMthdMB.hascheck = 1;
225       padMthdMB.flags[0] = 1;
226 
227       MBCreate(&padDfltMB, popW, 250-2+44, 10, 140, 19, "Defaults",
228 	       padColNames, padColLen, infofg, infobg, hicol, locol);
229 
230       BTCreate(&padDButt, popW, padHDial.x+padHDial.w-12, puhigh-140+6,
231 	       13,13, "", infofg, infobg, hicol, locol);
232 
233       BTCreate(&padOMButt, popW, padODial.x+padODial.w-12, puhigh-140+6,
234 	       13,13, "", infofg, infobg, hicol, locol);
235 
236       padHaveDooDads = 1;
237     }
238 
239     XMapWindow(theDisp, padWDial.win);
240     XMapWindow(theDisp, padHDial.win);
241     XMapWindow(theDisp, padODial.win);
242   }
243 
244 
245   XResizeWindow(theDisp, popW, (u_int) puwide, (u_int) puhigh);
246   XStoreName   (theDisp, popW, wname);
247   XSetIconName (theDisp, popW, wname);
248   attachPUD();
249 
250   bts = (BUTT *) malloc(n * sizeof(BUTT));
251   if (!bts) FatalError("unable to malloc buttons in popup\n");
252   nbts = n;
253   selected = 0;
254   text = txt;
255 
256   for (i=0; i<n; i++) {
257     BTCreate(&bts[i], popW, puwide - (n-i) * (80 + 10), puhigh - 10 - BUTTH,
258 	     80, BUTTH, labels[i]+1, infofg, infobg, hicol, locol);
259     accel[i] = labels[i][0];
260   }
261 
262 
263   if (poptyp == ISGRAB) {
264     BTSetActive(&bts[0], (int) strlen(gsBuf));
265     BTSetActive(&bts[1], (strlen(gsBuf)>(size_t)0 && atoi(gsBuf)>(size_t)0));
266   }
267   else if (poptyp == ISPAD) {
268     BTSetActive(&bts[0], (int) strlen(gsBuf));
269     i = pWIDE * 3;  RANGE(i,2048,9999);
270     DSetRange(&padWDial, 1.0, (double)i, padWDial.val, 1.0, 10.0);
271     i = pHIGH * 3;  RANGE(i,2048,9999);
272     DSetRange(&padHDial, 1.0, (double)i, padHDial.val, 1.0, 10.0);
273 
274     DSetActive(&padWDial, (padMode!=PAD_LOAD));  /* DSetRange activates dial */
275     DSetActive(&padHDial, (padMode!=PAD_LOAD));
276     DSetActive(&padODial, 1);
277 
278     switch (padMode) {
279     case PAD_SOLID:
280       padDfltMB.list  = padColNames;
281       padDfltMB.nlist = padColLen;
282       break;
283     case PAD_BGGEN:
284       padDfltMB.list  = padBgNames;
285       padDfltMB.nlist = padBgLen;
286       break;
287     case PAD_LOAD:
288       padDfltMB.list  = padLoadNames;
289       padDfltMB.nlist = padLoadLen;
290       break;
291     default: break;             /* shouldn't happen */
292     }
293   }
294 
295   /* center first button in window around mouse position, with constraint that
296      window be fully on the screen */
297 
298   popUp = poptyp;
299   if (startGrab == 2)
300     startGrab = 4;
301   else {
302     CenterMapWindow(popW, 40 + bts[0].x, BUTTH/2 + bts[0].y, puwide, puhigh);
303 
304     /* MUST wait for VisibilityNotify event to come in, else we run the risk
305        of UnMapping the window *before* the Map request completed.  This
306        appears to be bad, (It leaves an empty window frame up.) though it
307        generally only happens on slow servers.  Better safe than screwed... */
308 
309     XWindowEvent(theDisp, popW, VisibilityChangeMask, &event);
310   }
311 
312   /* block until this window gets closed */
313   while (popUp) {
314     XNextEvent(theDisp, &event);
315     HandleEvent(&event, &i);
316   }
317 
318   /* free stuff */
319   XUnmapWindow(theDisp, popW);
320   free(bts);
321 
322   return(selected);
323 }
324 
325 
326 /***************************************************/
ErrPopUp(txt,label)327 void ErrPopUp(txt, label)
328      const char *txt;
329      const char *label;
330 {
331   /* simplified interface to PopUp.  Takes a string and the label for the
332      (one) button */
333 
334   PopUp(txt, &label, 1);
335 }
336 
337 
338 /***************************************************/
GetStrPopUp(txt,labels,n,buf,buflen,filstr,allow)339 int GetStrPopUp(txt, labels, n, buf, buflen, filstr, allow)
340      const char *txt;
341      const char *labels[];
342      char *buf;
343      const char *filstr;
344      int   n, buflen, allow;
345 {
346   /* pops up a window with a prompt string, a 1-line editable
347      text thingy, and a row of buttons.  'txt' is the prompt
348      string, 'labels' are the labels for the buttons, 'n' is the
349      number of buttons, 'buf' is the buffer displayed and edited
350      in the window, buflen is its length, filstr is a filter string, of
351      characters to block from entry (or, if 'allow' is '1', a list
352      of the *only* characters allowed for entry)
353 
354      It returns the index of the button clicked on.  Note that the
355      button labels have 1-character accellerators at the front, same
356      as in PopUp().  Note that it would be suboptimal to make any
357      of the 1-character accellerators be the same character as one of
358      the edit-text command keys
359 
360      Also note that the filter string should only contain normal printable
361      characters (' ' through '\177'), as ctrl chars are pre-filtered
362      (ie, interpreted as emacs-like commands) */
363 
364   gsBuf = buf;        gsBufLen = buflen;
365   gsFilter = filstr;  gsAllow = allow;
366 
367   gsCurPos = strlen(gsBuf);
368   gsStPos = gsEnPos = 0;
369 
370   gsh = LINEHIGH+5;
371   gsx = 10 + icon_width + 20;
372   gsy = 10+(PUHIGH-30-BUTTH-gsh)/2;
373 
374   if (strlen(txt) > (size_t) 60)
375     gsy = PUHIGH - 10 - BUTTH - 10 - gsh - 20;
376 
377   gsw = PUWIDE - gsx - 10;
378 
379   changedGSBuf();      /* careful!  popW doesn't exist yet! */
380 
381   return doPopUp(txt, labels, n, ISGETSTR, "xv prompt");
382 }
383 
384 
385 /***************************************************/
GrabPopUp(pHide,pDelay)386 int GrabPopUp(pHide, pDelay)
387      int *pHide, *pDelay;
388 {
389   /* pops up Grab options dialog box */
390 
391   int                rv;
392   char               delaybuf[32], grabTxt[1024];
393   static const char *grabLabels[] = { "\nGrab", "aAutoGrab", "\033Cancel" };
394 
395   sprintf(delaybuf,"%d", *pDelay);
396   gsBuf = delaybuf;          gsBufLen = 3;
397   gsFilter = "0123456789";   gsAllow = 1;
398 
399   gsCurPos = strlen(gsBuf);
400   gsStPos = gsEnPos = 0;
401 
402   gsw = 32;
403   gsh = LINEHIGH+5;
404   gsx = 10 + StringWidth(DELAYSTR) + 5;
405   gsy = (PUHIGH-BUTTH-10-5-gsh);
406 
407   changedGSBuf();      /* careful!  popW doesn't exist yet! */
408 
409   /* window value gets filled in in doPopUp() */
410   CBCreate(&ahideCB, (Window) NULL,
411 	   PUWIDE-10-18-StringWidth(HIDESTR),
412 	   gsy+2, HIDESTR, infofg, infobg, hicol, locol);
413   ahideCB.val = *pHide;
414 
415   sprintf(grabTxt, "Grab: after delay, Left button grabs a window, ");
416   strcat (grabTxt, "Middle button ");
417   strcat (grabTxt, "grabs a rectangular area, Right button cancels.\n\n");
418   strcat (grabTxt, "AutoGrab: after delay, grabs ");
419   strcat (grabTxt, "the window the cursor is positioned in.  ");
420   strcat (grabTxt, "Delay must be non-zero.");
421 
422   rv = doPopUp(grabTxt, grabLabels, 3, ISGRAB, "xv grab");
423 
424   *pHide  = ahideCB.val;
425   *pDelay = atoi(delaybuf);
426   return rv;
427 }
428 
429 
430 /***************************************************/
PadPopUp(pMode,pStr,pWide,pHigh,pOpaque,pOmode)431 int PadPopUp(pMode, pStr, pWide,pHigh, pOpaque, pOmode)
432      int  *pMode, *pWide, *pHigh, *pOpaque, *pOmode;
433      char **pStr;
434 {
435   /* pops up 'Pad' options dialog box */
436 
437   int                rv, oldW, oldH, oldO;
438   static int         firsttime=1;
439   static const char *labels[] = { "\nOk", "\033Cancel" };
440 
441   if (firsttime) {
442     padSbuf[0] = '\0';
443     padBbuf[0] = '\0';
444     padLbuf[0] = '\0';
445 
446     sprintf(padSinst, "Enter a color name ('orange'), %s%s",
447 	    "or an RGB color specification.  ",
448 	    "(e.g. 'r,g,b' or '0xrrggbb')");
449     sprintf(padBinst, "Enter command line options for 'bggen'.  (%s)",
450 	    "No '-w', '-h', or '-g' options allowed.");
451     sprintf(padLinst, "Enter a filename.  The padded image %s",
452 	    "will be the same size as the loaded image.");
453 
454     /* can't create MBUTT or DIALs here, parent window must exist first... */
455 
456     padMode = PAD_SOLID;
457     padInst = padSinst;
458     padOMode = PAD_ORGB;
459     firsttime = 0;
460   }
461 
462 
463   buildPadLists();
464 
465   switch (padMode) {
466   case PAD_SOLID:  strcpy(padBuf, padSbuf);  break;
467   case PAD_BGGEN:  strcpy(padBuf, padBbuf);  break;
468   case PAD_LOAD:   strcpy(padBuf, padLbuf);  break;
469   }
470 
471 
472   gsBuf    = padBuf;         gsBufLen = 256;
473   gsFilter = "";             gsAllow  = 0;
474   gsCurPos = strlen(gsBuf);
475   gsStPos  = gsEnPos = 0;
476 
477   gsw = PAD_PUWIDE - 20;
478   gsh = LINEHIGH+5;
479   gsx = 10;
480   gsy = 40;
481 
482   changedGSBuf();      /* careful!  popW doesn't exist yet! */
483 
484   if (padHaveDooDads) {
485     oldW = (int)padWDial.val;
486     oldH = (int)padHDial.val;
487     oldO = (int)padODial.val;
488   }
489   else { oldW = pWIDE;  oldH = pHIGH;  oldO = 100; }
490 
491 
492 
493   rv = doPopUp("", labels, 2, ISPAD, "xv pad");
494 
495 
496 
497   if (rv == 0) {  /* copy padBuf to appropriate mode buffer */
498     switch (padMode) {
499     case PAD_SOLID:  strcpy(padSbuf, padBuf);  break;
500     case PAD_BGGEN:  strcpy(padBbuf, padBuf);  break;
501     case PAD_LOAD:   strcpy(padLbuf, padBuf);  break;
502     }
503   }
504 
505   if (rv == 1) {   /* cancelled:  restore normal values */
506     DSetVal(&padWDial, (double)oldW);
507     DSetVal(&padHDial, (double)oldH);
508     DSetVal(&padODial, (double)oldO);
509   }
510 
511   XUnmapWindow(theDisp, padWDial.win);
512   XUnmapWindow(theDisp, padHDial.win);
513   XUnmapWindow(theDisp, padODial.win);
514 
515   /* load up return values */
516   *pMode   = padMode;
517   *pStr    = padBuf;
518   *pWide   = (int)padWDial.val;
519   *pHigh   = (int)padHDial.val;
520   *pOpaque = (int)padODial.val;
521   *pOmode  = padOMode;
522 
523   return rv;
524 }
525 
526 
527 /***************************************************/
buildPadLists()528 static void buildPadLists()
529 {
530   /* generates padCol* and padBg* lists used in 'Defaults' MBUTT.  Grabs
531      all the X resources values it can, and adds appropriate defaults */
532 
533   rd_str_cl("foo", "", 1);                    /* rebuild database */
534 
535   build1PadList("color", padColVals, padColNames, &padColLen,
536 		padColDefVals, padColDefNames, padColDefLen);
537 
538   build1PadList("bggen", padBgVals, padBgNames, &padBgLen,
539 		padBgDefVals, padBgDefNames, padBgDefLen);
540 
541   build1PadList("load", padLoadVals, padLoadNames, &padLoadLen,
542 		(const char **) NULL, (const char **) NULL, 0);
543 }
544 
545 
546 /***************************************************/
build1PadList(typstr,vals,nams,lenp,dvals,dnams,dlen)547 static void build1PadList(typstr, vals, nams, lenp, dvals, dnams, dlen)
548      const char  *typstr;
549      const char **vals, **nams;
550      const char **dvals, **dnams;
551      int         *lenp, dlen;
552 {
553   int   i;
554   char  resname[128];
555   char *copy;
556 
557   for (i=0; i<*lenp; i++) {   /* kill old lists */
558     free((char *) nams[i]);
559     free((char *) vals[i]);
560   }
561   *lenp = 0;
562 
563   for (i=0; i<10; i++) {
564     sprintf(resname, "pad.%s.val%d", typstr, i);
565     if (rd_str_cl(resname, "Dialog.Menu.Slot",0)) {    /* got one! */
566       copy = strdup(def_str);
567       if (!copy) continue;
568       vals[*lenp] = copy;
569 
570       sprintf(resname, "pad.%s.name%d", typstr, i);
571       if (rd_str_cl(resname, "Dialog.Menu.Slot",0)) {  /* and it has a name! */
572         copy = strdup(def_str);
573 	if (!copy) { free((char *) vals[*lenp]); continue; }
574       }
575       else {  /* it doesn't have a name.  fabricate one */
576 	copy = malloc((size_t) 32);
577 	if (!copy) { free((char *) vals[*lenp]); continue; }
578 	strncpy(copy, vals[*lenp], (size_t) 31);
579 	copy[31] = '\0';
580       }
581       if (strlen(copy) > (size_t) 20) {   /* fix long names */
582 	char *sp = copy + 18;
583 
584 	*sp++ = '.';  *sp++ = '.';  *sp++ = '.';  *sp++ = '\0';
585       }
586       nams[*lenp] = copy;
587 
588       *lenp = (*lenp) + 1;
589     }
590   }
591 
592 
593   /* add 'built-in' defaults to the lists */
594   for (i=0; i<dlen && *lenp<PAD_MAXDEFLEN; i++) {
595     copy = strdup(dvals[i]);
596     if (!copy) break;
597     vals[*lenp] = copy;
598 
599     copy = strdup(dnams[i]);
600     if (!copy) { free((char *) vals[*lenp]); break; }
601     nams[*lenp] = copy;
602 
603     *lenp = (*lenp) + 1;
604   }
605 }
606 
607 
608 
609 /***************************************************/
ClosePopUp()610 void ClosePopUp()
611 {
612   /* closes popW:  if it's a pop-up, returns 'cancel'.  If it's an alert,
613      simply closes it */
614 
615   if      (popUp == ISALERT) CloseAlert();
616   else if (popUp == ISPOPUP) {
617     popUp = 0;
618     selected = nbts-1;
619   }
620 }
621 
622 
623 /***************************************************/
OpenAlert(txt)624 void OpenAlert(txt)
625      const char *txt;
626 {
627   /* pops up a window with txt displayed in it (*no buttons*).
628      returns immediately.  window is closed by 'CloseAlert()'.
629      No 'PopUp()' calls are allowed while an Alert is displayed. */
630 
631   XEvent event;
632 
633   if (firsttime) createPUD();
634 
635   XStoreName(theDisp, popW, "xv notice");
636   XSetIconName(theDisp, popW, "xv notice");
637   attachPUD();
638 
639   nbts = 0;
640   selected = 0;
641   text = txt;
642 
643   puwide = PUWIDE;  puhigh = PUHIGH;
644   XResizeWindow(theDisp, popW, (u_int) puwide, (u_int) puhigh);
645 
646   /* center last button in window around mouse position, with constraint that
647      window be fully on the screen */
648 
649   CenterMapWindow(popW, puwide/2, puhigh/2, puwide, puhigh);
650   popUp = ISALERT;
651 
652   /* MUST wait for VisibilityNotify event to come in, else we run the risk
653      of UnMapping the window *before* the Map request completed.  This
654      appears to be bad, (It leaves an empty window frame up.) though it
655      generally only happens on slow servers.  Better safe than screwed... */
656 
657   XWindowEvent(theDisp, popW, VisibilityChangeMask, &event);
658   drawPUD(0, 0, puwide, puhigh);
659   XFlush(theDisp);
660 }
661 
662 
663 /***************************************************/
CloseAlert()664 void CloseAlert()
665 {
666   popUp = 0;
667   XUnmapWindow(theDisp, popW);
668 }
669 
670 
671 /***************************************************/
PUCheckEvent(xev)672 int PUCheckEvent(xev)
673      XEvent *xev;
674 {
675   /* check event to see if it's for us.  If so, return 1, otherwise 0 */
676 
677   int rv = 0;
678 
679   if (!popUp) return(0);
680 
681   if (xev->type == Expose) {
682     XExposeEvent *e = (XExposeEvent *) xev;
683     if (e->window == popW) {
684       drawPUD(e->x, e->y, e->width, e->height);
685       rv = 1;
686     }
687     else if (popUp == ISPAD && padHaveDooDads && e->window == padWDial.win)
688       { DRedraw(&padWDial);  rv = 1; }
689     else if (popUp == ISPAD && padHaveDooDads && e->window == padHDial.win)
690       { DRedraw(&padHDial);  rv = 1; }
691     else if (popUp == ISPAD && padHaveDooDads && e->window == padODial.win)
692       { DRedraw(&padODial);  rv = 1; }
693   }
694 
695   else if (xev->type == ButtonPress) {
696     XButtonEvent *e = (XButtonEvent *) xev;
697 
698     if (e->button == Button1) {
699       if (e->window == popW) {
700 	clickPUD(e->x,e->y);
701 	rv = 1;
702       }
703       else if (popUp == ISPAD && padHaveDooDads && e->window == padWDial.win)
704 	{ DTrack(&padWDial, e->x, e->y);  rv = 1; }
705       else if (popUp == ISPAD && padHaveDooDads && e->window == padHDial.win)
706 	{ DTrack(&padHDial, e->x, e->y);  rv = 1; }
707       else if (popUp == ISPAD && padHaveDooDads && e->window == padODial.win)
708 	{ DTrack(&padODial, e->x, e->y);  rv = 1; }
709     }
710   }
711 
712 
713   else if (xev->type == KeyPress) {
714     XKeyEvent *e = (XKeyEvent *) xev;
715     char buf[128];  KeySym ks;
716     int stlen, i, shift, ck;
717 
718     stlen = XLookupString(e,buf,128,&ks,(XComposeStatus *) NULL);
719     shift = e->state & ShiftMask;
720     ck = CursorKey(ks, shift, 0);
721     buf[stlen] = '\0';
722 
723     RemapKeyCheck(ks, buf, &stlen);
724 
725     /* check cursor keys, which may or may not have a str assoc'd with them */
726     if (popUp==ISGETSTR || popUp==ISGRAB || popUp==ISPAD) {
727       if      (ck==CK_LEFT)  { doGetStrKey('\002'); rv = 1; }
728       else if (ck==CK_RIGHT) { doGetStrKey('\006'); rv = 1; }
729     }
730 
731     if (stlen && !rv) {      /* note: we accept kbd accel's in any win */
732       if (buf[0] == '\r') buf[0] = '\n';
733 
734       /* search for character in accel table */
735       for (i=0; i<nbts; i++) {
736 	if (buf[0] == accel[i] && buf[0] != ' ') {
737 	  FakeButtonPress(&bts[i]);
738 	  rv = 1;
739 	}
740       }
741 
742       if (!rv && buf[0]=='\033' && nbts==1) { /* ESC accepted in 1-but pu's */
743 	FakeButtonPress(&bts[0]);
744 	rv = 1;
745       }
746 
747       if (!rv && (popUp==ISGETSTR || popUp==ISGRAB || popUp==ISPAD)) {
748 	if (e->window == popW) { doGetStrKey(buf[0]);  rv = 1; }
749       }
750     }
751 
752     if (!stlen) rv = 1;  /* quietly eat mute keys */
753   }
754 
755 
756   else if (xev->type == ClientMessage) {
757     Atom proto, delwin;
758     XClientMessageEvent *client_event = (XClientMessageEvent *) xev;
759 
760     proto  = XInternAtom(theDisp, "WM_PROTOCOLS", FALSE);
761     delwin = XInternAtom(theDisp, "WM_DELETE_WINDOW", FALSE);
762 
763     if (client_event->message_type == proto &&
764 	client_event->data.l[0]    == delwin) {
765       /* it's a WM_DELETE_WINDOW event */
766 
767       if (client_event->window == popW) {
768 	FakeButtonPress(&bts[(nbts>1) ? nbts-1 : 0]);
769 	rv = 1;
770       }
771     }
772   }
773 
774   if (rv==0 && (xev->type == KeyPress || xev->type == ButtonPress)) {
775     XBell(theDisp, 0);
776     rv = 1;            /* eat it */
777   }
778 
779   return rv;
780 }
781 
782 
783 
784 #define TR_MAXLN 10
785 
786 /***************************************************/
TextRect(win,txt,x,y,w,h,fg)787 static void TextRect(win, txt, x, y, w, h, fg)
788      Window      win;
789      const char *txt;
790      int         x,y,w,h;
791      u_long      fg;
792 {
793   /* draws semi-complex strings in a rectangle */
794 
795   const char *sp;
796   const char *ep;
797   const char *oldep;
798   const char *start[TR_MAXLN];
799   int         i, inbreak, lineno, top, hardcr, maxln, len[TR_MAXLN];
800 
801   XSetForeground(theDisp, theGC, fg);
802 
803   sp = txt;  lineno = hardcr = 0;
804 
805   maxln = h / LINEHIGH;
806   RANGE(maxln,0,TR_MAXLN);
807   while (*sp && lineno<maxln) {
808 
809     /* drop off any leading spaces (except on first line or after \n) */
810     if (sp!=txt && !hardcr) {
811       while (*sp==' ') sp++;
812     }
813 
814     hardcr = 0;   ep = sp;
815 
816     /* increment ep until we   A) get too wide, B) hit eos or
817        C) hit a '\n' character */
818 
819     /* NOTE: ep points to the character AFTER the end of the line */
820 
821     while (XTextWidth(mfinfo, sp, (int)(ep-sp))<= w && *ep && *ep!='\n') ep++;
822     if (*ep=='\n') { ep++;  hardcr=1; }   /* eat newline */
823 
824     /* if we got too wide, back off until we find a break position
825        (last char before a space or a '/') */
826 
827     if (XTextWidth(mfinfo, sp, (int)(ep-sp)) > w) {
828       oldep = ep;  inbreak = 0;
829       while (ep!=sp) {
830 	ep--;
831 	if ( inbreak && *ep!=' ') { ep++;  break; }
832 	if (!inbreak && *ep==' ') inbreak = 1;
833 	if (*ep=='/') { ep++; break; }
834       }
835       if (ep==sp) ep = oldep-1;  /* can't break this line.  oh well */
836     }
837 
838     start[lineno] = sp;  len[lineno] = ep-sp;
839 
840     /* make sure we don't print a trailing '\n' character! */
841     if (len[lineno] > 0) {
842       while (sp[len[lineno]-1] == '\n') len[lineno] = len[lineno] - 1;
843     }
844 
845     sp = ep;
846     lineno++;
847   }
848 
849   top = y + h/2 + (ASCENT-DESCENT)/2 - ((lineno-1)*LINEHIGH)/2;
850   if (top<y+ASCENT) top = y+ASCENT;
851 
852   for (i=0, y=top; i<lineno; i++, y+=LINEHIGH) {
853     if (start[i][0] != '\n')
854       XDrawString(theDisp, win, theGC, x, y, start[i], len[i]);
855   }
856 }
857 
858 
859 /***************************************************/
createPUD()860 static void createPUD()
861 {
862   popW = CreateWindow("xv confirm", "XVconfirm", "+0+0",
863 		      PUWIDE, PUHIGH, infofg, infobg, 0);
864   if (!popW) FatalError("can't create popup window!");
865 
866   XSelectInput(theDisp, popW, ExposureMask | ButtonPressMask | KeyPressMask
867 	       | VisibilityChangeMask);
868   /* XSetTransientForHint(theDisp, popW, mainW); */
869 
870   XDefineCursor(theDisp, popW, arrow);
871   bts = (BUTT *) NULL;
872   nbts = selected = firsttime = 0;
873 }
874 
875 
876 /***************************************************/
attachPUD()877 static void attachPUD()
878 {
879   /* used to make PUD a transient window of something.  Doesn't
880      do anything anymore, as I got tired of having window layering
881      shifted around everytime a popup window happened.  Screw the
882      business about having the popup iconify when you iconify the
883      appropriate XV window.  There generally ISN'T an appropriate
884      XV window... */
885 }
886 
887 
888 /***************************************************/
drawPUD(x,y,w,h)889 static void drawPUD(x,y,w,h)
890 int x,y,w,h;
891 {
892   int  i,xt,yt;
893   XRectangle xr;
894 
895   xr.x = x;  xr.y = y;  xr.width = w;  xr.height = h;
896   XSetClipRectangles(theDisp, theGC, 0,0, &xr, 1, Unsorted);
897 
898   XSetForeground(theDisp, theGC, infofg);
899   XSetBackground(theDisp, theGC, infobg);
900 
901   if (popUp == ISGRAB) {
902     xt = 10;  yt = 10;
903     TextRect(popW, text, xt, yt, puwide-10-xt, gsy-20, infofg);
904     drawGSBuf();
905 
906     XSetForeground(theDisp, theGC, infofg);
907     DrawString(popW, 10,        gsy+ASCENT+4, DELAYSTR);
908     DrawString(popW, gsx+gsw+5, gsy+ASCENT+4, SECSTR);
909 
910     CBRedraw(&ahideCB);
911   }
912 
913   else if (popUp == ISPAD) {
914     drawGSBuf();
915 
916     XSetForeground(theDisp, theGC, infofg);
917     DrawString(popW, 10+44,10+ASCENT+4,"Pad Method:");
918 
919     MBRedraw(&padMthdMB);
920     MBRedraw(&padDfltMB);
921     DRedraw (&padWDial);
922     DRedraw (&padHDial);
923     BTRedraw(&padDButt);
924     BTRedraw(&padOMButt);
925 
926     XSetForeground(theDisp, theGC, infofg);
927     drawPadOMStr();
928 
929     XDrawRectangle(theDisp, popW, theGC, 10, puhigh-140, 16+2*74+84, 130);
930     Draw3dRect(popW, 10+1, puhigh-140+1, 16+2*74+84-2, 130-2,
931 	       R3D_IN,2,hicol,locol,infobg);
932     XSetForeground(theDisp, theGC, infofg);
933     CenterString(popW, 16+1+75-13, puhigh-16-100-12, "New Image Size");
934 
935     if (ctrlColor) {
936       XSetForeground(theDisp, theGC, locol);
937       XDrawLine(theDisp, popW, theGC, 16+1+75+75+5, puhigh-140 + 6+8,
938 		16+1+75+75+5, puhigh-10-4);
939     }
940 
941 
942     XSetForeground(theDisp, theGC, infofg);
943     XDrawRectangle(theDisp, popW, theGC, 268, puhigh-140,
944 		   (u_int) puwide - 10 - 268, 130-BUTTH-10);
945     Draw3dRect(popW, 268+1, puhigh-140+1, (u_int) puwide -10-268-2,
946 	       130-2 - BUTTH-10, R3D_IN,2,hicol,locol,infobg);
947 
948     TextRect(popW,padInst,268+5, puhigh-140+3, puwide-10-268-10,
949 	     130-6 - BUTTH-10, infofg);
950   }
951 
952   else {
953     XCopyPlane(theDisp, iconPix, popW, theGC, 0,0, icon_width, icon_height,
954 	       10,10+(puhigh-30-BUTTH-icon_height)/2, 1L);
955 
956     xt = 10+icon_width+20;  yt = 10;
957 
958     if (popUp == ISGETSTR) {
959       TextRect(popW, text, xt, yt, puwide-10-xt, gsy-20, infofg);
960       drawGSBuf();
961     }
962     else TextRect(popW,text,xt,yt,puwide-10-xt,puhigh-10-BUTTH-20,infofg);
963   }
964 
965 
966   for (i=0; i<nbts; i++) BTRedraw(&bts[i]);
967   XSetClipMask(theDisp, theGC, None);
968 }
969 
970 
971 /***************************************************/
drawPadOMStr()972 static void drawPadOMStr()
973 {
974   CenterString(popW, padODial.x + (padODial.w - 13)/2,
975 	       puhigh-16-100-12, padOMStr[padOMode]);
976 }
977 
978 /***************************************************/
clickPUD(x,y)979 static void clickPUD(x,y)
980      int x,y;
981 {
982   int i;
983   BUTT *bp = NULL;
984 
985   for (i=0; i<nbts; i++) {
986     bp = &bts[i];
987     if (PTINRECT(x, y, bp->x, bp->y, bp->w, bp->h)) break;
988   }
989 
990   if (i<nbts && bp && BTTrack(bp)) {
991     popUp = 0;  selected = i;  return;
992   }
993 
994   if (popUp==ISGRAB && CBClick(&ahideCB, x,y)) CBTrack(&ahideCB);
995 
996   else if (popUp == ISPAD) {
997     if (PTINRECT(x, y, padDButt.x, padDButt.y, padDButt.w, padDButt.h)) {
998       if (BTTrack(&padDButt)) {
999 	DSetVal(&padWDial, (double)pWIDE);
1000 	DSetVal(&padHDial, (double)pHIGH);
1001       }
1002     }
1003 
1004     else if (PTINRECT(x,y,padOMButt.x,padOMButt.y,padOMButt.w,padOMButt.h)) {
1005       if (BTTrack(&padOMButt)) {
1006 	XSetForeground(theDisp, theGC, infobg);
1007 	drawPadOMStr();
1008 	padOMode = (padOMode + 1) % PAD_OMAX;
1009 	XSetForeground(theDisp, theGC, infofg);
1010 	drawPadOMStr();
1011       }
1012     }
1013 
1014 
1015     else if (MBClick(&padMthdMB, x,y)) {
1016       i = MBTrack(&padMthdMB);
1017       if (i<0 || i==padMode) return;
1018 
1019       switch (i) {
1020       case PAD_SOLID:
1021 	strcpy(padBuf, padSbuf);
1022 	padDfltMB.list  = padColNames;
1023 	padDfltMB.nlist = padColLen;
1024 	padInst = padSinst;
1025 	break;
1026       case PAD_BGGEN:
1027 	strcpy(padBuf, padBbuf);
1028 	padDfltMB.list  = padBgNames;
1029 	padDfltMB.nlist = padBgLen;
1030 	padInst = padBinst;
1031 	break;
1032       case PAD_LOAD:
1033 	strcpy(padBuf,  padLbuf);
1034 	padDfltMB.list  = padLoadNames;
1035 	padDfltMB.nlist = padLoadLen;
1036 	padInst = padLinst;
1037 	break;
1038       default: break;             /* shouldn't happen */
1039       }
1040 
1041       gsCurPos = strlen(gsBuf);
1042       gsStPos = gsEnPos = 0;
1043       changedGSBuf();
1044       if (ctrlColor)
1045 	XClearArea(theDisp, popW, gsx+3,gsy+3,
1046 		   (u_int)gsw-5, (u_int)gsh-5, False);
1047       else
1048 	XClearArea(theDisp, popW, gsx+1,gsy+1,
1049 		   (u_int)gsw-1, (u_int)gsh-1, False);
1050       drawGSBuf();
1051 
1052       BTSetActive(&bts[0], (int) strlen(gsBuf));
1053 
1054       MBSelect(&padMthdMB, i);
1055       MBSetActive(&padDfltMB, 1);
1056       DSetActive (&padWDial,  (i!=PAD_LOAD));
1057       DSetActive (&padHDial,  (i!=PAD_LOAD));
1058 
1059       XClearArea(theDisp, popW, 184+5, puhigh-140+3,
1060 		 (u_int) puwide-10-184-10, 130-6 - BUTTH-10, True);
1061 
1062       padMode = i;
1063     }
1064 
1065     else if (MBClick(&padDfltMB, x,y)) {
1066       i = MBTrack(&padDfltMB);
1067       if (i<0) return;
1068 
1069       if      (padMode == PAD_SOLID) strcpy(padBuf, padColVals[i]);
1070       else if (padMode == PAD_BGGEN) strcpy(padBuf, padBgVals[i]);
1071       else if (padMode == PAD_LOAD)  strcpy(padBuf, padLoadVals[i]);
1072 
1073       gsCurPos = strlen(gsBuf);
1074       gsStPos = gsEnPos = 0;
1075       changedGSBuf();
1076       if (ctrlColor)
1077 	XClearArea(theDisp, popW, gsx+3,gsy+3,
1078 		   (u_int)gsw-5, (u_int)gsh-5, False);
1079       else
1080 	XClearArea(theDisp, popW, gsx+1,gsy+1,
1081 		   (u_int)gsw-1, (u_int)gsh-1, False);
1082       drawGSBuf();
1083 
1084       BTSetActive(&bts[0], (int) strlen(gsBuf));
1085     }
1086   }
1087 }
1088 
1089 
1090 
1091 /***************************************************/
doGetStrKey(c)1092 static void doGetStrKey(c)
1093      int c;
1094 {
1095   if (doGSKey(c)) XBell(theDisp, 0);
1096 }
1097 
1098 
1099 /***************************************************/
doGSKey(c)1100 static int doGSKey(c)
1101      int c;
1102 {
1103   /* handle characters typed at GetStrPopUp window.  Button accel. keys
1104      have already been checked for elsewhere.  Practical upshot is that
1105      we don't have to do anything with ESC or Return (as these will normally
1106      be Cancel and Ok buttons)
1107 
1108      Normally returns '0'.  Returns '1' if character wasn't accepted, for
1109      whatever reason. */
1110 
1111   int i, len, flen;
1112 
1113   len = strlen(gsBuf);
1114   if (gsFilter) flen = strlen(gsFilter);
1115            else flen = 0;
1116 
1117 
1118   if (c>=' ' && c<'\177') {              /* 'NORMAL' CHARACTERS */
1119     if (flen) {                          /* check filter string */
1120       for (i=0; i<flen && c!=gsFilter[i]; i++);
1121       if (!gsAllow && i< flen) return 1;    /* found in 'disallow' filter */
1122       if ( gsAllow && i==flen) return 1;    /* not found in 'allow' filter */
1123     }
1124 
1125     if (len >= gsBufLen-1) return 1;     /* at max length */
1126 
1127     xvbcopy(&gsBuf[gsCurPos], &gsBuf[gsCurPos+1], (size_t) len-gsCurPos+1);
1128     gsBuf[gsCurPos]=c;  gsCurPos++;
1129   }
1130 
1131 
1132   else if (c=='\010') {                 /* BS */
1133     if (gsCurPos==0) return 1;                     /* at beginning of str */
1134     xvbcopy(&gsBuf[gsCurPos], &gsBuf[gsCurPos-1], (size_t) len-gsCurPos+1);
1135     gsCurPos--;
1136   }
1137 
1138   else if (c=='\025') {                 /* ^U: clear entire line */
1139     gsBuf[0] = '\0';
1140     gsCurPos = 0;
1141   }
1142 
1143   else if (c=='\013') {                 /* ^K: clear to end of line */
1144     gsBuf[gsCurPos] = '\0';
1145   }
1146 
1147   else if (c=='\001') {                 /* ^A: move to beginning */
1148     gsCurPos = 0;
1149   }
1150 
1151   else if (c=='\005') {                 /* ^E: move to end */
1152     gsCurPos = len;
1153   }
1154 
1155   else if (c=='\004' || c=='\177') {    /* ^D or DEL: delete character at gsCurPos */
1156     if (gsCurPos==len) return 1;
1157     xvbcopy(&gsBuf[gsCurPos+1], &gsBuf[gsCurPos], (size_t) len-gsCurPos);
1158   }
1159 
1160   else if (c=='\002') {                 /* ^B: move backwards char */
1161     if (gsCurPos==0) return 1;
1162     gsCurPos--;
1163   }
1164 
1165   else if (c=='\006') {                 /* ^F: move forwards char */
1166     if (gsCurPos==len) return 1;
1167     gsCurPos++;
1168   }
1169 
1170   else return 1;                        /* unhandled character */
1171 
1172   changedGSBuf();      /* compute gsEnPos, gsStPos */
1173 
1174   if (ctrlColor)
1175     XClearArea(theDisp, popW, gsx+3,gsy+3, (u_int)gsw-5, (u_int)gsh-5, False);
1176   else
1177     XClearArea(theDisp, popW, gsx+1,gsy+1, (u_int)gsw-1, (u_int)gsh-1, False);
1178 
1179   drawGSBuf();
1180 
1181   if (popUp == ISGETSTR || popUp == ISPAD) {
1182     /* if we have a string of any sort, turn on the default '\n' button
1183        (if there is one) */
1184     for (i=0; i<nbts && accel[i]!='\n'; i++);
1185     if (i<nbts) BTSetActive(&bts[i], (strlen(gsBuf) > (size_t) 0));
1186   }
1187   else if (popUp == ISGRAB) {
1188     /* need a string of length 1 to enable Grab (bts[0]), and a string
1189        with an atoi() of at least '1' to enable AutoGrab (bts[1]) */
1190     BTSetActive(&bts[0], (strlen(gsBuf) > (size_t) 0));
1191     BTSetActive(&bts[1], (strlen(gsBuf)>(size_t)0 && atoi(gsBuf)>(size_t)0));
1192   }
1193 
1194   return(0);
1195 }
1196 
1197 
1198 
1199 /***************************************************/
changedGSBuf()1200 static void changedGSBuf()
1201 {
1202   /* cursor position (or whatever) may have changed.  adjust displayed
1203      portion of gsBuf */
1204 
1205   int len;
1206 
1207   len = strlen(gsBuf);
1208 
1209   if (gsCurPos < gsStPos) gsStPos = gsCurPos;
1210   if (gsCurPos > gsEnPos) gsEnPos = gsCurPos;
1211 
1212   if (gsStPos>len) gsStPos = (len>0) ? len-1 : 0;
1213   if (gsEnPos>len) gsEnPos = (len>0) ? len-1 : 0;
1214 
1215   /* while substring is shorter than window, inc enPos */
1216 
1217   while (XTextWidth(mfinfo, &gsBuf[gsStPos], gsEnPos-gsStPos) < (gsw-6)
1218 	 && gsEnPos<len) { gsEnPos++; }
1219 
1220   /* while substring is longer than window, dec enpos, unless enpos==curpos,
1221      in which case, inc stpos */
1222 
1223   while (XTextWidth(mfinfo, &gsBuf[gsStPos], gsEnPos-gsStPos) > (gsw-6)) {
1224     if (gsEnPos != gsCurPos) gsEnPos--;
1225     else gsStPos++;
1226   }
1227 }
1228 
1229 
1230 /***************************************************/
drawGSBuf()1231 static void drawGSBuf()
1232 {
1233   /* draw edittext thingy in GetStrPopUp window */
1234 
1235   int cpos;
1236 
1237   XSetForeground(theDisp, theGC, infofg);
1238   XDrawRectangle(theDisp, popW, theGC, gsx, gsy, (u_int) gsw, (u_int) gsh);
1239   Draw3dRect(popW, gsx+1, gsy+1, (u_int) gsw-2, (u_int) gsh-2,
1240 	     R3D_IN, 2, hicol,locol,infobg);
1241 
1242   XSetForeground(theDisp, theGC, infofg);
1243 
1244   if (gsStPos>0) {  /* draw a "there's more over here" doowah */
1245     XDrawLine(theDisp, popW, theGC, gsx+1, gsy+1, gsx+1, gsy + gsh-1);
1246     XDrawLine(theDisp, popW, theGC, gsx+2, gsy+1, gsx+2, gsy + gsh-1);
1247     XDrawLine(theDisp, popW, theGC, gsx+3, gsy+1, gsx+3, gsy + gsh-1);
1248   }
1249 
1250   if ((size_t) gsEnPos < strlen(gsBuf)) {
1251     /* draw a "there's more over here" doowah */
1252     XDrawLine(theDisp, popW, theGC, gsx+gsw-3, gsy+1, gsx+gsw-3, gsy+gsh-1);
1253     XDrawLine(theDisp, popW, theGC, gsx+gsw-2, gsy+1, gsx+gsw-2, gsy+gsh-1);
1254     XDrawLine(theDisp, popW, theGC, gsx+gsw-1, gsy+1, gsx+gsw-1, gsy+gsh-1);
1255   }
1256 
1257   XDrawString(theDisp, popW, theGC, gsx+4, gsy+ASCENT+4,
1258 	      gsBuf+gsStPos, gsEnPos-gsStPos);
1259 
1260   cpos = gsx+XTextWidth(mfinfo, &gsBuf[gsStPos], gsCurPos-gsStPos);
1261   XDrawLine(theDisp,popW,theGC, 4+cpos, gsy+3,         4+cpos, gsy+2+CHIGH+1);
1262   XDrawLine(theDisp,popW,theGC, 4+cpos, gsy+2+CHIGH+1, 6+cpos, gsy+2+CHIGH+3);
1263   XDrawLine(theDisp,popW,theGC, 4+cpos, gsy+2+CHIGH+1, 2+cpos, gsy+2+CHIGH+3);
1264 }
1265