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