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