OSDN Git Service

*** empty log message ***
[pf3gnuchains/sourceware.git] / tk / generic / tk3d.c
1 /* 
2  * tk3d.c --
3  *
4  *      This module provides procedures to draw borders in
5  *      the 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
11  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12  *
13  * RCS: @(#) $Id$
14  */
15
16 #include "tk3d.h"
17
18 /*
19  * The following table defines the string values for reliefs, which are
20  * used by Tk_GetReliefFromObj.
21  */
22
23 static CONST char *reliefStrings[] = {"flat", "groove", "raised",
24                                     "ridge", "solid", "sunken", 
25                                     (char *) NULL};
26
27 /*
28  * Forward declarations for procedures defined in this file:
29  */
30
31 static void             BorderInit _ANSI_ARGS_((TkDisplay *dispPtr));
32 static void             DupBorderObjProc _ANSI_ARGS_((Tcl_Obj *srcObjPtr,
33                             Tcl_Obj *dupObjPtr));
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));
40
41 /*
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
46  * is set.
47  */
48
49 Tcl_ObjType tkBorderObjType = {
50     "border",                   /* name */
51     FreeBorderObjProc,          /* freeIntRepProc */
52     DupBorderObjProc,           /* dupIntRepProc */
53     NULL,                       /* updateStringProc */
54     NULL                        /* setFromAnyProc */
55 };
56 \f
57 /*
58  *----------------------------------------------------------------------
59  *
60  * Tk_Alloc3DBorderFromObj --
61  *
62  *      Given a Tcl_Obj *, map the value to a corresponding
63  *      Tk_3DBorder structure based on the tkwin given.
64  *
65  * Results:
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.
71  *
72  * Side effects:
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.
77  *
78  *----------------------------------------------------------------------
79  */
80
81 Tk_3DBorder
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
86                                  * background. */
87 {
88     TkBorder *borderPtr;
89
90     if (objPtr->typePtr != &tkBorderObjType) {
91         InitBorderObj(objPtr);
92     }
93     borderPtr = (TkBorder *) objPtr->internalRep.twoPtrValue.ptr1;
94
95     /*
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.
98      */
99
100     if (borderPtr != NULL) {
101         if (borderPtr->resourceRefCount == 0) {
102             /*
103              * This is a stale reference: it refers to a border that's
104              * no longer in use.  Clear the reference.
105              */
106
107             FreeBorderObjProc(objPtr);
108             borderPtr = NULL;
109         } else if ((Tk_Screen(tkwin) == borderPtr->screen)
110                 && (Tk_Colormap(tkwin) == borderPtr->colormap)) {
111             borderPtr->resourceRefCount++;
112             return (Tk_3DBorder) borderPtr;
113         }
114     }
115
116     /*
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.
120      */
121
122     /*
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.
126      *
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.
131      */
132
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;
145             }
146         }
147     }
148
149     /*
150      * Still no luck.  Call Tk_Get3DBorder to allocate a new border.
151      */
152
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++;
158     }
159     return (Tk_3DBorder) borderPtr;
160 }
161 \f
162 /*
163  *--------------------------------------------------------------
164  *
165  * Tk_Get3DBorder --
166  *
167  *      Create a data structure for displaying a 3-D border.
168  *
169  * Results:
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.
175  *
176  * Side effects:
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.
180  *
181  *--------------------------------------------------------------
182  */
183
184 Tk_3DBorder
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
188                                  * be drawn. */
189     Tk_Uid colorName;           /* String giving name of color
190                                  * for window background. */
191 {
192     Tcl_HashEntry *hashPtr;
193     TkBorder *borderPtr, *existingBorderPtr;
194     int new;
195     XGCValues gcValues;
196     XColor *bgColorPtr;
197     TkDisplay *dispPtr;
198
199     dispPtr = ((TkWindow *) tkwin)->dispPtr;
200
201     if (!dispPtr->borderInit) {
202         BorderInit(dispPtr);
203     }
204
205     hashPtr = Tcl_CreateHashEntry(&dispPtr->borderTable, colorName, &new);
206     if (!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;
214             }
215         }
216     } else {
217         existingBorderPtr = NULL;
218     }
219
220     /*
221      * No satisfactory border exists yet.  Initialize a new one.
222      */
223
224     bgColorPtr = Tk_GetColor(interp, tkwin, colorName);
225     if (bgColorPtr == NULL) {
226         if (new) {
227             Tcl_DeleteHashEntry(hashPtr);
228         }
229         return NULL;
230     }
231
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);
249
250     /*
251      * Create the information for displaying the background color,
252      * but delay the allocation of shadows until they are actually
253      * needed for drawing.
254      */
255
256     gcValues.foreground = borderPtr->bgColorPtr->pixel;
257     borderPtr->bgGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
258     return (Tk_3DBorder) borderPtr;
259 }
260 \f
261 /*
262  *--------------------------------------------------------------
263  *
264  * Tk_Draw3DRectangle --
265  *
266  *      Draw a 3-D border at a given place in a given window.
267  *
268  * Results:
269  *      None.
270  *
271  * Side effects:
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.
276  *
277  *--------------------------------------------------------------
278  */
279
280 void
281 Tk_Draw3DRectangle(tkwin, drawable, border, x, y, width, height,
282         borderWidth, relief)
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
289                                  * pixels. */
290     int relief;                 /* Type of relief: TK_RELIEF_RAISED,
291                                  * TK_RELIEF_SUNKEN, TK_RELIEF_GROOVE, etc. */
292 {
293     if (width < 2*borderWidth) {
294         borderWidth = width/2;
295     }
296     if (height < 2*borderWidth) {
297         borderWidth = height/2;
298     }
299     Tk_3DVerticalBevel(tkwin, drawable, border, x, y, borderWidth, height,
300             1, relief);
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,
304             1, 1, 1, relief);
305     Tk_3DHorizontalBevel(tkwin, drawable, border, x, y+height-borderWidth,
306             width, borderWidth, 0, 0, 0, relief);
307 }
308 \f
309 /*
310  *--------------------------------------------------------------
311  *
312  * Tk_NameOf3DBorder --
313  *
314  *      Given a border, return a textual string identifying the
315  *      border's color.
316  *
317  * Results:
318  *      The return value is the string that was used to create
319  *      the border.
320  *
321  * Side effects:
322  *      None.
323  *
324  *--------------------------------------------------------------
325  */
326
327 CONST char *
328 Tk_NameOf3DBorder(border)
329     Tk_3DBorder border;         /* Token for border. */
330 {
331     TkBorder *borderPtr = (TkBorder *) border;
332
333     return borderPtr->hashPtr->key.string;
334 }
335 \f
336 /*
337  *--------------------------------------------------------------------
338  *
339  * Tk_3DBorderColor --
340  *
341  *      Given a 3D border, return the X color used for the "flat"
342  *      surfaces.
343  *
344  * Results:
345  *      Returns the color used drawing flat surfaces with the border.
346  *
347  * Side effects:
348  *      None.
349  *
350  *--------------------------------------------------------------------
351  */
352 XColor *
353 Tk_3DBorderColor(border)
354     Tk_3DBorder border;         /* Border whose color is wanted. */
355 {
356     return(((TkBorder *) border)->bgColorPtr);
357 }
358 \f
359 /*
360  *--------------------------------------------------------------------
361  *
362  * Tk_3DBorderGC --
363  *
364  *      Given a 3D border, returns one of the graphics contexts used to
365  *      draw the border.
366  *
367  * Results:
368  *      Returns the graphics context given by the "which" argument.
369  *
370  * Side effects:
371  *      None.
372  *
373  *--------------------------------------------------------------------
374  */
375 GC
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
381                                  * TK_3D_DARK_GC. */
382 {
383     TkBorder * borderPtr = (TkBorder *) border;
384
385     if ((borderPtr->lightGC == None) && (which != TK_3D_FLAT_GC)) {
386         TkpGetShadows(borderPtr, tkwin);
387     }
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;
394     }
395     panic("bogus \"which\" value in Tk_3DBorderGC");
396
397     /*
398      * The code below will never be executed, but it's needed to
399      * keep compilers happy.
400      */
401
402     return (GC) None;
403 }
404 \f
405 /*
406  *--------------------------------------------------------------
407  *
408  * Tk_Free3DBorder --
409  *
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.
414  *
415  * Results:
416  *      None.
417  *
418  * Side effects:
419  *      Resources are freed.
420  *
421  *--------------------------------------------------------------
422  */
423
424 void
425 Tk_Free3DBorder(border)
426     Tk_3DBorder border;         /* Token for border to be released. */
427 {
428     TkBorder *borderPtr = (TkBorder *) border;
429     Display *display = DisplayOfScreen(borderPtr->screen);
430     TkBorder *prevPtr;
431
432     borderPtr->resourceRefCount--;
433     if (borderPtr->resourceRefCount > 0) {
434         return;
435     }
436
437     prevPtr = (TkBorder *) Tcl_GetHashValue(borderPtr->hashPtr);
438     TkpFreeBorder(borderPtr);
439     if (borderPtr->bgColorPtr != NULL) {
440         Tk_FreeColor(borderPtr->bgColorPtr);
441     }
442     if (borderPtr->darkColorPtr != NULL) {
443         Tk_FreeColor(borderPtr->darkColorPtr);
444     }
445     if (borderPtr->lightColorPtr != NULL) {
446         Tk_FreeColor(borderPtr->lightColorPtr);
447     }
448     if (borderPtr->shadow != None) {
449         Tk_FreeBitmap(display, borderPtr->shadow);
450     }
451     if (borderPtr->bgGC != None) {
452         Tk_FreeGC(display, borderPtr->bgGC);
453     }
454     if (borderPtr->darkGC != None) {
455         Tk_FreeGC(display, borderPtr->darkGC);
456     }
457     if (borderPtr->lightGC != None) {
458         Tk_FreeGC(display, borderPtr->lightGC);
459     }
460     if (prevPtr == borderPtr) {
461         if (borderPtr->nextPtr == NULL) {
462             Tcl_DeleteHashEntry(borderPtr->hashPtr);
463         } else {
464             Tcl_SetHashValue(borderPtr->hashPtr, borderPtr->nextPtr);
465         }
466     } else {
467         while (prevPtr->nextPtr != borderPtr) {
468             prevPtr = prevPtr->nextPtr;
469         }
470         prevPtr->nextPtr = borderPtr->nextPtr;
471     }
472     if (borderPtr->objRefCount == 0) {
473         ckfree((char *) borderPtr);
474     }
475 }
476 \f
477 /*
478  *----------------------------------------------------------------------
479  *
480  * Tk_Free3DBorderFromObj --
481  *
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.
486  *
487  * Results:
488  *      None.
489  *
490  * Side effects:
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.
494  *
495  *----------------------------------------------------------------------
496  */
497
498 void
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. */
503 {
504     Tk_Free3DBorder(Tk_Get3DBorderFromObj(tkwin, objPtr));
505     FreeBorderObjProc(objPtr);
506 }
507 \f
508 /*
509  *---------------------------------------------------------------------------
510  *
511  * FreeBorderObjProc -- 
512  *
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.
516  *
517  * Results:
518  *      None.
519  *
520  * Side effects:
521  *      The object reference count is decremented. When both it
522  *      and the hash ref count go to zero, the border's resources
523  *      are released.
524  *
525  *---------------------------------------------------------------------------
526  */
527
528 static void
529 FreeBorderObjProc(objPtr)
530     Tcl_Obj *objPtr;            /* The object we are releasing. */
531 {
532     TkBorder *borderPtr = (TkBorder *) objPtr->internalRep.twoPtrValue.ptr1;
533
534     if (borderPtr != NULL) {
535         borderPtr->objRefCount--;
536         if ((borderPtr->objRefCount == 0) 
537                 && (borderPtr->resourceRefCount == 0)) {
538             ckfree((char *) borderPtr);
539         }
540         objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) NULL;
541     }
542 }
543 \f
544 /*
545  *---------------------------------------------------------------------------
546  *
547  * DupBorderObjProc -- 
548  *
549  *      When a cached border object is duplicated, this is called to
550  *      update the internal reps.
551  *
552  * Results:
553  *      None.
554  *
555  * Side effects:
556  *      The border's objRefCount is incremented and the internal rep
557  *      of the copy is set to point to it.
558  *
559  *---------------------------------------------------------------------------
560  */
561
562 static void
563 DupBorderObjProc(srcObjPtr, dupObjPtr)
564     Tcl_Obj *srcObjPtr;         /* The object we are copying from. */
565     Tcl_Obj *dupObjPtr;         /* The object we are copying to. */
566 {
567     TkBorder *borderPtr = (TkBorder *) srcObjPtr->internalRep.twoPtrValue.ptr1;
568     
569     dupObjPtr->typePtr = srcObjPtr->typePtr;
570     dupObjPtr->internalRep.twoPtrValue.ptr1 = (VOID *) borderPtr;
571
572     if (borderPtr != NULL) {
573         borderPtr->objRefCount++;
574     }
575 }
576 \f
577 /*
578  *----------------------------------------------------------------------
579  *
580  * Tk_SetBackgroundFromBorder --
581  *
582  *      Change the background of a window to one appropriate for a given
583  *      3-D border.
584  *
585  * Results:
586  *      None.
587  *
588  * Side effects:
589  *      Tkwin's background gets modified.
590  *
591  *----------------------------------------------------------------------
592  */
593
594 void
595 Tk_SetBackgroundFromBorder(tkwin, border)
596     Tk_Window tkwin;            /* Window whose background is to be set. */
597     Tk_3DBorder border;         /* Token for border. */
598 {
599     register TkBorder *borderPtr = (TkBorder *) border;
600
601     Tk_SetWindowBackground(tkwin, borderPtr->bgColorPtr->pixel);
602 }
603 \f
604 /*
605  *----------------------------------------------------------------------
606  *
607  * Tk_GetReliefFromObj --
608  *
609  *      Return an integer value based on the value of the objPtr.
610  *
611  * Results:
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.
615  *
616  * Side effects:
617  *      The object gets converted by Tcl_GetIndexFromObj.
618  *
619  *----------------------------------------------------------------------
620  */
621
622 int
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 
626                                  * value from. */
627     int *resultPtr;             /* Where to place the answer. */
628 {
629     return Tcl_GetIndexFromObj(interp, objPtr, reliefStrings, "relief", 0, 
630             resultPtr);
631 }
632 \f
633 /*
634  *----------------------------------------------------------------------
635  *
636  * Tk_GetRelief --
637  *
638  *      Parse a relief description and return the corresponding
639  *      relief value, or an error.
640  *
641  * Results:
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.
645  *
646  * Side effects:
647  *      None.
648  *
649  *----------------------------------------------------------------------
650  */
651
652 int
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. */
657 {
658     char c;
659     size_t length;
660
661     c = name[0];
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)
666             && (length >= 2)) {
667         *reliefPtr = TK_RELIEF_GROOVE;
668     } else if ((c == 'r') && (strncmp(name, "raised", length) == 0)
669             && (length >= 2)) {
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;
677     } else {
678         char buf[200];
679
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);
683         return TCL_ERROR;
684     }
685     return TCL_OK;
686 }
687 \f
688 /*
689  *--------------------------------------------------------------
690  *
691  * Tk_NameOfRelief --
692  *
693  *      Given a relief value, produce a string describing that
694  *      relief value.
695  *
696  * Results:
697  *      The return value is a static string that is equivalent
698  *      to relief.
699  *
700  * Side effects:
701  *      None.
702  *
703  *--------------------------------------------------------------
704  */
705
706 CONST char *
707 Tk_NameOfRelief(relief)
708     int relief;         /* One of TK_RELIEF_FLAT, TK_RELIEF_RAISED,
709                          * or TK_RELIEF_SUNKEN. */
710 {
711     if (relief == TK_RELIEF_FLAT) {
712         return "flat";
713     } else if (relief == TK_RELIEF_SUNKEN) {
714         return "sunken";
715     } else if (relief == TK_RELIEF_RAISED) {
716         return "raised";
717     } else if (relief == TK_RELIEF_GROOVE) {
718         return "groove";
719     } else if (relief == TK_RELIEF_RIDGE) {
720         return "ridge";
721     } else if (relief == TK_RELIEF_SOLID) {
722         return "solid";
723     } else if (relief == TK_RELIEF_NULL) {
724         return "";
725     } else {
726         return "unknown relief";
727     }
728 }
729 \f
730 /*
731  *--------------------------------------------------------------
732  *
733  * Tk_Draw3DPolygon --
734  *
735  *      Draw a border with 3-D appearance around the edge of a
736  *      given polygon.
737  *
738  * Results:
739  *      None.
740  *
741  * Side effects:
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
746  *      is negative).
747  *
748  *--------------------------------------------------------------
749  */
750
751 void
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. */
768 {
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;
773     GC gc;
774     int i, lightOnLeft, dx, dy, parallel, pointsSeen;
775     Display *display = Tk_Display(tkwin);
776
777     if (borderPtr->lightGC == None) {
778         TkpGetShadows(borderPtr, tkwin);
779     }
780
781     /*
782      * Handle grooves and ridges with recursive calls.
783      */
784
785     if ((leftRelief == TK_RELIEF_GROOVE) || (leftRelief == TK_RELIEF_RIDGE)) {
786         int halfWidth;
787
788         halfWidth = borderWidth/2;
789         Tk_Draw3DPolygon(tkwin, drawable, border, pointPtr, numPoints,
790                 halfWidth, (leftRelief == TK_RELIEF_GROOVE) ? TK_RELIEF_RAISED
791                 : TK_RELIEF_SUNKEN);
792         Tk_Draw3DPolygon(tkwin, drawable, border, pointPtr, numPoints,
793                 -halfWidth, (leftRelief == TK_RELIEF_GROOVE) ? TK_RELIEF_SUNKEN
794                 : TK_RELIEF_RAISED);
795         return;
796     }
797
798     /*
799      * If the polygon is already closed, drop the last point from it
800      * (we'll close it automatically).
801      */
802
803     p1Ptr = &pointPtr[numPoints-1];
804     p2Ptr = &pointPtr[0];
805     if ((p1Ptr->x == p2Ptr->x) && (p1Ptr->y == p2Ptr->y)) {
806         numPoints--;
807     }
808
809     /*
810      * The loop below is executed once for each vertex in the polgon.
811      * At the beginning of each iteration things look like this:
812      *
813      *          poly[1]       /
814      *             *        /
815      *             |      /
816      *             b1   * poly[0] (pointPtr[i-1])
817      *             |    |
818      *             |    |
819      *             |    |
820      *             |    |
821      *             |    |
822      *             |    | *p1Ptr            *p2Ptr
823      *             b2   *--------------------*
824      *             |
825      *             |
826      *             x-------------------------
827      *
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.
837      */
838
839     /*
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
846      * polygon.
847      */
848
849     pointsSeen = 0;
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)) {
853             p2Ptr = pointPtr;
854         }
855         if ((p2Ptr->x == p1Ptr->x) && (p2Ptr->y == p1Ptr->y)) {
856             /*
857              * Ignore duplicate points (they'd cause core dumps in
858              * ShiftLine calls below).
859              */
860             continue;
861         }
862         ShiftLine(p1Ptr, p2Ptr, borderWidth, &newB1);
863         newB2.x = newB1.x + (p2Ptr->x - p1Ptr->x);
864         newB2.y = newB1.y + (p2Ptr->y - p1Ptr->y);
865         poly[3] = *p1Ptr;
866         parallel = 0;
867         if (pointsSeen >= 1) {
868             parallel = Intersect(&newB1, &newB2, &b1, &b2, &poly[2]);
869
870             /*
871              * If two consecutive segments of the polygon are parallel,
872              * then things get more complex.  Consider the following
873              * diagram:
874              *
875              * poly[1]
876              *    *----b1-----------b2------a
877              *                                \
878              *                                  \
879              *         *---------*----------*    b
880              *        poly[0]  *p2Ptr   *p1Ptr  /
881              *                                /
882              *              --*--------*----c
883              *              newB1    newB2
884              *
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
888              * b and c as above.
889              *
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.
897              */
898
899             if (parallel) {
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]);
908             }
909         }
910         if (pointsSeen >= 2) {
911             dx = poly[3].x - poly[0].x;
912             dy = poly[3].y - poly[0].y;
913             if (dx > 0) {
914                 lightOnLeft = (dy <= dx);
915             } else {
916                 lightOnLeft = (dy < dx);
917             }
918             if (lightOnLeft ^ (leftRelief == TK_RELIEF_RAISED)) {
919                 gc = borderPtr->lightGC;
920             } else {
921                 gc = borderPtr->darkGC;
922             }
923             XFillPolygon(display, drawable, gc, poly, 4, Convex,
924                     CoordModeOrigin);
925         }
926         b1.x = newB1.x;
927         b1.y = newB1.y;
928         b2.x = newB2.x;
929         b2.y = newB2.y;
930         poly[0].x = poly[3].x;
931         poly[0].y = poly[3].y;
932         if (parallel) {
933             poly[1].x = c.x;
934             poly[1].y = c.y;
935         } else if (pointsSeen >= 1) {
936             poly[1].x = poly[2].x;
937             poly[1].y = poly[2].y;
938         }
939         pointsSeen++;
940     }
941 }
942 \f
943 /*
944  *----------------------------------------------------------------------
945  *
946  * Tk_Fill3DRectangle --
947  *
948  *      Fill a rectangular area, supplying a 3D border if desired.
949  *
950  * Results:
951  *      None.
952  *
953  * Side effects:
954  *      Information gets drawn on the screen.
955  *
956  *----------------------------------------------------------------------
957  */
958
959 void
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. */
970 {
971     register TkBorder *borderPtr = (TkBorder *) border;
972     int doubleBorder;
973
974     /*
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.
978      */
979   
980     if (relief == TK_RELIEF_FLAT) {
981         borderWidth = 0;
982     } else {
983         /*
984          * We need to make this extra check, otherwise we will leave
985          * garbage in thin frames [Bug: 3596]
986          */
987         if (width < 2*borderWidth) {
988             borderWidth = width/2;
989         }
990         if (height < 2*borderWidth) {
991             borderWidth = height/2;
992         }
993     }
994     doubleBorder = 2*borderWidth;
995
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));
1001     }
1002     if (borderWidth) {
1003         Tk_Draw3DRectangle(tkwin, drawable, border, x, y, width,
1004                 height, borderWidth, relief);
1005     }
1006 }
1007 \f
1008 /*
1009  *----------------------------------------------------------------------
1010  *
1011  * Tk_Fill3DPolygon --
1012  *
1013  *      Fill a polygonal area, supplying a 3D border if desired.
1014  *
1015  * Results:
1016  *      None.
1017  *
1018  * Side effects:
1019  *      Information gets drawn on the screen.
1020  *
1021  *----------------------------------------------------------------------
1022  */
1023
1024 void
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. */
1041 {
1042     register TkBorder *borderPtr = (TkBorder *) border;
1043
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);
1049     }
1050 }
1051 \f
1052 /*
1053  *--------------------------------------------------------------
1054  *
1055  * BorderInit --
1056  *
1057  *      Initialize the structures used for border management.
1058  *
1059  * Results:
1060  *      None.
1061  *
1062  * Side effects:
1063  *      Read the code.
1064  *
1065  *-------------------------------------------------------------
1066  */
1067
1068 static void
1069 BorderInit(dispPtr)
1070      TkDisplay * dispPtr;     /* Used to access thread-specific data. */
1071 {
1072     dispPtr->borderInit = 1;
1073     Tcl_InitHashTable(&dispPtr->borderTable, TCL_STRING_KEYS);
1074 }
1075 \f
1076 /*
1077  *--------------------------------------------------------------
1078  *
1079  * ShiftLine --
1080  *
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.
1084  *
1085  * Results:
1086  *      None.
1087  *
1088  * Side effects:
1089  *      None.
1090  *
1091  *--------------------------------------------------------------
1092  */
1093
1094 static void
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
1103                                  * line here. */
1104 {
1105     int dx, dy, dxNeg, dyNeg;
1106
1107     /*
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.
1118      */
1119
1120     static int shiftTable[129];
1121
1122     /*
1123      * Initialize the table if this is the first time it is
1124      * used.
1125      */
1126
1127     if (shiftTable[0] == 0) {
1128         int i;
1129         double tangent, cosine;
1130
1131         for (i = 0; i <= 128; i++) {
1132             tangent = i/128.0;
1133             cosine = 128/cos(atan(tangent)) + .5;
1134             shiftTable[i] = (int) cosine;
1135         }
1136     }
1137
1138     *p3Ptr = *p1Ptr;
1139     dx = p2Ptr->x - p1Ptr->x;
1140     dy = p2Ptr->y - p1Ptr->y;
1141     if (dy < 0) {
1142         dyNeg = 1;
1143         dy = -dy;
1144     } else {
1145         dyNeg = 0;
1146     }
1147     if (dx < 0) {
1148         dxNeg = 1;
1149         dx = -dx;
1150     } else {
1151         dxNeg = 0;
1152     }
1153     if (dy <= dx) {
1154         dy = ((distance * shiftTable[(dy<<7)/dx]) + 64) >> 7;
1155         if (!dxNeg) {
1156             dy = -dy;
1157         }
1158         p3Ptr->y += dy;
1159     } else {
1160         dx = ((distance * shiftTable[(dx<<7)/dy]) + 64) >> 7;
1161         if (dyNeg) {
1162             dx = -dx;
1163         }
1164         p3Ptr->x += dx;
1165     }
1166 }
1167 \f
1168 /*
1169  *--------------------------------------------------------------
1170  *
1171  * Intersect --
1172  *
1173  *      Find the intersection point between two lines.
1174  *
1175  * Results:
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.
1180  *
1181  * Side effects:
1182  *      None.
1183  *
1184  *--------------------------------------------------------------
1185  */
1186
1187 static int
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. */
1194 {
1195     int dxadyb, dxbdya, dxadxb, dyadyb, p, q;
1196
1197     /*
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.
1201      */
1202
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);
1207
1208     if (dxadyb == dxbdya) {
1209         return -1;
1210     }
1211     p = (a1Ptr->x*dxbdya - b1Ptr->x*dxadyb + (b1Ptr->y - a1Ptr->y)*dxadxb);
1212     q = dxbdya - dxadyb;
1213     if (q < 0) {
1214         p = -p;
1215         q = -q;
1216     }
1217     if (p < 0) {
1218         iPtr->x = - ((-p + q/2)/q);
1219     } else {
1220         iPtr->x = (p + q/2)/q;
1221     }
1222     p = (a1Ptr->y*dxadyb - b1Ptr->y*dxbdya + (b1Ptr->x - a1Ptr->x)*dyadyb);
1223     q = dxadyb - dxbdya;
1224     if (q < 0) {
1225         p = -p;
1226         q = -q;
1227     }
1228     if (p < 0) {
1229         iPtr->y = - ((-p + q/2)/q);
1230     } else {
1231         iPtr->y = (p + q/2)/q;
1232     }
1233     return 0;
1234 }
1235 \f
1236 /*
1237  *----------------------------------------------------------------------
1238  *
1239  * Tk_Get3DBorderFromObj --
1240  *
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.
1244  *
1245  * Results:
1246  *      Returns the Tk_3DBorder that matches the tkwin and the string rep
1247  *      of the name of the border given in objPtr.
1248  *
1249  * Side effects:
1250  *      If the object is not already a border, the conversion will free
1251  *      any old internal representation. 
1252  *
1253  *----------------------------------------------------------------------
1254  */
1255
1256 Tk_3DBorder
1257 Tk_Get3DBorderFromObj(tkwin, objPtr)
1258     Tk_Window tkwin;
1259     Tcl_Obj *objPtr;            /* The object whose string value selects
1260                                  * a border. */
1261 {
1262     TkBorder *borderPtr = NULL;
1263     Tcl_HashEntry *hashPtr;
1264     TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
1265
1266     if (objPtr->typePtr != &tkBorderObjType) {
1267         InitBorderObj(objPtr);
1268     }
1269
1270     /*
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...
1275      */
1276
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)) {
1282         /*
1283          * The object already points to the right border structure.
1284          * Just return it.
1285          */
1286         return (Tk_3DBorder) borderPtr;
1287     }
1288
1289     /*
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.
1299      */
1300
1301     hashPtr = Tcl_FindHashEntry(&dispPtr->borderTable, Tcl_GetString(objPtr));
1302     if (hashPtr == NULL) {
1303         goto error;
1304     }
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;
1313         }
1314     }
1315
1316     error:
1317     panic("Tk_Get3DBorderFromObj called with non-existent border!");
1318     /*
1319      * The following code isn't reached; it's just there to please compilers.
1320      */
1321     return NULL;
1322 }
1323 \f
1324 /*
1325  *----------------------------------------------------------------------
1326  *
1327  * InitBorderObj --
1328  *
1329  *      Attempt to generate a border internal form for the Tcl object
1330  *      "objPtr".
1331  *
1332  * Results:
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.
1336  *
1337  * Side effects:
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.
1340  *
1341  *----------------------------------------------------------------------
1342  */
1343
1344 static void
1345 InitBorderObj(objPtr)
1346     Tcl_Obj *objPtr;            /* The object to convert. */
1347 {
1348     Tcl_ObjType *typePtr;
1349
1350     /*
1351      * Free the old internalRep before setting the new one. 
1352      */
1353
1354     Tcl_GetString(objPtr);
1355     typePtr = objPtr->typePtr;
1356     if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) {
1357         (*typePtr->freeIntRepProc)(objPtr);
1358     }
1359     objPtr->typePtr = &tkBorderObjType;
1360     objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) NULL;
1361 }
1362 \f
1363 /*
1364  *----------------------------------------------------------------------
1365  *
1366  * TkDebugBorder --
1367  *
1368  *      This procedure returns debugging information about a border.
1369  *
1370  * Results:
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.
1375  *
1376  * Side effects:
1377  *      None.
1378  *
1379  *----------------------------------------------------------------------
1380  */
1381
1382 Tcl_Obj *
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. */
1387 {
1388     TkBorder *borderPtr;
1389     Tcl_HashEntry *hashPtr;
1390     Tcl_Obj *resultPtr, *objPtr;
1391     TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
1392
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");
1399         }
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);
1407         }
1408     }
1409     return resultPtr;
1410 }