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