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