Build Fix -- compatibility issue with newer autoconf
[sxemacs] / src / ui / lwlib / xlwgcs.c
1  /* Tabs Widget for SXEmacs.
2     Copyright (C) 1999 Edward A. Falk
3
4     This file is part of SXEmacs.
5
6 SXEmacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 SXEmacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
18
19 /* Synched up with: Gcs.c 1.7 */
20
21  /* #### This code is duplicated many times within lwlib and XEmacs. It
22     should be modularised. */
23
24 /*
25  * Gcs.c - Utility functions to allocate GCs.
26  *
27  * Author: Edward A. Falk
28  *         falk@falconer.vip.best.com
29  *
30  * Date: Sept 29, 1998
31  */
32
33 /* Functions:
34  *
35  * GC AllocFgGC(w, fg, font)
36  *      Return a GC with foreground set as specified.
37  *      If font is None, then the returned GC is allocated with font specified
38  *      as a "don't care" value.
39  *
40  * GC
41  * AllocBackgroundGC(w, font)
42  *      Return a GC with the foreground set to the widget's background color.
43  *
44  * GC
45  * AllocGreyGC(w, fg, font, contrast, be_nice_to_cmap)
46  *      Widget  w ;
47  *      Pixel   fg ;
48  *      Font    font ;
49  *      int     contrast ;
50  *      int     be_nice_to_cmap ;
51  *
52  *      Return a GC suitable for rendering a widget in its "inactive" color.
53  *      Normally returns a GC with a color somewhere between the widget's
54  *      background color and the specified foreground. If font is None, then
55  *      the returned GC is allocated with font specified as "don't care".
56  *      If be_nice_to_cmap is True, the returned GC is created using a 50%
57  *      dither instead of a new color.
58  *
59  *
60  * GC
61  * AllocShadeGC(w, fg, bg, font, contrast, be_nice_to_cmap)
62  *      Widget  w ;
63  *      Pixel   fg, bg ;
64  *      Font    font ;
65  *      int     contrast ;
66  *      int     be_nice_to_cmap ;
67  *
68  *      Return a GC suitable for rendering in a shade somewhere between
69  *      bg and fg, as determined by contrast (0 = bg, 100 = fg)
70  *      If font is None, then the returned GC is allocated with
71  *      font specified as "don't care".  If be_nice_to_cmap
72  *      is True, the returned GC is created using a 50% dither
73  *      instead of a new color.
74  *
75  *
76  * GC
77  * AllocTopShadowGC(w, contrast, be_nice_to_cmap)
78  *      Return a GC suitable for rendering the "top shadow" decorations of
79  *      a widget.  Returns a GC with foreground computed from widget's
80  *      background color and contrast.  If be_nice_to_cmap is True, the
81  *      returned GC will use a foreground color of white.  If widget depth
82  *      is 1, this function will use a foreground color of black.
83  *
84  * GC
85  * AllocBotShadowGC(w, contrast, be_nice_to_cmap)
86  *      Return a GC suitable for rendering the "bottom shadow" decorations
87  *      of a widget. Returns a GC with foreground computed from widget's
88  *      background color and contrast. If be_nice_to_cmap is True, the
89  *      returned GC will use a foreground color of black.
90  *
91  * GC
92  * AllocArmGC(w, contrast, be_nice_to_cmap)
93  *      Return a GC suitable for rendering the "armed" decorations of a
94  *      widget. This GC would typically be used to fill in the widget's
95  *      background. Returns a GC with foreground computed from widget's
96  *      background color and contrast.  If be_nice_to_cmap is True, the
97  *      returned GC will use a foreground color of black and a 50% dither.
98  *
99  *
100  * void
101  * Draw3dBox(w, x,y,wid,hgt,s, topgc, botgc)
102  *      Utility function.  Draws a raised shadow box with outside dimensions
103  *      as specified by x,y,wid,hgt and shadow width specified by s.
104  *      A lowered shadow box may be generated by swapping topgc and botgc.
105  *
106  */
107
108 #include        <config.h>
109 #include        <stdio.h>
110
111 #include        <X11/Xlib.h>
112 #include        <X11/IntrinsicP.h>
113 #include        <X11/StringDefs.h>
114 #include        "ui/X11/xmu.h"
115 #include        "xlwgcs.h"
116
117         /* Color & GC allocation.
118          *
119          * Frame widgets use the following graphics contexts:
120          *
121          *  Foreground          tab label text drawn this way
122          *  Insensitive Fg      foreground color greyed out.
123          *  Background          frame background color
124          *  Top shadow          upper-left highlight around widget
125          *  Bottom shadow       lower-right highlight around widget
126          *  Arm shadow          button pressed and ready to be released
127          *
128          *
129          * GC's are defined as follows, depending on attributes and
130          * window depth:
131          *
132          * Monochrome:
133          *      Foreground = foreground color attribute or BlackPixel()
134          *      Grey = Foreground color + 50% dither
135          *      Background = background color attribute or WhitePixel()
136          *      top shadow = foreground
137          *      bottom shadow = foreground
138          *      arm shadow = (what?)
139          *
140          * Color, beNiceToColormap=true:
141          *      Foreground = foreground color attribute or BlackPixel()
142          *      Grey = Foreground color + 50% dither
143          *      Background = background color attribute or WhitePixel()
144          *      top shadow = white
145          *      bottom shadow = black
146          *      arm shadow = (what?)
147          *
148          * Color, beNiceToColormap=false:
149          *      Foreground = foreground color attribute or BlackPixel()
150          *      Grey = (foreground color + background color)/2
151          *      Background = background color attribute or WhitePixel()
152          *      top shadow = background * 1.2
153          *      bottom shadow = background * .6
154          *      arm shadow = background * .8
155          *
156          * Special cases:
157          *      If background is white,   ??
158          *      if background is black,   ??
159          *
160          *
161          * If the widget's background is solid white or solid black,
162          * this code just picks some numbers.  (The choice is designed
163          * to be compatible with ThreeD interface.)
164          */
165
166 #if     XtSpecificationRelease  < 5
167
168 static GC XtAllocateGC(Widget, int, unsigned long, XGCValues *,
169                        unsigned long, unsigned long);
170
171 #endif
172
173 #if     NeedFunctionPrototypes
174 static Pixmap getDitherPixmap(Widget, int contrast);
175 #else
176 static Pixmap getDitherPixmap();
177 #endif
178
179         /* return a GC with the specified foreground and optional font */
180
181 GC AllocFgGC(Widget w, Pixel fg, Font font)
182 {
183         XGCValues values;
184         unsigned long vmask, dcmask;
185
186         values.foreground = fg;
187         values.font = font;
188
189         if (font != None) {
190                 vmask = GCForeground | GCFont;
191                 dcmask = GCSubwindowMode | GCDashOffset |
192                     GCDashList | GCArcMode | GCBackground | GCGraphicsExposures;
193         } else {
194                 vmask = GCForeground;
195                 dcmask = GCFont | GCSubwindowMode | GCDashOffset |
196                     GCDashList | GCArcMode | GCBackground | GCGraphicsExposures;
197         }
198
199         return XtAllocateGC(w, w->core.depth, vmask, &values, 0L, dcmask);
200 }
201
202         /* return gc with widget background color as the foreground */
203
204 GC AllocBackgroundGC(Widget w, Font font)
205 {
206         return AllocFgGC(w, w->core.background_pixel, font);
207 }
208
209         /* Allocate an "inactive" GC.  Color is grey (possibly via
210          * dither pattern).
211          */
212
213 GC
214 AllocGreyGC(Widget w, Pixel fg, Font font, int contrast, Bool be_nice_to_cmap)
215 {
216         return AllocShadeGC(w, fg, w->core.background_pixel,
217                             font, contrast, be_nice_to_cmap);
218 }
219
220         /* Allocate a GC somewhere between two colors.  */
221
222 GC
223 AllocShadeGC(Widget w, Pixel fg, Pixel bg, Font font,
224              int contrast, Bool be_nice_to_cmap)
225 {
226         XGCValues values;
227         unsigned long vmask, dcmask;
228
229         values.foreground = fg;
230         values.background = bg;
231         values.font = font;
232
233         if (font != None) {
234                 vmask = GCForeground | GCFont;
235                 dcmask = GCSubwindowMode | GCDashOffset |
236                     GCDashList | GCArcMode | GCGraphicsExposures;
237         } else {
238                 vmask = GCForeground;
239                 dcmask = GCFont | GCSubwindowMode | GCDashOffset |
240                     GCDashList | GCArcMode | GCGraphicsExposures;
241         }
242 #ifdef HAVE_XMU
243         if (be_nice_to_cmap || w->core.depth == 1) {
244                 if (contrast <= 5)
245                         values.foreground = bg;
246                 else if (contrast >= 95)
247                         values.foreground = fg;
248                 else {
249                         vmask |= GCBackground | GCStipple | GCFillStyle;
250                         values.fill_style = FillOpaqueStippled;
251                         values.stipple = getDitherPixmap(w, contrast);
252                 }
253
254                 return XtAllocateGC(w, w->core.depth, vmask, &values, 0L,
255                                     dcmask);
256         } else
257 #endif
258         {
259                 dcmask |= GCBackground;
260                 values.foreground = AllocGreyPixel(w, fg, bg, contrast);
261                 return XtAllocateGC(w, w->core.depth, vmask, &values, 0L,
262                                     dcmask);
263         }
264 }
265
266         /* return top-shadow gc. */
267
268 GC AllocTopShadowGC(Widget w, int contrast, Bool be_nice_to_cmap)
269 {
270         Screen *scr = XtScreen(w);
271         XGCValues values;
272
273         if (w->core.depth == 1)
274                 values.foreground = BlackPixelOfScreen(scr);
275         else if (be_nice_to_cmap)
276                 values.foreground = WhitePixelOfScreen(scr);
277         else
278                 values.foreground = AllocShadowPixel(w, 100 + contrast);
279
280         return XtAllocateGC(w, w->core.depth,
281                             GCForeground, &values,
282                             0L,
283                             GCBackground | GCFont | GCSubwindowMode |
284                             GCGraphicsExposures | GCDashOffset | GCDashList |
285                             GCArcMode);
286 }
287
288         /* return bottom-shadow gc. */
289
290 GC AllocBotShadowGC(Widget w, int contrast, Bool be_nice_to_cmap)
291 {
292         Screen *scr = XtScreen(w);
293         XGCValues values;
294
295         if (w->core.depth == 1 || be_nice_to_cmap)
296                 values.foreground = BlackPixelOfScreen(scr);
297         else
298                 values.foreground = AllocShadowPixel(w, 100 - contrast);
299
300         return XtAllocateGC(w, w->core.depth,
301                             GCForeground, &values,
302                             0L,
303                             GCBackground | GCFont | GCSubwindowMode |
304                             GCGraphicsExposures | GCDashOffset | GCDashList |
305                             GCArcMode);
306 }
307
308         /* return arm-shadow gc. */
309
310 GC AllocArmGC(Widget w, int contrast, Bool be_nice_to_cmap)
311 {
312         Screen *scr = XtScreen(w);
313         XGCValues values;
314
315         /* Not clear exactly what we should do here.  Take a look at
316          * Xaw3d to see what they do.
317          */
318 #ifdef HAVE_XMU
319         if (w->core.depth == 1 || be_nice_to_cmap) {
320                 values.background = w->core.background_pixel;
321                 if (values.background == BlackPixelOfScreen(scr))
322                         values.foreground = WhitePixelOfScreen(scr);
323                 else
324                         values.foreground = BlackPixelOfScreen(scr);
325                 values.fill_style = FillStippled;
326                 values.stipple =
327                     XmuCreateStippledPixmap(XtScreen(w), 1L, 0L, 1);
328
329                 return XtAllocateGC(w, w->core.depth,
330                                     GCForeground | GCBackground | GCStipple |
331                                     GCFillStyle, &values, 0L,
332                                     GCFont | GCSubwindowMode |
333                                     GCGraphicsExposures | GCDashOffset |
334                                     GCDashList | GCArcMode);
335         } else
336 #endif
337         {
338                 values.foreground = AllocShadowPixel(w, 100 - contrast);
339                 return XtAllocateGC(w, w->core.depth,
340                                     GCForeground, &values,
341                                     0L,
342                                     GCBackground | GCFont | GCSubwindowMode |
343                                     GCGraphicsExposures | GCDashOffset |
344                                     GCDashList | GCArcMode);
345         }
346 }
347
348 Pixel AllocShadowPixel(Widget w, int scale)
349 {
350         XColor get_c, set_c;
351         Display *dpy = XtDisplay(w);
352         Screen *scr = XtScreen(w);
353         Colormap cmap;
354         Pixel maxColor;
355
356         cmap = w->core.colormap;
357
358         get_c.pixel = w->core.background_pixel;
359         if (get_c.pixel == WhitePixelOfScreen(scr) ||
360             get_c.pixel == BlackPixelOfScreen(scr)) {
361                 /* what we *ought* to do is choose gray75 as the base color,
362                  * or perhaps gray83.  Instead, we choose colors that are
363                  * the same as ThreeD would choose.
364                  */
365                 if (scale > 100)
366                         scale = 200 - scale;
367                 set_c.red = set_c.green = set_c.blue = 65535 * scale / 100;
368         } else {
369                 XQueryColor(dpy, cmap, &get_c);
370                 /* adjust scale so that brightest component does not
371                  * exceed 65535; otherwise hue would change.
372                  */
373                 if (scale > 100) {
374                         maxColor = Max(get_c.red, Max(get_c.green, get_c.blue));
375                         if (scale * maxColor > 65535 * 100)
376                                 scale = 65535 * 100 / maxColor;
377                 }
378                 set_c.red = scale * get_c.red / 100;
379                 set_c.green = scale * get_c.green / 100;
380                 set_c.blue = scale * get_c.blue / 100;
381         }
382         set_c.flags = DoRed | DoGreen | DoBlue;
383         if (XAllocColor(dpy, cmap, &set_c))
384                 return set_c.pixel;
385         else if (scale > 100)
386                 return WhitePixelOfScreen(scr);
387         else
388                 return BlackPixelOfScreen(scr);
389 }
390
391         /* Allocate a pixel partway between foreground and background */
392
393 Pixel AllocGreyPixel(Widget w, Pixel fg, Pixel bg, int scale)
394 {
395         XColor get_cf, get_cb;
396         Display *dpy = XtDisplay(w);
397         Colormap cmap;
398
399         cmap = w->core.colormap;
400
401         get_cf.pixel = fg;
402         get_cb.pixel = bg;
403
404         XQueryColor(dpy, cmap, &get_cf);
405         XQueryColor(dpy, cmap, &get_cb);
406
407         return AllocGreyPixelC(w, &get_cf, &get_cb, scale);
408 }
409
410         /* Allocate a pixel partway between foreground and background */
411
412 Pixel AllocGreyPixelC(Widget w, XColor * fg, XColor * bg, int scale)
413 {
414         XColor set_c;
415         Display *dpy = XtDisplay(w);
416         int r, g, b;
417         Colormap cmap = w->core.colormap;
418
419         r = (fg->red * scale + bg->red * (100 - scale)) / 100;
420         g = (fg->green * scale + bg->green * (100 - scale)) / 100;
421         b = (fg->blue * scale + bg->blue * (100 - scale)) / 100;
422
423         if (scale > 100 || scale < 0) { /* look out for overflow */
424                 int minc, maxc;
425                 maxc = Max(r, Max(g, b));
426                 minc = Min(r, Min(g, b));
427                 if (maxc > 65535) {
428                         maxc /= 16;
429                         r = r * (65535 / 16) / maxc;
430                         g = g * (65535 / 16) / maxc;
431                         b = b * (65535 / 16) / maxc;
432                 }
433                 if (minc < 0) {
434                         r = Max(r, 0);
435                         g = Max(g, 0);
436                         b = Max(b, 0);
437                 }
438         }
439
440         set_c.red = r;
441         set_c.green = g;
442         set_c.blue = b;
443         set_c.flags = DoRed | DoGreen | DoBlue;
444         (void)XAllocColor(dpy, cmap, &set_c);
445         return set_c.pixel;
446 }
447
448         /* draw a 3-d box */
449
450 void
451 Draw3dBox(Widget w, int x, int y, int wid, int hgt, int s, GC topgc, GC botgc)
452 {
453         Display *dpy = XtDisplay(w);
454         Window win = XtWindow(w);
455
456         if (s == 0)
457                 return;
458
459         if (s == 1) {
460                 XDrawLine(dpy, win, botgc, x, y + hgt - 1, x + wid - 1,
461                           y + hgt - 1);
462                 XDrawLine(dpy, win, botgc, x + wid - 1, y, x + wid - 1,
463                           y + hgt - 1);
464                 XDrawLine(dpy, win, topgc, x, y, x, y + hgt - 1);
465                 XDrawLine(dpy, win, topgc, x, y, x + wid - 1, y);
466         } else {
467                 XPoint pts[6];
468
469                 /* bottom-right shadow */
470                 pts[0].x = x;
471                 pts[0].y = y + hgt;
472                 pts[1].x = s;
473                 pts[1].y = -s;
474                 pts[2].x = wid - 2 * s;
475                 pts[2].y = 0;
476                 pts[3].x = 0;
477                 pts[3].y = -(hgt - 2 * s);
478                 pts[4].x = s;
479                 pts[4].y = -s;
480                 pts[5].x = 0;
481                 pts[5].y = hgt;
482                 XFillPolygon(dpy, win, botgc, pts, 6, Nonconvex,
483                              CoordModePrevious);
484
485                 /* top-left shadow */
486                 pts[0].x = x;
487                 pts[0].y = y;
488                 pts[1].x = wid;
489                 pts[1].y = 0;
490                 pts[2].x = -s;
491                 pts[2].y = s;
492                 pts[3].x = -wid + 2 * s;
493                 pts[3].y = 0;
494                 pts[4].x = 0;
495                 pts[4].y = hgt - 2 * s;
496                 pts[5].x = -s;
497                 pts[5].y = s;
498                 XFillPolygon(dpy, win, topgc, pts, 6, Nonconvex,
499                              CoordModePrevious);
500         }
501 }
502
503 #if XtSpecificationRelease < 5
504
505 static GC
506 XtAllocateGC(Widget w, int depth, unsigned long mask, XGCValues * values,
507              unsigned long dynamic, unsigned long dontcare)
508 {
509         return XtGetGC(w, mask, values);
510 }
511 #endif
512
513 #ifdef HAVE_XMU
514
515 static unsigned char screen0[2] = { 0, 0 };
516 static unsigned char screen25[2] = { 0, 0xaa };
517 static unsigned char screen75[2] = { 0xaa, 0xff };
518 static unsigned char screen100[2] = { 0xff, 0xff };
519
520 static Pixmap getDitherPixmap(Widget w, int contrast)
521 {
522         Display *dpy = XtDisplay(w);
523         Window win = XtWindow(w);
524
525         if (contrast <= 5)
526                 return XCreateBitmapFromData(dpy, win, (char *)screen0, 2, 2);
527         else if (contrast <= 37)
528                 return XCreateBitmapFromData(dpy, win, (char *)screen25, 2, 2);
529         else if (contrast <= 62)
530                 return XmuCreateStippledPixmap(XtScreen(w), 1L, 0L, 1);
531         else if (contrast <= 95)
532                 return XCreateBitmapFromData(dpy, win, (char *)screen75, 2, 2);
533         else
534                 return XCreateBitmapFromData(dpy, win, (char *)screen100, 2, 2);
535 }
536
537 #endif                          /* HAVE_XMU */