1/* 2 * tk3d.c -- 3 * 4 * This module provides procedures to draw borders in the 5 * three-dimensional Motif style. 6 * 7 * Copyright (c) 1990-1994 The Regents of the University of California. 8 * Copyright (c) 1994-1997 Sun Microsystems, Inc. 9 * 10 * See the file "license.terms" for information on usage and redistribution of 11 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 12 * 13 * RCS: @(#) $Id$ 14 */ 15 16#include "tkInt.h" 17#include "tk3d.h" 18 19/* 20 * The following table defines the string values for reliefs, which are used 21 * by Tk_GetReliefFromObj. 22 */ 23 24static CONST char *reliefStrings[] = { 25 "flat", "groove", "raised", "ridge", "solid", "sunken", NULL 26}; 27 28/* 29 * Forward declarations for functions defined in this file: 30 */ 31 32static void BorderInit(TkDisplay *dispPtr); 33static void DupBorderObjProc(Tcl_Obj *srcObjPtr, 34 Tcl_Obj *dupObjPtr); 35static void FreeBorderObjProc(Tcl_Obj *objPtr); 36static int Intersect(XPoint *a1Ptr, XPoint *a2Ptr, 37 XPoint *b1Ptr, XPoint *b2Ptr, XPoint *iPtr); 38static void InitBorderObj(Tcl_Obj *objPtr); 39static void ShiftLine(XPoint *p1Ptr, XPoint *p2Ptr, 40 int distance, XPoint *p3Ptr); 41 42/* 43 * The following structure defines the implementation of the "border" Tcl 44 * object, used for drawing. The border object remembers the hash table entry 45 * associated with a border. The actual allocation and deallocation of the 46 * border should be done by the configuration package when the border option 47 * is set. 48 */ 49 50Tcl_ObjType tkBorderObjType = { 51 "border", /* name */ 52 FreeBorderObjProc, /* freeIntRepProc */ 53 DupBorderObjProc, /* dupIntRepProc */ 54 NULL, /* updateStringProc */ 55 NULL /* setFromAnyProc */ 56}; 57 58/* 59 *---------------------------------------------------------------------- 60 * 61 * Tk_Alloc3DBorderFromObj -- 62 * 63 * Given a Tcl_Obj *, map the value to a corresponding Tk_3DBorder 64 * structure based on the tkwin given. 65 * 66 * Results: 67 * The return value is a token for a data structure describing a 3-D 68 * border. This token may be passed to functions such as 69 * Tk_Draw3DRectangle and Tk_Free3DBorder. If an error prevented the 70 * border from being created then NULL is returned and an error message 71 * will be left in the interp's result. 72 * 73 * Side effects: 74 * The border is added to an internal database with a reference count. 75 * For each call to this function, there should eventually be a call to 76 * FreeBorderObjProc so that the database is cleaned up when borders 77 * aren't in use anymore. 78 * 79 *---------------------------------------------------------------------- 80 */ 81 82Tk_3DBorder 83Tk_Alloc3DBorderFromObj( 84 Tcl_Interp *interp, /* Interp for error results. */ 85 Tk_Window tkwin, /* Need the screen the border is used on.*/ 86 Tcl_Obj *objPtr) /* Object giving name of color for window 87 * background. */ 88{ 89 TkBorder *borderPtr; 90 91 if (objPtr->typePtr != &tkBorderObjType) { 92 InitBorderObj(objPtr); 93 } 94 borderPtr = (TkBorder *) objPtr->internalRep.twoPtrValue.ptr1; 95 96 /* 97 * If the object currently points to a TkBorder, see if it's the one we 98 * want. If so, increment its reference count and return. 99 */ 100 101 if (borderPtr != NULL) { 102 if (borderPtr->resourceRefCount == 0) { 103 /* 104 * This is a stale reference: it refers to a border that's no 105 * longer in use. Clear the reference. 106 */ 107 108 FreeBorderObjProc(objPtr); 109 borderPtr = NULL; 110 } else if ((Tk_Screen(tkwin) == borderPtr->screen) 111 && (Tk_Colormap(tkwin) == borderPtr->colormap)) { 112 borderPtr->resourceRefCount++; 113 return (Tk_3DBorder) borderPtr; 114 } 115 } 116 117 /* 118 * The object didn't point to the border that we wanted. Search the list 119 * of borders with the same name to see if one of the others is the right 120 * one. 121 */ 122 123 /* 124 * If the cached value is NULL, either the object type was not a color 125 * going in, or the object is a color type but had previously been freed. 126 * 127 * If the value is not NULL, the internal rep is the value of the color 128 * the last time this object was accessed. Check the screen and colormap 129 * of the last access, and if they match, we are done. 130 */ 131 132 if (borderPtr != NULL) { 133 TkBorder *firstBorderPtr = 134 (TkBorder *) Tcl_GetHashValue(borderPtr->hashPtr); 135 FreeBorderObjProc(objPtr); 136 for (borderPtr = firstBorderPtr ; borderPtr != NULL; 137 borderPtr = borderPtr->nextPtr) { 138 if ((Tk_Screen(tkwin) == borderPtr->screen) 139 && (Tk_Colormap(tkwin) == borderPtr->colormap)) { 140 borderPtr->resourceRefCount++; 141 borderPtr->objRefCount++; 142 objPtr->internalRep.twoPtrValue.ptr1 = (void *) borderPtr; 143 return (Tk_3DBorder) borderPtr; 144 } 145 } 146 } 147 148 /* 149 * Still no luck. Call Tk_Get3DBorder to allocate a new border. 150 */ 151 152 borderPtr = (TkBorder *) Tk_Get3DBorder(interp, tkwin, 153 Tcl_GetString(objPtr)); 154 objPtr->internalRep.twoPtrValue.ptr1 = (void *) borderPtr; 155 if (borderPtr != NULL) { 156 borderPtr->objRefCount++; 157 } 158 return (Tk_3DBorder) borderPtr; 159} 160 161/* 162 *-------------------------------------------------------------- 163 * 164 * Tk_Get3DBorder -- 165 * 166 * Create a data structure for displaying a 3-D border. 167 * 168 * Results: 169 * The return value is a token for a data structure describing a 3-D 170 * border. This token may be passed to functions such as 171 * Tk_Draw3DRectangle and Tk_Free3DBorder. If an error prevented the 172 * border from being created then NULL is returned and an error message 173 * will be left in the interp's result. 174 * 175 * Side effects: 176 * Data structures, graphics contexts, etc. are allocated. It is the 177 * caller's responsibility to eventually call Tk_Free3DBorder to release 178 * the resources. 179 * 180 *-------------------------------------------------------------- 181 */ 182 183Tk_3DBorder 184Tk_Get3DBorder( 185 Tcl_Interp *interp, /* Place to store an error message. */ 186 Tk_Window tkwin, /* Token for window in which border will be 187 * drawn. */ 188 Tk_Uid colorName) /* String giving name of color for window 189 * background. */ 190{ 191 Tcl_HashEntry *hashPtr; 192 TkBorder *borderPtr, *existingBorderPtr; 193 int isNew; 194 XGCValues gcValues; 195 XColor *bgColorPtr; 196 TkDisplay *dispPtr; 197 198 dispPtr = ((TkWindow *) tkwin)->dispPtr; 199 200 if (!dispPtr->borderInit) { 201 BorderInit(dispPtr); 202 } 203 204 hashPtr = Tcl_CreateHashEntry(&dispPtr->borderTable, colorName, &isNew); 205 if (!isNew) { 206 existingBorderPtr = (TkBorder *) Tcl_GetHashValue(hashPtr); 207 for (borderPtr = existingBorderPtr; borderPtr != NULL; 208 borderPtr = borderPtr->nextPtr) { 209 if ((Tk_Screen(tkwin) == borderPtr->screen) 210 && (Tk_Colormap(tkwin) == borderPtr->colormap)) { 211 borderPtr->resourceRefCount++; 212 return (Tk_3DBorder) borderPtr; 213 } 214 } 215 } else { 216 existingBorderPtr = NULL; 217 } 218 219 /* 220 * No satisfactory border exists yet. Initialize a new one. 221 */ 222 223 bgColorPtr = Tk_GetColor(interp, tkwin, colorName); 224 if (bgColorPtr == NULL) { 225 if (isNew) { 226 Tcl_DeleteHashEntry(hashPtr); 227 } 228 return NULL; 229 } 230 231 borderPtr = TkpGetBorder(); 232 borderPtr->screen = Tk_Screen(tkwin); 233 borderPtr->visual = Tk_Visual(tkwin); 234 borderPtr->depth = Tk_Depth(tkwin); 235 borderPtr->colormap = Tk_Colormap(tkwin); 236 borderPtr->resourceRefCount = 1; 237 borderPtr->objRefCount = 0; 238 borderPtr->bgColorPtr = bgColorPtr; 239 borderPtr->darkColorPtr = NULL; 240 borderPtr->lightColorPtr = NULL; 241 borderPtr->shadow = None; 242 borderPtr->bgGC = None; 243 borderPtr->darkGC = None; 244 borderPtr->lightGC = None; 245 borderPtr->hashPtr = hashPtr; 246 borderPtr->nextPtr = existingBorderPtr; 247 Tcl_SetHashValue(hashPtr, borderPtr); 248 249 /* 250 * Create the information for displaying the background color, but delay 251 * the allocation of shadows until they are actually needed for drawing. 252 */ 253 254 gcValues.foreground = borderPtr->bgColorPtr->pixel; 255 borderPtr->bgGC = Tk_GetGC(tkwin, GCForeground, &gcValues); 256 return (Tk_3DBorder) borderPtr; 257} 258 259/* 260 *-------------------------------------------------------------- 261 * 262 * Tk_Draw3DRectangle -- 263 * 264 * Draw a 3-D border at a given place in a given window. 265 * 266 * Results: 267 * None. 268 * 269 * Side effects: 270 * A 3-D border will be drawn in the indicated drawable. The outside 271 * edges of the border will be determined by x, y, width, and height. The 272 * inside edges of the border will be determined by the borderWidth 273 * argument. 274 * 275 *-------------------------------------------------------------- 276 */ 277 278void 279Tk_Draw3DRectangle( 280 Tk_Window tkwin, /* Window for which border was allocated. */ 281 Drawable drawable, /* X window or pixmap in which to draw. */ 282 Tk_3DBorder border, /* Token for border to draw. */ 283 int x, int y, int width, int height, 284 /* Outside area of region in which border will 285 * be drawn. */ 286 int borderWidth, /* Desired width for border, in pixels. */ 287 int relief) /* Type of relief: TK_RELIEF_RAISED, 288 * TK_RELIEF_SUNKEN, TK_RELIEF_GROOVE, etc. */ 289{ 290 if (width < 2*borderWidth) { 291 borderWidth = width/2; 292 } 293 if (height < 2*borderWidth) { 294 borderWidth = height/2; 295 } 296 Tk_3DVerticalBevel(tkwin, drawable, border, x, y, borderWidth, height, 297 1, relief); 298 Tk_3DVerticalBevel(tkwin, drawable, border, x+width-borderWidth, y, 299 borderWidth, height, 0, relief); 300 Tk_3DHorizontalBevel(tkwin, drawable, border, x, y, width, borderWidth, 301 1, 1, 1, relief); 302 Tk_3DHorizontalBevel(tkwin, drawable, border, x, y+height-borderWidth, 303 width, borderWidth, 0, 0, 0, relief); 304} 305 306/* 307 *-------------------------------------------------------------- 308 * 309 * Tk_NameOf3DBorder -- 310 * 311 * Given a border, return a textual string identifying the border's 312 * color. 313 * 314 * Results: 315 * The return value is the string that was used to create the border. 316 * 317 * Side effects: 318 * None. 319 * 320 *-------------------------------------------------------------- 321 */ 322 323CONST char * 324Tk_NameOf3DBorder( 325 Tk_3DBorder border) /* Token for border. */ 326{ 327 TkBorder *borderPtr = (TkBorder *) border; 328 329 return borderPtr->hashPtr->key.string; 330} 331 332/* 333 *-------------------------------------------------------------------- 334 * 335 * Tk_3DBorderColor -- 336 * 337 * Given a 3D border, return the X color used for the "flat" surfaces. 338 * 339 * Results: 340 * Returns the color used drawing flat surfaces with the border. 341 * 342 * Side effects: 343 * None. 344 * 345 *-------------------------------------------------------------------- 346 */ 347XColor * 348Tk_3DBorderColor( 349 Tk_3DBorder border) /* Border whose color is wanted. */ 350{ 351 return ((TkBorder *) border)->bgColorPtr; 352} 353 354/* 355 *-------------------------------------------------------------------- 356 * 357 * Tk_3DBorderGC -- 358 * 359 * Given a 3D border, returns one of the graphics contexts used to draw 360 * the border. 361 * 362 * Results: 363 * Returns the graphics context given by the "which" argument. 364 * 365 * Side effects: 366 * None. 367 * 368 *-------------------------------------------------------------------- 369 */ 370GC 371Tk_3DBorderGC( 372 Tk_Window tkwin, /* Window for which border was allocated. */ 373 Tk_3DBorder border, /* Border whose GC is wanted. */ 374 int which) /* Selects one of the border's 3 GC's: 375 * TK_3D_FLAT_GC, TK_3D_LIGHT_GC, or 376 * TK_3D_DARK_GC. */ 377{ 378 TkBorder * borderPtr = (TkBorder *) border; 379 380 if ((borderPtr->lightGC == None) && (which != TK_3D_FLAT_GC)) { 381 TkpGetShadows(borderPtr, tkwin); 382 } 383 if (which == TK_3D_FLAT_GC) { 384 return borderPtr->bgGC; 385 } else if (which == TK_3D_LIGHT_GC) { 386 return borderPtr->lightGC; 387 } else if (which == TK_3D_DARK_GC){ 388 return borderPtr->darkGC; 389 } 390 Tcl_Panic("bogus \"which\" value in Tk_3DBorderGC"); 391 392 /* 393 * The code below will never be executed, but it's needed to keep 394 * compilers happy. 395 */ 396 397 return (GC) None; 398} 399 400/* 401 *-------------------------------------------------------------- 402 * 403 * Tk_Free3DBorder -- 404 * 405 * This function is called when a 3D border is no longer needed. It frees 406 * the resources associated with the border. After this call, the caller 407 * should never again use the "border" token. 408 * 409 * Results: 410 * None. 411 * 412 * Side effects: 413 * Resources are freed. 414 * 415 *-------------------------------------------------------------- 416 */ 417 418void 419Tk_Free3DBorder( 420 Tk_3DBorder border) /* Token for border to be released. */ 421{ 422 TkBorder *borderPtr = (TkBorder *) border; 423 Display *display = DisplayOfScreen(borderPtr->screen); 424 TkBorder *prevPtr; 425 426 borderPtr->resourceRefCount--; 427 if (borderPtr->resourceRefCount > 0) { 428 return; 429 } 430 431 prevPtr = (TkBorder *) Tcl_GetHashValue(borderPtr->hashPtr); 432 TkpFreeBorder(borderPtr); 433 if (borderPtr->bgColorPtr != NULL) { 434 Tk_FreeColor(borderPtr->bgColorPtr); 435 } 436 if (borderPtr->darkColorPtr != NULL) { 437 Tk_FreeColor(borderPtr->darkColorPtr); 438 } 439 if (borderPtr->lightColorPtr != NULL) { 440 Tk_FreeColor(borderPtr->lightColorPtr); 441 } 442 if (borderPtr->shadow != None) { 443 Tk_FreeBitmap(display, borderPtr->shadow); 444 } 445 if (borderPtr->bgGC != None) { 446 Tk_FreeGC(display, borderPtr->bgGC); 447 } 448 if (borderPtr->darkGC != None) { 449 Tk_FreeGC(display, borderPtr->darkGC); 450 } 451 if (borderPtr->lightGC != None) { 452 Tk_FreeGC(display, borderPtr->lightGC); 453 } 454 if (prevPtr == borderPtr) { 455 if (borderPtr->nextPtr == NULL) { 456 Tcl_DeleteHashEntry(borderPtr->hashPtr); 457 } else { 458 Tcl_SetHashValue(borderPtr->hashPtr, borderPtr->nextPtr); 459 } 460 } else { 461 while (prevPtr->nextPtr != borderPtr) { 462 prevPtr = prevPtr->nextPtr; 463 } 464 prevPtr->nextPtr = borderPtr->nextPtr; 465 } 466 if (borderPtr->objRefCount == 0) { 467 ckfree((char *) borderPtr); 468 } 469} 470 471/* 472 *---------------------------------------------------------------------- 473 * 474 * Tk_Free3DBorderFromObj -- 475 * 476 * This function is called to release a border allocated by 477 * Tk_Alloc3DBorderFromObj. It does not throw away the Tcl_Obj *; it only 478 * gets rid of the hash table entry for this border and clears the cached 479 * value that is normally stored in the object. 480 * 481 * Results: 482 * None. 483 * 484 * Side effects: 485 * The reference count associated with the border represented by objPtr 486 * is decremented, and the border's resources are released to X if there 487 * are no remaining uses for it. 488 * 489 *---------------------------------------------------------------------- 490 */ 491 492void 493Tk_Free3DBorderFromObj( 494 Tk_Window tkwin, /* The window this border lives in. Needed for 495 * the screen and colormap values. */ 496 Tcl_Obj *objPtr) /* The Tcl_Obj * to be freed. */ 497{ 498 Tk_Free3DBorder(Tk_Get3DBorderFromObj(tkwin, objPtr)); 499 FreeBorderObjProc(objPtr); 500} 501 502/* 503 *--------------------------------------------------------------------------- 504 * 505 * FreeBorderObjProc -- 506 * 507 * This proc is called to release an object reference to a border. Called 508 * when the object's internal rep is released or when the cached 509 * borderPtr needs to be changed. 510 * 511 * Results: 512 * None. 513 * 514 * Side effects: 515 * The object reference count is decremented. When both it and the hash 516 * ref count go to zero, the border's resources are released. 517 * 518 *--------------------------------------------------------------------------- 519 */ 520 521static void 522FreeBorderObjProc( 523 Tcl_Obj *objPtr) /* The object we are releasing. */ 524{ 525 TkBorder *borderPtr = (TkBorder *) objPtr->internalRep.twoPtrValue.ptr1; 526 527 if (borderPtr != NULL) { 528 borderPtr->objRefCount--; 529 if ((borderPtr->objRefCount == 0) 530 && (borderPtr->resourceRefCount == 0)) { 531 ckfree((char *) borderPtr); 532 } 533 objPtr->internalRep.twoPtrValue.ptr1 = NULL; 534 } 535} 536 537/* 538 *--------------------------------------------------------------------------- 539 * 540 * DupBorderObjProc -- 541 * 542 * When a cached border object is duplicated, this is called to update 543 * the internal reps. 544 * 545 * Results: 546 * None. 547 * 548 * Side effects: 549 * The border's objRefCount is incremented and the internal rep of the 550 * copy is set to point to it. 551 * 552 *--------------------------------------------------------------------------- 553 */ 554 555static void 556DupBorderObjProc( 557 Tcl_Obj *srcObjPtr, /* The object we are copying from. */ 558 Tcl_Obj *dupObjPtr) /* The object we are copying to. */ 559{ 560 TkBorder *borderPtr = (TkBorder *) srcObjPtr->internalRep.twoPtrValue.ptr1; 561 562 dupObjPtr->typePtr = srcObjPtr->typePtr; 563 dupObjPtr->internalRep.twoPtrValue.ptr1 = (void *) borderPtr; 564 565 if (borderPtr != NULL) { 566 borderPtr->objRefCount++; 567 } 568} 569 570/* 571 *---------------------------------------------------------------------- 572 * 573 * Tk_SetBackgroundFromBorder -- 574 * 575 * Change the background of a window to one appropriate for a given 3-D 576 * border. 577 * 578 * Results: 579 * None. 580 * 581 * Side effects: 582 * Tkwin's background gets modified. 583 * 584 *---------------------------------------------------------------------- 585 */ 586 587void 588Tk_SetBackgroundFromBorder( 589 Tk_Window tkwin, /* Window whose background is to be set. */ 590 Tk_3DBorder border) /* Token for border. */ 591{ 592 register TkBorder *borderPtr = (TkBorder *) border; 593 594 Tk_SetWindowBackground(tkwin, borderPtr->bgColorPtr->pixel); 595} 596 597/* 598 *---------------------------------------------------------------------- 599 * 600 * Tk_GetReliefFromObj -- 601 * 602 * Return an integer value based on the value of the objPtr. 603 * 604 * Results: 605 * The return value is a standard Tcl result. If an error occurs during 606 * conversion, an error message is left in the interpreter's result 607 * unless "interp" is NULL. 608 * 609 * Side effects: 610 * The object gets converted by Tcl_GetIndexFromObj. 611 * 612 *---------------------------------------------------------------------- 613 */ 614 615int 616Tk_GetReliefFromObj( 617 Tcl_Interp *interp, /* Used for error reporting. */ 618 Tcl_Obj *objPtr, /* The object we are trying to get the value 619 * from. */ 620 int *resultPtr) /* Where to place the answer. */ 621{ 622 return Tcl_GetIndexFromObj(interp, objPtr, reliefStrings, "relief", 0, 623 resultPtr); 624} 625 626/* 627 *---------------------------------------------------------------------- 628 * 629 * Tk_GetRelief -- 630 * 631 * Parse a relief description and return the corresponding relief value, 632 * or an error. 633 * 634 * Results: 635 * A standard Tcl return value. If all goes well then *reliefPtr is 636 * filled in with one of the values TK_RELIEF_RAISED, TK_RELIEF_FLAT, or 637 * TK_RELIEF_SUNKEN. 638 * 639 * Side effects: 640 * None. 641 * 642 *---------------------------------------------------------------------- 643 */ 644 645int 646Tk_GetRelief( 647 Tcl_Interp *interp, /* For error messages. */ 648 CONST char *name, /* Name of a relief type. */ 649 int *reliefPtr) /* Where to store converted relief. */ 650{ 651 char c; 652 size_t length; 653 654 c = name[0]; 655 length = strlen(name); 656 if ((c == 'f') && (strncmp(name, "flat", length) == 0)) { 657 *reliefPtr = TK_RELIEF_FLAT; 658 } else if ((c == 'g') && (strncmp(name, "groove", length) == 0) 659 && (length >= 2)) { 660 *reliefPtr = TK_RELIEF_GROOVE; 661 } else if ((c == 'r') && (strncmp(name, "raised", length) == 0) 662 && (length >= 2)) { 663 *reliefPtr = TK_RELIEF_RAISED; 664 } else if ((c == 'r') && (strncmp(name, "ridge", length) == 0)) { 665 *reliefPtr = TK_RELIEF_RIDGE; 666 } else if ((c == 's') && (strncmp(name, "solid", length) == 0)) { 667 *reliefPtr = TK_RELIEF_SOLID; 668 } else if ((c == 's') && (strncmp(name, "sunken", length) == 0)) { 669 *reliefPtr = TK_RELIEF_SUNKEN; 670 } else { 671 char buf[200]; 672 673 sprintf(buf, "bad relief type \"%.50s\": must be %s", 674 name, "flat, groove, raised, ridge, solid, or sunken"); 675 Tcl_SetResult(interp, buf, TCL_VOLATILE); 676 return TCL_ERROR; 677 } 678 return TCL_OK; 679} 680 681/* 682 *-------------------------------------------------------------- 683 * 684 * Tk_NameOfRelief -- 685 * 686 * Given a relief value, produce a string describing that relief value. 687 * 688 * Results: 689 * The return value is a static string that is equivalent to relief. 690 * 691 * Side effects: 692 * None. 693 * 694 *-------------------------------------------------------------- 695 */ 696 697CONST char * 698Tk_NameOfRelief( 699 int relief) /* One of TK_RELIEF_FLAT, TK_RELIEF_RAISED, or 700 * TK_RELIEF_SUNKEN. */ 701{ 702 if (relief == TK_RELIEF_FLAT) { 703 return "flat"; 704 } else if (relief == TK_RELIEF_SUNKEN) { 705 return "sunken"; 706 } else if (relief == TK_RELIEF_RAISED) { 707 return "raised"; 708 } else if (relief == TK_RELIEF_GROOVE) { 709 return "groove"; 710 } else if (relief == TK_RELIEF_RIDGE) { 711 return "ridge"; 712 } else if (relief == TK_RELIEF_SOLID) { 713 return "solid"; 714 } else if (relief == TK_RELIEF_NULL) { 715 return ""; 716 } else { 717 return "unknown relief"; 718 } 719} 720 721/* 722 *-------------------------------------------------------------- 723 * 724 * Tk_Draw3DPolygon -- 725 * 726 * Draw a border with 3-D appearance around the edge of a given polygon. 727 * 728 * Results: 729 * None. 730 * 731 * Side effects: 732 * Information is drawn in "drawable" in the form of a 3-D border 733 * borderWidth units width wide on the left of the trajectory given by 734 * pointPtr and numPoints (or -borderWidth units wide on the right side, 735 * if borderWidth is negative). 736 * 737 *-------------------------------------------------------------- 738 */ 739 740void 741Tk_Draw3DPolygon( 742 Tk_Window tkwin, /* Window for which border was allocated. */ 743 Drawable drawable, /* X window or pixmap in which to draw. */ 744 Tk_3DBorder border, /* Token for border to draw. */ 745 XPoint *pointPtr, /* Array of points describing polygon. All 746 * points must be absolute 747 * (CoordModeOrigin). */ 748 int numPoints, /* Number of points at *pointPtr. */ 749 int borderWidth, /* Width of border, measured in pixels to the 750 * left of the polygon's trajectory. May be 751 * negative. */ 752 int leftRelief) /* TK_RELIEF_RAISED or TK_RELIEF_SUNKEN: 753 * indicates how stuff to left of trajectory 754 * looks relative to stuff on right. */ 755{ 756 XPoint poly[4], b1, b2, newB1, newB2; 757 XPoint perp, c, shift1, shift2; /* Used for handling parallel lines. */ 758 register XPoint *p1Ptr, *p2Ptr; 759 TkBorder *borderPtr = (TkBorder *) border; 760 GC gc; 761 int i, lightOnLeft, dx, dy, parallel, pointsSeen; 762 Display *display = Tk_Display(tkwin); 763 764 if (borderPtr->lightGC == None) { 765 TkpGetShadows(borderPtr, tkwin); 766 } 767 768 /* 769 * Handle grooves and ridges with recursive calls. 770 */ 771 772 if ((leftRelief == TK_RELIEF_GROOVE) || (leftRelief == TK_RELIEF_RIDGE)) { 773 int halfWidth; 774 775 halfWidth = borderWidth/2; 776 Tk_Draw3DPolygon(tkwin, drawable, border, pointPtr, numPoints, 777 halfWidth, (leftRelief == TK_RELIEF_GROOVE) ? TK_RELIEF_RAISED 778 : TK_RELIEF_SUNKEN); 779 Tk_Draw3DPolygon(tkwin, drawable, border, pointPtr, numPoints, 780 -halfWidth, (leftRelief == TK_RELIEF_GROOVE) ? TK_RELIEF_SUNKEN 781 : TK_RELIEF_RAISED); 782 return; 783 } 784 785 /* 786 * If the polygon is already closed, drop the last point from it (we'll 787 * close it automatically). 788 */ 789 790 p1Ptr = &pointPtr[numPoints-1]; 791 p2Ptr = &pointPtr[0]; 792 if ((p1Ptr->x == p2Ptr->x) && (p1Ptr->y == p2Ptr->y)) { 793 numPoints--; 794 } 795 796 /* 797 * The loop below is executed once for each vertex in the polgon. At the 798 * beginning of each iteration things look like this: 799 * 800 * poly[1] / 801 * * / 802 * | / 803 * b1 * poly[0] (pointPtr[i-1]) 804 * | | 805 * | | 806 * | | 807 * | | 808 * | | 809 * | | *p1Ptr *p2Ptr 810 * b2 *--------------------* 811 * | 812 * | 813 * x------------------------- 814 * 815 * The job of this iteration is to do the following: 816 * (a) Compute x (the border corner corresponding to pointPtr[i]) and put 817 * it in poly[2]. As part of this, compute a new b1 and b2 value for 818 * the next side of the polygon. 819 * (b) Put pointPtr[i] into poly[3]. 820 * (c) Draw the polygon given by poly[0..3]. 821 * (d) Advance poly[0], poly[1], b1, and b2 for the next side of the 822 * polygon. 823 */ 824 825 /* 826 * The above situation doesn't first come into existence until two points 827 * have been processed; the first two points are used to "prime the pump", 828 * so some parts of the processing are ommitted for these points. The 829 * variable "pointsSeen" keeps track of the priming process; it has to be 830 * separate from i in order to be able to ignore duplicate points in the 831 * polygon. 832 */ 833 834 pointsSeen = 0; 835 for (i = -2, p1Ptr = &pointPtr[numPoints-2], p2Ptr = p1Ptr+1; 836 i < numPoints; i++, p1Ptr = p2Ptr, p2Ptr++) { 837 if ((i == -1) || (i == numPoints-1)) { 838 p2Ptr = pointPtr; 839 } 840 if ((p2Ptr->x == p1Ptr->x) && (p2Ptr->y == p1Ptr->y)) { 841 /* 842 * Ignore duplicate points (they'd cause core dumps in ShiftLine 843 * calls below). 844 */ 845 846 continue; 847 } 848 ShiftLine(p1Ptr, p2Ptr, borderWidth, &newB1); 849 newB2.x = newB1.x + (p2Ptr->x - p1Ptr->x); 850 newB2.y = newB1.y + (p2Ptr->y - p1Ptr->y); 851 poly[3] = *p1Ptr; 852 parallel = 0; 853 if (pointsSeen >= 1) { 854 parallel = Intersect(&newB1, &newB2, &b1, &b2, &poly[2]); 855 856 /* 857 * If two consecutive segments of the polygon are parallel, then 858 * things get more complex. Consider the following diagram: 859 * 860 * poly[1] 861 * *----b1-----------b2------a 862 * \ 863 * \ 864 * *---------*----------* b 865 * poly[0] *p2Ptr *p1Ptr / 866 * / 867 * --*--------*----c 868 * newB1 newB2 869 * 870 * Instead of using x and *p1Ptr for poly[2] and poly[3], as in 871 * the original diagram, use a and b as above. Then instead of 872 * using x and *p1Ptr for the new poly[0] and poly[1], use b and c 873 * as above. 874 * 875 * Do the computation in three stages: 876 * 1. Compute a point "perp" such that the line p1Ptr-perp is 877 * perpendicular to p1Ptr-p2Ptr. 878 * 2. Compute the points a and c by intersecting the lines b1-b2 879 * and newB1-newB2 with p1Ptr-perp. 880 * 3. Compute b by shifting p1Ptr-perp to the right and 881 * intersecting it with p1Ptr-p2Ptr. 882 */ 883 884 if (parallel) { 885 perp.x = p1Ptr->x + (p2Ptr->y - p1Ptr->y); 886 perp.y = p1Ptr->y - (p2Ptr->x - p1Ptr->x); 887 (void) Intersect(p1Ptr, &perp, &b1, &b2, &poly[2]); 888 (void) Intersect(p1Ptr, &perp, &newB1, &newB2, &c); 889 ShiftLine(p1Ptr, &perp, borderWidth, &shift1); 890 shift2.x = shift1.x + (perp.x - p1Ptr->x); 891 shift2.y = shift1.y + (perp.y - p1Ptr->y); 892 (void) Intersect(p1Ptr, p2Ptr, &shift1, &shift2, &poly[3]); 893 } 894 } 895 if (pointsSeen >= 2) { 896 dx = poly[3].x - poly[0].x; 897 dy = poly[3].y - poly[0].y; 898 if (dx > 0) { 899 lightOnLeft = (dy <= dx); 900 } else { 901 lightOnLeft = (dy < dx); 902 } 903 if (lightOnLeft ^ (leftRelief == TK_RELIEF_RAISED)) { 904 gc = borderPtr->lightGC; 905 } else { 906 gc = borderPtr->darkGC; 907 } 908 XFillPolygon(display, drawable, gc, poly, 4, Convex, 909 CoordModeOrigin); 910 } 911 b1.x = newB1.x; 912 b1.y = newB1.y; 913 b2.x = newB2.x; 914 b2.y = newB2.y; 915 poly[0].x = poly[3].x; 916 poly[0].y = poly[3].y; 917 if (parallel) { 918 poly[1].x = c.x; 919 poly[1].y = c.y; 920 } else if (pointsSeen >= 1) { 921 poly[1].x = poly[2].x; 922 poly[1].y = poly[2].y; 923 } 924 pointsSeen++; 925 } 926} 927 928/* 929 *---------------------------------------------------------------------- 930 * 931 * Tk_Fill3DRectangle -- 932 * 933 * Fill a rectangular area, supplying a 3D border if desired. 934 * 935 * Results: 936 * None. 937 * 938 * Side effects: 939 * Information gets drawn on the screen. 940 * 941 *---------------------------------------------------------------------- 942 */ 943 944void 945Tk_Fill3DRectangle( 946 Tk_Window tkwin, /* Window for which border was allocated. */ 947 Drawable drawable, /* X window or pixmap in which to draw. */ 948 Tk_3DBorder border, /* Token for border to draw. */ 949 int x, int y, int width, int height, 950 /* Outside area of rectangular region. */ 951 int borderWidth, /* Desired width for border, in pixels. Border 952 * will be *inside* region. */ 953 int relief) /* Indicates 3D effect: TK_RELIEF_FLAT, 954 * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */ 955{ 956 register TkBorder *borderPtr = (TkBorder *) border; 957 int doubleBorder; 958 959 /* 960 * This code is slightly tricky because it only draws the background in 961 * areas not covered by the 3D border. This avoids flashing effects on the 962 * screen for the border region. 963 */ 964 965 if (relief == TK_RELIEF_FLAT) { 966 borderWidth = 0; 967 } else { 968 /* 969 * We need to make this extra check, otherwise we will leave garbage 970 * in thin frames [Bug: 3596] 971 */ 972 973 if (width < 2*borderWidth) { 974 borderWidth = width/2; 975 } 976 if (height < 2*borderWidth) { 977 borderWidth = height/2; 978 } 979 } 980 doubleBorder = 2*borderWidth; 981 982 if ((width > doubleBorder) && (height > doubleBorder)) { 983 XFillRectangle(Tk_Display(tkwin), drawable, borderPtr->bgGC, 984 x + borderWidth, y + borderWidth, 985 (unsigned int) (width - doubleBorder), 986 (unsigned int) (height - doubleBorder)); 987 } 988 if (borderWidth) { 989 Tk_Draw3DRectangle(tkwin, drawable, border, x, y, width, 990 height, borderWidth, relief); 991 } 992} 993 994/* 995 *---------------------------------------------------------------------- 996 * 997 * Tk_Fill3DPolygon -- 998 * 999 * Fill a polygonal area, supplying a 3D border if desired. 1000 * 1001 * Results: 1002 * None. 1003 * 1004 * Side effects: 1005 * Information gets drawn on the screen. 1006 * 1007 *---------------------------------------------------------------------- 1008 */ 1009 1010void 1011Tk_Fill3DPolygon( 1012 Tk_Window tkwin, /* Window for which border was allocated. */ 1013 Drawable drawable, /* X window or pixmap in which to draw. */ 1014 Tk_3DBorder border, /* Token for border to draw. */ 1015 XPoint *pointPtr, /* Array of points describing polygon. All 1016 * points must be absolute 1017 * (CoordModeOrigin). */ 1018 int numPoints, /* Number of points at *pointPtr. */ 1019 int borderWidth, /* Width of border, measured in pixels to the 1020 * left of the polygon's trajectory. May be 1021 * negative. */ 1022 int leftRelief) /* Indicates 3D effect of left side of 1023 * trajectory relative to right: 1024 * TK_RELIEF_FLAT, TK_RELIEF_RAISED, or 1025 * TK_RELIEF_SUNKEN. */ 1026{ 1027 register TkBorder *borderPtr = (TkBorder *) border; 1028 1029 XFillPolygon(Tk_Display(tkwin), drawable, borderPtr->bgGC, 1030 pointPtr, numPoints, Complex, CoordModeOrigin); 1031 if (leftRelief != TK_RELIEF_FLAT) { 1032 Tk_Draw3DPolygon(tkwin, drawable, border, pointPtr, numPoints, 1033 borderWidth, leftRelief); 1034 } 1035} 1036 1037/* 1038 *-------------------------------------------------------------- 1039 * 1040 * BorderInit -- 1041 * 1042 * Initialize the structures used for border management. 1043 * 1044 * Results: 1045 * None. 1046 * 1047 * Side effects: 1048 * Read the code. 1049 * 1050 *------------------------------------------------------------- 1051 */ 1052 1053static void 1054BorderInit( 1055 TkDisplay *dispPtr) /* Used to access thread-specific data. */ 1056{ 1057 dispPtr->borderInit = 1; 1058 Tcl_InitHashTable(&dispPtr->borderTable, TCL_STRING_KEYS); 1059} 1060 1061/* 1062 *-------------------------------------------------------------- 1063 * 1064 * ShiftLine -- 1065 * 1066 * Given two points on a line, compute a point on a new line that is 1067 * parallel to the given line and a given distance away from it. 1068 * 1069 * Results: 1070 * None. 1071 * 1072 * Side effects: 1073 * None. 1074 * 1075 *-------------------------------------------------------------- 1076 */ 1077 1078static void 1079ShiftLine( 1080 XPoint *p1Ptr, /* First point on line. */ 1081 XPoint *p2Ptr, /* Second point on line. */ 1082 int distance, /* New line is to be this many units to the 1083 * left of original line, when looking from p1 1084 * to p2. May be negative. */ 1085 XPoint *p3Ptr) /* Store coords of point on new line here. */ 1086{ 1087 int dx, dy, dxNeg, dyNeg; 1088 1089 /* 1090 * The table below is used for a quick approximation in computing the new 1091 * point. An index into the table is 128 times the slope of the original 1092 * line (the slope must always be between 0 and 1). The value of the table 1093 * entry is 128 times the amount to displace the new line in y for each 1094 * unit of perpendicular distance. In other words, the table maps from the 1095 * tangent of an angle to the inverse of its cosine. If the slope of the 1096 * original line is greater than 1, then the displacement is done in x 1097 * rather than in y. 1098 */ 1099 1100 static int shiftTable[129]; 1101 1102 /* 1103 * Initialize the table if this is the first time it is used. 1104 */ 1105 1106 if (shiftTable[0] == 0) { 1107 int i; 1108 double tangent, cosine; 1109 1110 for (i = 0; i <= 128; i++) { 1111 tangent = i/128.0; 1112 cosine = 128/cos(atan(tangent)) + .5; 1113 shiftTable[i] = (int) cosine; 1114 } 1115 } 1116 1117 *p3Ptr = *p1Ptr; 1118 dx = p2Ptr->x - p1Ptr->x; 1119 dy = p2Ptr->y - p1Ptr->y; 1120 if (dy < 0) { 1121 dyNeg = 1; 1122 dy = -dy; 1123 } else { 1124 dyNeg = 0; 1125 } 1126 if (dx < 0) { 1127 dxNeg = 1; 1128 dx = -dx; 1129 } else { 1130 dxNeg = 0; 1131 } 1132 if (dy <= dx) { 1133 dy = ((distance * shiftTable[(dy<<7)/dx]) + 64) >> 7; 1134 if (!dxNeg) { 1135 dy = -dy; 1136 } 1137 p3Ptr->y += dy; 1138 } else { 1139 dx = ((distance * shiftTable[(dx<<7)/dy]) + 64) >> 7; 1140 if (dyNeg) { 1141 dx = -dx; 1142 } 1143 p3Ptr->x += dx; 1144 } 1145} 1146 1147/* 1148 *-------------------------------------------------------------- 1149 * 1150 * Intersect -- 1151 * 1152 * Find the intersection point between two lines. 1153 * 1154 * Results: 1155 * Under normal conditions 0 is returned and the point at *iPtr is filled 1156 * in with the intersection between the two lines. If the two lines are 1157 * parallel, then -1 is returned and *iPtr isn't modified. 1158 * 1159 * Side effects: 1160 * None. 1161 * 1162 *-------------------------------------------------------------- 1163 */ 1164 1165static int 1166Intersect( 1167 XPoint *a1Ptr, /* First point of first line. */ 1168 XPoint *a2Ptr, /* Second point of first line. */ 1169 XPoint *b1Ptr, /* First point of second line. */ 1170 XPoint *b2Ptr, /* Second point of second line. */ 1171 XPoint *iPtr) /* Filled in with intersection point. */ 1172{ 1173 int dxadyb, dxbdya, dxadxb, dyadyb, p, q; 1174 1175 /* 1176 * The code below is just a straightforward manipulation of two equations 1177 * of the form y = (x-x1)*(y2-y1)/(x2-x1) + y1 to solve for the 1178 * x-coordinate of intersection, then the y-coordinate. 1179 */ 1180 1181 dxadyb = (a2Ptr->x - a1Ptr->x)*(b2Ptr->y - b1Ptr->y); 1182 dxbdya = (b2Ptr->x - b1Ptr->x)*(a2Ptr->y - a1Ptr->y); 1183 dxadxb = (a2Ptr->x - a1Ptr->x)*(b2Ptr->x - b1Ptr->x); 1184 dyadyb = (a2Ptr->y - a1Ptr->y)*(b2Ptr->y - b1Ptr->y); 1185 1186 if (dxadyb == dxbdya) { 1187 return -1; 1188 } 1189 p = (a1Ptr->x*dxbdya - b1Ptr->x*dxadyb + (b1Ptr->y - a1Ptr->y)*dxadxb); 1190 q = dxbdya - dxadyb; 1191 if (q < 0) { 1192 p = -p; 1193 q = -q; 1194 } 1195 if (p < 0) { 1196 iPtr->x = - ((-p + q/2)/q); 1197 } else { 1198 iPtr->x = (p + q/2)/q; 1199 } 1200 p = (a1Ptr->y*dxadyb - b1Ptr->y*dxbdya + (b1Ptr->x - a1Ptr->x)*dyadyb); 1201 q = dxadyb - dxbdya; 1202 if (q < 0) { 1203 p = -p; 1204 q = -q; 1205 } 1206 if (p < 0) { 1207 iPtr->y = - ((-p + q/2)/q); 1208 } else { 1209 iPtr->y = (p + q/2)/q; 1210 } 1211 return 0; 1212} 1213 1214/* 1215 *---------------------------------------------------------------------- 1216 * 1217 * Tk_Get3DBorderFromObj -- 1218 * 1219 * Returns the border referred to by a Tcl object. The border must 1220 * already have been allocated via a call to Tk_Alloc3DBorderFromObj or 1221 * Tk_Get3DBorder. 1222 * 1223 * Results: 1224 * Returns the Tk_3DBorder that matches the tkwin and the string rep of 1225 * the name of the border given in objPtr. 1226 * 1227 * Side effects: 1228 * If the object is not already a border, the conversion will free any 1229 * old internal representation. 1230 * 1231 *---------------------------------------------------------------------- 1232 */ 1233 1234Tk_3DBorder 1235Tk_Get3DBorderFromObj( 1236 Tk_Window tkwin, 1237 Tcl_Obj *objPtr) /* The object whose string value selects a 1238 * border. */ 1239{ 1240 TkBorder *borderPtr = NULL; 1241 Tcl_HashEntry *hashPtr; 1242 TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr; 1243 1244 if (objPtr->typePtr != &tkBorderObjType) { 1245 InitBorderObj(objPtr); 1246 } 1247 1248 /* 1249 * If we are lucky (and the user doesn't use too many different displays, 1250 * screens, or colormaps...) then the TkBorder structure we need will be 1251 * cached in the internal representation of the Tcl_Obj. Check it out... 1252 */ 1253 1254 borderPtr = (TkBorder *) objPtr->internalRep.twoPtrValue.ptr1; 1255 if ((borderPtr != NULL) 1256 && (borderPtr->resourceRefCount > 0) 1257 && (Tk_Screen(tkwin) == borderPtr->screen) 1258 && (Tk_Colormap(tkwin) == borderPtr->colormap)) { 1259 /* 1260 * The object already points to the right border structure. Just 1261 * return it. 1262 */ 1263 1264 return (Tk_3DBorder) borderPtr; 1265 } 1266 1267 /* 1268 * If we make it here, it means we aren't so lucky. Either there was no 1269 * cached TkBorder in the Tcl_Obj, or the TkBorder that was there is for 1270 * the wrong screen/colormap. Either way, we have to search for the right 1271 * TkBorder. For each color name, there is linked list of TkBorder 1272 * structures, one structure for each screen/colormap combination. The 1273 * head of the linked list is recorded in a hash table (where the key is 1274 * the color name) attached to the TkDisplay structure. Walk this list to 1275 * find the right TkBorder structure. 1276 */ 1277 1278 hashPtr = Tcl_FindHashEntry(&dispPtr->borderTable, Tcl_GetString(objPtr)); 1279 if (hashPtr == NULL) { 1280 goto error; 1281 } 1282 for (borderPtr = (TkBorder *) Tcl_GetHashValue(hashPtr); 1283 (borderPtr != NULL); borderPtr = borderPtr->nextPtr) { 1284 if ((Tk_Screen(tkwin) == borderPtr->screen) 1285 && (Tk_Colormap(tkwin) == borderPtr->colormap)) { 1286 FreeBorderObjProc(objPtr); 1287 objPtr->internalRep.twoPtrValue.ptr1 = (void *) borderPtr; 1288 borderPtr->objRefCount++; 1289 return (Tk_3DBorder) borderPtr; 1290 } 1291 } 1292 1293 error: 1294 Tcl_Panic("Tk_Get3DBorderFromObj called with non-existent border!"); 1295 /* 1296 * The following code isn't reached; it's just there to please compilers. 1297 */ 1298 return NULL; 1299} 1300 1301/* 1302 *---------------------------------------------------------------------- 1303 * 1304 * InitBorderObj -- 1305 * 1306 * Attempt to generate a border internal form for the Tcl object 1307 * "objPtr". 1308 * 1309 * Results: 1310 * The return value is a standard Tcl result. If an error occurs during 1311 * conversion, an error message is left in the interpreter's result 1312 * unless "interp" is NULL. 1313 * 1314 * Side effects: 1315 * If no error occurs, a blank internal format for a border value is 1316 * intialized. The final form cannot be done without a Tk_Window. 1317 * 1318 *---------------------------------------------------------------------- 1319 */ 1320 1321static void 1322InitBorderObj( 1323 Tcl_Obj *objPtr) /* The object to convert. */ 1324{ 1325 const Tcl_ObjType *typePtr; 1326 1327 /* 1328 * Free the old internalRep before setting the new one. 1329 */ 1330 1331 Tcl_GetString(objPtr); 1332 typePtr = objPtr->typePtr; 1333 if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) { 1334 (*typePtr->freeIntRepProc)(objPtr); 1335 } 1336 objPtr->typePtr = &tkBorderObjType; 1337 objPtr->internalRep.twoPtrValue.ptr1 = NULL; 1338} 1339 1340/* 1341 *---------------------------------------------------------------------- 1342 * 1343 * TkDebugBorder -- 1344 * 1345 * This function returns debugging information about a border. 1346 * 1347 * Results: 1348 * The return value is a list with one sublist for each TkBorder 1349 * corresponding to "name". Each sublist has two elements that contain 1350 * the resourceRefCount and objRefCount fields from the TkBorder 1351 * structure. 1352 * 1353 * Side effects: 1354 * None. 1355 * 1356 *---------------------------------------------------------------------- 1357 */ 1358 1359Tcl_Obj * 1360TkDebugBorder( 1361 Tk_Window tkwin, /* The window in which the border will be used 1362 * (not currently used). */ 1363 char *name) /* Name of the desired color. */ 1364{ 1365 TkBorder *borderPtr; 1366 Tcl_HashEntry *hashPtr; 1367 Tcl_Obj *resultPtr, *objPtr; 1368 TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr; 1369 1370 resultPtr = Tcl_NewObj(); 1371 hashPtr = Tcl_FindHashEntry(&dispPtr->borderTable, name); 1372 if (hashPtr != NULL) { 1373 borderPtr = (TkBorder *) Tcl_GetHashValue(hashPtr); 1374 if (borderPtr == NULL) { 1375 Tcl_Panic("TkDebugBorder found empty hash table entry"); 1376 } 1377 for ( ; (borderPtr != NULL); borderPtr = borderPtr->nextPtr) { 1378 objPtr = Tcl_NewObj(); 1379 Tcl_ListObjAppendElement(NULL, objPtr, 1380 Tcl_NewIntObj(borderPtr->resourceRefCount)); 1381 Tcl_ListObjAppendElement(NULL, objPtr, 1382 Tcl_NewIntObj(borderPtr->objRefCount)); 1383 Tcl_ListObjAppendElement(NULL, resultPtr, objPtr); 1384 } 1385 } 1386 return resultPtr; 1387} 1388 1389/* 1390 * Local Variables: 1391 * mode: c 1392 * c-basic-offset: 4 1393 * fill-column: 78 1394 * End: 1395 */ 1396