4 * This module provides procedures to draw borders in
5 * the three-dimensional Motif style.
7 * Copyright (c) 1990-1994 The Regents of the University of California.
8 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
19 * The following table defines the string values for reliefs, which are
20 * used by Tk_GetReliefFromObj.
23 static CONST char *reliefStrings[] = {"flat", "groove", "raised",
24 "ridge", "solid", "sunken",
28 * Forward declarations for procedures defined in this file:
31 static void BorderInit _ANSI_ARGS_((TkDisplay *dispPtr));
32 static void DupBorderObjProc _ANSI_ARGS_((Tcl_Obj *srcObjPtr,
34 static void FreeBorderObjProc _ANSI_ARGS_((Tcl_Obj *objPtr));
35 static int Intersect _ANSI_ARGS_((XPoint *a1Ptr, XPoint *a2Ptr,
36 XPoint *b1Ptr, XPoint *b2Ptr, XPoint *iPtr));
37 static void InitBorderObj _ANSI_ARGS_((Tcl_Obj *objPtr));
38 static void ShiftLine _ANSI_ARGS_((XPoint *p1Ptr, XPoint *p2Ptr,
39 int distance, XPoint *p3Ptr));
42 * The following structure defines the implementation of the "border" Tcl
43 * object, used for drawing. The border object remembers the hash table entry
44 * associated with a border. The actual allocation and deallocation of the
45 * border should be done by the configuration package when the border option
49 Tcl_ObjType tkBorderObjType = {
51 FreeBorderObjProc, /* freeIntRepProc */
52 DupBorderObjProc, /* dupIntRepProc */
53 NULL, /* updateStringProc */
54 NULL /* setFromAnyProc */
58 *----------------------------------------------------------------------
60 * Tk_Alloc3DBorderFromObj --
62 * Given a Tcl_Obj *, map the value to a corresponding
63 * Tk_3DBorder structure based on the tkwin given.
66 * The return value is a token for a data structure describing a
67 * 3-D border. This token may be passed to procedures such as
68 * Tk_Draw3DRectangle and Tk_Free3DBorder. If an error prevented
69 * the border from being created then NULL is returned and an error
70 * message will be left in the interp's result.
73 * The border is added to an internal database with a reference
74 * count. For each call to this procedure, there should eventually
75 * be a call to FreeBorderObjProc so that the database is
76 * cleaned up when borders aren't in use anymore.
78 *----------------------------------------------------------------------
82 Tk_Alloc3DBorderFromObj(interp, tkwin, objPtr)
83 Tcl_Interp *interp; /* Interp for error results. */
84 Tk_Window tkwin; /* Need the screen the border is used on.*/
85 Tcl_Obj *objPtr; /* Object giving name of color for window
90 if (objPtr->typePtr != &tkBorderObjType) {
91 InitBorderObj(objPtr);
93 borderPtr = (TkBorder *) objPtr->internalRep.twoPtrValue.ptr1;
96 * If the object currently points to a TkBorder, see if it's the
97 * one we want. If so, increment its reference count and return.
100 if (borderPtr != NULL) {
101 if (borderPtr->resourceRefCount == 0) {
103 * This is a stale reference: it refers to a border that's
104 * no longer in use. Clear the reference.
107 FreeBorderObjProc(objPtr);
109 } else if ((Tk_Screen(tkwin) == borderPtr->screen)
110 && (Tk_Colormap(tkwin) == borderPtr->colormap)) {
111 borderPtr->resourceRefCount++;
112 return (Tk_3DBorder) borderPtr;
117 * The object didn't point to the border that we wanted. Search
118 * the list of borders with the same name to see if one of the
119 * others is the right one.
123 * If the cached value is NULL, either the object type was not a
124 * color going in, or the object is a color type but had
125 * previously been freed.
127 * If the value is not NULL, the internal rep is the value
128 * of the color the last time this object was accessed. Check
129 * the screen and colormap of the last access, and if they
130 * match, we are done.
133 if (borderPtr != NULL) {
134 TkBorder *firstBorderPtr =
135 (TkBorder *) Tcl_GetHashValue(borderPtr->hashPtr);
136 FreeBorderObjProc(objPtr);
137 for (borderPtr = firstBorderPtr ; borderPtr != NULL;
138 borderPtr = borderPtr->nextPtr) {
139 if ((Tk_Screen(tkwin) == borderPtr->screen)
140 && (Tk_Colormap(tkwin) == borderPtr->colormap)) {
141 borderPtr->resourceRefCount++;
142 borderPtr->objRefCount++;
143 objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) borderPtr;
144 return (Tk_3DBorder) borderPtr;
150 * Still no luck. Call Tk_Get3DBorder to allocate a new border.
153 borderPtr = (TkBorder *) Tk_Get3DBorder(interp, tkwin,
154 Tcl_GetString(objPtr));
155 objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) borderPtr;
156 if (borderPtr != NULL) {
157 borderPtr->objRefCount++;
159 return (Tk_3DBorder) borderPtr;
163 *--------------------------------------------------------------
167 * Create a data structure for displaying a 3-D border.
170 * The return value is a token for a data structure describing a
171 * 3-D border. This token may be passed to procedures such as
172 * Tk_Draw3DRectangle and Tk_Free3DBorder. If an error prevented
173 * the border from being created then NULL is returned and an error
174 * message will be left in the interp's result.
177 * Data structures, graphics contexts, etc. are allocated.
178 * It is the caller's responsibility to eventually call
179 * Tk_Free3DBorder to release the resources.
181 *--------------------------------------------------------------
185 Tk_Get3DBorder(interp, tkwin, colorName)
186 Tcl_Interp *interp; /* Place to store an error message. */
187 Tk_Window tkwin; /* Token for window in which border will
189 Tk_Uid colorName; /* String giving name of color
190 * for window background. */
192 Tcl_HashEntry *hashPtr;
193 TkBorder *borderPtr, *existingBorderPtr;
199 dispPtr = ((TkWindow *) tkwin)->dispPtr;
201 if (!dispPtr->borderInit) {
205 hashPtr = Tcl_CreateHashEntry(&dispPtr->borderTable, colorName, &new);
207 existingBorderPtr = (TkBorder *) Tcl_GetHashValue(hashPtr);
208 for (borderPtr = existingBorderPtr; borderPtr != NULL;
209 borderPtr = borderPtr->nextPtr) {
210 if ((Tk_Screen(tkwin) == borderPtr->screen)
211 && (Tk_Colormap(tkwin) == borderPtr->colormap)) {
212 borderPtr->resourceRefCount++;
213 return (Tk_3DBorder) borderPtr;
217 existingBorderPtr = NULL;
221 * No satisfactory border exists yet. Initialize a new one.
224 bgColorPtr = Tk_GetColor(interp, tkwin, colorName);
225 if (bgColorPtr == NULL) {
227 Tcl_DeleteHashEntry(hashPtr);
232 borderPtr = TkpGetBorder();
233 borderPtr->screen = Tk_Screen(tkwin);
234 borderPtr->visual = Tk_Visual(tkwin);
235 borderPtr->depth = Tk_Depth(tkwin);
236 borderPtr->colormap = Tk_Colormap(tkwin);
237 borderPtr->resourceRefCount = 1;
238 borderPtr->objRefCount = 0;
239 borderPtr->bgColorPtr = bgColorPtr;
240 borderPtr->darkColorPtr = NULL;
241 borderPtr->lightColorPtr = NULL;
242 borderPtr->shadow = None;
243 borderPtr->bgGC = None;
244 borderPtr->darkGC = None;
245 borderPtr->lightGC = None;
246 borderPtr->hashPtr = hashPtr;
247 borderPtr->nextPtr = existingBorderPtr;
248 Tcl_SetHashValue(hashPtr, borderPtr);
251 * Create the information for displaying the background color,
252 * but delay the allocation of shadows until they are actually
253 * needed for drawing.
256 gcValues.foreground = borderPtr->bgColorPtr->pixel;
257 borderPtr->bgGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
258 return (Tk_3DBorder) borderPtr;
262 *--------------------------------------------------------------
264 * Tk_Draw3DRectangle --
266 * Draw a 3-D border at a given place in a given window.
272 * A 3-D border will be drawn in the indicated drawable.
273 * The outside edges of the border will be determined by x,
274 * y, width, and height. The inside edges of the border
275 * will be determined by the borderWidth argument.
277 *--------------------------------------------------------------
281 Tk_Draw3DRectangle(tkwin, drawable, border, x, y, width, height,
283 Tk_Window tkwin; /* Window for which border was allocated. */
284 Drawable drawable; /* X window or pixmap in which to draw. */
285 Tk_3DBorder border; /* Token for border to draw. */
286 int x, y, width, height; /* Outside area of region in
287 * which border will be drawn. */
288 int borderWidth; /* Desired width for border, in
290 int relief; /* Type of relief: TK_RELIEF_RAISED,
291 * TK_RELIEF_SUNKEN, TK_RELIEF_GROOVE, etc. */
293 if (width < 2*borderWidth) {
294 borderWidth = width/2;
296 if (height < 2*borderWidth) {
297 borderWidth = height/2;
299 Tk_3DVerticalBevel(tkwin, drawable, border, x, y, borderWidth, height,
301 Tk_3DVerticalBevel(tkwin, drawable, border, x+width-borderWidth, y,
302 borderWidth, height, 0, relief);
303 Tk_3DHorizontalBevel(tkwin, drawable, border, x, y, width, borderWidth,
305 Tk_3DHorizontalBevel(tkwin, drawable, border, x, y+height-borderWidth,
306 width, borderWidth, 0, 0, 0, relief);
310 *--------------------------------------------------------------
312 * Tk_NameOf3DBorder --
314 * Given a border, return a textual string identifying the
318 * The return value is the string that was used to create
324 *--------------------------------------------------------------
328 Tk_NameOf3DBorder(border)
329 Tk_3DBorder border; /* Token for border. */
331 TkBorder *borderPtr = (TkBorder *) border;
333 return borderPtr->hashPtr->key.string;
337 *--------------------------------------------------------------------
339 * Tk_3DBorderColor --
341 * Given a 3D border, return the X color used for the "flat"
345 * Returns the color used drawing flat surfaces with the border.
350 *--------------------------------------------------------------------
353 Tk_3DBorderColor(border)
354 Tk_3DBorder border; /* Border whose color is wanted. */
356 return(((TkBorder *) border)->bgColorPtr);
360 *--------------------------------------------------------------------
364 * Given a 3D border, returns one of the graphics contexts used to
368 * Returns the graphics context given by the "which" argument.
373 *--------------------------------------------------------------------
376 Tk_3DBorderGC(tkwin, border, which)
377 Tk_Window tkwin; /* Window for which border was allocated. */
378 Tk_3DBorder border; /* Border whose GC is wanted. */
379 int which; /* Selects one of the border's 3 GC's:
380 * TK_3D_FLAT_GC, TK_3D_LIGHT_GC, or
383 TkBorder * borderPtr = (TkBorder *) border;
385 if ((borderPtr->lightGC == None) && (which != TK_3D_FLAT_GC)) {
386 TkpGetShadows(borderPtr, tkwin);
388 if (which == TK_3D_FLAT_GC) {
389 return borderPtr->bgGC;
390 } else if (which == TK_3D_LIGHT_GC) {
391 return borderPtr->lightGC;
392 } else if (which == TK_3D_DARK_GC){
393 return borderPtr->darkGC;
395 panic("bogus \"which\" value in Tk_3DBorderGC");
398 * The code below will never be executed, but it's needed to
399 * keep compilers happy.
406 *--------------------------------------------------------------
410 * This procedure is called when a 3D border is no longer
411 * needed. It frees the resources associated with the
412 * border. After this call, the caller should never again
413 * use the "border" token.
419 * Resources are freed.
421 *--------------------------------------------------------------
425 Tk_Free3DBorder(border)
426 Tk_3DBorder border; /* Token for border to be released. */
428 TkBorder *borderPtr = (TkBorder *) border;
429 Display *display = DisplayOfScreen(borderPtr->screen);
432 borderPtr->resourceRefCount--;
433 if (borderPtr->resourceRefCount > 0) {
437 prevPtr = (TkBorder *) Tcl_GetHashValue(borderPtr->hashPtr);
438 TkpFreeBorder(borderPtr);
439 if (borderPtr->bgColorPtr != NULL) {
440 Tk_FreeColor(borderPtr->bgColorPtr);
442 if (borderPtr->darkColorPtr != NULL) {
443 Tk_FreeColor(borderPtr->darkColorPtr);
445 if (borderPtr->lightColorPtr != NULL) {
446 Tk_FreeColor(borderPtr->lightColorPtr);
448 if (borderPtr->shadow != None) {
449 Tk_FreeBitmap(display, borderPtr->shadow);
451 if (borderPtr->bgGC != None) {
452 Tk_FreeGC(display, borderPtr->bgGC);
454 if (borderPtr->darkGC != None) {
455 Tk_FreeGC(display, borderPtr->darkGC);
457 if (borderPtr->lightGC != None) {
458 Tk_FreeGC(display, borderPtr->lightGC);
460 if (prevPtr == borderPtr) {
461 if (borderPtr->nextPtr == NULL) {
462 Tcl_DeleteHashEntry(borderPtr->hashPtr);
464 Tcl_SetHashValue(borderPtr->hashPtr, borderPtr->nextPtr);
467 while (prevPtr->nextPtr != borderPtr) {
468 prevPtr = prevPtr->nextPtr;
470 prevPtr->nextPtr = borderPtr->nextPtr;
472 if (borderPtr->objRefCount == 0) {
473 ckfree((char *) borderPtr);
478 *----------------------------------------------------------------------
480 * Tk_Free3DBorderFromObj --
482 * This procedure is called to release a border allocated by
483 * Tk_Alloc3DBorderFromObj. It does not throw away the Tcl_Obj *;
484 * it only gets rid of the hash table entry for this border
485 * and clears the cached value that is normally stored in the object.
491 * The reference count associated with the border represented by
492 * objPtr is decremented, and the border's resources are released
493 * to X if there are no remaining uses for it.
495 *----------------------------------------------------------------------
499 Tk_Free3DBorderFromObj(tkwin, objPtr)
500 Tk_Window tkwin; /* The window this border lives in. Needed
501 * for the screen and colormap values. */
502 Tcl_Obj *objPtr; /* The Tcl_Obj * to be freed. */
504 Tk_Free3DBorder(Tk_Get3DBorderFromObj(tkwin, objPtr));
505 FreeBorderObjProc(objPtr);
509 *---------------------------------------------------------------------------
511 * FreeBorderObjProc --
513 * This proc is called to release an object reference to a border.
514 * Called when the object's internal rep is released or when
515 * the cached borderPtr needs to be changed.
521 * The object reference count is decremented. When both it
522 * and the hash ref count go to zero, the border's resources
525 *---------------------------------------------------------------------------
529 FreeBorderObjProc(objPtr)
530 Tcl_Obj *objPtr; /* The object we are releasing. */
532 TkBorder *borderPtr = (TkBorder *) objPtr->internalRep.twoPtrValue.ptr1;
534 if (borderPtr != NULL) {
535 borderPtr->objRefCount--;
536 if ((borderPtr->objRefCount == 0)
537 && (borderPtr->resourceRefCount == 0)) {
538 ckfree((char *) borderPtr);
540 objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) NULL;
545 *---------------------------------------------------------------------------
547 * DupBorderObjProc --
549 * When a cached border object is duplicated, this is called to
550 * update the internal reps.
556 * The border's objRefCount is incremented and the internal rep
557 * of the copy is set to point to it.
559 *---------------------------------------------------------------------------
563 DupBorderObjProc(srcObjPtr, dupObjPtr)
564 Tcl_Obj *srcObjPtr; /* The object we are copying from. */
565 Tcl_Obj *dupObjPtr; /* The object we are copying to. */
567 TkBorder *borderPtr = (TkBorder *) srcObjPtr->internalRep.twoPtrValue.ptr1;
569 dupObjPtr->typePtr = srcObjPtr->typePtr;
570 dupObjPtr->internalRep.twoPtrValue.ptr1 = (VOID *) borderPtr;
572 if (borderPtr != NULL) {
573 borderPtr->objRefCount++;
578 *----------------------------------------------------------------------
580 * Tk_SetBackgroundFromBorder --
582 * Change the background of a window to one appropriate for a given
589 * Tkwin's background gets modified.
591 *----------------------------------------------------------------------
595 Tk_SetBackgroundFromBorder(tkwin, border)
596 Tk_Window tkwin; /* Window whose background is to be set. */
597 Tk_3DBorder border; /* Token for border. */
599 register TkBorder *borderPtr = (TkBorder *) border;
601 Tk_SetWindowBackground(tkwin, borderPtr->bgColorPtr->pixel);
605 *----------------------------------------------------------------------
607 * Tk_GetReliefFromObj --
609 * Return an integer value based on the value of the objPtr.
612 * The return value is a standard Tcl result. If an error occurs during
613 * conversion, an error message is left in the interpreter's result
614 * unless "interp" is NULL.
617 * The object gets converted by Tcl_GetIndexFromObj.
619 *----------------------------------------------------------------------
623 Tk_GetReliefFromObj(interp, objPtr, resultPtr)
624 Tcl_Interp *interp; /* Used for error reporting. */
625 Tcl_Obj *objPtr; /* The object we are trying to get the
627 int *resultPtr; /* Where to place the answer. */
629 return Tcl_GetIndexFromObj(interp, objPtr, reliefStrings, "relief", 0,
634 *----------------------------------------------------------------------
638 * Parse a relief description and return the corresponding
639 * relief value, or an error.
642 * A standard Tcl return value. If all goes well then
643 * *reliefPtr is filled in with one of the values
644 * TK_RELIEF_RAISED, TK_RELIEF_FLAT, or TK_RELIEF_SUNKEN.
649 *----------------------------------------------------------------------
653 Tk_GetRelief(interp, name, reliefPtr)
654 Tcl_Interp *interp; /* For error messages. */
655 CONST char *name; /* Name of a relief type. */
656 int *reliefPtr; /* Where to store converted relief. */
662 length = strlen(name);
663 if ((c == 'f') && (strncmp(name, "flat", length) == 0)) {
664 *reliefPtr = TK_RELIEF_FLAT;
665 } else if ((c == 'g') && (strncmp(name, "groove", length) == 0)
667 *reliefPtr = TK_RELIEF_GROOVE;
668 } else if ((c == 'r') && (strncmp(name, "raised", length) == 0)
670 *reliefPtr = TK_RELIEF_RAISED;
671 } else if ((c == 'r') && (strncmp(name, "ridge", length) == 0)) {
672 *reliefPtr = TK_RELIEF_RIDGE;
673 } else if ((c == 's') && (strncmp(name, "solid", length) == 0)) {
674 *reliefPtr = TK_RELIEF_SOLID;
675 } else if ((c == 's') && (strncmp(name, "sunken", length) == 0)) {
676 *reliefPtr = TK_RELIEF_SUNKEN;
680 sprintf(buf, "bad relief type \"%.50s\": must be %s",
681 name, "flat, groove, raised, ridge, solid, or sunken");
682 Tcl_SetResult(interp, buf, TCL_VOLATILE);
689 *--------------------------------------------------------------
693 * Given a relief value, produce a string describing that
697 * The return value is a static string that is equivalent
703 *--------------------------------------------------------------
707 Tk_NameOfRelief(relief)
708 int relief; /* One of TK_RELIEF_FLAT, TK_RELIEF_RAISED,
709 * or TK_RELIEF_SUNKEN. */
711 if (relief == TK_RELIEF_FLAT) {
713 } else if (relief == TK_RELIEF_SUNKEN) {
715 } else if (relief == TK_RELIEF_RAISED) {
717 } else if (relief == TK_RELIEF_GROOVE) {
719 } else if (relief == TK_RELIEF_RIDGE) {
721 } else if (relief == TK_RELIEF_SOLID) {
723 } else if (relief == TK_RELIEF_NULL) {
726 return "unknown relief";
731 *--------------------------------------------------------------
733 * Tk_Draw3DPolygon --
735 * Draw a border with 3-D appearance around the edge of a
742 * Information is drawn in "drawable" in the form of a
743 * 3-D border borderWidth units width wide on the left
744 * of the trajectory given by pointPtr and numPoints (or
745 * -borderWidth units wide on the right side, if borderWidth
748 *--------------------------------------------------------------
752 Tk_Draw3DPolygon(tkwin, drawable, border, pointPtr, numPoints,
753 borderWidth, leftRelief)
754 Tk_Window tkwin; /* Window for which border was allocated. */
755 Drawable drawable; /* X window or pixmap in which to draw. */
756 Tk_3DBorder border; /* Token for border to draw. */
757 XPoint *pointPtr; /* Array of points describing
758 * polygon. All points must be
759 * absolute (CoordModeOrigin). */
760 int numPoints; /* Number of points at *pointPtr. */
761 int borderWidth; /* Width of border, measured in
762 * pixels to the left of the polygon's
763 * trajectory. May be negative. */
764 int leftRelief; /* TK_RELIEF_RAISED or
765 * TK_RELIEF_SUNKEN: indicates how
766 * stuff to left of trajectory looks
767 * relative to stuff on right. */
769 XPoint poly[4], b1, b2, newB1, newB2;
770 XPoint perp, c, shift1, shift2; /* Used for handling parallel lines. */
771 register XPoint *p1Ptr, *p2Ptr;
772 TkBorder *borderPtr = (TkBorder *) border;
774 int i, lightOnLeft, dx, dy, parallel, pointsSeen;
775 Display *display = Tk_Display(tkwin);
777 if (borderPtr->lightGC == None) {
778 TkpGetShadows(borderPtr, tkwin);
782 * Handle grooves and ridges with recursive calls.
785 if ((leftRelief == TK_RELIEF_GROOVE) || (leftRelief == TK_RELIEF_RIDGE)) {
788 halfWidth = borderWidth/2;
789 Tk_Draw3DPolygon(tkwin, drawable, border, pointPtr, numPoints,
790 halfWidth, (leftRelief == TK_RELIEF_GROOVE) ? TK_RELIEF_RAISED
792 Tk_Draw3DPolygon(tkwin, drawable, border, pointPtr, numPoints,
793 -halfWidth, (leftRelief == TK_RELIEF_GROOVE) ? TK_RELIEF_SUNKEN
799 * If the polygon is already closed, drop the last point from it
800 * (we'll close it automatically).
803 p1Ptr = &pointPtr[numPoints-1];
804 p2Ptr = &pointPtr[0];
805 if ((p1Ptr->x == p2Ptr->x) && (p1Ptr->y == p2Ptr->y)) {
810 * The loop below is executed once for each vertex in the polgon.
811 * At the beginning of each iteration things look like this:
816 * b1 * poly[0] (pointPtr[i-1])
823 * b2 *--------------------*
826 * x-------------------------
828 * The job of this iteration is to do the following:
829 * (a) Compute x (the border corner corresponding to
830 * pointPtr[i]) and put it in poly[2]. As part of
831 * this, compute a new b1 and b2 value for the next
832 * side of the polygon.
833 * (b) Put pointPtr[i] into poly[3].
834 * (c) Draw the polygon given by poly[0..3].
835 * (d) Advance poly[0], poly[1], b1, and b2 for the
836 * next side of the polygon.
840 * The above situation doesn't first come into existence until
841 * two points have been processed; the first two points are
842 * used to "prime the pump", so some parts of the processing
843 * are ommitted for these points. The variable "pointsSeen"
844 * keeps track of the priming process; it has to be separate
845 * from i in order to be able to ignore duplicate points in the
850 for (i = -2, p1Ptr = &pointPtr[numPoints-2], p2Ptr = p1Ptr+1;
851 i < numPoints; i++, p1Ptr = p2Ptr, p2Ptr++) {
852 if ((i == -1) || (i == numPoints-1)) {
855 if ((p2Ptr->x == p1Ptr->x) && (p2Ptr->y == p1Ptr->y)) {
857 * Ignore duplicate points (they'd cause core dumps in
858 * ShiftLine calls below).
862 ShiftLine(p1Ptr, p2Ptr, borderWidth, &newB1);
863 newB2.x = newB1.x + (p2Ptr->x - p1Ptr->x);
864 newB2.y = newB1.y + (p2Ptr->y - p1Ptr->y);
867 if (pointsSeen >= 1) {
868 parallel = Intersect(&newB1, &newB2, &b1, &b2, &poly[2]);
871 * If two consecutive segments of the polygon are parallel,
872 * then things get more complex. Consider the following
876 * *----b1-----------b2------a
879 * *---------*----------* b
880 * poly[0] *p2Ptr *p1Ptr /
885 * Instead of using x and *p1Ptr for poly[2] and poly[3], as
886 * in the original diagram, use a and b as above. Then instead
887 * of using x and *p1Ptr for the new poly[0] and poly[1], use
890 * Do the computation in three stages:
891 * 1. Compute a point "perp" such that the line p1Ptr-perp
892 * is perpendicular to p1Ptr-p2Ptr.
893 * 2. Compute the points a and c by intersecting the lines
894 * b1-b2 and newB1-newB2 with p1Ptr-perp.
895 * 3. Compute b by shifting p1Ptr-perp to the right and
896 * intersecting it with p1Ptr-p2Ptr.
900 perp.x = p1Ptr->x + (p2Ptr->y - p1Ptr->y);
901 perp.y = p1Ptr->y - (p2Ptr->x - p1Ptr->x);
902 (void) Intersect(p1Ptr, &perp, &b1, &b2, &poly[2]);
903 (void) Intersect(p1Ptr, &perp, &newB1, &newB2, &c);
904 ShiftLine(p1Ptr, &perp, borderWidth, &shift1);
905 shift2.x = shift1.x + (perp.x - p1Ptr->x);
906 shift2.y = shift1.y + (perp.y - p1Ptr->y);
907 (void) Intersect(p1Ptr, p2Ptr, &shift1, &shift2, &poly[3]);
910 if (pointsSeen >= 2) {
911 dx = poly[3].x - poly[0].x;
912 dy = poly[3].y - poly[0].y;
914 lightOnLeft = (dy <= dx);
916 lightOnLeft = (dy < dx);
918 if (lightOnLeft ^ (leftRelief == TK_RELIEF_RAISED)) {
919 gc = borderPtr->lightGC;
921 gc = borderPtr->darkGC;
923 XFillPolygon(display, drawable, gc, poly, 4, Convex,
930 poly[0].x = poly[3].x;
931 poly[0].y = poly[3].y;
935 } else if (pointsSeen >= 1) {
936 poly[1].x = poly[2].x;
937 poly[1].y = poly[2].y;
944 *----------------------------------------------------------------------
946 * Tk_Fill3DRectangle --
948 * Fill a rectangular area, supplying a 3D border if desired.
954 * Information gets drawn on the screen.
956 *----------------------------------------------------------------------
960 Tk_Fill3DRectangle(tkwin, drawable, border, x, y, width,
961 height, borderWidth, relief)
962 Tk_Window tkwin; /* Window for which border was allocated. */
963 Drawable drawable; /* X window or pixmap in which to draw. */
964 Tk_3DBorder border; /* Token for border to draw. */
965 int x, y, width, height; /* Outside area of rectangular region. */
966 int borderWidth; /* Desired width for border, in
967 * pixels. Border will be *inside* region. */
968 int relief; /* Indicates 3D effect: TK_RELIEF_FLAT,
969 * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
971 register TkBorder *borderPtr = (TkBorder *) border;
975 * This code is slightly tricky because it only draws the background
976 * in areas not covered by the 3D border. This avoids flashing
977 * effects on the screen for the border region.
980 if (relief == TK_RELIEF_FLAT) {
984 * We need to make this extra check, otherwise we will leave
985 * garbage in thin frames [Bug: 3596]
987 if (width < 2*borderWidth) {
988 borderWidth = width/2;
990 if (height < 2*borderWidth) {
991 borderWidth = height/2;
994 doubleBorder = 2*borderWidth;
996 if ((width > doubleBorder) && (height > doubleBorder)) {
997 XFillRectangle(Tk_Display(tkwin), drawable, borderPtr->bgGC,
998 x + borderWidth, y + borderWidth,
999 (unsigned int) (width - doubleBorder),
1000 (unsigned int) (height - doubleBorder));
1003 Tk_Draw3DRectangle(tkwin, drawable, border, x, y, width,
1004 height, borderWidth, relief);
1009 *----------------------------------------------------------------------
1011 * Tk_Fill3DPolygon --
1013 * Fill a polygonal area, supplying a 3D border if desired.
1019 * Information gets drawn on the screen.
1021 *----------------------------------------------------------------------
1025 Tk_Fill3DPolygon(tkwin, drawable, border, pointPtr, numPoints,
1026 borderWidth, leftRelief)
1027 Tk_Window tkwin; /* Window for which border was allocated. */
1028 Drawable drawable; /* X window or pixmap in which to draw. */
1029 Tk_3DBorder border; /* Token for border to draw. */
1030 XPoint *pointPtr; /* Array of points describing
1031 * polygon. All points must be
1032 * absolute (CoordModeOrigin). */
1033 int numPoints; /* Number of points at *pointPtr. */
1034 int borderWidth; /* Width of border, measured in
1035 * pixels to the left of the polygon's
1036 * trajectory. May be negative. */
1037 int leftRelief; /* Indicates 3D effect of left side of
1038 * trajectory relative to right:
1039 * TK_RELIEF_FLAT, TK_RELIEF_RAISED,
1040 * or TK_RELIEF_SUNKEN. */
1042 register TkBorder *borderPtr = (TkBorder *) border;
1044 XFillPolygon(Tk_Display(tkwin), drawable, borderPtr->bgGC,
1045 pointPtr, numPoints, Complex, CoordModeOrigin);
1046 if (leftRelief != TK_RELIEF_FLAT) {
1047 Tk_Draw3DPolygon(tkwin, drawable, border, pointPtr, numPoints,
1048 borderWidth, leftRelief);
1053 *--------------------------------------------------------------
1057 * Initialize the structures used for border management.
1065 *-------------------------------------------------------------
1070 TkDisplay * dispPtr; /* Used to access thread-specific data. */
1072 dispPtr->borderInit = 1;
1073 Tcl_InitHashTable(&dispPtr->borderTable, TCL_STRING_KEYS);
1077 *--------------------------------------------------------------
1081 * Given two points on a line, compute a point on a
1082 * new line that is parallel to the given line and
1083 * a given distance away from it.
1091 *--------------------------------------------------------------
1095 ShiftLine(p1Ptr, p2Ptr, distance, p3Ptr)
1096 XPoint *p1Ptr; /* First point on line. */
1097 XPoint *p2Ptr; /* Second point on line. */
1098 int distance; /* New line is to be this many
1099 * units to the left of original
1100 * line, when looking from p1 to
1101 * p2. May be negative. */
1102 XPoint *p3Ptr; /* Store coords of point on new
1105 int dx, dy, dxNeg, dyNeg;
1108 * The table below is used for a quick approximation in
1109 * computing the new point. An index into the table
1110 * is 128 times the slope of the original line (the slope
1111 * must always be between 0 and 1). The value of the table
1112 * entry is 128 times the amount to displace the new line
1113 * in y for each unit of perpendicular distance. In other
1114 * words, the table maps from the tangent of an angle to
1115 * the inverse of its cosine. If the slope of the original
1116 * line is greater than 1, then the displacement is done in
1117 * x rather than in y.
1120 static int shiftTable[129];
1123 * Initialize the table if this is the first time it is
1127 if (shiftTable[0] == 0) {
1129 double tangent, cosine;
1131 for (i = 0; i <= 128; i++) {
1133 cosine = 128/cos(atan(tangent)) + .5;
1134 shiftTable[i] = (int) cosine;
1139 dx = p2Ptr->x - p1Ptr->x;
1140 dy = p2Ptr->y - p1Ptr->y;
1154 dy = ((distance * shiftTable[(dy<<7)/dx]) + 64) >> 7;
1160 dx = ((distance * shiftTable[(dx<<7)/dy]) + 64) >> 7;
1169 *--------------------------------------------------------------
1173 * Find the intersection point between two lines.
1176 * Under normal conditions 0 is returned and the point
1177 * at *iPtr is filled in with the intersection between
1178 * the two lines. If the two lines are parallel, then
1179 * -1 is returned and *iPtr isn't modified.
1184 *--------------------------------------------------------------
1188 Intersect(a1Ptr, a2Ptr, b1Ptr, b2Ptr, iPtr)
1189 XPoint *a1Ptr; /* First point of first line. */
1190 XPoint *a2Ptr; /* Second point of first line. */
1191 XPoint *b1Ptr; /* First point of second line. */
1192 XPoint *b2Ptr; /* Second point of second line. */
1193 XPoint *iPtr; /* Filled in with intersection point. */
1195 int dxadyb, dxbdya, dxadxb, dyadyb, p, q;
1198 * The code below is just a straightforward manipulation of two
1199 * equations of the form y = (x-x1)*(y2-y1)/(x2-x1) + y1 to solve
1200 * for the x-coordinate of intersection, then the y-coordinate.
1203 dxadyb = (a2Ptr->x - a1Ptr->x)*(b2Ptr->y - b1Ptr->y);
1204 dxbdya = (b2Ptr->x - b1Ptr->x)*(a2Ptr->y - a1Ptr->y);
1205 dxadxb = (a2Ptr->x - a1Ptr->x)*(b2Ptr->x - b1Ptr->x);
1206 dyadyb = (a2Ptr->y - a1Ptr->y)*(b2Ptr->y - b1Ptr->y);
1208 if (dxadyb == dxbdya) {
1211 p = (a1Ptr->x*dxbdya - b1Ptr->x*dxadyb + (b1Ptr->y - a1Ptr->y)*dxadxb);
1212 q = dxbdya - dxadyb;
1218 iPtr->x = - ((-p + q/2)/q);
1220 iPtr->x = (p + q/2)/q;
1222 p = (a1Ptr->y*dxadyb - b1Ptr->y*dxbdya + (b1Ptr->x - a1Ptr->x)*dyadyb);
1223 q = dxadyb - dxbdya;
1229 iPtr->y = - ((-p + q/2)/q);
1231 iPtr->y = (p + q/2)/q;
1237 *----------------------------------------------------------------------
1239 * Tk_Get3DBorderFromObj --
1241 * Returns the border referred to by a Tcl object. The border must
1242 * already have been allocated via a call to Tk_Alloc3DBorderFromObj
1243 * or Tk_Get3DBorder.
1246 * Returns the Tk_3DBorder that matches the tkwin and the string rep
1247 * of the name of the border given in objPtr.
1250 * If the object is not already a border, the conversion will free
1251 * any old internal representation.
1253 *----------------------------------------------------------------------
1257 Tk_Get3DBorderFromObj(tkwin, objPtr)
1259 Tcl_Obj *objPtr; /* The object whose string value selects
1262 TkBorder *borderPtr = NULL;
1263 Tcl_HashEntry *hashPtr;
1264 TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
1266 if (objPtr->typePtr != &tkBorderObjType) {
1267 InitBorderObj(objPtr);
1271 * If we are lucky (and the user doesn't use too many different
1272 * displays, screens, or colormaps...) then the TkBorder
1273 * structure we need will be cached in the internal
1274 * representation of the Tcl_Obj. Check it out...
1277 borderPtr = (TkBorder *) objPtr->internalRep.twoPtrValue.ptr1;
1278 if ((borderPtr != NULL)
1279 && (borderPtr->resourceRefCount > 0)
1280 && (Tk_Screen(tkwin) == borderPtr->screen)
1281 && (Tk_Colormap(tkwin) == borderPtr->colormap)) {
1283 * The object already points to the right border structure.
1286 return (Tk_3DBorder) borderPtr;
1290 * If we make it here, it means we aren't so lucky. Either there
1291 * was no cached TkBorder in the Tcl_Obj, or the TkBorder that was
1292 * there is for the wrong screen/colormap. Either way, we have
1293 * to search for the right TkBorder. For each color name, there is
1294 * linked list of TkBorder structures, one structure for each
1295 * screen/colormap combination. The head of the linked list is
1296 * recorded in a hash table (where the key is the color name)
1297 * attached to the TkDisplay structure. Walk this list to find
1298 * the right TkBorder structure.
1301 hashPtr = Tcl_FindHashEntry(&dispPtr->borderTable, Tcl_GetString(objPtr));
1302 if (hashPtr == NULL) {
1305 for (borderPtr = (TkBorder *) Tcl_GetHashValue(hashPtr);
1306 (borderPtr != NULL); borderPtr = borderPtr->nextPtr) {
1307 if ((Tk_Screen(tkwin) == borderPtr->screen)
1308 && (Tk_Colormap(tkwin) == borderPtr->colormap)) {
1309 FreeBorderObjProc(objPtr);
1310 objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) borderPtr;
1311 borderPtr->objRefCount++;
1312 return (Tk_3DBorder) borderPtr;
1317 panic("Tk_Get3DBorderFromObj called with non-existent border!");
1319 * The following code isn't reached; it's just there to please compilers.
1325 *----------------------------------------------------------------------
1329 * Attempt to generate a border internal form for the Tcl object
1333 * The return value is a standard Tcl result. If an error occurs during
1334 * conversion, an error message is left in the interpreter's result
1335 * unless "interp" is NULL.
1338 * If no error occurs, a blank internal format for a border value
1339 * is intialized. The final form cannot be done without a Tk_Window.
1341 *----------------------------------------------------------------------
1345 InitBorderObj(objPtr)
1346 Tcl_Obj *objPtr; /* The object to convert. */
1348 Tcl_ObjType *typePtr;
1351 * Free the old internalRep before setting the new one.
1354 Tcl_GetString(objPtr);
1355 typePtr = objPtr->typePtr;
1356 if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) {
1357 (*typePtr->freeIntRepProc)(objPtr);
1359 objPtr->typePtr = &tkBorderObjType;
1360 objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) NULL;
1364 *----------------------------------------------------------------------
1368 * This procedure returns debugging information about a border.
1371 * The return value is a list with one sublist for each TkBorder
1372 * corresponding to "name". Each sublist has two elements that
1373 * contain the resourceRefCount and objRefCount fields from the
1374 * TkBorder structure.
1379 *----------------------------------------------------------------------
1383 TkDebugBorder(tkwin, name)
1384 Tk_Window tkwin; /* The window in which the border will be
1385 * used (not currently used). */
1386 char *name; /* Name of the desired color. */
1388 TkBorder *borderPtr;
1389 Tcl_HashEntry *hashPtr;
1390 Tcl_Obj *resultPtr, *objPtr;
1391 TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
1393 resultPtr = Tcl_NewObj();
1394 hashPtr = Tcl_FindHashEntry(&dispPtr->borderTable, name);
1395 if (hashPtr != NULL) {
1396 borderPtr = (TkBorder *) Tcl_GetHashValue(hashPtr);
1397 if (borderPtr == NULL) {
1398 panic("TkDebugBorder found empty hash table entry");
1400 for ( ; (borderPtr != NULL); borderPtr = borderPtr->nextPtr) {
1401 objPtr = Tcl_NewObj();
1402 Tcl_ListObjAppendElement(NULL, objPtr,
1403 Tcl_NewIntObj(borderPtr->resourceRefCount));
1404 Tcl_ListObjAppendElement(NULL, objPtr,
1405 Tcl_NewIntObj(borderPtr->objRefCount));
1406 Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);