OSDN Git Service

Initial revision
[pf3gnuchains/pf3gnuchains4x.git] / tk / win / tkWin3d.c
1 /* 
2  * tkWin3d.c --
3  *
4  *      This file contains the platform specific routines for
5  *      drawing 3d borders in the Windows 95 style.
6  *
7  * Copyright (c) 1996 by Sun Microsystems, Inc.
8  *
9  * See the file "license.terms" for information on usage and redistribution
10  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11  *
12  * RCS: @(#) $Id$
13  */
14
15 #include <tk3d.h>
16 #include <tkWinInt.h>
17
18 /*
19  * This structure is used to keep track of the extra colors used by
20  * Windows 3d borders.
21  */
22
23 typedef struct {
24     TkBorder info;
25     XColor *light2ColorPtr; /* System3dLight */
26     XColor *dark2ColorPtr;  /* System3dDarkShadow */
27 } WinBorder;
28
29 \f
30 /*
31  *----------------------------------------------------------------------
32  *
33  * TkpGetBorder --
34  *
35  *      This function allocates a new TkBorder structure.
36  *
37  * Results:
38  *      Returns a newly allocated TkBorder.
39  *
40  * Side effects:
41  *      None.
42  *
43  *----------------------------------------------------------------------
44  */
45
46 TkBorder *
47 TkpGetBorder()
48 {
49     WinBorder *borderPtr = (WinBorder *) ckalloc(sizeof(WinBorder));
50     borderPtr->light2ColorPtr = NULL;
51     borderPtr->dark2ColorPtr = NULL;
52     return (TkBorder *) borderPtr;
53 }
54 \f
55 /*
56  *----------------------------------------------------------------------
57  *
58  * TkpFreeBorder --
59  *
60  *      This function frees any colors allocated by the platform
61  *      specific part of this module.
62  *
63  * Results:
64  *      None.
65  *
66  * Side effects:
67  *      May deallocate some colors.
68  *
69  *----------------------------------------------------------------------
70  */
71
72 void
73 TkpFreeBorder(borderPtr)
74     TkBorder *borderPtr;
75 {
76     WinBorder *winBorderPtr = (WinBorder *) borderPtr;
77     if (winBorderPtr->light2ColorPtr) {
78         Tk_FreeColor(winBorderPtr->light2ColorPtr);
79     }
80     if (winBorderPtr->dark2ColorPtr) {
81         Tk_FreeColor(winBorderPtr->dark2ColorPtr);
82     }
83 }
84 \f
85 /*
86  *--------------------------------------------------------------
87  *
88  * Tk_3DVerticalBevel --
89  *
90  *      This procedure draws a vertical bevel along one side of
91  *      an object.  The bevel is always rectangular in shape:
92  *                      |||
93  *                      |||
94  *                      |||
95  *                      |||
96  *                      |||
97  *                      |||
98  *      An appropriate shadow color is chosen for the bevel based
99  *      on the leftBevel and relief arguments.  Normally this
100  *      procedure is called first, then Tk_3DHorizontalBevel is
101  *      called next to draw neat corners.
102  *
103  * Results:
104  *      None.
105  *
106  * Side effects:
107  *      Graphics are drawn in drawable.
108  *
109  *--------------------------------------------------------------
110  */
111
112 void
113 Tk_3DVerticalBevel(tkwin, drawable, border, x, y, width, height,
114         leftBevel, relief)
115     Tk_Window tkwin;            /* Window for which border was allocated. */
116     Drawable drawable;          /* X window or pixmap in which to draw. */
117     Tk_3DBorder border;         /* Token for border to draw. */
118     int x, y, width, height;    /* Area of vertical bevel. */
119     int leftBevel;              /* Non-zero means this bevel forms the
120                                  * left side of the object;  0 means it
121                                  * forms the right side. */
122     int relief;                 /* Kind of bevel to draw.  For example,
123                                  * TK_RELIEF_RAISED means interior of
124                                  * object should appear higher than
125                                  * exterior. */
126 {
127     TkBorder *borderPtr = (TkBorder *) border;
128     int left, right;
129     Display *display = Tk_Display(tkwin);
130     TkWinDCState state;
131     HDC dc = TkWinGetDrawableDC(display, drawable, &state);
132     int half;
133
134     if ((borderPtr->lightGC == None) && (relief != TK_RELIEF_FLAT)) {
135         TkpGetShadows(borderPtr, tkwin);
136     }
137
138     switch (relief) {
139         case TK_RELIEF_RAISED:
140             left = (leftBevel)
141                 ? borderPtr->lightGC->foreground
142                 : borderPtr->darkGC->foreground;
143             right = (leftBevel)
144                 ? ((WinBorder *)borderPtr)->light2ColorPtr->pixel
145                 : ((WinBorder *)borderPtr)->dark2ColorPtr->pixel;
146             break;
147         case TK_RELIEF_SUNKEN:
148             left = (leftBevel)
149                 ? borderPtr->darkGC->foreground
150                 : ((WinBorder *)borderPtr)->light2ColorPtr->pixel;
151             right = (leftBevel)
152                 ? ((WinBorder *)borderPtr)->dark2ColorPtr->pixel
153                 : borderPtr->lightGC->foreground;
154             break;
155         case TK_RELIEF_RIDGE:
156             left = borderPtr->lightGC->foreground;
157             right = borderPtr->darkGC->foreground;
158             break;
159         case TK_RELIEF_GROOVE:
160             left = borderPtr->darkGC->foreground;
161             right = borderPtr->lightGC->foreground;
162             break;
163         case TK_RELIEF_FLAT:
164             left = right = borderPtr->bgGC->foreground;
165             break;
166         case TK_RELIEF_SOLID:
167             left = right = RGB(0,0,0);
168             break;
169     }
170     half = width/2;
171     if (leftBevel && (width & 1)) {
172         half++;
173     }
174     TkWinFillRect(dc, x, y, half, height, left);
175     TkWinFillRect(dc, x+half, y, width-half, height, right);
176     TkWinReleaseDrawableDC(drawable, dc, &state);
177 }
178 \f
179 /*
180  *--------------------------------------------------------------
181  *
182  * Tk_3DHorizontalBevel --
183  *
184  *      This procedure draws a horizontal bevel along one side of
185  *      an object.  The bevel has mitered corners (depending on
186  *      leftIn and rightIn arguments).
187  *
188  * Results:
189  *      None.
190  *
191  * Side effects:
192  *      None.
193  *
194  *--------------------------------------------------------------
195  */
196
197 void
198 Tk_3DHorizontalBevel(tkwin, drawable, border, x, y, width, height,
199         leftIn, rightIn, topBevel, relief)
200     Tk_Window tkwin;            /* Window for which border was allocated. */
201     Drawable drawable;          /* X window or pixmap in which to draw. */
202     Tk_3DBorder border;         /* Token for border to draw. */
203     int x, y, width, height;    /* Bounding box of area of bevel.  Height
204                                  * gives width of border. */
205     int leftIn, rightIn;        /* Describes whether the left and right
206                                  * edges of the bevel angle in or out as
207                                  * they go down.  For example, if "leftIn"
208                                  * is true, the left side of the bevel
209                                  * looks like this:
210                                  *      ___________
211                                  *       __________
212                                  *        _________
213                                  *         ________
214                                  */
215     int topBevel;               /* Non-zero means this bevel forms the
216                                  * top side of the object;  0 means it
217                                  * forms the bottom side. */
218     int relief;                 /* Kind of bevel to draw.  For example,
219                                  * TK_RELIEF_RAISED means interior of
220                                  * object should appear higher than
221                                  * exterior. */
222 {
223     TkBorder *borderPtr = (TkBorder *) border;
224     Display *display = Tk_Display(tkwin);
225     int bottom, halfway, x1, x2, x1Delta, x2Delta;
226     TkWinDCState state;
227     HDC dc = TkWinGetDrawableDC(display, drawable, &state);
228     int topColor, bottomColor;
229
230     if ((borderPtr->lightGC == None) && (relief != TK_RELIEF_FLAT)) {
231         TkpGetShadows(borderPtr, tkwin);
232     }
233
234     /*
235      * Compute a GC for the top half of the bevel and a GC for the
236      * bottom half (they're the same in many cases).
237      */
238
239     switch (relief) {
240         case TK_RELIEF_RAISED:
241             topColor = (topBevel)
242                 ? borderPtr->lightGC->foreground
243                 : borderPtr->darkGC->foreground;
244             bottomColor = (topBevel)
245                 ? ((WinBorder *)borderPtr)->light2ColorPtr->pixel
246                 : ((WinBorder *)borderPtr)->dark2ColorPtr->pixel;
247             break;
248         case TK_RELIEF_SUNKEN:
249             topColor = (topBevel)
250                 ? borderPtr->darkGC->foreground
251                 : ((WinBorder *)borderPtr)->light2ColorPtr->pixel;
252             bottomColor = (topBevel)
253                 ? ((WinBorder *)borderPtr)->dark2ColorPtr->pixel
254                 : borderPtr->lightGC->foreground;
255             break;
256         case TK_RELIEF_RIDGE:
257             topColor = borderPtr->lightGC->foreground;
258             bottomColor = borderPtr->darkGC->foreground;
259             break;
260         case TK_RELIEF_GROOVE:
261             topColor = borderPtr->darkGC->foreground;
262             bottomColor = borderPtr->lightGC->foreground;
263             break;
264         case TK_RELIEF_FLAT:
265             topColor = bottomColor = borderPtr->bgGC->foreground;
266             break;
267         case TK_RELIEF_SOLID:
268             topColor = bottomColor = RGB(0,0,0);
269     }
270
271     /*
272      * Compute various other geometry-related stuff.
273      */
274
275     if (leftIn) {
276         x1 = x+1;
277     } else {
278         x1 = x+height-1;
279     }
280     x2 = x+width;
281     if (rightIn) {
282         x2--;
283     } else {
284         x2 -= height;
285     }
286     x1Delta = (leftIn) ? 1 : -1;
287     x2Delta = (rightIn) ? -1 : 1;
288     halfway = y + height/2;
289     if (topBevel && (height & 1)) {
290         halfway++;
291     }
292     bottom = y + height;
293
294     /*
295      * Draw one line for each y-coordinate covered by the bevel.
296      */
297
298     for ( ; y < bottom; y++) {
299         /*
300          * In some weird cases (such as large border widths for skinny
301          * rectangles) x1 can be >= x2.  Don't draw the lines
302          * in these cases.
303          */
304
305         if (x1 < x2) {
306             TkWinFillRect(dc, x1, y, x2-x1, 1,
307                 (y < halfway) ? topColor : bottomColor);
308         }
309         x1 += x1Delta;
310         x2 += x2Delta;
311     }
312     TkWinReleaseDrawableDC(drawable, dc, &state);
313 }
314 \f
315 /*
316  *----------------------------------------------------------------------
317  *
318  * TkpGetShadows --
319  *
320  *      This procedure computes the shadow colors for a 3-D border
321  *      and fills in the corresponding fields of the Border structure.
322  *      It's called lazily, so that the colors aren't allocated until
323  *      something is actually drawn with them.  That way, if a border
324  *      is only used for flat backgrounds the shadow colors will
325  *      never be allocated.
326  *
327  * Results:
328  *      None.
329  *
330  * Side effects:
331  *      The lightGC and darkGC fields in borderPtr get filled in,
332  *      if they weren't already.
333  *
334  *----------------------------------------------------------------------
335  */
336
337 void
338 TkpGetShadows(borderPtr, tkwin)
339     TkBorder *borderPtr;        /* Information about border. */
340     Tk_Window tkwin;            /* Window where border will be used for
341                                  * drawing. */
342 {
343     XColor lightColor, darkColor;
344     int tmp1, tmp2;
345     XGCValues gcValues;
346
347     if (borderPtr->lightGC != None) {
348         return;
349     }
350
351     /*
352      * Handle the special case of the default system colors.
353      */
354
355     if ((TkWinIndexOfColor(borderPtr->bgColorPtr) == COLOR_3DFACE)
356         || (TkWinIndexOfColor(borderPtr->bgColorPtr) == COLOR_WINDOW)) {
357         borderPtr->darkColorPtr = Tk_GetColor(NULL, tkwin,
358             Tk_GetUid("SystemButtonShadow"));
359         gcValues.foreground = borderPtr->darkColorPtr->pixel;
360         borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
361         borderPtr->lightColorPtr = Tk_GetColor(NULL, tkwin,
362             Tk_GetUid("SystemButtonHighlight"));
363         gcValues.foreground = borderPtr->lightColorPtr->pixel;
364         borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
365         ((WinBorder*)borderPtr)->dark2ColorPtr = Tk_GetColor(NULL, tkwin,
366                 Tk_GetUid("System3dDarkShadow"));
367         ((WinBorder*)borderPtr)->light2ColorPtr = Tk_GetColor(NULL, tkwin,
368                 Tk_GetUid("System3dLight"));
369         return;
370     } else {
371         darkColor.red = 0;
372         darkColor.green = 0;
373         darkColor.blue = 0;
374         ((WinBorder*)borderPtr)->dark2ColorPtr = Tk_GetColorByValue(tkwin,
375             &darkColor);
376         lightColor = *(borderPtr->bgColorPtr);
377         ((WinBorder*)borderPtr)->light2ColorPtr = Tk_GetColorByValue(tkwin, 
378             &lightColor);
379     }
380     
381     /*
382      * First, handle the case of a color display with lots of colors.
383      * The shadow colors get computed using whichever formula results
384      * in the greatest change in color:
385      * 1. Lighter shadow is half-way to white, darker shadow is half
386      *    way to dark.
387      * 2. Lighter shadow is 40% brighter than background, darker shadow
388      *    is 40% darker than background.
389      */
390
391     if (Tk_Depth(tkwin) >= 6) {
392         /*
393          * This is a color display with lots of colors.  For the dark
394          * shadow, cut 40% from each of the background color components.
395          * For the light shadow, boost each component by 40% or half-way
396          * to white, whichever is greater (the first approach works
397          * better for unsaturated colors, the second for saturated ones).
398          */
399
400         darkColor.red = (60 * (int) borderPtr->bgColorPtr->red)/100;
401         darkColor.green = (60 * (int) borderPtr->bgColorPtr->green)/100;
402         darkColor.blue = (60 * (int) borderPtr->bgColorPtr->blue)/100;
403         borderPtr->darkColorPtr = Tk_GetColorByValue(tkwin, &darkColor);
404         gcValues.foreground = borderPtr->darkColorPtr->pixel;
405         borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
406
407         /*
408          * Compute the colors using integers, not using lightColor.red
409          * etc.: these are shorts and may have problems with integer
410          * overflow.
411          */
412
413         tmp1 = (14 * (int) borderPtr->bgColorPtr->red)/10;
414         if (tmp1 > MAX_INTENSITY) {
415             tmp1 = MAX_INTENSITY;
416         }
417         tmp2 = (MAX_INTENSITY + (int) borderPtr->bgColorPtr->red)/2;
418         lightColor.red = (tmp1 > tmp2) ? tmp1 : tmp2;
419         tmp1 = (14 * (int) borderPtr->bgColorPtr->green)/10;
420         if (tmp1 > MAX_INTENSITY) {
421             tmp1 = MAX_INTENSITY;
422         }
423         tmp2 = (MAX_INTENSITY + (int) borderPtr->bgColorPtr->green)/2;
424         lightColor.green = (tmp1 > tmp2) ? tmp1 : tmp2;
425         tmp1 = (14 * (int) borderPtr->bgColorPtr->blue)/10;
426         if (tmp1 > MAX_INTENSITY) {
427             tmp1 = MAX_INTENSITY;
428         }
429         tmp2 = (MAX_INTENSITY + (int) borderPtr->bgColorPtr->blue)/2;
430         lightColor.blue = (tmp1 > tmp2) ? tmp1 : tmp2;
431         borderPtr->lightColorPtr = Tk_GetColorByValue(tkwin, &lightColor);
432         gcValues.foreground = borderPtr->lightColorPtr->pixel;
433         borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
434         return;
435     }
436
437     if (borderPtr->shadow == None) {
438         borderPtr->shadow = Tk_GetBitmap((Tcl_Interp *) NULL, tkwin,
439                 Tk_GetUid("gray50"));
440         if (borderPtr->shadow == None) {
441             panic("TkpGetShadows couldn't allocate bitmap for border");
442         }
443     }
444     if (borderPtr->visual->map_entries > 2) {
445         /*
446          * This isn't a monochrome display, but the colormap either
447          * ran out of entries or didn't have very many to begin with.
448          * Generate the light shadows with a white stipple and the
449          * dark shadows with a black stipple.
450          */
451
452         gcValues.foreground = borderPtr->bgColorPtr->pixel;
453         gcValues.background = BlackPixelOfScreen(borderPtr->screen);
454         gcValues.stipple = borderPtr->shadow;
455         gcValues.fill_style = FillOpaqueStippled;
456         borderPtr->darkGC = Tk_GetGC(tkwin,
457                 GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues);
458         gcValues.foreground = WhitePixelOfScreen(borderPtr->screen);
459         gcValues.background = borderPtr->bgColorPtr->pixel;
460         borderPtr->lightGC = Tk_GetGC(tkwin,
461                 GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues);
462         return;
463     }
464
465     /*
466      * This is just a measly monochrome display, hardly even worth its
467      * existence on this earth.  Make one shadow a 50% stipple and the
468      * other the opposite of the background.
469      */
470
471     gcValues.foreground = WhitePixelOfScreen(borderPtr->screen);
472     gcValues.background = BlackPixelOfScreen(borderPtr->screen);
473     gcValues.stipple = borderPtr->shadow;
474     gcValues.fill_style = FillOpaqueStippled;
475     borderPtr->lightGC = Tk_GetGC(tkwin,
476             GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues);
477     if (borderPtr->bgColorPtr->pixel
478             == WhitePixelOfScreen(borderPtr->screen)) {
479         gcValues.foreground = BlackPixelOfScreen(borderPtr->screen);
480         borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
481     } else {
482         borderPtr->darkGC = borderPtr->lightGC;
483         borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
484     }
485 }
486 \f
487 /*
488  *----------------------------------------------------------------------
489  *
490  * TkWinGetBorderPixels --
491  *
492  *      This routine returns the 5 COLORREFs used to draw a given
493  *      3d border.  
494  *
495  * Results:
496  *      Returns the colors in the specified array.
497  *
498  * Side effects:
499  *      May cause the remaining colors to be allocated.
500  *
501  *----------------------------------------------------------------------
502  */
503
504 COLORREF
505 TkWinGetBorderPixels(tkwin, border, which)
506     Tk_Window tkwin;
507     Tk_3DBorder border;
508     int which;                  /* One of TK_3D_FLAT_GC, TK_3D_LIGHT_GC,
509                                  * TK_3D_DARK_GC, TK_3D_LIGHT2, TK_3D_DARK2 */
510 {
511     WinBorder *borderPtr = (WinBorder *) border;
512     
513     if (borderPtr->info.lightGC == None) {
514         TkpGetShadows(&borderPtr->info, tkwin);
515     }
516     switch (which) {
517         case TK_3D_FLAT_GC:
518             return borderPtr->info.bgColorPtr->pixel;
519         case TK_3D_LIGHT_GC:
520             if (borderPtr->info.lightColorPtr == NULL) {
521                 return WhitePixelOfScreen(borderPtr->info.screen);
522             }
523             return borderPtr->info.lightColorPtr->pixel;
524         case TK_3D_DARK_GC:
525             if (borderPtr->info.darkColorPtr == NULL) {
526                 return BlackPixelOfScreen(borderPtr->info.screen);
527             }
528             return borderPtr->info.darkColorPtr->pixel;
529         case TK_3D_LIGHT2:
530             return borderPtr->light2ColorPtr->pixel;
531         case TK_3D_DARK2:
532             return borderPtr->dark2ColorPtr->pixel;
533     }
534     return 0;
535 }