1 /*
2  * xvbutt.c - regular, 'radio', 'checkbox', and 'menu' pushbuttons
3  *
4  * callable functions:
5  *
6  *   BTCreate()             -  create a button
7  *   BTSetActive()          -  change 'active' status of button
8  *   BTRedraw()             -  redraw button
9  *   BTTrack()              -  clicked in button.  track until mouse up
10  *
11  *   RBCreate()             -  create an RBUTT and append to supplied list
12  *   RBRedraw()             -  redraw one or all RBUTTs in a list
13  *   RBSelect()             -  change selected item in list of RBUTTs
14  *   RBWhich()              -  returns index of selected RBUTT in list
15  *   RBCount()              -  returns # of RBUTTs in list
16  *   RBSetActive()          -  sets active status of an RBUTT
17  *   RBClick()              -  finds clicked-on rb in a list
18  *   RBTrack()              -  tracks rb after click, until release
19  *
20  *   CBCreate()             -  create a CBUTT (checkbox button)
21  *   CBRedraw()             -  redraw a CBUTT
22  *   CBSetActive()          -  change active status of a CBUTT
23  *   CBClick()              -  returns true if given CB was clicked on
24  *   CBTrack()              -  tracks CBUTT after click, until release
25  *
26  *   MBCreate()             -  create a MBUTT (menu button)
27  *   MBRedraw()             -  redraw a MBUTT
28  *   MBSetActive()          -  change active status of a MBUTT
29  *   MBWhich()              -  returns # of first checked selection
30  *   MBSelect()             -  similar to RBSelect() ...
31  *   MBClick()              -  returns true if given MB was clicked on
32  *   MBTrack()              -  tracks MBUTT after click, until release
33  */
34 
35 
36 #include "copyright.h"
37 #include "xv.h"
38 
39 #include "bits/cboard50"
40 #include "bits/rb_frame"
41 #include "bits/rb_frame1"
42 #include "bits/rb_top"
43 #include "bits/rb_bot"
44 #include "bits/rb_dtop"
45 #include "bits/rb_dbot"
46 #include "bits/rb_body"
47 #include "bits/rb_dot"
48 #include "bits/cb_check"
49 #include "bits/mb_chk"
50 
51 
52 static Pixmap cboard50 = (Pixmap) NULL;   /* 50% gray checkerboard */
53 
54 
55 static int    rbpixmade = 0;
56 static Pixmap rb_on, rb_on1, rb_off, rb_off1;
57 
58 static int    cbpixmade = 0;
59 static Pixmap cbcheck;
60 
61 static int    mbpixmade = 0;
62 static Pixmap mbchk;
63 
64 static void drawRB     PARM((RBUTT *, int));
65 static void drawCB     PARM((CBUTT *, int));
66 
67 
68 
69 /******************* BUTT ROUTINES ************************/
70 
71 
72 
73 /**********************************************/
BTCreate(bp,win,x,y,w,h,str,fg,bg,hi,lo)74 void BTCreate(bp,win,x,y,w,h,str,fg,bg,hi,lo)
75      BUTT         *bp;
76      Window        win;
77      int           x,y;
78      unsigned int  w,h;
79      const char   *str;
80      unsigned long fg,bg,hi,lo;
81 {
82   bp->win = win;
83   bp->x = x;  bp->y = y;  bp->w = w;  bp->h = h;
84   bp->str = str;
85   bp->fg = fg;  bp->bg = bg;  bp->hi = hi;  bp->lo = lo;
86   bp->lit = 0;
87   bp->active = 1;
88   bp->toggle = 0;
89   bp->pix = None;
90   bp->colorpix = 0;
91   bp->style = 0;
92   bp->fwidth = 3;
93 
94   if (!cboard50) {
95     cboard50 = MakePix1(rootW, cboard50_bits, cboard50_width, cboard50_height);
96     if (!cboard50) FatalError("Unable to create cboard50 bitmap\n");
97   }
98 }
99 
100 
101 
102 /**********************************************/
BTSetActive(bp,act)103 void BTSetActive(bp,act)
104 BUTT         *bp;
105 int           act;
106 {
107   if (bp->active != act) {
108     bp->active = act;
109     BTRedraw(bp);
110   }
111 }
112 
113 
114 
115 /**********************************************/
BTRedraw(bp)116 void BTRedraw(bp)
117 BUTT *bp;
118 {
119   int          x,y,r,x1,y1;
120   unsigned int w,h;
121   XPoint       tpts[10], bpts[10], ipts[5];
122 
123   x = bp->x;  y=bp->y;  w=bp->w;  h=bp->h;  r=bp->fwidth;
124 
125   if (!bp->active) bp->lit = 0;
126   if (bp->lit) {
127     r -= 1;
128     if (r<0) r = 0;
129   }
130 
131   if (!ctrlColor) {
132     /* set up 'ipts' */
133     ipts[0].x = x+r;        ipts[0].y = y+r;         /* topleft */
134     ipts[1].x = x+r;        ipts[1].y = y+h-r;       /* botleft */
135     ipts[2].x = x+w-r;      ipts[2].y = y+h-r;       /* botright */
136     ipts[3].x = x+w-r;      ipts[3].y = y+r;         /* topright */
137     ipts[4].x = ipts[0].x;  ipts[4].y = ipts[0].y;   /* close path */
138 
139     /* top left polygon */
140     tpts[0].x = x;            tpts[0].y = y;
141     tpts[1].x = x;            tpts[1].y = y+h;
142     tpts[2].x = ipts[1].x;    tpts[2].y = ipts[1].y;
143     tpts[3].x = ipts[0].x;    tpts[3].y = ipts[0].y;
144     tpts[4].x = ipts[3].x;    tpts[4].y = ipts[3].y;
145     tpts[5].x = x+w;          tpts[5].y = y;
146     tpts[6].x = x;            tpts[6].y = y;
147 
148     /* bot left polygon */
149     bpts[0].x = x;            bpts[0].y = y+h;
150     bpts[1].x = ipts[1].x;    bpts[1].y = ipts[1].y;
151     bpts[2].x = ipts[2].x;    bpts[2].y = ipts[2].y;
152     bpts[3].x = ipts[3].x;    bpts[3].y = ipts[3].y;
153     bpts[4].x = x+w;          bpts[4].y = y;
154     bpts[5].x = x+w;          bpts[5].y = y+h;
155     bpts[6].x = x;            bpts[6].y = y+h;
156 
157 
158     /* clear button and draw frame */
159     XSetForeground(theDisp, theGC, bp->bg);
160     XFillRectangle(theDisp, bp->win, theGC, x, y, w, h);
161     XSetForeground(theDisp, theGC, bp->fg);
162     XDrawRectangle(theDisp, bp->win, theGC, x, y, w, h);
163 
164     XSetForeground(theDisp, theGC, bp->fg);
165     XSetFillStyle(theDisp, theGC, FillStippled);
166     XSetStipple(theDisp, theGC, cboard50);
167     XFillPolygon(theDisp, bp->win, theGC, bpts, 7, Nonconvex, CoordModeOrigin);
168     XSetFillStyle(theDisp,theGC,FillSolid);
169 
170     XSetForeground(theDisp, theGC, bp->fg);
171     XDrawLines(theDisp, bp->win, theGC, ipts, 5, CoordModeOrigin);  /* inset */
172 
173     XDrawLine(theDisp, bp->win, theGC, x+1,             y + 1,
174 	      ipts[0].x, ipts[0].y);
175     XDrawLine(theDisp, bp->win, theGC, x+1,             y + (int) h - 1,
176 	      ipts[1].x, ipts[1].y);
177     XDrawLine(theDisp, bp->win, theGC, x + (int) w - 1, y + (int) h - 1,
178 	      ipts[2].x, ipts[2].y);
179     XDrawLine(theDisp, bp->win, theGC, x + (int) w - 1, y+1,
180 	      ipts[3].x, ipts[3].y);
181 
182     if (bp->lit) {
183       XDrawRectangle(theDisp, bp->win, theGC, x+2, y+2, w-4, h-4);
184       XDrawRectangle(theDisp, bp->win, theGC, x+1, y+1, w-2, h-2);
185     }
186   }
187 
188   else {   /* ctrlColor */
189     XSetForeground(theDisp, theGC, bp->bg);
190     XFillRectangle(theDisp, bp->win, theGC, x+1, y+1, w-1, h-1);
191 
192     Draw3dRect(bp->win, x+1, y+1, w-2, h-2, R3D_OUT, bp->fwidth,
193 	       bp->hi, bp->lo, bp->bg);
194 
195     XSetForeground(theDisp, theGC, bp->fg);
196     XDrawRectangle(theDisp, bp->win, theGC, x, y, w, h);
197 
198     if (bp->lit)
199       XDrawRectangle(theDisp, bp->win, theGC, x+1, y+1, w-2, h-2);
200   }
201 
202 
203 
204 
205   XSetForeground(theDisp, theGC, bp->fg);
206 
207   if (bp->pix != None) {                    /* draw pixmap centered in butt */
208     x1 = x+(1+w-bp->pw)/2;
209     y1 = y+(1+h-bp->ph)/2;
210 
211     XSetBackground(theDisp, theGC, bp->bg);
212 
213     if (bp->colorpix)
214       XCopyArea (theDisp,bp->pix, bp->win, theGC, 0,0,bp->pw,bp->ph, x1,y1);
215     else
216       XCopyPlane(theDisp,bp->pix, bp->win, theGC, 0,0,bp->pw,bp->ph, x1,y1,1L);
217 
218     if (!bp->active) DimRect(bp->win, x1,y1, bp->pw, bp->ph, bp->bg);
219   }
220 
221   else {                                    /* draw string centered in butt */
222     x1 = CENTERX(mfinfo, x + w/2, bp->str);
223     y1 = CENTERY(mfinfo, y + h/2);
224 
225     if (bp->active) {
226       DrawString(bp->win, x1,y1, bp->str);
227     }
228     else {  /* stipple if not active */
229       XSetFillStyle(theDisp, theGC, FillStippled);
230       XSetStipple(theDisp, theGC, dimStip);
231       DrawString(bp->win, x1,y1, bp->str);
232       XSetFillStyle(theDisp,theGC,FillSolid);
233     }
234   }
235 
236 }
237 
238 
239 
240 /**********************************************/
BTTrack(bp)241 int BTTrack(bp)
242 BUTT *bp;
243 {
244   /* called when we've gotten a click inside 'bp'.  returns 1 if button
245      was still selected lit when mouse was released. */
246 
247   Window       rW, cW;
248   int          x, y, rx, ry, rval, inval;
249   unsigned int mask;
250 
251   if (!bp->active) return 0;   /* inactive button */
252 
253   inval = bp->lit;
254   bp->lit = !bp->lit;
255 
256   BTRedraw(bp);  XFlush(theDisp);
257   Timer(120);  /* long enough for turn on to be visible */
258 
259   while (XQueryPointer(theDisp,bp->win,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
260     if (!(mask & Button1Mask)) break;    /* button released */
261 
262     if (bp->lit==inval && PTINRECT(x, y, bp->x, bp->y, bp->w, bp->h)) {
263       bp->lit = !inval;  BTRedraw(bp);  XFlush(theDisp);
264     }
265 
266     if (bp->lit!=inval && !PTINRECT(x, y, bp->x, bp->y, bp->w, bp->h)) {
267       bp->lit = inval;  BTRedraw(bp);  XFlush(theDisp);
268     }
269   }
270 
271   rval = (bp->lit != inval);
272 
273   if (bp->lit && !bp->toggle)
274     { bp->lit = 0;  BTRedraw(bp);  XFlush(theDisp); }
275 
276   return(rval);
277 }
278 
279 
280 
281 
282 /******************* RBUTT ROUTINES ************************/
283 
284 
285 #define RBSIZE rb_frame_width
286 
287 
288 /***********************************************/
RBCreate(rblist,win,x,y,str,fg,bg,hi,lo)289 RBUTT *RBCreate(rblist, win, x,y,str, fg, bg, hi, lo)
290       RBUTT        *rblist;
291       Window        win;
292       int           x,y;
293       const char   *str;
294       unsigned long fg,bg,hi,lo;
295 {
296   /* mallocs an RBUTT, fills in the fields, and appends it to rblist
297      if rblist is NULL, this is the first rb in the list.  It will
298      be made the 'selected' one
299 
300      Note: no need to check return status.  It'll fatal error if it
301      can't malloc */
302 
303   RBUTT *rb, *rbptr;
304   Pixmap rb_frame, rb_frame1, rb_top, rb_bot, rb_dtop, rb_dbot, rb_body,
305          rb_dot;
306 
307   rb = (RBUTT *) malloc(sizeof(RBUTT));
308   if (!rb) FatalError("couldn't malloc RBUTT");
309 
310   /* fill in the fields of the structure */
311   rb->win      = win;
312   rb->x        = x;
313   rb->y        = y;
314   rb->str      = str;
315   rb->selected = 0;
316   rb->active   = 1;
317   rb->next     = (struct rbutt *) NULL;
318   rb->fg       = fg;
319   rb->bg       = bg;
320   rb->hi       = hi;
321   rb->lo       = lo;
322 
323   if (rblist) {            /* append to end of list */
324     rbptr = rblist;
325     while (rbptr->next) rbptr = (RBUTT *) rbptr->next;
326     rbptr->next = (struct rbutt *) rb;
327   }
328   else {                   /* this is the first one in the list.  select it */
329     rb->selected = 1;
330   }
331 
332 
333   /* and, on an unrelated note, if the RB pixmaps haven't been created yet,
334      do so.  We'll be needing them, y'see... */
335 
336   if (!rbpixmade) {
337     rb_frame  = MakePix1(rootW, rb_frame_bits,  RBSIZE, RBSIZE);
338     rb_frame1 = MakePix1(rootW, rb_frame1_bits, RBSIZE, RBSIZE);
339     rb_top    = MakePix1(rootW, rb_top_bits,    RBSIZE, RBSIZE);
340     rb_bot    = MakePix1(rootW, rb_bot_bits,    RBSIZE, RBSIZE);
341     rb_dtop   = MakePix1(rootW, rb_dtop_bits,   RBSIZE, RBSIZE);
342     rb_dbot   = MakePix1(rootW, rb_dbot_bits,   RBSIZE, RBSIZE);
343     rb_body   = MakePix1(rootW, rb_body_bits,   RBSIZE, RBSIZE);
344     rb_dot    = MakePix1(rootW, rb_dot_bits,    RBSIZE, RBSIZE);
345 
346     rb_on     = XCreatePixmap(theDisp, rootW, RBSIZE, RBSIZE, dispDEEP);
347     rb_on1    = XCreatePixmap(theDisp, rootW, RBSIZE, RBSIZE, dispDEEP);
348     rb_off    = XCreatePixmap(theDisp, rootW, RBSIZE, RBSIZE, dispDEEP);
349     rb_off1   = XCreatePixmap(theDisp, rootW, RBSIZE, RBSIZE, dispDEEP);
350 
351     if (!rb_frame || !rb_frame1 || !rb_top || !rb_bot || !rb_dtop ||
352 	!rb_dbot  || !rb_body   || !rb_dot || !rb_on  || !rb_on1  ||
353 	!rb_off   || !rb_off1)
354       FatalError("unable to create radio-button pixmaps");
355 
356 
357     /* generate rb_on,on1,off,off1 pixmaps from mask pixmaps */
358     XSetForeground(theDisp, theGC, bg);
359     XFillRectangle(theDisp, rb_on,   theGC, 0,0,RBSIZE,RBSIZE);
360     XFillRectangle(theDisp, rb_on1,  theGC, 0,0,RBSIZE,RBSIZE);
361     XFillRectangle(theDisp, rb_off,  theGC, 0,0,RBSIZE,RBSIZE);
362     XFillRectangle(theDisp, rb_off1, theGC, 0,0,RBSIZE,RBSIZE);
363 
364     XSetFillStyle(theDisp, theGC, FillStippled);
365 
366     if (ctrlColor) {
367       XSetStipple(theDisp, theGC, rb_top);
368       XSetForeground(theDisp, theGC, fg);
369       XFillRectangle(theDisp, rb_on,   theGC, 0,0,RBSIZE,RBSIZE);
370       XFillRectangle(theDisp, rb_on1,  theGC, 0,0,RBSIZE,RBSIZE);
371       XSetForeground(theDisp, theGC, hi);
372       XFillRectangle(theDisp, rb_off,  theGC, 0,0,RBSIZE,RBSIZE);
373       XFillRectangle(theDisp, rb_off1, theGC, 0,0,RBSIZE,RBSIZE);
374 
375       XSetStipple(theDisp, theGC, rb_body);
376       XSetForeground(theDisp, theGC, lo);
377       XFillRectangle(theDisp, rb_on,   theGC, 0,0,RBSIZE,RBSIZE);
378       XFillRectangle(theDisp, rb_on1,  theGC, 0,0,RBSIZE,RBSIZE);
379       XSetForeground(theDisp, theGC, bg);
380       XFillRectangle(theDisp, rb_off,  theGC, 0,0,RBSIZE,RBSIZE);
381       XFillRectangle(theDisp, rb_off1, theGC, 0,0,RBSIZE,RBSIZE);
382 
383       XSetStipple(theDisp, theGC, rb_bot);
384       XSetForeground(theDisp, theGC, bg);
385       XFillRectangle(theDisp, rb_on,   theGC, 0,0,RBSIZE,RBSIZE);
386       XFillRectangle(theDisp, rb_on1,  theGC, 0,0,RBSIZE,RBSIZE);
387       XSetForeground(theDisp, theGC, lo);
388       XFillRectangle(theDisp, rb_off,  theGC, 0,0,RBSIZE,RBSIZE);
389       XFillRectangle(theDisp, rb_off1, theGC, 0,0,RBSIZE,RBSIZE);
390 
391       XSetStipple(theDisp, theGC, rb_dtop);
392       XSetForeground(theDisp, theGC, bg);
393       XFillRectangle(theDisp, rb_on,   theGC, 0,0,RBSIZE,RBSIZE);
394       XFillRectangle(theDisp, rb_on1,  theGC, 0,0,RBSIZE,RBSIZE);
395       XSetForeground(theDisp, theGC, hi);
396       XFillRectangle(theDisp, rb_off,  theGC, 0,0,RBSIZE,RBSIZE);
397       XFillRectangle(theDisp, rb_off1, theGC, 0,0,RBSIZE,RBSIZE);
398 
399       XSetStipple(theDisp, theGC, rb_dbot);
400       XSetForeground(theDisp, theGC, fg);
401       XFillRectangle(theDisp, rb_on,   theGC, 0,0,RBSIZE,RBSIZE);
402       XFillRectangle(theDisp, rb_on1,  theGC, 0,0,RBSIZE,RBSIZE);
403       XSetForeground(theDisp, theGC, lo);
404       XFillRectangle(theDisp, rb_off,  theGC, 0,0,RBSIZE,RBSIZE);
405       XFillRectangle(theDisp, rb_off1, theGC, 0,0,RBSIZE,RBSIZE);
406     }
407     else {
408       XSetStipple(theDisp, theGC, rb_dot);
409       XSetForeground(theDisp, theGC, fg);
410       XFillRectangle(theDisp, rb_on,   theGC, 0,0,RBSIZE,RBSIZE);
411       XFillRectangle(theDisp, rb_on1,  theGC, 0,0,RBSIZE,RBSIZE);
412     }
413 
414     XSetStipple(theDisp, theGC, rb_frame);
415     XSetForeground(theDisp, theGC, fg);
416     XFillRectangle(theDisp, rb_on,   theGC, 0,0,RBSIZE,RBSIZE);
417     XFillRectangle(theDisp, rb_off,  theGC, 0,0,RBSIZE,RBSIZE);
418 
419     XSetStipple(theDisp, theGC, rb_frame1);
420     XSetForeground(theDisp, theGC, fg);
421     XFillRectangle(theDisp, rb_on1,  theGC, 0,0,RBSIZE,RBSIZE);
422     XFillRectangle(theDisp, rb_off1, theGC, 0,0,RBSIZE,RBSIZE);
423 
424     XSetFillStyle(theDisp, theGC, FillSolid);
425 
426     /* destroy mask pixmaps */
427     XFreePixmap(theDisp, rb_frame);
428     XFreePixmap(theDisp, rb_frame1);
429     XFreePixmap(theDisp, rb_top);
430     XFreePixmap(theDisp, rb_bot);
431     XFreePixmap(theDisp, rb_dtop);
432     XFreePixmap(theDisp, rb_dbot);
433     XFreePixmap(theDisp, rb_body);
434 
435     rbpixmade = 1;
436   }
437 
438   return(rb);
439 }
440 
441 
442 
443 
444 /***********************************************/
RBRedraw(rblist,num)445 void RBRedraw(rblist, num)
446 RBUTT *rblist;
447 int    num;
448 {
449   /* redraws the 'num-th' RB in the list.  if num < 0, redraws entire list */
450 
451   RBUTT *rb;
452   int    i;
453 
454   /* point 'rb' at the appropriate RBUTT, *if* we're not drawing entire list */
455   if (num>=0) {
456     i=0;  rb=rblist;
457     while (i!=num && rb) { rb = (RBUTT *) rb->next;  i++; }
458     if (!rb) return;                     /* num is out of range.  do nothing */
459     drawRB(rb,0);
460   }
461 
462   else {                                 /* draw entire list */
463     rb = rblist;
464     while (rb) {
465       drawRB(rb,0);
466       rb = (RBUTT *) rb->next;
467     }
468   }
469 }
470 
471 
472 /***********************************************/
drawRB(rb,lit)473 static void drawRB(rb, lit)
474      RBUTT *rb;
475      int   lit;
476 {
477   /* draws the rb being pointed at */
478 
479   Pixmap pix;
480 
481   if (!rb) return;  /* rb = NULL */
482 
483   XSetForeground(theDisp, theGC, rb->fg);
484 
485   if (rb->selected) { pix = (lit) ? rb_on1 : rb_on; }
486   else { pix = (lit) ? rb_off1 : rb_off; }
487 
488   XCopyArea(theDisp, pix, rb->win, theGC, 0,0,RBSIZE,RBSIZE, rb->x, rb->y);
489   DrawString(rb->win, rb->x + RBSIZE + 4,
490 	     rb->y + RBSIZE/2 - CHIGH/2 + ASCENT, rb->str);
491 
492   if (!rb->active) {  /* if non-active, dim button and string */
493     DimRect(rb->win, rb->x, rb->y, RBSIZE, RBSIZE, rb->bg);
494     DimRect(rb->win, rb->x + RBSIZE + 4, rb->y + RBSIZE/2 - CHIGH/2,
495 	    (u_int) StringWidth(rb->str), (u_int) CHIGH, rb->bg);
496   }
497 }
498 
499 
500 /***********************************************/
RBSelect(rblist,n)501 void RBSelect(rblist, n)
502 RBUTT *rblist;
503 int    n;
504 {
505   RBUTT *rbold, *rb;
506   int    i;
507 
508   /* makes rb #n the selected rb in the list.  Does all redrawing.  Does
509      nothing if rb already selected */
510 
511   /* get pointers to the currently selected rb and the desired rb */
512   rbold = rblist;
513   while (rbold && !rbold->selected) rbold = (RBUTT *) rbold->next;
514   if (!rbold) return;    /* no currently selected item.  shouldn't happen */
515 
516   rb = rblist;  i=0;
517   while (rb && i!=n) {rb = (RBUTT *) rb->next;  i++; }
518   if (!rb) return;    /* 'n' is out of range */
519 
520 
521   if (rb == rbold) return;   /* 'n' is already selected.  do nothing */
522 
523   rbold->selected = 0;
524   rb->selected    = 1;
525   drawRB(rbold, 0);
526   drawRB(rb, 0);
527 }
528 
529 
530 
531 /***********************************************/
RBWhich(rblist)532 int RBWhich(rblist)
533      RBUTT *rblist;
534 {
535   int i;
536 
537   /* returns index of currently selected rb.  if none, returns -1 */
538 
539   i = 0;
540   while (rblist && !rblist->selected)
541     { rblist = (RBUTT *) rblist->next;  i++; }
542 
543   if (!rblist) return -1;             /* didn't find one */
544   return i;
545 }
546 
547 
548 /***********************************************/
RBCount(rblist)549 int RBCount(rblist)
550      RBUTT *rblist;
551 {
552   int i;
553 
554   /* returns # of rb's in the list */
555 
556   i = 0;
557   while (rblist) { rblist = (RBUTT *) rblist->next; i++; }
558   return i;
559 }
560 
561 
562 /***********************************************/
RBSetActive(rblist,n,act)563 void RBSetActive(rblist, n, act)
564      RBUTT *rblist;
565      int n,act;
566 {
567   RBUTT *rb;
568   int    i;
569 
570   /* sets 'active' status of rb #n.  does redrawing */
571 
572   rb=rblist;  i=0;
573   while (rb && i!=n) { rb = (RBUTT *) rb->next; i++; }
574   if (!rb) return;                         /* n out of range.  do nothing */
575 
576   if (rb->active != act) {
577     rb->active = act;
578     drawRB(rb, 0);
579   }
580 }
581 
582 
583 /***********************************************/
RBClick(rblist,mx,my)584 int RBClick(rblist, mx, my)
585 RBUTT *rblist;
586 int    mx,my;
587 {
588   int i;
589 
590   /* searches through rblist to see if mouse click at mx,my is in the
591      clickable region of any of the rb's.  If it finds one, it returns
592      it's index in the list.  If not, returns -1 */
593 
594   i = 0;
595   while (rblist) {
596     if (PTINRECT(mx, my, rblist->x, rblist->y, RBSIZE, RBSIZE)) break;
597 
598     rblist = (RBUTT *) rblist->next;
599     i++;
600   }
601 
602   if (!rblist) return -1;
603   return(i);
604 }
605 
606 
607 /***********************************************/
RBTrack(rblist,n)608 int RBTrack(rblist, n)
609      RBUTT *rblist;
610      int    n;
611 {
612   RBUTT       *rb;
613   Window       rW, cW;
614   int          i, x, y, rx, ry, lit, rv;
615   unsigned int mask;
616 
617   /* returns '1' if selection changed */
618 
619   rb=rblist;  i=0;
620   while (rb && i!=n) { rb = (RBUTT *) rb->next; i++; }
621   if (!rb) return 0;                    /* n out of range */
622 
623   /* called once we've figured out that the mouse clicked in 'rb' */
624 
625   if (!rb->active) return 0;
626 
627   lit = 1;
628   drawRB(rb, lit);
629   XFlush(theDisp);
630   Timer(75);          /* give chance for 'turn on' to become visible */
631 
632   while (XQueryPointer(theDisp,rb->win,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
633     if (!(mask & Button1Mask)) break;    /* button released */
634 
635     if (!lit && PTINRECT(x, y, rb->x, rb->y, RBSIZE, RBSIZE)) {
636       lit=1;
637       drawRB(rb, lit);
638       XFlush(theDisp);
639     }
640 
641     if (lit && !PTINRECT(x, y, rb->x, rb->y, RBSIZE, RBSIZE)) {
642       lit=0;
643       drawRB(rb, lit);
644       XFlush(theDisp);
645     }
646   }
647 
648   rv = 0;
649 
650   if (lit) {
651     drawRB(rb, 0);
652     if (RBWhich(rblist) != n) rv = 1;
653     RBSelect(rblist, n);
654   }
655 
656   XFlush(theDisp);
657   return rv;
658 }
659 
660 
661 
662 
663 /******************* CBUTT ROUTINES ************************/
664 
665 
666 
667 #define XVCBSIZE 16
668 
669 /***********************************************/
CBCreate(cb,win,x,y,str,fg,bg,hi,lo)670 void CBCreate(cb, win, x,y, str, fg, bg, hi, lo)
671       CBUTT        *cb;
672       Window        win;
673       int           x,y;
674       const char   *str;
675       unsigned long fg,bg,hi,lo;
676 {
677   /* fill in the fields of the structure */
678   cb->win      = win;
679   cb->x        = x;
680   cb->y        = y;
681   cb->str      = str;
682   cb->val      = 0;
683   cb->active   = 1;
684   cb->fg       = fg;
685   cb->bg       = bg;
686   cb->hi       = hi;
687   cb->lo       = lo;
688 
689   /* and, on an unrelated note, if the CB pixmaps haven't been created yet,
690      do so.  We'll be needing them, y'see... */
691 
692   if (!cbpixmade) {
693     cbcheck = XCreatePixmapFromBitmapData(theDisp, rootW,
694 	     (char *) cb_check_bits,
695 	     cb_check_width, cb_check_height, fg, bg, dispDEEP);
696 
697     cbpixmade = 1;
698   }
699 }
700 
701 
702 
703 
704 /***********************************************/
CBRedraw(cb)705 void CBRedraw(cb)
706 CBUTT *cb;
707 {
708   /* draws the cb being pointed at */
709 
710   XSetForeground(theDisp, theGC, cb->bg);
711   XFillRectangle(theDisp, cb->win, theGC, cb->x+2, cb->y+2,
712 		 XVCBSIZE-3,XVCBSIZE-3);
713 
714   XSetForeground(theDisp, theGC, cb->fg);
715   XDrawRectangle(theDisp, cb->win, theGC, cb->x, cb->y, XVCBSIZE, XVCBSIZE);
716   Draw3dRect(cb->win, cb->x+1, cb->y+1, XVCBSIZE-2, XVCBSIZE-2, R3D_OUT, 2,
717 	     cb->hi, cb->lo, cb->bg);
718 
719   if (cb->val) XCopyArea(theDisp, cbcheck, cb->win, theGC,
720 			 0, 0, cb_check_width, cb_check_height,
721 			 cb->x+3, cb->y+3);
722 
723   XSetForeground(theDisp, theGC, cb->fg);
724   DrawString(cb->win, cb->x + XVCBSIZE+4,
725 	     cb->y+XVCBSIZE/2 - CHIGH/2 + ASCENT, cb->str);
726 
727   if (!cb->active) {  /* if non-active, dim button and string */
728     DimRect(cb->win, cb->x, cb->y, XVCBSIZE, XVCBSIZE, cb->bg);
729     DimRect(cb->win, cb->x + XVCBSIZE+4, cb->y+XVCBSIZE/2 - CHIGH/2,
730 	    (u_int) StringWidth(cb->str), (u_int) CHIGH, cb->bg);
731   }
732 }
733 
734 
735 /**********************************************/
CBSetActive(cb,act)736 void CBSetActive(cb,act)
737 CBUTT        *cb;
738 int           act;
739 {
740   if (cb->active != act) {
741     cb->active = act;
742     CBRedraw(cb);
743   }
744 }
745 
746 
747 /***********************************************/
CBClick(cb,mx,my)748 int CBClick(cb, mx, my)
749 CBUTT *cb;
750 int    mx,my;
751 {
752   if (PTINRECT(mx, my, cb->x, cb->y, XVCBSIZE,XVCBSIZE)) return 1;
753   return 0;
754 }
755 
756 
757 /***********************************************/
CBTrack(cb)758 int CBTrack(cb)
759 CBUTT *cb;
760 {
761   Window       rW, cW;
762   int          x, y, rx, ry, lit;
763   unsigned int mask;
764 
765   /* called once we've figured out that the mouse clicked in 'cb' */
766 
767   if (!cb->active) return 0;
768 
769   XSetForeground(theDisp, theGC, cb->fg);
770 
771   lit = 1;
772   drawCB(cb, lit);
773   XFlush(theDisp);
774   Timer(75);          /* give chance for 'turn on' to become visible */
775 
776   while (XQueryPointer(theDisp,cb->win,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
777     if (!(mask & Button1Mask)) break;    /* button released */
778 
779     if (!lit && PTINRECT(x, y, cb->x, cb->y, XVCBSIZE, XVCBSIZE)) {
780       lit=1;
781       drawCB(cb,lit);
782       XFlush(theDisp);
783     }
784 
785     if (lit && !PTINRECT(x, y, cb->x, cb->y, XVCBSIZE, XVCBSIZE)) {
786       lit=0;
787       drawCB(cb,lit);
788       XFlush(theDisp);
789     }
790   }
791 
792   if (lit) {
793     cb->val = !cb->val;
794     drawCB(cb,0);
795     CBRedraw(cb);
796   }
797 
798   XFlush(theDisp);
799 
800   return(lit);
801 }
802 
803 
804 /***********************************************/
drawCB(cb,lit)805 static void drawCB(cb, lit)
806 CBUTT *cb;
807 int lit;
808 {
809   /* draws highlighting */
810   if (lit) {
811     if (ctrlColor)
812       Draw3dRect(cb->win, cb->x+1, cb->y+1, XVCBSIZE-2, XVCBSIZE-2, R3D_IN, 2,
813 		 cb->hi, cb->lo, cb->bg);
814     else {
815       XSetForeground(theDisp, theGC, cb->fg);
816       XDrawRectangle(theDisp, cb->win, theGC, cb->x+1, cb->y+1,
817 		     XVCBSIZE-2, XVCBSIZE-2);
818     }
819   }
820 
821   else {
822     if (ctrlColor)
823       Draw3dRect(cb->win, cb->x+1, cb->y+1, XVCBSIZE-2, XVCBSIZE-2, R3D_OUT, 2,
824 		 cb->hi, cb->lo, cb->bg);
825     else {
826       XSetForeground(theDisp, theGC, cb->bg);
827       XDrawRectangle(theDisp, cb->win, theGC, cb->x+1, cb->y+1,
828 		     XVCBSIZE-2, XVCBSIZE-2);
829     }
830   }
831 }
832 
833 
834 
835 /******************* MBUTT ROUTINES ************************/
836 
837 
838 
839 /***********************************************/
MBCreate(mb,win,x,y,w,h,title,list,nlist,fg,bg,hi,lo)840 void MBCreate(mb, win, x, y, w, h, title, list, nlist, fg, bg, hi, lo)
841      MBUTT        *mb;
842      Window        win;
843      int           x,y;
844      unsigned int  w,h;
845      const char   *title;
846      const char  * const *list;
847      int           nlist;
848      unsigned long fg,bg,hi,lo;
849 {
850   XSetWindowAttributes xswa;
851   unsigned long        xswamask;
852   int i;
853 
854   if (!mbpixmade) {
855     mbchk = XCreatePixmapFromBitmapData(theDisp, rootW, (char *) mb_chk_bits,
856 	     mb_chk_width, mb_chk_height, fg, bg, dispDEEP);
857     mbpixmade = 1;
858   }
859 
860 
861   /* fill in the fields of the structure */
862   mb->win      = win;
863   mb->x        = x;
864   mb->y        = y;
865   mb->w        = w;
866   mb->h        = h;
867   mb->title    = title;
868   mb->active   = 1;
869   mb->list     = list;
870   mb->nlist    = nlist;
871   mb->hascheck = 0;
872   mb->fg       = fg;
873   mb->bg       = bg;
874   mb->hi       = hi;
875   mb->lo       = lo;
876 
877   mb->pix      = (Pixmap) NULL;
878   mb->pw = mb->ph = 0;
879 
880   for (i=0; i<MAXMBLEN; i++) {
881     mb->flags[i] = 0;
882     mb->dim[i] = 0;
883   }
884 
885   /* create popup window (it gets mapped, pos'd and sized later) */
886   xswa.background_pixel = bg;
887   xswa.border_pixel     = fg;
888   xswa.save_under       = True;
889   xswamask = CWBackPixel | CWBorderPixel | CWSaveUnder;
890 
891   mb->mwin = XCreateWindow(theDisp, mb->win, x, y, w, h,
892 			   (u_int) 2, (int) dispDEEP, InputOutput,
893 			   theVisual, xswamask, &xswa);
894 
895   if (!mb->mwin) FatalError("can't create popup menu window!");
896 
897   XSelectInput(theDisp, mb->mwin, ExposureMask | VisibilityChangeMask);
898   XSetTransientForHint(theDisp, mb->mwin, mb->win);
899 }
900 
901 
902 
903 
904 /***********************************************/
MBRedraw(mb)905 void MBRedraw(mb)
906      MBUTT *mb;
907 {
908   /* draws a menu button in it's normal state.  (When it's actively being
909      used (to select an item), all drawing is handled in MBTrack) */
910 
911   int          x,y,i,r,x1,y1;
912   unsigned int w,h;
913 
914   r = 2;  /* amt of shadow */
915   x = mb->x;  y = mb->y;  w = mb->w;  h = mb->h;
916   x1 = x + (int) w;
917   y1 = y + (int) h;
918 
919   XSetForeground(theDisp, theGC, mb->bg);
920   XFillRectangle(theDisp, mb->win, theGC, x+1, y+1, w-1, h-1);
921 
922   XSetForeground(theDisp, theGC, mb->fg);
923   XDrawRectangle(theDisp, mb->win, theGC, x, y, w, h);
924   Draw3dRect(mb->win, x+1, y+1, w-2, h-2, R3D_OUT, 2, mb->hi, mb->lo, mb->bg);
925 
926   XSetForeground(theDisp, theGC, mb->fg);
927 
928   /* draw shadow */
929   for (i=1; i<=r; i++) {
930     XDrawLine(theDisp, mb->win, theGC, x+r,  y1+i, x1+i, y1+i);
931     XDrawLine(theDisp, mb->win, theGC, x1+i, y1+i, x1+i, y+r);
932   }
933 
934   if (mb->pix != None) {                    /* draw pixmap centered in butt */
935     x1 = x + (1+w - mb->pw)/2;
936     y1 = y + (1+h - mb->ph)/2;
937 
938     XSetForeground(theDisp, theGC, mb->fg);
939     XSetBackground(theDisp, theGC, mb->bg);
940     XCopyPlane(theDisp, mb->pix, mb->win, theGC, 0,0,
941 	       (u_int) mb->pw, (u_int) mb->ph, x1,y1, 1L);
942     if (!mb->active)
943       DimRect(mb->win, x1,y1, (u_int) mb->pw, (u_int) mb->ph, mb->bg);
944   }
945 
946   else {                                    /* draw string centered in butt */
947     const char *str;
948     char       *tmp;
949     char        stbuf[256];
950 
951     if (mb->title) str = mb->title;
952     else {  /* find first checked item, and show that as the title */
953       int i;
954 
955       if (mb->list) {
956 	for (i=0; i<mb->nlist && !mb->flags[i]; i++);
957 	if (i==mb->nlist) i = 0;   /* shouldn't happen */
958 	str = mb->list[i];
959       }
960       else str = "";
961     }
962 
963     /* truncate at TAB, if any */
964     strcpy(stbuf, str);
965     if ((tmp = (char *) index(stbuf, '\t')) != NULL) *tmp = '\0';
966     str = stbuf;
967 
968     x1 = CENTERX(mfinfo, x + w/2, str);
969     y1 = CENTERY(mfinfo, y + h/2);
970 
971     if (mb->active) {
972       DrawString(mb->win, x1,y1, str);
973     }
974     else {  /* stipple if not active */
975       XSetFillStyle(theDisp, theGC, FillStippled);
976       XSetStipple(theDisp, theGC, dimStip);
977       DrawString(mb->win, x1,y1, str);
978       XSetFillStyle(theDisp,theGC,FillSolid);
979     }
980   }
981 }
982 
983 
984 /**********************************************/
MBSetActive(mb,act)985 void MBSetActive(mb,act)
986      MBUTT *mb;
987      int    act;
988 {
989   if (mb->active != act) {
990     mb->active = act;
991     MBRedraw(mb);
992   }
993 }
994 
995 
996 /**********************************************/
MBWhich(mb)997 int MBWhich(mb)
998      MBUTT *mb;
999 {
1000   /* returns index of first checked selection, or '-1' if nothing selected */
1001 
1002   int i;
1003 
1004   if (!mb->hascheck) return -1;
1005 
1006   for (i=0; i<mb->nlist; i++)
1007     if (mb->flags[i]) return i;
1008 
1009   return -1;
1010 }
1011 
1012 
1013 /**********************************************/
MBSelect(mb,n)1014 void MBSelect(mb, n)
1015      MBUTT *mb;
1016      int    n;
1017 {
1018   /* makes entry #n the selected entry (ie, the only one with a check mark)
1019      Does all redrawing.  Does nothing if entry #n already selected.
1020      Don't let it select 'dim' entries */
1021 
1022   int i;
1023 
1024   if (n<0 || n>mb->nlist) return;               /* # out of range */
1025   if (!mb->hascheck)      return;               /* shouldn't happen */
1026   if (mb->flags[n])       return;               /* already selected */
1027 
1028   for (i=0; i<MAXMBLEN; i++) mb->flags[i] = 0;
1029 
1030   mb->flags[n] = 1;
1031   if (!mb->title) MBRedraw(mb);                 /* mb shows cur selection */
1032 }
1033 
1034 
1035 
1036 /***********************************************/
MBClick(mb,mx,my)1037 int MBClick(mb, mx, my)
1038 MBUTT *mb;
1039 int    mx,my;
1040 {
1041   if (PTINRECT(mx, my, mb->x, mb->y, mb->w, mb->h)) return 1;
1042   return 0;
1043 }
1044 
1045 
1046 /***********************************************/
MBTrack(mb)1047 int MBTrack(mb)
1048      MBUTT *mb;
1049 {
1050   Window       rW, cW, win;
1051   int          i, x, y, rx, ry, extratop, hascheck;
1052   unsigned int mask;
1053   int          mwide, mhigh, mx, my, j, lit, lastlit;
1054   int          mtabwide;
1055   XSizeHints   hints;
1056   XEvent       event;
1057 
1058 
1059   /* returns selected menu choice index, or '-1' if none */
1060 
1061   if (!mb->active || !mb->nlist) return -1;
1062 
1063   extratop = (mb->title) ? LINEHIGH+3 : 1-SPACING; /*add extra line for title*/
1064 
1065   mtabwide = 0;
1066 
1067   mwide = 1;                              /* compute maximum width */
1068   for (i=0; i<mb->nlist; i++) {
1069     if (!index(mb->list[i], '\t')) {
1070       j = StringWidth(mb->list[i]);
1071       if (j > mwide) mwide = j;
1072     }
1073     else {
1074       char *sp, str[256];
1075 
1076       strcpy(str, mb->list[i]);
1077       sp = (char *) index(str, '\t');
1078       j = StringWidth(sp+1);
1079       if (j>mtabwide) mtabwide = j;
1080 
1081       *sp = '\0';
1082       j = StringWidth(str);
1083       if ((j + 4 + mtabwide)>mwide) mwide = (j+4+mtabwide);
1084     }
1085   }
1086   mwide += 8;                             /* extra room at edges */
1087 
1088   /* make wider if any checked menu items */
1089   for (i=0; i<mb->nlist && !mb->flags[i]; i++);
1090   hascheck = (i<mb->nlist || mb->hascheck);
1091 
1092   if (hascheck && mb->title) mwide += 8;
1093 
1094   if (mwide < (mb->w+1)) mwide = mb->w+1; /* at least as wide as button */
1095 
1096   mhigh = mb->nlist * LINEHIGH + 2 + extratop;
1097 
1098   mx = mb->x-1;  my = mb->y - 1;
1099   if (mb->title && mwide > mb->w) mx -= ((mwide - mb->w)/2);
1100 
1101 
1102   /* create/map window, and warp mouse if we had to move the window */
1103   win = mb->mwin;
1104   XMoveResizeWindow(theDisp, win, mx, my, (u_int) mwide, (u_int) mhigh);
1105 
1106   hints.width  = hints.min_width  = hints.max_width  = mwide;
1107   hints.height = hints.min_height = hints.max_height = mhigh;
1108   hints.x = mx;  hints.y = my;
1109   hints.flags  = (USSize | PMinSize | PMaxSize | PPosition);
1110   XSetNormalHints(theDisp, win, &hints);
1111 
1112   XMapRaised(theDisp, win);
1113 
1114   /* wait for window to become mapped */
1115   XWindowEvent(theDisp, win, VisibilityChangeMask, &event);
1116 
1117 
1118   /* draw the menu */
1119   XSetForeground(theDisp, theGC, mb->fg);
1120   x = (hascheck) ? 12 : 4;
1121   if (mb->title) {                /* draw a title on this menu */
1122     CenterString(win, mwide/2-1, (extratop-2)/2, mb->title);
1123 
1124     if (ctrlColor) {
1125       XSetForeground(theDisp, theGC, mb->fg);
1126       XDrawLine(theDisp, win, theGC, 0, extratop-2, mwide, extratop-2);
1127       XSetForeground(theDisp, theGC, mb->lo);
1128       XDrawLine(theDisp, win, theGC, 0, extratop-1, mwide, extratop-1);
1129       XSetForeground(theDisp, theGC, mb->hi);
1130       XDrawLine(theDisp, win, theGC, 0, extratop,   mwide, extratop);
1131       XSetForeground(theDisp, theGC, mb->fg);
1132     }
1133     else {  /* b/w system */
1134       XDrawLine(theDisp, win, theGC, 0, extratop-2, mwide, extratop-2);
1135       XDrawLine(theDisp, win, theGC, 0, extratop,   mwide, extratop);
1136     }
1137   }
1138 
1139   y = ASCENT + SPACING + extratop;
1140   for (i=0; i<mb->nlist; i++) {
1141     char txtstr[256], *tabstr;
1142 
1143     strcpy(txtstr, mb->list[i]);
1144     if ((tabstr = (char *) index(txtstr, '\t'))) {
1145       *tabstr = '\0';  tabstr++;
1146     }
1147 
1148     if (mb->flags[i]) {
1149       XCopyArea(theDisp, mbchk, win, theGC, 0, 0, mb_chk_width, mb_chk_height,
1150 		x - 10, y - 8);
1151     }
1152 
1153     if (!strcmp(mb->list[i], MBSEP)) {
1154       mb->dim[i] = 1;    /* don't select this one */
1155       if (ctrlColor) {
1156 	XSetForeground(theDisp, theGC, mb->fg);
1157 	XDrawLine(theDisp,win,theGC,4,y-(ASCENT/2)-1, mwide-5, y-(ASCENT/2)-1);
1158 
1159 	XSetForeground(theDisp, theGC, mb->lo);
1160 	XDrawLine(theDisp,win,theGC,4,y-(ASCENT/2),   mwide-5, y-(ASCENT/2));
1161 
1162 	XSetForeground(theDisp, theGC, mb->hi);
1163 	XDrawLine(theDisp,win,theGC,4,y-(ASCENT/2)+1, mwide-5, y-(ASCENT/2)+1);
1164 	XSetForeground(theDisp, theGC, mb->fg);
1165       }
1166       else
1167 	XDrawLine(theDisp, win, theGC, 4, y-(ASCENT/2), mwide-5, y-(ASCENT/2));
1168     }
1169     else {
1170       DrawString(win, x, y, txtstr);
1171       if (tabstr)
1172 	DrawString(win, mwide - mtabwide - 4, y, tabstr);
1173 
1174       if (mb->dim[i])
1175 	DimRect(win, x, y-ASCENT, (u_int) mwide, (u_int) CHIGH, mb->bg);
1176       XSetForeground(theDisp, theGC, mb->fg);
1177     }
1178 
1179     y += LINEHIGH;
1180   }
1181 
1182   XFlush(theDisp);
1183 
1184 
1185   /* track the mouse */
1186   XSetFunction(theDisp, theGC, GXinvert);         /* go in to 'invert' mode */
1187   XSetPlaneMask(theDisp, theGC, mb->fg ^ mb->bg);
1188 
1189   lit = lastlit = -1;
1190   while (XQueryPointer(theDisp,win,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
1191     if (!(mask & Button1Mask)) break;    /* button released */
1192 
1193     /* determine which choice the mouse is in.  -1 if none */
1194     j = extratop+2;
1195     if (x < 0 || x > mwide) lit = -1;
1196     else {
1197       for (i=0; i<mb->nlist; i++, j+=LINEHIGH) {
1198 	if (y>=j && y < j+LINEHIGH) { lit = i; break; }
1199       }
1200       if (i == mb->nlist) lit = -1;
1201     }
1202 
1203     /* handle dimmed selections */
1204     if (lit >= 0 && mb->dim[lit]) lit = -1;
1205 
1206     if (lit != lastlit) {
1207       if (lit >= 0) {
1208 	y = extratop + 2 + lit*LINEHIGH;
1209 	XFillRectangle(theDisp,win,theGC,0,y,(u_int) mwide,(u_int) LINEHIGH);
1210       }
1211       if (lastlit >= 0) {
1212 	y = extratop + 2 + lastlit*LINEHIGH;
1213 	XFillRectangle(theDisp,win,theGC,0,y,(u_int) mwide,(u_int) LINEHIGH);
1214       }
1215       lastlit = lit;
1216     }
1217   }
1218 
1219   /* flash the selected choice, if any */
1220   if (lit >= 0) {
1221     y = extratop + 2 + lit*LINEHIGH;
1222     for (i=0; i<5; i++) {
1223       XFillRectangle(theDisp, win, theGC, 0,y,(u_int) mwide,(u_int) LINEHIGH);
1224       XFlush(theDisp);
1225       Timer(50);
1226     }
1227   }
1228 
1229   XSetFunction(theDisp, theGC, GXcopy);   /* back to 'normal' mode */
1230   XSetPlaneMask(theDisp, theGC, AllPlanes);
1231 
1232   /* could try eating all remaining events for 'win' before unmapping */
1233 
1234   XSync(theDisp, False);                  /* make sure 'map' has taken place */
1235   XUnmapWindow(theDisp, win);
1236 
1237   MBRedraw(mb);
1238 
1239   return lit;
1240 }
1241 
1242 
1243 
1244 
1245