home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tkisrc04.zip / tk / os2 / tk3d.c < prev    next >
C/C++ Source or Header  |  1998-08-07  |  39KB  |  1,329 lines

  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-1995 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.  * SCCS: @(#) tk3d.c 1.52 96/02/15 18:51:30
  14.  */
  15.  
  16. #include "tkPort.h"
  17. #include "tkInt.h"
  18.  
  19. /*
  20.  * One of the following data structures is allocated for
  21.  * each 3-D border currently in use.  Structures of this
  22.  * type are indexed by borderTable, so that a single
  23.  * structure can be shared for several uses.
  24.  */
  25.  
  26. typedef struct {
  27.     Screen *screen;        /* Screen on which the border will be used. */
  28.     Visual *visual;        /* Visual for all windows and pixmaps using
  29.                  * the border. */
  30.     int depth;            /* Number of bits per pixel of drawables where
  31.                  * the border will be used. */
  32.     Colormap colormap;        /* Colormap out of which pixels are
  33.                  * allocated. */
  34.     int refCount;        /* Number of different users of
  35.                  * this border.  */
  36.     XColor *bgColorPtr;        /* Background color (intensity
  37.                  * between lightColorPtr and
  38.                  * darkColorPtr). */
  39.     XColor *darkColorPtr;    /* Color for darker areas (must free when
  40.                  * deleting structure). NULL means shadows
  41.                  * haven't been allocated yet.*/
  42.     XColor *lightColorPtr;    /* Color used for lighter areas of border
  43.                  * (must free this when deleting structure).
  44.                  * NULL means shadows haven't been allocated
  45.                  * yet. */
  46.     Pixmap shadow;        /* Stipple pattern to use for drawing
  47.                  * shadows areas.  Used for displays with
  48.                  * <= 64 colors or where colormap has filled
  49.                  * up. */
  50.     GC bgGC;            /* Used (if necessary) to draw areas in
  51.                  * the background color. */
  52.     GC darkGC;            /* Used to draw darker parts of the
  53.                  * border. None means the shadow colors
  54.                  * haven't been allocated yet.*/
  55.     GC lightGC;            /* Used to draw lighter parts of
  56.                  * the border. None means the shadow colors
  57.                  * haven't been allocated yet. */
  58.     Tcl_HashEntry *hashPtr;    /* Entry in borderTable (needed in
  59.                  * order to delete structure). */
  60. } Border;
  61.  
  62. /*
  63.  * Hash table to map from a border's values (color, etc.) to a
  64.  * Border structure for those values.
  65.  */
  66.  
  67. static Tcl_HashTable borderTable;
  68. typedef struct {
  69.     Tk_Uid colorName;        /* Color for border. */
  70.     Colormap colormap;        /* Colormap used for allocating border
  71.                  * colors. */
  72.     Screen *screen;        /* Screen on which border will be drawn. */
  73. } BorderKey;
  74.  
  75. /*
  76.  * Maximum intensity for a color:
  77.  */
  78.  
  79. #define MAX_INTENSITY 65535
  80.  
  81.  
  82. static int initialized = 0;    /* 0 means static structures haven't
  83.                  * been initialized yet. */
  84.  
  85. /*
  86.  * Forward declarations for procedures defined in this file:
  87.  */
  88.  
  89. static void        BorderInit _ANSI_ARGS_((void));
  90. static void        GetShadows _ANSI_ARGS_((Border *borderPtr,
  91.                 Tk_Window tkwin));
  92. static int        Intersect _ANSI_ARGS_((XPoint *a1Ptr, XPoint *a2Ptr,
  93.                 XPoint *b1Ptr, XPoint *b2Ptr, XPoint *iPtr));
  94. static void        ShiftLine _ANSI_ARGS_((XPoint *p1Ptr, XPoint *p2Ptr,
  95.                 int distance, XPoint *p3Ptr));
  96.  
  97. /*
  98.  *--------------------------------------------------------------
  99.  *
  100.  * Tk_Get3DBorder --
  101.  *
  102.  *    Create a data structure for displaying a 3-D border.
  103.  *
  104.  * Results:
  105.  *    The return value is a token for a data structure
  106.  *    describing a 3-D border.  This token may be passed
  107.  *    to Tk_Draw3DRectangle and Tk_Free3DBorder.  If an
  108.  *    error prevented the border from being created then
  109.  *    NULL is returned and an error message will be left
  110.  *    in interp->result.
  111.  *
  112.  * Side effects:
  113.  *    Data structures, graphics contexts, etc. are allocated.
  114.  *    It is the caller's responsibility to eventually call
  115.  *    Tk_Free3DBorder to release the resources.
  116.  *
  117.  *--------------------------------------------------------------
  118.  */
  119.  
  120. Tk_3DBorder
  121. Tk_Get3DBorder(interp, tkwin, colorName)
  122.     Tcl_Interp *interp;        /* Place to store an error message. */
  123.     Tk_Window tkwin;        /* Token for window in which border will
  124.                  * be drawn. */
  125.     Tk_Uid colorName;        /* String giving name of color
  126.                  * for window background. */
  127. {
  128.     BorderKey key;
  129.     Tcl_HashEntry *hashPtr;
  130.     register Border *borderPtr;
  131.     int new;
  132.     XGCValues gcValues;
  133.  
  134.     if (!initialized) {
  135.     BorderInit();
  136.     }
  137.  
  138.     /*
  139.      * First, check to see if there's already a border that will work
  140.      * for this request.
  141.      */
  142.  
  143.     key.colorName = colorName;
  144.     key.colormap = Tk_Colormap(tkwin);
  145.     key.screen = Tk_Screen(tkwin);
  146.  
  147.     hashPtr = Tcl_CreateHashEntry(&borderTable, (char *) &key, &new);
  148.     if (!new) {
  149.     borderPtr = (Border *) Tcl_GetHashValue(hashPtr);
  150.     borderPtr->refCount++;
  151.     } else {
  152.  
  153.     /*
  154.      * No satisfactory border exists yet.  Initialize a new one.
  155.      */
  156.     
  157.     borderPtr = (Border *) ckalloc(sizeof(Border));
  158.     borderPtr->screen = Tk_Screen(tkwin);
  159.     borderPtr->visual = Tk_Visual(tkwin);
  160.     borderPtr->depth = Tk_Depth(tkwin);
  161.     borderPtr->colormap = key.colormap;
  162.     borderPtr->refCount = 1;
  163.     borderPtr->bgColorPtr = NULL;
  164.     borderPtr->darkColorPtr = NULL;
  165.     borderPtr->lightColorPtr = NULL;
  166.     borderPtr->shadow = None;
  167.     borderPtr->bgGC = None;
  168.     borderPtr->darkGC = None;
  169.     borderPtr->lightGC = None;
  170.     borderPtr->hashPtr = hashPtr;
  171.     Tcl_SetHashValue(hashPtr, borderPtr);
  172.     
  173.     /*
  174.      * Create the information for displaying the background color,
  175.      * but delay the allocation of shadows until they are actually
  176.      * needed for drawing.
  177.      */
  178.     
  179.     borderPtr->bgColorPtr = Tk_GetColor(interp, tkwin, colorName);
  180.     if (borderPtr->bgColorPtr == NULL) {
  181.         goto error;
  182.     }
  183.     gcValues.foreground = borderPtr->bgColorPtr->pixel;
  184.     borderPtr->bgGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
  185.     }
  186.     return (Tk_3DBorder) borderPtr;
  187.  
  188.     error:
  189.     Tk_Free3DBorder((Tk_3DBorder) borderPtr);
  190.     return NULL;
  191. }
  192.  
  193. /*
  194.  *--------------------------------------------------------------
  195.  *
  196.  * Tk_3DVerticalBevel --
  197.  *
  198.  *    This procedure draws a vertical bevel along one side of
  199.  *    an object.  The bevel is always rectangular in shape:
  200.  *            |||
  201.  *            |||
  202.  *            |||
  203.  *            |||
  204.  *            |||
  205.  *            |||
  206.  *    An appropriate shadow color is chosen for the bevel based
  207.  *    on the leftBevel and relief arguments.  Normally this
  208.  *    procedure is called first, then Tk_3DHorizontalBevel is
  209.  *    called next to draw neat corners.
  210.  *
  211.  * Results:
  212.  *    None.
  213.  *
  214.  * Side effects:
  215.  *    Graphics are drawn in drawable.
  216.  *
  217.  *--------------------------------------------------------------
  218.  */
  219.  
  220. void
  221. Tk_3DVerticalBevel(tkwin, drawable, border, x, y, width, height,
  222.     leftBevel, relief)
  223.     Tk_Window tkwin;        /* Window for which border was allocated. */
  224.     Drawable drawable;        /* X window or pixmap in which to draw. */
  225.     Tk_3DBorder border;        /* Token for border to draw. */
  226.     int x, y, width, height;    /* Area of vertical bevel. */
  227.     int leftBevel;        /* Non-zero means this bevel forms the
  228.                  * left side of the object;  0 means it
  229.                  * forms the right side. */
  230.     int relief;            /* Kind of bevel to draw.  For example,
  231.                  * TK_RELIEF_RAISED means interior of
  232.                  * object should appear higher than
  233.                  * exterior. */
  234. {
  235.     Border *borderPtr = (Border *) border;
  236.     GC left, right;
  237.     Display *display = Tk_Display(tkwin);
  238.  
  239.     if ((borderPtr->lightGC == None) && (relief != TK_RELIEF_FLAT)) {
  240.     GetShadows(borderPtr, tkwin);
  241.     }
  242.     if (relief == TK_RELIEF_RAISED) {
  243.     XFillRectangle(display, drawable, 
  244.         (leftBevel) ? borderPtr->lightGC : borderPtr->darkGC,
  245.         x, y, (unsigned) width, (unsigned) height);
  246.     } else if (relief == TK_RELIEF_SUNKEN) {
  247.     XFillRectangle(display, drawable, 
  248.         (leftBevel) ? borderPtr->darkGC : borderPtr->lightGC,
  249.         x, y, (unsigned) width, (unsigned) height);
  250.     } else if (relief == TK_RELIEF_RIDGE) {
  251.     int half;
  252.  
  253.     left = borderPtr->lightGC;
  254.     right = borderPtr->darkGC;
  255.     ridgeGroove:
  256.     half = width/2;
  257.     if (!leftBevel && (width & 1)) {
  258.         half++;
  259.     }
  260.     XFillRectangle(display, drawable, left, x, y, (unsigned) half,
  261.         (unsigned) height);
  262.     XFillRectangle(display, drawable, right, x+half, y,
  263.         (unsigned) (width-half), (unsigned) height);
  264.     } else if (relief == TK_RELIEF_GROOVE) {
  265.     left = borderPtr->darkGC;
  266.     right = borderPtr->lightGC;
  267.     goto ridgeGroove;
  268.     } else if (relief == TK_RELIEF_FLAT) {
  269.     XFillRectangle(display, drawable, borderPtr->bgGC, x, y,
  270.         (unsigned) width, (unsigned) height);
  271.     }
  272. }
  273.  
  274. /*
  275.  *--------------------------------------------------------------
  276.  *
  277.  * Tk_3DHorizontalBevel --
  278.  *
  279.  *    This procedure draws a horizontal bevel along one side of
  280.  *    an object.  The bevel has mitered corners (depending on
  281.  *    leftIn and rightIn arguments).
  282.  *
  283.  * Results:
  284.  *    None.
  285.  *
  286.  * Side effects:
  287.  *    None.
  288.  *
  289.  *--------------------------------------------------------------
  290.  */
  291.  
  292. void
  293. Tk_3DHorizontalBevel(tkwin, drawable, border, x, y, width, height,
  294.     leftIn, rightIn, topBevel, relief)
  295.     Tk_Window tkwin;        /* Window for which border was allocated. */
  296.     Drawable drawable;        /* X window or pixmap in which to draw. */
  297.     Tk_3DBorder border;        /* Token for border to draw. */
  298.     int x, y, width, height;    /* Bounding box of area of bevel.  Height
  299.                  * gives width of border. */
  300.     int leftIn, rightIn;    /* Describes whether the left and right
  301.                  * edges of the bevel angle in or out as
  302.                  * they go down.  For example, if "leftIn"
  303.                  * is true, the left side of the bevel
  304.                  * looks like this:
  305.                  *    ___________
  306.                  *     __________
  307.                  *      _________
  308.                  *       ________
  309.                  */
  310.     int topBevel;        /* Non-zero means this bevel forms the
  311.                  * top side of the object;  0 means it
  312.                  * forms the bottom side. */
  313.     int relief;            /* Kind of bevel to draw.  For example,
  314.                  * TK_RELIEF_RAISED means interior of
  315.                  * object should appear higher than
  316.                  * exterior. */
  317. {
  318.     Border *borderPtr = (Border *) border;
  319.     Display *display = Tk_Display(tkwin);
  320.     int bottom, halfway, x1, x2, x1Delta, x2Delta;
  321.     GC topGC = None, bottomGC = None;
  322.                 /* Initializations needed only to prevent
  323.                  * compiler warnings. */
  324.  
  325.     if ((borderPtr->lightGC == None) && (relief != TK_RELIEF_FLAT)) {
  326.     GetShadows(borderPtr, tkwin);
  327.     }
  328.  
  329.     /*
  330.      * Compute a GC for the top half of the bevel and a GC for the
  331.      * bottom half (they're the same in many cases).
  332.      */
  333.  
  334.     switch (relief) {
  335.     case TK_RELIEF_RAISED:
  336.         topGC = bottomGC =
  337.             (topBevel) ? borderPtr->lightGC : borderPtr->darkGC;
  338.         break;
  339.     case TK_RELIEF_SUNKEN:
  340.         topGC = bottomGC =
  341.             (topBevel) ? borderPtr->darkGC : borderPtr->lightGC;
  342.         break;
  343.     case TK_RELIEF_RIDGE:
  344.         topGC = borderPtr->lightGC;
  345.         bottomGC = borderPtr->darkGC;
  346.         break;
  347.     case TK_RELIEF_GROOVE:
  348.         topGC = borderPtr->darkGC;
  349.         bottomGC = borderPtr->lightGC;
  350.         break;
  351.     case TK_RELIEF_FLAT:
  352.         topGC = bottomGC = borderPtr->bgGC;
  353.         break;
  354.     }
  355.  
  356.     /*
  357.      * Compute various other geometry-related stuff.
  358.      */
  359.  
  360.     x1 = x;
  361.     if (!leftIn) {
  362.     x1 += height;
  363.     }
  364.     x2 = x+width;
  365.     if (!rightIn) {
  366.     x2 -= height;
  367.     }
  368.     x1Delta = (leftIn) ? 1 : -1;
  369.     x2Delta = (rightIn) ? -1 : 1;
  370.     halfway = y + height/2;
  371.     if (!topBevel && (height & 1)) {
  372.     halfway++;
  373.     }
  374.     bottom = y + height;
  375.  
  376.     /*
  377.      * Draw one line for each y-coordinate covered by the bevel.
  378.      */
  379.  
  380.     for ( ; y < bottom; y++) {
  381.     /*
  382.      * In some weird cases (such as large border widths for skinny
  383.      * rectangles) x1 can be >= x2.  Don't draw the lines
  384.      * in these cases.
  385.      */
  386.  
  387.     if (x1 < x2) {
  388.         XFillRectangle(display, drawable,
  389.         (y < halfway) ? topGC : bottomGC, x1, y,
  390.         (unsigned) (x2-x1), (unsigned) 1);
  391.     }
  392.     x1 += x1Delta;
  393.     x2 += x2Delta;
  394.     }
  395. }
  396.  
  397. /*
  398.  *--------------------------------------------------------------
  399.  *
  400.  * Tk_Draw3DRectangle --
  401.  *
  402.  *    Draw a 3-D border at a given place in a given window.
  403.  *
  404.  * Results:
  405.  *    None.
  406.  *
  407.  * Side effects:
  408.  *    A 3-D border will be drawn in the indicated drawable.
  409.  *    The outside edges of the border will be determined by x,
  410.  *    y, width, and height.  The inside edges of the border
  411.  *    will be determined by the borderWidth argument.
  412.  *
  413.  *--------------------------------------------------------------
  414.  */
  415.  
  416. void
  417. Tk_Draw3DRectangle(tkwin, drawable, border, x, y, width, height,
  418.     borderWidth, relief)
  419.     Tk_Window tkwin;        /* Window for which border was allocated. */
  420.     Drawable drawable;        /* X window or pixmap in which to draw. */
  421.     Tk_3DBorder border;        /* Token for border to draw. */
  422.     int x, y, width, height;    /* Outside area of region in
  423.                  * which border will be drawn. */
  424.     int borderWidth;        /* Desired width for border, in
  425.                  * pixels. */
  426.     int relief;            /* Type of relief: TK_RELIEF_RAISED,
  427.                  * TK_RELIEF_SUNKEN, TK_RELIEF_GROOVE, etc. */
  428. {
  429.     if (width < 2*borderWidth) {
  430.     borderWidth = width/2;
  431.     }
  432.     if (height < 2*borderWidth) {
  433.     borderWidth = height/2;
  434.     }
  435.     Tk_3DVerticalBevel(tkwin, drawable, border, x, y, borderWidth, height,
  436.         1, relief);
  437.     Tk_3DVerticalBevel(tkwin, drawable, border, x+width-borderWidth, y,
  438.         borderWidth, height, 0, relief);
  439.     Tk_3DHorizontalBevel(tkwin, drawable, border, x, y, width, borderWidth,
  440.         1, 1, 1, relief);
  441.     Tk_3DHorizontalBevel(tkwin, drawable, border, x, y+height-borderWidth,
  442.         width, borderWidth, 0, 0, 0, relief);
  443. }
  444.  
  445. /*
  446.  *--------------------------------------------------------------
  447.  *
  448.  * Tk_NameOf3DBorder --
  449.  *
  450.  *    Given a border, return a textual string identifying the
  451.  *    border's color.
  452.  *
  453.  * Results:
  454.  *    The return value is the string that was used to create
  455.  *    the border.
  456.  *
  457.  * Side effects:
  458.  *    None.
  459.  *
  460.  *--------------------------------------------------------------
  461.  */
  462.  
  463. char *
  464. Tk_NameOf3DBorder(border)
  465.     Tk_3DBorder border;        /* Token for border. */
  466. {
  467.     Border *borderPtr = (Border *) border;
  468.  
  469.     return ((BorderKey *) borderPtr->hashPtr->key.words)->colorName;
  470. }
  471.  
  472. /*
  473.  *--------------------------------------------------------------------
  474.  *
  475.  * Tk_3DBorderColor --
  476.  *
  477.  *    Given a 3D border, return the X color used for the "flat"
  478.  *    surfaces.
  479.  *
  480.  * Results:
  481.  *    Returns the color used drawing flat surfaces with the border.
  482.  *
  483.  * Side effects:
  484.  *    None.
  485.  *
  486.  *--------------------------------------------------------------------
  487.  */
  488. XColor *
  489. Tk_3DBorderColor(border)
  490.     Tk_3DBorder border;        /* Border whose color is wanted. */
  491. {
  492.     return(((Border *) border)->bgColorPtr);
  493. }
  494.  
  495. /*
  496.  *--------------------------------------------------------------------
  497.  *
  498.  * Tk_3DBorderGC --
  499.  *
  500.  *    Given a 3D border, return the X color used for the "flat"
  501.  *    surfaces.
  502.  *
  503.  * Results:
  504.  *    Returns the color used drawing flat surfaces with the border.
  505.  *
  506.  * Side effects:
  507.  *    None.
  508.  *
  509.  *--------------------------------------------------------------------
  510.  */
  511. GC
  512. Tk_3DBorderGC(tkwin, border, which)
  513.     Tk_Window tkwin;        /* Window for which border was allocated. */
  514.     Tk_3DBorder border;        /* Border whose GC is wanted. */
  515.     int which;            /* Selects one of the border's 3 GC's:
  516.                  * TK_3D_FLAT_GC, TK_3D_LIGHT_GC, or
  517.                  * TK_3D_DARK_GC. */
  518. {
  519.     Border * borderPtr = (Border *) border;
  520.  
  521.     if ((borderPtr->lightGC == None) && (which != TK_3D_FLAT_GC)) {
  522.     GetShadows(borderPtr, tkwin);
  523.     }
  524.     if (which == TK_3D_FLAT_GC) {
  525.     return borderPtr->bgGC;
  526.     } else if (which == TK_3D_LIGHT_GC) {
  527.     return borderPtr->lightGC;
  528.     } else if (which == TK_3D_DARK_GC){
  529.     return borderPtr->darkGC;
  530.     }
  531.     panic("bogus \"which\" value in Tk_3DBorderGC");
  532.  
  533.     /*
  534.      * The code below will never be executed, but it's needed to
  535.      * keep compilers happy.
  536.      */
  537.  
  538.     return (GC) None;
  539. }
  540.  
  541. /*
  542.  *--------------------------------------------------------------
  543.  *
  544.  * Tk_Free3DBorder --
  545.  *
  546.  *    This procedure is called when a 3D border is no longer
  547.  *    needed.  It frees the resources associated with the
  548.  *    border.  After this call, the caller should never again
  549.  *    use the "border" token.
  550.  *
  551.  * Results:
  552.  *    None.
  553.  *
  554.  * Side effects:
  555.  *    Resources are freed.
  556.  *
  557.  *--------------------------------------------------------------
  558.  */
  559.  
  560. void
  561. Tk_Free3DBorder(border)
  562.     Tk_3DBorder border;        /* Token for border to be released. */
  563. {
  564.     register Border *borderPtr = (Border *) border;
  565.     Display *display = DisplayOfScreen(borderPtr->screen);
  566.  
  567.     borderPtr->refCount--;
  568.     if (borderPtr->refCount == 0) {
  569.     if (borderPtr->bgColorPtr != NULL) {
  570.         Tk_FreeColor(borderPtr->bgColorPtr);
  571.     }
  572.     if (borderPtr->darkColorPtr != NULL) {
  573.         Tk_FreeColor(borderPtr->darkColorPtr);
  574.     }
  575.     if (borderPtr->lightColorPtr != NULL) {
  576.         Tk_FreeColor(borderPtr->lightColorPtr);
  577.     }
  578.     if (borderPtr->shadow != None) {
  579.         Tk_FreeBitmap(display, borderPtr->shadow);
  580.     }
  581.     if (borderPtr->bgGC != None) {
  582.         Tk_FreeGC(display, borderPtr->bgGC);
  583.     }
  584.     if (borderPtr->darkGC != None) {
  585.         Tk_FreeGC(display, borderPtr->darkGC);
  586.     }
  587.     if (borderPtr->lightGC != None) {
  588.         Tk_FreeGC(display, borderPtr->lightGC);
  589.     }
  590.     Tcl_DeleteHashEntry(borderPtr->hashPtr);
  591.     ckfree((char *) borderPtr);
  592.     }
  593. }
  594.  
  595. /*
  596.  *----------------------------------------------------------------------
  597.  *
  598.  * Tk_SetBackgroundFromBorder --
  599.  *
  600.  *    Change the background of a window to one appropriate for a given
  601.  *    3-D border.
  602.  *
  603.  * Results:
  604.  *    None.
  605.  *
  606.  * Side effects:
  607.  *    Tkwin's background gets modified.
  608.  *
  609.  *----------------------------------------------------------------------
  610.  */
  611.  
  612. void
  613. Tk_SetBackgroundFromBorder(tkwin, border)
  614.     Tk_Window tkwin;        /* Window whose background is to be set. */
  615.     Tk_3DBorder border;        /* Token for border. */
  616. {
  617.     register Border *borderPtr = (Border *) border;
  618.  
  619.     Tk_SetWindowBackground(tkwin, borderPtr->bgColorPtr->pixel);
  620. }
  621.  
  622. /*
  623.  *----------------------------------------------------------------------
  624.  *
  625.  * Tk_GetRelief --
  626.  *
  627.  *    Parse a relief description and return the corresponding
  628.  *    relief value, or an error.
  629.  *
  630.  * Results:
  631.  *    A standard Tcl return value.  If all goes well then
  632.  *    *reliefPtr is filled in with one of the values
  633.  *    TK_RELIEF_RAISED, TK_RELIEF_FLAT, or TK_RELIEF_SUNKEN.
  634.  *
  635.  * Side effects:
  636.  *    None.
  637.  *
  638.  *----------------------------------------------------------------------
  639.  */
  640.  
  641. int
  642. Tk_GetRelief(interp, name, reliefPtr)
  643.     Tcl_Interp *interp;        /* For error messages. */
  644.     char *name;            /* Name of a relief type. */
  645.     int *reliefPtr;        /* Where to store converted relief. */
  646. {
  647.     char c;
  648.     size_t length;
  649.  
  650.     c = name[0];
  651.     length = strlen(name);
  652.     if ((c == 'f') && (strncmp(name, "flat", length) == 0)) {
  653.     *reliefPtr = TK_RELIEF_FLAT;
  654.     } else if ((c == 'g') && (strncmp(name, "groove", length) == 0)
  655.         && (length >= 2)) {
  656.         *reliefPtr = TK_RELIEF_GROOVE;
  657.     } else if ((c == 'r') && (strncmp(name, "raised", length) == 0)
  658.         && (length >= 2)) {
  659.     *reliefPtr = TK_RELIEF_RAISED;
  660.     } else if ((c == 'r') && (strncmp(name, "ridge", length) == 0)) {
  661.         *reliefPtr = TK_RELIEF_RIDGE;
  662.     } else if ((c == 's') && (strncmp(name, "sunken", length) == 0)) {
  663.     *reliefPtr = TK_RELIEF_SUNKEN;
  664.     } else {
  665.     sprintf(interp->result, "bad relief type \"%.50s\": must be %s",
  666.         name, "flat, groove, raised, ridge, or sunken");
  667.     return TCL_ERROR;
  668.     }
  669.     return TCL_OK;
  670. }
  671.  
  672. /*
  673.  *--------------------------------------------------------------
  674.  *
  675.  * Tk_NameOfRelief --
  676.  *
  677.  *    Given a relief value, produce a string describing that
  678.  *    relief value.
  679.  *
  680.  * Results:
  681.  *    The return value is a static string that is equivalent
  682.  *    to relief.
  683.  *
  684.  * Side effects:
  685.  *    None.
  686.  *
  687.  *--------------------------------------------------------------
  688.  */
  689.  
  690. char *
  691. Tk_NameOfRelief(relief)
  692.     int relief;        /* One of TK_RELIEF_FLAT, TK_RELIEF_RAISED,
  693.              * or TK_RELIEF_SUNKEN. */
  694. {
  695.     if (relief == TK_RELIEF_FLAT) {
  696.     return "flat";
  697.     } else if (relief == TK_RELIEF_SUNKEN) {
  698.     return "sunken";
  699.     } else if (relief == TK_RELIEF_RAISED) {
  700.     return "raised";
  701.     } else if (relief == TK_RELIEF_GROOVE) {
  702.     return "groove";
  703.     } else if (relief == TK_RELIEF_RIDGE) {
  704.     return "ridge";
  705.     } else {
  706.     return "unknown relief";
  707.     }
  708. }
  709.  
  710. /*
  711.  *--------------------------------------------------------------
  712.  *
  713.  * Tk_Draw3DPolygon --
  714.  *
  715.  *    Draw a border with 3-D appearance around the edge of a
  716.  *    given polygon.
  717.  *
  718.  * Results:
  719.  *    None.
  720.  *
  721.  * Side effects:
  722.  *    Information is drawn in "drawable" in the form of a
  723.  *    3-D border borderWidth units width wide on the left
  724.  *    of the trajectory given by pointPtr and numPoints (or
  725.  *    -borderWidth units wide on the right side, if borderWidth
  726.  *    is negative).
  727.  *
  728.  *--------------------------------------------------------------
  729.  */
  730.  
  731. void
  732. Tk_Draw3DPolygon(tkwin, drawable, border, pointPtr, numPoints,
  733.     borderWidth, leftRelief)
  734.     Tk_Window tkwin;        /* Window for which border was allocated. */
  735.     Drawable drawable;        /* X window or pixmap in which to draw. */
  736.     Tk_3DBorder border;        /* Token for border to draw. */
  737.     XPoint *pointPtr;        /* Array of points describing
  738.                  * polygon.  All points must be
  739.                  * absolute (CoordModeOrigin). */
  740.     int numPoints;        /* Number of points at *pointPtr. */
  741.     int borderWidth;        /* Width of border, measured in
  742.                  * pixels to the left of the polygon's
  743.                  * trajectory.   May be negative. */
  744.     int leftRelief;        /* TK_RELIEF_RAISED or
  745.                  * TK_RELIEF_SUNKEN: indicates how
  746.                  * stuff to left of trajectory looks
  747.                  * relative to stuff on right. */
  748. {
  749.     XPoint poly[4], b1, b2, newB1, newB2;
  750.     XPoint perp, c, shift1, shift2;    /* Used for handling parallel lines. */
  751.     register XPoint *p1Ptr, *p2Ptr;
  752.     Border *borderPtr = (Border *) border;
  753.     GC gc;
  754.     int i, lightOnLeft, dx, dy, parallel, pointsSeen;
  755.     Display *display = Tk_Display(tkwin);
  756.  
  757.     if (borderPtr->lightGC == None) {
  758.     GetShadows(borderPtr, tkwin);
  759.     }
  760.  
  761.     /*
  762.      * Handle grooves and ridges with recursive calls.
  763.      */
  764.  
  765.     if ((leftRelief == TK_RELIEF_GROOVE) || (leftRelief == TK_RELIEF_RIDGE)) {
  766.     int halfWidth;
  767.  
  768.     halfWidth = borderWidth/2;
  769.     Tk_Draw3DPolygon(tkwin, drawable, border, pointPtr, numPoints,
  770.         halfWidth, (leftRelief == TK_RELIEF_GROOVE) ? TK_RELIEF_RAISED
  771.         : TK_RELIEF_SUNKEN);
  772.     Tk_Draw3DPolygon(tkwin, drawable, border, pointPtr, numPoints,
  773.         -halfWidth, (leftRelief == TK_RELIEF_GROOVE) ? TK_RELIEF_SUNKEN
  774.         : TK_RELIEF_RAISED);
  775.     return;
  776.     }
  777.  
  778.     /*
  779.      * If the polygon is already closed, drop the last point from it
  780.      * (we'll close it automatically).
  781.      */
  782.  
  783.     p1Ptr = &pointPtr[numPoints-1];
  784.     p2Ptr = &pointPtr[0];
  785.     if ((p1Ptr->x == p2Ptr->x) && (p1Ptr->y == p2Ptr->y)) {
  786.     numPoints--;
  787.     }
  788.  
  789.     /*
  790.      * The loop below is executed once for each vertex in the polgon.
  791.      * At the beginning of each iteration things look like this:
  792.      *
  793.      *          poly[1]       /
  794.      *             *        /
  795.      *             |      /
  796.      *             b1   * poly[0] (pointPtr[i-1])
  797.      *             |    |
  798.      *             |    |
  799.      *             |    |
  800.      *             |    |
  801.      *             |    |
  802.      *             |    | *p1Ptr            *p2Ptr
  803.      *             b2   *--------------------*
  804.      *             |
  805.      *             |
  806.      *             x-------------------------
  807.      *
  808.      * The job of this iteration is to do the following:
  809.      * (a) Compute x (the border corner corresponding to
  810.      *     pointPtr[i]) and put it in poly[2].  As part of
  811.      *       this, compute a new b1 and b2 value for the next
  812.      *       side of the polygon.
  813.      * (b) Put pointPtr[i] into poly[3].
  814.      * (c) Draw the polygon given by poly[0..3].
  815.      * (d) Advance poly[0], poly[1], b1, and b2 for the
  816.      *     next side of the polygon.
  817.      */
  818.  
  819.     /*
  820.      * The above situation doesn't first come into existence until
  821.      * two points have been processed;  the first two points are
  822.      * used to "prime the pump", so some parts of the processing
  823.      * are ommitted for these points.  The variable "pointsSeen"
  824.      * keeps track of the priming process;  it has to be separate
  825.      * from i in order to be able to ignore duplicate points in the
  826.      * polygon.
  827.      */
  828.  
  829.     pointsSeen = 0;
  830.     for (i = -2, p1Ptr = &pointPtr[numPoints-2], p2Ptr = p1Ptr+1;
  831.         i < numPoints; i++, p1Ptr = p2Ptr, p2Ptr++) {
  832.     if ((i == -1) || (i == numPoints-1)) {
  833.         p2Ptr = pointPtr;
  834.     }
  835.     if ((p2Ptr->x == p1Ptr->x) && (p2Ptr->y == p1Ptr->y)) {
  836.         /*
  837.          * Ignore duplicate points (they'd cause core dumps in
  838.          * ShiftLine calls below).
  839.          */
  840.         continue;
  841.     }
  842.     ShiftLine(p1Ptr, p2Ptr, borderWidth, &newB1);
  843.     newB2.x = newB1.x + (p2Ptr->x - p1Ptr->x);
  844.     newB2.y = newB1.y + (p2Ptr->y - p1Ptr->y);
  845.     poly[3] = *p1Ptr;
  846.     parallel = 0;
  847.     if (pointsSeen >= 1) {
  848.         parallel = Intersect(&newB1, &newB2, &b1, &b2, &poly[2]);
  849.  
  850.         /*
  851.          * If two consecutive segments of the polygon are parallel,
  852.          * then things get more complex.  Consider the following
  853.          * diagram:
  854.          *
  855.          * poly[1]
  856.          *    *----b1-----------b2------a
  857.          *                                \
  858.          *                                  \
  859.          *         *---------*----------*    b
  860.          *        poly[0]  *p2Ptr   *p1Ptr  /
  861.          *                                /
  862.          *              --*--------*----c
  863.          *              newB1    newB2
  864.          *
  865.          * Instead of using x and *p1Ptr for poly[2] and poly[3], as
  866.          * in the original diagram, use a and b as above.  Then instead
  867.          * of using x and *p1Ptr for the new poly[0] and poly[1], use
  868.          * b and c as above.
  869.          *
  870.          * Do the computation in three stages:
  871.          * 1. Compute a point "perp" such that the line p1Ptr-perp
  872.          *    is perpendicular to p1Ptr-p2Ptr.
  873.          * 2. Compute the points a and c by intersecting the lines
  874.          *    b1-b2 and newB1-newB2 with p1Ptr-perp.
  875.          * 3. Compute b by shifting p1Ptr-perp to the right and
  876.          *    intersecting it with p1Ptr-p2Ptr.
  877.          */
  878.  
  879.         if (parallel) {
  880.         perp.x = p1Ptr->x + (p2Ptr->y - p1Ptr->y);
  881.         perp.y = p1Ptr->y - (p2Ptr->x - p1Ptr->x);
  882.         (void) Intersect(p1Ptr, &perp, &b1, &b2, &poly[2]);
  883.         (void) Intersect(p1Ptr, &perp, &newB1, &newB2, &c);
  884.         ShiftLine(p1Ptr, &perp, borderWidth, &shift1);
  885.         shift2.x = shift1.x + (perp.x - p1Ptr->x);
  886.         shift2.y = shift1.y + (perp.y - p1Ptr->y);
  887.         (void) Intersect(p1Ptr, p2Ptr, &shift1, &shift2, &poly[3]);
  888.         }
  889.     }
  890.     if (pointsSeen >= 2) {
  891.         dx = poly[3].x - poly[0].x;
  892.         dy = poly[3].y - poly[0].y;
  893.         if (dx > 0) {
  894.         lightOnLeft = (dy <= dx);
  895.         } else {
  896.         lightOnLeft = (dy < dx);
  897.         }
  898.         if (lightOnLeft ^ (leftRelief == TK_RELIEF_RAISED)) {
  899.         gc = borderPtr->lightGC;
  900.         } else {
  901.         gc = borderPtr->darkGC;
  902.         }
  903.         XFillPolygon(display, drawable, gc, poly, 4, Convex,
  904.             CoordModeOrigin);
  905.     }
  906.     b1.x = newB1.x;
  907.     b1.y = newB1.y;
  908.     b2.x = newB2.x;
  909.     b2.y = newB2.y;
  910.     poly[0].x = poly[3].x;
  911.     poly[0].y = poly[3].y;
  912.     if (parallel) {
  913.         poly[1].x = c.x;
  914.         poly[1].y = c.y;
  915.     } else if (pointsSeen >= 1) {
  916.         poly[1].x = poly[2].x;
  917.         poly[1].y = poly[2].y;
  918.     }
  919.     pointsSeen++;
  920.     }
  921. }
  922.  
  923. /*
  924.  *----------------------------------------------------------------------
  925.  *
  926.  * Tk_Fill3DRectangle --
  927.  *
  928.  *    Fill a rectangular area, supplying a 3D border if desired.
  929.  *
  930.  * Results:
  931.  *    None.
  932.  *
  933.  * Side effects:
  934.  *    Information gets drawn on the screen.
  935.  *
  936.  *----------------------------------------------------------------------
  937.  */
  938.  
  939. void
  940. Tk_Fill3DRectangle(tkwin, drawable, border, x, y, width,
  941.     height, borderWidth, relief)
  942.     Tk_Window tkwin;        /* Window for which border was allocated. */
  943.     Drawable drawable;        /* X window or pixmap in which to draw. */
  944.     Tk_3DBorder border;        /* Token for border to draw. */
  945.     int x, y, width, height;    /* Outside area of rectangular region. */
  946.     int borderWidth;        /* Desired width for border, in
  947.                  * pixels. Border will be *inside* region. */
  948.     int relief;            /* Indicates 3D effect: TK_RELIEF_FLAT,
  949.                  * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
  950. {
  951.     register Border *borderPtr = (Border *) border;
  952.  
  953.     XFillRectangle(Tk_Display(tkwin), drawable, borderPtr->bgGC,
  954.         x, y, (unsigned int) width, (unsigned int) height);
  955.     if (relief != TK_RELIEF_FLAT) {
  956.     Tk_Draw3DRectangle(tkwin, drawable, border, x, y, width,
  957.         height, borderWidth, relief);
  958.     }
  959. }
  960.  
  961. /*
  962.  *----------------------------------------------------------------------
  963.  *
  964.  * Tk_Fill3DPolygon --
  965.  *
  966.  *    Fill a polygonal area, supplying a 3D border if desired.
  967.  *
  968.  * Results:
  969.  *    None.
  970.  *
  971.  * Side effects:
  972.  *    Information gets drawn on the screen.
  973.  *
  974.  *----------------------------------------------------------------------
  975.  */
  976.  
  977. void
  978. Tk_Fill3DPolygon(tkwin, drawable, border, pointPtr, numPoints,
  979.     borderWidth, leftRelief)
  980.     Tk_Window tkwin;        /* Window for which border was allocated. */
  981.     Drawable drawable;        /* X window or pixmap in which to draw. */
  982.     Tk_3DBorder border;        /* Token for border to draw. */
  983.     XPoint *pointPtr;        /* Array of points describing
  984.                  * polygon.  All points must be
  985.                  * absolute (CoordModeOrigin). */
  986.     int numPoints;        /* Number of points at *pointPtr. */
  987.     int borderWidth;        /* Width of border, measured in
  988.                  * pixels to the left of the polygon's
  989.                  * trajectory.   May be negative. */
  990.     int leftRelief;            /* Indicates 3D effect of left side of
  991.                  * trajectory relative to right:
  992.                  * TK_RELIEF_FLAT, TK_RELIEF_RAISED,
  993.                  * or TK_RELIEF_SUNKEN. */
  994. {
  995.     register Border *borderPtr = (Border *) border;
  996.  
  997.     XFillPolygon(Tk_Display(tkwin), drawable, borderPtr->bgGC,
  998.         pointPtr, numPoints, Complex, CoordModeOrigin);
  999.     if (leftRelief != TK_RELIEF_FLAT) {
  1000.     Tk_Draw3DPolygon(tkwin, drawable, border, pointPtr, numPoints,
  1001.         borderWidth, leftRelief);
  1002.     }
  1003. }
  1004.  
  1005. /*
  1006.  *--------------------------------------------------------------
  1007.  *
  1008.  * BorderInit --
  1009.  *
  1010.  *    Initialize the structures used for border management.
  1011.  *
  1012.  * Results:
  1013.  *    None.
  1014.  *
  1015.  * Side effects:
  1016.  *    Read the code.
  1017.  *
  1018.  *-------------------------------------------------------------
  1019.  */
  1020.  
  1021. static void
  1022. BorderInit()
  1023. {
  1024.     initialized = 1;
  1025.     Tcl_InitHashTable(&borderTable, sizeof(BorderKey)/sizeof(int));
  1026. }
  1027.  
  1028. /*
  1029.  *--------------------------------------------------------------
  1030.  *
  1031.  * ShiftLine --
  1032.  *
  1033.  *    Given two points on a line, compute a point on a
  1034.  *    new line that is parallel to the given line and
  1035.  *    a given distance away from it.
  1036.  *
  1037.  * Results:
  1038.  *    None.
  1039.  *
  1040.  * Side effects:
  1041.  *    None.
  1042.  *
  1043.  *--------------------------------------------------------------
  1044.  */
  1045.  
  1046. static void
  1047. ShiftLine(p1Ptr, p2Ptr, distance, p3Ptr)
  1048.     XPoint *p1Ptr;        /* First point on line. */
  1049.     XPoint *p2Ptr;        /* Second point on line. */
  1050.     int distance;        /* New line is to be this many
  1051.                  * units to the left of original
  1052.                  * line, when looking from p1 to
  1053.                  * p2.  May be negative. */
  1054.     XPoint *p3Ptr;        /* Store coords of point on new
  1055.                  * line here. */
  1056. {
  1057.     int dx, dy, dxNeg, dyNeg;
  1058.  
  1059.     /*
  1060.      * The table below is used for a quick approximation in
  1061.      * computing the new point.  An index into the table
  1062.      * is 128 times the slope of the original line (the slope
  1063.      * must always be between 0 and 1).  The value of the table
  1064.      * entry is 128 times the amount to displace the new line
  1065.      * in y for each unit of perpendicular distance.  In other
  1066.      * words, the table maps from the tangent of an angle to
  1067.      * the inverse of its cosine.  If the slope of the original
  1068.      * line is greater than 1, then the displacement is done in
  1069.      * x rather than in y.
  1070.      */
  1071.  
  1072.     static int shiftTable[129];
  1073.  
  1074.     /*
  1075.      * Initialize the table if this is the first time it is
  1076.      * used.
  1077.      */
  1078.  
  1079.     if (shiftTable[0] == 0) {
  1080.     int i;
  1081.     double tangent, cosine;
  1082.  
  1083.     for (i = 0; i <= 128; i++) {
  1084.         tangent = i/128.0;
  1085.         cosine = 128/cos(atan(tangent)) + .5;
  1086.         shiftTable[i] = cosine;
  1087.     }
  1088.     }
  1089.  
  1090.     *p3Ptr = *p1Ptr;
  1091.     dx = p2Ptr->x - p1Ptr->x;
  1092.     dy = p2Ptr->y - p1Ptr->y;
  1093.     if (dy < 0) {
  1094.     dyNeg = 1;
  1095.     dy = -dy;
  1096.     } else {
  1097.     dyNeg = 0;
  1098.     }
  1099.     if (dx < 0) {
  1100.     dxNeg = 1;
  1101.     dx = -dx;
  1102.     } else {
  1103.     dxNeg = 0;
  1104.     }
  1105.     if (dy <= dx) {
  1106.     dy = ((distance * shiftTable[(dy<<7)/dx]) + 64) >> 7;
  1107.     if (!dxNeg) {
  1108.         dy = -dy;
  1109.     }
  1110.     p3Ptr->y += dy;
  1111.     } else {
  1112.     dx = ((distance * shiftTable[(dx<<7)/dy]) + 64) >> 7;
  1113.     if (dyNeg) {
  1114.         dx = -dx;
  1115.     }
  1116.     p3Ptr->x += dx;
  1117.     }
  1118. }
  1119.  
  1120. /*
  1121.  *--------------------------------------------------------------
  1122.  *
  1123.  * Intersect --
  1124.  *
  1125.  *    Find the intersection point between two lines.
  1126.  *
  1127.  * Results:
  1128.  *    Under normal conditions 0 is returned and the point
  1129.  *    at *iPtr is filled in with the intersection between
  1130.  *    the two lines.  If the two lines are parallel, then
  1131.  *    -1 is returned and *iPtr isn't modified.
  1132.  *
  1133.  * Side effects:
  1134.  *    None.
  1135.  *
  1136.  *--------------------------------------------------------------
  1137.  */
  1138.  
  1139. static int
  1140. Intersect(a1Ptr, a2Ptr, b1Ptr, b2Ptr, iPtr)
  1141.     XPoint *a1Ptr;        /* First point of first line. */
  1142.     XPoint *a2Ptr;        /* Second point of first line. */
  1143.     XPoint *b1Ptr;        /* First point of second line. */
  1144.     XPoint *b2Ptr;        /* Second point of second line. */
  1145.     XPoint *iPtr;        /* Filled in with intersection point. */
  1146. {
  1147.     int dxadyb, dxbdya, dxadxb, dyadyb, p, q;
  1148.  
  1149.     /*
  1150.      * The code below is just a straightforward manipulation of two
  1151.      * equations of the form y = (x-x1)*(y2-y1)/(x2-x1) + y1 to solve
  1152.      * for the x-coordinate of intersection, then the y-coordinate.
  1153.      */
  1154.  
  1155.     dxadyb = (a2Ptr->x - a1Ptr->x)*(b2Ptr->y - b1Ptr->y);
  1156.     dxbdya = (b2Ptr->x - b1Ptr->x)*(a2Ptr->y - a1Ptr->y);
  1157.     dxadxb = (a2Ptr->x - a1Ptr->x)*(b2Ptr->x - b1Ptr->x);
  1158.     dyadyb = (a2Ptr->y - a1Ptr->y)*(b2Ptr->y - b1Ptr->y);
  1159.  
  1160.     if (dxadyb == dxbdya) {
  1161.     return -1;
  1162.     }
  1163.     p = (a1Ptr->x*dxbdya - b1Ptr->x*dxadyb + (b1Ptr->y - a1Ptr->y)*dxadxb);
  1164.     q = dxbdya - dxadyb;
  1165.     if (q < 0) {
  1166.     p = -p;
  1167.     q = -q;
  1168.     }
  1169.     if (p < 0) {
  1170.     iPtr->x = - ((-p + q/2)/q);
  1171.     } else {
  1172.     iPtr->x = (p + q/2)/q;
  1173.     }
  1174.     p = (a1Ptr->y*dxadyb - b1Ptr->y*dxbdya + (b1Ptr->x - a1Ptr->x)*dyadyb);
  1175.     q = dxadyb - dxbdya;
  1176.     if (q < 0) {
  1177.     p = -p;
  1178.     q = -q;
  1179.     }
  1180.     if (p < 0) {
  1181.     iPtr->y = - ((-p + q/2)/q);
  1182.     } else {
  1183.     iPtr->y = (p + q/2)/q;
  1184.     }
  1185.     return 0;
  1186. }
  1187.  
  1188. /*
  1189.  *----------------------------------------------------------------------
  1190.  *
  1191.  * GetShadows --
  1192.  *
  1193.  *    This procedure computes the shadow colors for a 3-D border
  1194.  *    and fills in the corresponding fields of the Border structure.
  1195.  *    It's called lazily, so that the colors aren't allocated until
  1196.  *    something is actually drawn with them.  That way, if a border
  1197.  *    is only used for flat backgrounds the shadow colors will
  1198.  *    never be allocated.
  1199.  *
  1200.  * Results:
  1201.  *    None.
  1202.  *
  1203.  * Side effects:
  1204.  *    The lightGC and darkGC fields in borderPtr get filled in,
  1205.  *    if they weren't already.
  1206.  *
  1207.  *----------------------------------------------------------------------
  1208.  */
  1209.  
  1210. static void
  1211. GetShadows(borderPtr, tkwin)
  1212.     Border *borderPtr;        /* Information about border. */
  1213.     Tk_Window tkwin;        /* Window where border will be used for
  1214.                  * drawing. */
  1215. {
  1216.     XColor lightColor, darkColor;
  1217.     int stressed, tmp1, tmp2;
  1218.     XGCValues gcValues;
  1219.  
  1220.     if (borderPtr->lightGC != None) {
  1221.     return;
  1222.     }
  1223.     stressed = TkCmapStressed(tkwin, borderPtr->colormap);
  1224.  
  1225.     /*
  1226.      * First, handle the case of a color display with lots of colors.
  1227.      * The shadow colors get computed using whichever formula results
  1228.      * in the greatest change in color:
  1229.      * 1. Lighter shadow is half-way to white, darker shadow is half
  1230.      *    way to dark.
  1231.      * 2. Lighter shadow is 40% brighter than background, darker shadow
  1232.      *    is 40% darker than background.
  1233.      */
  1234.  
  1235.     if (!stressed && (Tk_Depth(tkwin) >= 6)) {
  1236.     /*
  1237.      * This is a color display with lots of colors.  For the dark
  1238.      * shadow, cut 40% from each of the background color components.
  1239.      * For the light shadow, boost each component by 40% or half-way
  1240.      * to white, whichever is greater (the first approach works
  1241.      * better for unsaturated colors, the second for saturated ones).
  1242.      */
  1243.  
  1244.     darkColor.red = (60 * (int) borderPtr->bgColorPtr->red)/100;
  1245.     darkColor.green = (60 * (int) borderPtr->bgColorPtr->green)/100;
  1246.     darkColor.blue = (60 * (int) borderPtr->bgColorPtr->blue)/100;
  1247.     borderPtr->darkColorPtr = Tk_GetColorByValue(tkwin, &darkColor);
  1248.     gcValues.foreground = borderPtr->darkColorPtr->pixel;
  1249.     borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
  1250.  
  1251.     /*
  1252.      * Compute the colors using integers, not using lightColor.red
  1253.      * etc.: these are shorts and may have problems with integer
  1254.      * overflow.
  1255.      */
  1256.  
  1257.     tmp1 = (14 * (int) borderPtr->bgColorPtr->red)/10;
  1258.     if (tmp1 > MAX_INTENSITY) {
  1259.         tmp1 = MAX_INTENSITY;
  1260.     }
  1261.     tmp2 = (MAX_INTENSITY + (int) borderPtr->bgColorPtr->red)/2;
  1262.     lightColor.red = (tmp1 > tmp2) ? tmp1 : tmp2;
  1263.     tmp1 = (14 * (int) borderPtr->bgColorPtr->green)/10;
  1264.     if (tmp1 > MAX_INTENSITY) {
  1265.         tmp1 = MAX_INTENSITY;
  1266.     }
  1267.     tmp2 = (MAX_INTENSITY + (int) borderPtr->bgColorPtr->green)/2;
  1268.     lightColor.green = (tmp1 > tmp2) ? tmp1 : tmp2;
  1269.     tmp1 = (14 * (int) borderPtr->bgColorPtr->blue)/10;
  1270.     if (tmp1 > MAX_INTENSITY) {
  1271.         tmp1 = MAX_INTENSITY;
  1272.     }
  1273.     tmp2 = (MAX_INTENSITY + (int) borderPtr->bgColorPtr->blue)/2;
  1274.     lightColor.blue = (tmp1 > tmp2) ? tmp1 : tmp2;
  1275.     borderPtr->lightColorPtr = Tk_GetColorByValue(tkwin, &lightColor);
  1276.     gcValues.foreground = borderPtr->lightColorPtr->pixel;
  1277.     borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
  1278.     return;
  1279.     }
  1280.  
  1281.     if (borderPtr->shadow == None) {
  1282.     borderPtr->shadow = Tk_GetBitmap((Tcl_Interp *) NULL, tkwin,
  1283.         Tk_GetUid("gray50"));
  1284.     if (borderPtr->shadow == None) {
  1285.         panic("GetShadows couldn't allocate bitmap for border");
  1286.     }
  1287.     }
  1288.     if (borderPtr->visual->map_entries > 2) {
  1289.     /*
  1290.      * This isn't a monochrome display, but the colormap either
  1291.      * ran out of entries or didn't have very many to begin with.
  1292.      * Generate the light shadows with a white stipple and the
  1293.      * dark shadows with a black stipple.
  1294.      */
  1295.  
  1296.     gcValues.foreground = borderPtr->bgColorPtr->pixel;
  1297.     gcValues.background = BlackPixelOfScreen(borderPtr->screen);
  1298.     gcValues.stipple = borderPtr->shadow;
  1299.     gcValues.fill_style = FillOpaqueStippled;
  1300.     borderPtr->darkGC = Tk_GetGC(tkwin,
  1301.         GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues);
  1302.     gcValues.background = WhitePixelOfScreen(borderPtr->screen);
  1303.     borderPtr->lightGC = Tk_GetGC(tkwin,
  1304.         GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues);
  1305.     return;
  1306.     }
  1307.  
  1308.     /*
  1309.      * This is just a measly monochrome display, hardly even worth its
  1310.      * existence on this earth.  Make one shadow a 50% stipple and the
  1311.      * other the opposite of the background.
  1312.      */
  1313.  
  1314.     gcValues.foreground = WhitePixelOfScreen(borderPtr->screen);
  1315.     gcValues.background = BlackPixelOfScreen(borderPtr->screen);
  1316.     gcValues.stipple = borderPtr->shadow;
  1317.     gcValues.fill_style = FillOpaqueStippled;
  1318.     borderPtr->lightGC = Tk_GetGC(tkwin,
  1319.         GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues);
  1320.     if (borderPtr->bgColorPtr->pixel
  1321.         == WhitePixelOfScreen(borderPtr->screen)) {
  1322.     gcValues.foreground = BlackPixelOfScreen(borderPtr->screen);
  1323.     borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
  1324.     } else {
  1325.     borderPtr->darkGC = borderPtr->lightGC;
  1326.     borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
  1327.     }
  1328. }
  1329.