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: tkWin3d.c,v 1.5 2000/04/14 01:36:35 ericm Exp $ 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 20 * Windows 3d borders. 21 */ 22 23typedef struct { 24 TkBorder info; 25 XColor *light2ColorPtr; /* System3dLight */ 26 XColor *dark2ColorPtr; /* System3dDarkShadow */ 27} WinBorder; 28 29 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 46TkBorder * 47TkpGetBorder() 48{ 49 WinBorder *borderPtr = (WinBorder *) ckalloc(sizeof(WinBorder)); 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 61 * specific part of this module. 62 * 63 * Results: 64 * None. 65 * 66 * Side effects: 67 * May deallocate some colors. 68 * 69 *---------------------------------------------------------------------- 70 */ 71 72void 73TkpFreeBorder(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 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 112void 113Tk_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 ? ((WinBorder *)borderPtr)->dark2ColorPtr->pixel 150 : ((WinBorder *)borderPtr)->light2ColorPtr->pixel; 151 right = (leftBevel) 152 ? borderPtr->darkGC->foreground 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 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 197void 198Tk_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 ? ((WinBorder *)borderPtr)->dark2ColorPtr->pixel 251 : ((WinBorder *)borderPtr)->light2ColorPtr->pixel; 252 bottomColor = (topBevel) 253 ? borderPtr->darkGC->foreground 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 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 337void 338TkpGetShadows(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 int r, g, b; 346 XGCValues gcValues; 347 348 if (borderPtr->lightGC != None) { 349 return; 350 } 351 352 /* 353 * Handle the special case of the default system colors. 354 */ 355 356 if ((TkWinIndexOfColor(borderPtr->bgColorPtr) == COLOR_3DFACE) 357 || (TkWinIndexOfColor(borderPtr->bgColorPtr) == COLOR_WINDOW)) { 358 borderPtr->darkColorPtr = Tk_GetColor(NULL, tkwin, 359 Tk_GetUid("SystemButtonShadow")); 360 gcValues.foreground = borderPtr->darkColorPtr->pixel; 361 borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues); 362 borderPtr->lightColorPtr = Tk_GetColor(NULL, tkwin, 363 Tk_GetUid("SystemButtonHighlight")); 364 gcValues.foreground = borderPtr->lightColorPtr->pixel; 365 borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues); 366 ((WinBorder*)borderPtr)->dark2ColorPtr = Tk_GetColor(NULL, tkwin, 367 Tk_GetUid("System3dDarkShadow")); 368 ((WinBorder*)borderPtr)->light2ColorPtr = Tk_GetColor(NULL, tkwin, 369 Tk_GetUid("System3dLight")); 370 return; 371 } else { 372 darkColor.red = 0; 373 darkColor.green = 0; 374 darkColor.blue = 0; 375 ((WinBorder*)borderPtr)->dark2ColorPtr = Tk_GetColorByValue(tkwin, 376 &darkColor); 377 lightColor = *(borderPtr->bgColorPtr); 378 ((WinBorder*)borderPtr)->light2ColorPtr = Tk_GetColorByValue(tkwin, 379 &lightColor); 380 } 381 382 /* 383 * First, handle the case of a color display with lots of colors. 384 * The shadow colors get computed using whichever formula results 385 * in the greatest change in color: 386 * 1. Lighter shadow is half-way to white, darker shadow is half 387 * way to dark. 388 * 2. Lighter shadow is 40% brighter than background, darker shadow 389 * is 40% darker than background. 390 */ 391 392 if (Tk_Depth(tkwin) >= 6) { 393 /* 394 * This is a color display with lots of colors. For the dark 395 * shadow, cut 40% from each of the background color components. 396 * But if the background is already very dark, make the 397 * dark color a little lighter than the background by increasing 398 * each color component 1/4th of the way to MAX_INTENSITY. 399 * 400 * For the light shadow, boost each component by 40% or half-way 401 * to white, whichever is greater (the first approach works 402 * better for unsaturated colors, the second for saturated ones). 403 * But if the background is already very bright, instead choose a 404 * slightly darker color for the light shadow by reducing each 405 * color component by 10%. 406 * 407 * Compute the colors using integers, not using lightColor.red 408 * etc.: these are shorts and may have problems with integer 409 * overflow. 410 */ 411 412 /* 413 * Compute the dark shadow color 414 */ 415 416 r = (int) borderPtr->bgColorPtr->red; 417 g = (int) borderPtr->bgColorPtr->green; 418 b = (int) borderPtr->bgColorPtr->blue; 419 420 if (r*0.5*r + g*1.0*g + b*0.28*b < MAX_INTENSITY*0.05*MAX_INTENSITY) { 421 darkColor.red = (MAX_INTENSITY + 3*r)/4; 422 darkColor.green = (MAX_INTENSITY + 3*g)/4; 423 darkColor.blue = (MAX_INTENSITY + 3*b)/4; 424 } else { 425 darkColor.red = (60 * r)/100; 426 darkColor.green = (60 * g)/100; 427 darkColor.blue = (60 * b)/100; 428 } 429 430 /* 431 * Allocate the dark shadow color and its GC 432 */ 433 434 borderPtr->darkColorPtr = Tk_GetColorByValue(tkwin, &darkColor); 435 gcValues.foreground = borderPtr->darkColorPtr->pixel; 436 borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues); 437 438 /* 439 * Compute the light shadow color 440 */ 441 442 if (g > MAX_INTENSITY*0.95) { 443 lightColor.red = (90 * r)/100; 444 lightColor.green = (90 * g)/100; 445 lightColor.blue = (90 * b)/100; 446 } else { 447 tmp1 = (14 * r)/10; 448 if (tmp1 > MAX_INTENSITY) { 449 tmp1 = MAX_INTENSITY; 450 } 451 tmp2 = (MAX_INTENSITY + r)/2; 452 lightColor.red = (tmp1 > tmp2) ? tmp1 : tmp2; 453 tmp1 = (14 * g)/10; 454 if (tmp1 > MAX_INTENSITY) { 455 tmp1 = MAX_INTENSITY; 456 } 457 tmp2 = (MAX_INTENSITY + g)/2; 458 lightColor.green = (tmp1 > tmp2) ? tmp1 : tmp2; 459 tmp1 = (14 * b)/10; 460 if (tmp1 > MAX_INTENSITY) { 461 tmp1 = MAX_INTENSITY; 462 } 463 tmp2 = (MAX_INTENSITY + b)/2; 464 lightColor.blue = (tmp1 > tmp2) ? tmp1 : tmp2; 465 } 466 467 /* 468 * Allocate the light shadow color and its GC 469 */ 470 471 borderPtr->lightColorPtr = Tk_GetColorByValue(tkwin, &lightColor); 472 gcValues.foreground = borderPtr->lightColorPtr->pixel; 473 borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues); 474 return; 475 } 476 477 if (borderPtr->shadow == None) { 478 borderPtr->shadow = Tk_GetBitmap((Tcl_Interp *) NULL, tkwin, 479 Tk_GetUid("gray50")); 480 if (borderPtr->shadow == None) { 481 panic("TkpGetShadows couldn't allocate bitmap for border"); 482 } 483 } 484 if (borderPtr->visual->map_entries > 2) { 485 /* 486 * This isn't a monochrome display, but the colormap either 487 * ran out of entries or didn't have very many to begin with. 488 * Generate the light shadows with a white stipple and the 489 * dark shadows with a black stipple. 490 */ 491 492 gcValues.foreground = borderPtr->bgColorPtr->pixel; 493 gcValues.background = BlackPixelOfScreen(borderPtr->screen); 494 gcValues.stipple = borderPtr->shadow; 495 gcValues.fill_style = FillOpaqueStippled; 496 borderPtr->darkGC = Tk_GetGC(tkwin, 497 GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues); 498 gcValues.foreground = WhitePixelOfScreen(borderPtr->screen); 499 gcValues.background = borderPtr->bgColorPtr->pixel; 500 borderPtr->lightGC = Tk_GetGC(tkwin, 501 GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues); 502 return; 503 } 504 505 /* 506 * This is just a measly monochrome display, hardly even worth its 507 * existence on this earth. Make one shadow a 50% stipple and the 508 * other the opposite of the background. 509 */ 510 511 gcValues.foreground = WhitePixelOfScreen(borderPtr->screen); 512 gcValues.background = BlackPixelOfScreen(borderPtr->screen); 513 gcValues.stipple = borderPtr->shadow; 514 gcValues.fill_style = FillOpaqueStippled; 515 borderPtr->lightGC = Tk_GetGC(tkwin, 516 GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues); 517 if (borderPtr->bgColorPtr->pixel 518 == WhitePixelOfScreen(borderPtr->screen)) { 519 gcValues.foreground = BlackPixelOfScreen(borderPtr->screen); 520 borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues); 521 } else { 522 borderPtr->darkGC = borderPtr->lightGC; 523 borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues); 524 } 525} 526 527/* 528 *---------------------------------------------------------------------- 529 * 530 * TkWinGetBorderPixels -- 531 * 532 * This routine returns the 5 COLORREFs used to draw a given 533 * 3d border. 534 * 535 * Results: 536 * Returns the colors in the specified array. 537 * 538 * Side effects: 539 * May cause the remaining colors to be allocated. 540 * 541 *---------------------------------------------------------------------- 542 */ 543 544COLORREF 545TkWinGetBorderPixels(tkwin, border, which) 546 Tk_Window tkwin; 547 Tk_3DBorder border; 548 int which; /* One of TK_3D_FLAT_GC, TK_3D_LIGHT_GC, 549 * TK_3D_DARK_GC, TK_3D_LIGHT2, TK_3D_DARK2 */ 550{ 551 WinBorder *borderPtr = (WinBorder *) border; 552 553 if (borderPtr->info.lightGC == None) { 554 TkpGetShadows(&borderPtr->info, tkwin); 555 } 556 switch (which) { 557 case TK_3D_FLAT_GC: 558 return borderPtr->info.bgColorPtr->pixel; 559 case TK_3D_LIGHT_GC: 560 if (borderPtr->info.lightColorPtr == NULL) { 561 return WhitePixelOfScreen(borderPtr->info.screen); 562 } 563 return borderPtr->info.lightColorPtr->pixel; 564 case TK_3D_DARK_GC: 565 if (borderPtr->info.darkColorPtr == NULL) { 566 return BlackPixelOfScreen(borderPtr->info.screen); 567 } 568 return borderPtr->info.darkColorPtr->pixel; 569 case TK_3D_LIGHT2: 570 return borderPtr->light2ColorPtr->pixel; 571 case TK_3D_DARK2: 572 return borderPtr->dark2ColorPtr->pixel; 573 } 574 return 0; 575} 576