449 lines
13 KiB
C
449 lines
13 KiB
C
|
/*
|
|||
|
* tkUnix3d.c --
|
|||
|
*
|
|||
|
* This file contains the platform specific routines for
|
|||
|
* drawing 3d borders in the Motif style.
|
|||
|
*
|
|||
|
* Copyright (c) 1996 by Sun Microsystems, Inc.
|
|||
|
*
|
|||
|
* See the file "license.terms" for information on usage and redistribution
|
|||
|
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|||
|
*
|
|||
|
* SCCS: @(#) tkUnix3d.c 1.3 96/11/20 14:24:38
|
|||
|
*/
|
|||
|
|
|||
|
#include <tk3d.h>
|
|||
|
|
|||
|
/*
|
|||
|
* This structure is used to keep track of the extra colors used
|
|||
|
* by Unix 3d borders.
|
|||
|
*/
|
|||
|
|
|||
|
typedef struct {
|
|||
|
TkBorder info;
|
|||
|
GC solidGC; /* Used to draw solid relief. */
|
|||
|
} UnixBorder;
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TkpGetBorder --
|
|||
|
*
|
|||
|
* This function allocates a new TkBorder structure.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Returns a newly allocated TkBorder.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
TkBorder *
|
|||
|
TkpGetBorder()
|
|||
|
{
|
|||
|
UnixBorder *borderPtr = (UnixBorder *) ckalloc(sizeof(UnixBorder));
|
|||
|
borderPtr->solidGC = None;
|
|||
|
return (TkBorder *) borderPtr;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TkpFreeBorder --
|
|||
|
*
|
|||
|
* This function frees any colors allocated by the platform
|
|||
|
* specific part of this module.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* May deallocate some colors.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
TkpFreeBorder(borderPtr)
|
|||
|
TkBorder *borderPtr;
|
|||
|
{
|
|||
|
UnixBorder *unixBorderPtr = (UnixBorder *) borderPtr;
|
|||
|
Display *display = DisplayOfScreen(borderPtr->screen);
|
|||
|
|
|||
|
if (unixBorderPtr->solidGC != None) {
|
|||
|
Tk_FreeGC(display, unixBorderPtr->solidGC);
|
|||
|
}
|
|||
|
}
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* Tk_3DVerticalBevel --
|
|||
|
*
|
|||
|
* This procedure draws a vertical bevel along one side of
|
|||
|
* an object. The bevel is always rectangular in shape:
|
|||
|
* |||
|
|||
|
* |||
|
|||
|
* |||
|
|||
|
* |||
|
|||
|
* |||
|
|||
|
* |||
|
|||
|
* An appropriate shadow color is chosen for the bevel based
|
|||
|
* on the leftBevel and relief arguments. Normally this
|
|||
|
* procedure is called first, then Tk_3DHorizontalBevel is
|
|||
|
* called next to draw neat corners.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Graphics are drawn in drawable.
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
Tk_3DVerticalBevel(tkwin, drawable, border, x, y, width, height,
|
|||
|
leftBevel, relief)
|
|||
|
Tk_Window tkwin; /* Window for which border was allocated. */
|
|||
|
Drawable drawable; /* X window or pixmap in which to draw. */
|
|||
|
Tk_3DBorder border; /* Token for border to draw. */
|
|||
|
int x, y, width, height; /* Area of vertical bevel. */
|
|||
|
int leftBevel; /* Non-zero means this bevel forms the
|
|||
|
* left side of the object; 0 means it
|
|||
|
* forms the right side. */
|
|||
|
int relief; /* Kind of bevel to draw. For example,
|
|||
|
* TK_RELIEF_RAISED means interior of
|
|||
|
* object should appear higher than
|
|||
|
* exterior. */
|
|||
|
{
|
|||
|
TkBorder *borderPtr = (TkBorder *) border;
|
|||
|
GC left, right;
|
|||
|
Display *display = Tk_Display(tkwin);
|
|||
|
|
|||
|
if ((borderPtr->lightGC == None) && (relief != TK_RELIEF_FLAT)) {
|
|||
|
TkpGetShadows(borderPtr, tkwin);
|
|||
|
}
|
|||
|
|
|||
|
if (relief == TK_RELIEF_RAISED) {
|
|||
|
XFillRectangle(display, drawable,
|
|||
|
(leftBevel) ? borderPtr->lightGC : borderPtr->darkGC,
|
|||
|
x, y, (unsigned) width, (unsigned) height);
|
|||
|
} else if (relief == TK_RELIEF_SUNKEN) {
|
|||
|
XFillRectangle(display, drawable,
|
|||
|
(leftBevel) ? borderPtr->darkGC : borderPtr->lightGC,
|
|||
|
x, y, (unsigned) width, (unsigned) height);
|
|||
|
} else if (relief == TK_RELIEF_RIDGE) {
|
|||
|
int half;
|
|||
|
|
|||
|
left = borderPtr->lightGC;
|
|||
|
right = borderPtr->darkGC;
|
|||
|
ridgeGroove:
|
|||
|
half = width/2;
|
|||
|
if (!leftBevel && (width & 1)) {
|
|||
|
half++;
|
|||
|
}
|
|||
|
XFillRectangle(display, drawable, left, x, y, (unsigned) half,
|
|||
|
(unsigned) height);
|
|||
|
XFillRectangle(display, drawable, right, x+half, y,
|
|||
|
(unsigned) (width-half), (unsigned) height);
|
|||
|
} else if (relief == TK_RELIEF_GROOVE) {
|
|||
|
left = borderPtr->darkGC;
|
|||
|
right = borderPtr->lightGC;
|
|||
|
goto ridgeGroove;
|
|||
|
} else if (relief == TK_RELIEF_FLAT) {
|
|||
|
XFillRectangle(display, drawable, borderPtr->bgGC, x, y,
|
|||
|
(unsigned) width, (unsigned) height);
|
|||
|
} else if (relief == TK_RELIEF_SOLID) {
|
|||
|
UnixBorder *unixBorderPtr = (UnixBorder *) borderPtr;
|
|||
|
if (unixBorderPtr->solidGC == None) {
|
|||
|
XGCValues gcValues;
|
|||
|
|
|||
|
gcValues.foreground = BlackPixelOfScreen(borderPtr->screen);
|
|||
|
unixBorderPtr->solidGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
|
|||
|
}
|
|||
|
XFillRectangle(display, drawable, unixBorderPtr->solidGC, x, y,
|
|||
|
(unsigned) width, (unsigned) height);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* Tk_3DHorizontalBevel --
|
|||
|
*
|
|||
|
* This procedure draws a horizontal bevel along one side of
|
|||
|
* an object. The bevel has mitered corners (depending on
|
|||
|
* leftIn and rightIn arguments).
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None.
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
Tk_3DHorizontalBevel(tkwin, drawable, border, x, y, width, height,
|
|||
|
leftIn, rightIn, topBevel, relief)
|
|||
|
Tk_Window tkwin; /* Window for which border was allocated. */
|
|||
|
Drawable drawable; /* X window or pixmap in which to draw. */
|
|||
|
Tk_3DBorder border; /* Token for border to draw. */
|
|||
|
int x, y, width, height; /* Bounding box of area of bevel. Height
|
|||
|
* gives width of border. */
|
|||
|
int leftIn, rightIn; /* Describes whether the left and right
|
|||
|
* edges of the bevel angle in or out as
|
|||
|
* they go down. For example, if "leftIn"
|
|||
|
* is true, the left side of the bevel
|
|||
|
* looks like this:
|
|||
|
* ___________
|
|||
|
* __________
|
|||
|
* _________
|
|||
|
* ________
|
|||
|
*/
|
|||
|
int topBevel; /* Non-zero means this bevel forms the
|
|||
|
* top side of the object; 0 means it
|
|||
|
* forms the bottom side. */
|
|||
|
int relief; /* Kind of bevel to draw. For example,
|
|||
|
* TK_RELIEF_RAISED means interior of
|
|||
|
* object should appear higher than
|
|||
|
* exterior. */
|
|||
|
{
|
|||
|
TkBorder *borderPtr = (TkBorder *) border;
|
|||
|
Display *display = Tk_Display(tkwin);
|
|||
|
int bottom, halfway, x1, x2, x1Delta, x2Delta;
|
|||
|
UnixBorder *unixBorderPtr = (UnixBorder *) borderPtr;
|
|||
|
GC topGC = None, bottomGC = None;
|
|||
|
/* Initializations needed only to prevent
|
|||
|
* compiler warnings. */
|
|||
|
|
|||
|
if ((borderPtr->lightGC == None) && (relief != TK_RELIEF_FLAT) &&
|
|||
|
(relief != TK_RELIEF_SOLID)) {
|
|||
|
TkpGetShadows(borderPtr, tkwin);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Compute a GC for the top half of the bevel and a GC for the
|
|||
|
* bottom half (they're the same in many cases).
|
|||
|
*/
|
|||
|
|
|||
|
switch (relief) {
|
|||
|
case TK_RELIEF_FLAT:
|
|||
|
topGC = bottomGC = borderPtr->bgGC;
|
|||
|
break;
|
|||
|
case TK_RELIEF_GROOVE:
|
|||
|
topGC = borderPtr->darkGC;
|
|||
|
bottomGC = borderPtr->lightGC;
|
|||
|
break;
|
|||
|
case TK_RELIEF_RAISED:
|
|||
|
topGC = bottomGC =
|
|||
|
(topBevel) ? borderPtr->lightGC : borderPtr->darkGC;
|
|||
|
break;
|
|||
|
case TK_RELIEF_RIDGE:
|
|||
|
topGC = borderPtr->lightGC;
|
|||
|
bottomGC = borderPtr->darkGC;
|
|||
|
break;
|
|||
|
case TK_RELIEF_SOLID:
|
|||
|
if (unixBorderPtr->solidGC == None) {
|
|||
|
XGCValues gcValues;
|
|||
|
|
|||
|
gcValues.foreground = BlackPixelOfScreen(borderPtr->screen);
|
|||
|
unixBorderPtr->solidGC = Tk_GetGC(tkwin, GCForeground,
|
|||
|
&gcValues);
|
|||
|
}
|
|||
|
XFillRectangle(display, drawable, unixBorderPtr->solidGC, x, y,
|
|||
|
(unsigned) width, (unsigned) height);
|
|||
|
return;
|
|||
|
case TK_RELIEF_SUNKEN:
|
|||
|
topGC = bottomGC =
|
|||
|
(topBevel) ? borderPtr->darkGC : borderPtr->lightGC;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Compute various other geometry-related stuff.
|
|||
|
*/
|
|||
|
|
|||
|
x1 = x;
|
|||
|
if (!leftIn) {
|
|||
|
x1 += height;
|
|||
|
}
|
|||
|
x2 = x+width;
|
|||
|
if (!rightIn) {
|
|||
|
x2 -= height;
|
|||
|
}
|
|||
|
x1Delta = (leftIn) ? 1 : -1;
|
|||
|
x2Delta = (rightIn) ? -1 : 1;
|
|||
|
halfway = y + height/2;
|
|||
|
if (!topBevel && (height & 1)) {
|
|||
|
halfway++;
|
|||
|
}
|
|||
|
bottom = y + height;
|
|||
|
|
|||
|
/*
|
|||
|
* Draw one line for each y-coordinate covered by the bevel.
|
|||
|
*/
|
|||
|
|
|||
|
for ( ; y < bottom; y++) {
|
|||
|
/*
|
|||
|
* In some weird cases (such as large border widths for skinny
|
|||
|
* rectangles) x1 can be >= x2. Don't draw the lines
|
|||
|
* in these cases.
|
|||
|
*/
|
|||
|
|
|||
|
if (x1 < x2) {
|
|||
|
XFillRectangle(display, drawable,
|
|||
|
(y < halfway) ? topGC : bottomGC, x1, y,
|
|||
|
(unsigned) (x2-x1), (unsigned) 1);
|
|||
|
}
|
|||
|
x1 += x1Delta;
|
|||
|
x2 += x2Delta;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TkpGetShadows --
|
|||
|
*
|
|||
|
* This procedure computes the shadow colors for a 3-D border
|
|||
|
* and fills in the corresponding fields of the Border structure.
|
|||
|
* It's called lazily, so that the colors aren't allocated until
|
|||
|
* something is actually drawn with them. That way, if a border
|
|||
|
* is only used for flat backgrounds the shadow colors will
|
|||
|
* never be allocated.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* The lightGC and darkGC fields in borderPtr get filled in,
|
|||
|
* if they weren't already.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
TkpGetShadows(borderPtr, tkwin)
|
|||
|
TkBorder *borderPtr; /* Information about border. */
|
|||
|
Tk_Window tkwin; /* Window where border will be used for
|
|||
|
* drawing. */
|
|||
|
{
|
|||
|
XColor lightColor, darkColor;
|
|||
|
int stressed, tmp1, tmp2;
|
|||
|
XGCValues gcValues;
|
|||
|
|
|||
|
if (borderPtr->lightGC != None) {
|
|||
|
return;
|
|||
|
}
|
|||
|
stressed = TkpCmapStressed(tkwin, borderPtr->colormap);
|
|||
|
|
|||
|
/*
|
|||
|
* First, handle the case of a color display with lots of colors.
|
|||
|
* The shadow colors get computed using whichever formula results
|
|||
|
* in the greatest change in color:
|
|||
|
* 1. Lighter shadow is half-way to white, darker shadow is half
|
|||
|
* way to dark.
|
|||
|
* 2. Lighter shadow is 40% brighter than background, darker shadow
|
|||
|
* is 40% darker than background.
|
|||
|
*/
|
|||
|
|
|||
|
if (!stressed && (Tk_Depth(tkwin) >= 6)) {
|
|||
|
/*
|
|||
|
* This is a color display with lots of colors. For the dark
|
|||
|
* shadow, cut 40% from each of the background color components.
|
|||
|
* For the light shadow, boost each component by 40% or half-way
|
|||
|
* to white, whichever is greater (the first approach works
|
|||
|
* better for unsaturated colors, the second for saturated ones).
|
|||
|
*/
|
|||
|
|
|||
|
darkColor.red = (60 * (int) borderPtr->bgColorPtr->red)/100;
|
|||
|
darkColor.green = (60 * (int) borderPtr->bgColorPtr->green)/100;
|
|||
|
darkColor.blue = (60 * (int) borderPtr->bgColorPtr->blue)/100;
|
|||
|
borderPtr->darkColorPtr = Tk_GetColorByValue(tkwin, &darkColor);
|
|||
|
gcValues.foreground = borderPtr->darkColorPtr->pixel;
|
|||
|
borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
|
|||
|
|
|||
|
/*
|
|||
|
* Compute the colors using integers, not using lightColor.red
|
|||
|
* etc.: these are shorts and may have problems with integer
|
|||
|
* overflow.
|
|||
|
*/
|
|||
|
|
|||
|
tmp1 = (14 * (int) borderPtr->bgColorPtr->red)/10;
|
|||
|
if (tmp1 > MAX_INTENSITY) {
|
|||
|
tmp1 = MAX_INTENSITY;
|
|||
|
}
|
|||
|
tmp2 = (MAX_INTENSITY + (int) borderPtr->bgColorPtr->red)/2;
|
|||
|
lightColor.red = (tmp1 > tmp2) ? tmp1 : tmp2;
|
|||
|
tmp1 = (14 * (int) borderPtr->bgColorPtr->green)/10;
|
|||
|
if (tmp1 > MAX_INTENSITY) {
|
|||
|
tmp1 = MAX_INTENSITY;
|
|||
|
}
|
|||
|
tmp2 = (MAX_INTENSITY + (int) borderPtr->bgColorPtr->green)/2;
|
|||
|
lightColor.green = (tmp1 > tmp2) ? tmp1 : tmp2;
|
|||
|
tmp1 = (14 * (int) borderPtr->bgColorPtr->blue)/10;
|
|||
|
if (tmp1 > MAX_INTENSITY) {
|
|||
|
tmp1 = MAX_INTENSITY;
|
|||
|
}
|
|||
|
tmp2 = (MAX_INTENSITY + (int) borderPtr->bgColorPtr->blue)/2;
|
|||
|
lightColor.blue = (tmp1 > tmp2) ? tmp1 : tmp2;
|
|||
|
borderPtr->lightColorPtr = Tk_GetColorByValue(tkwin, &lightColor);
|
|||
|
gcValues.foreground = borderPtr->lightColorPtr->pixel;
|
|||
|
borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (borderPtr->shadow == None) {
|
|||
|
borderPtr->shadow = Tk_GetBitmap((Tcl_Interp *) NULL, tkwin,
|
|||
|
Tk_GetUid("gray50"));
|
|||
|
if (borderPtr->shadow == None) {
|
|||
|
panic("TkpGetShadows couldn't allocate bitmap for border");
|
|||
|
}
|
|||
|
}
|
|||
|
if (borderPtr->visual->map_entries > 2) {
|
|||
|
/*
|
|||
|
* This isn't a monochrome display, but the colormap either
|
|||
|
* ran out of entries or didn't have very many to begin with.
|
|||
|
* Generate the light shadows with a white stipple and the
|
|||
|
* dark shadows with a black stipple.
|
|||
|
*/
|
|||
|
|
|||
|
gcValues.foreground = borderPtr->bgColorPtr->pixel;
|
|||
|
gcValues.background = BlackPixelOfScreen(borderPtr->screen);
|
|||
|
gcValues.stipple = borderPtr->shadow;
|
|||
|
gcValues.fill_style = FillOpaqueStippled;
|
|||
|
borderPtr->darkGC = Tk_GetGC(tkwin,
|
|||
|
GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues);
|
|||
|
gcValues.background = WhitePixelOfScreen(borderPtr->screen);
|
|||
|
borderPtr->lightGC = Tk_GetGC(tkwin,
|
|||
|
GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* This is just a measly monochrome display, hardly even worth its
|
|||
|
* existence on this earth. Make one shadow a 50% stipple and the
|
|||
|
* other the opposite of the background.
|
|||
|
*/
|
|||
|
|
|||
|
gcValues.foreground = WhitePixelOfScreen(borderPtr->screen);
|
|||
|
gcValues.background = BlackPixelOfScreen(borderPtr->screen);
|
|||
|
gcValues.stipple = borderPtr->shadow;
|
|||
|
gcValues.fill_style = FillOpaqueStippled;
|
|||
|
borderPtr->lightGC = Tk_GetGC(tkwin,
|
|||
|
GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues);
|
|||
|
if (borderPtr->bgColorPtr->pixel
|
|||
|
== WhitePixelOfScreen(borderPtr->screen)) {
|
|||
|
gcValues.foreground = BlackPixelOfScreen(borderPtr->screen);
|
|||
|
borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
|
|||
|
} else {
|
|||
|
borderPtr->darkGC = borderPtr->lightGC;
|
|||
|
borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
|
|||
|
}
|
|||
|
}
|