1 /*
2 * tkUnix3d.c --
3 *
4 * This file contains the platform specific routines for
5 * drawing 3d borders in the Motif style.
6 *
7 * Copyright (c) 1996 by Sun Microsystems, Inc.
8 *
9 * See the file "license.terms" for information on usage and redistribution
10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id: tkUnix3d.c,v 1.7 2002/08/31 06:12:31 das Exp $
13 */
14
15 #include "tk3d.h"
16
17
18 #if !(defined(__WIN32__) || defined(MAC_TCL) || defined(MAC_OSX_TK))
19 #include "tkUnixInt.h"
20 #endif
21
22 /*
23 * This structure is used to keep track of the extra colors used
24 * by Unix 3d borders.
25 */
26
27 typedef struct {
28 TkBorder info;
29 GC solidGC; /* Used to draw solid relief. */
30 } UnixBorder;
31
32 /*
33 *----------------------------------------------------------------------
34 *
35 * TkpGetBorder --
36 *
37 * This function allocates a new TkBorder structure.
38 *
39 * Results:
40 * Returns a newly allocated TkBorder.
41 *
42 * Side effects:
43 * None.
44 *
45 *----------------------------------------------------------------------
46 */
47
48 TkBorder *
TkpGetBorder()49 TkpGetBorder()
50 {
51 UnixBorder *borderPtr = (UnixBorder *) ckalloc(sizeof(UnixBorder));
52 borderPtr->solidGC = None;
53 return (TkBorder *) borderPtr;
54 }
55
56
57 /*
58 *----------------------------------------------------------------------
59 *
60 * TkpFreeBorder --
61 *
62 * This function frees any colors allocated by the platform
63 * specific part of this module.
64 *
65 * Results:
66 * None.
67 *
68 * Side effects:
69 * May deallocate some colors.
70 *
71 *----------------------------------------------------------------------
72 */
73
74 void
TkpFreeBorder(borderPtr)75 TkpFreeBorder(borderPtr)
76 TkBorder *borderPtr;
77 {
78 UnixBorder *unixBorderPtr = (UnixBorder *) borderPtr;
79 Display *display = DisplayOfScreen(borderPtr->screen);
80
81 if (unixBorderPtr->solidGC != None) {
82 Tk_FreeGC(display, unixBorderPtr->solidGC);
83 }
84 }
85 /*
86 *--------------------------------------------------------------
87 *
88 * Tk_3DVerticalBevel --
89 *
90 * This procedure draws a vertical bevel along one side of
91 * an object. The bevel is always rectangular in shape:
92 * |||
93 * |||
94 * |||
95 * |||
96 * |||
97 * |||
98 * An appropriate shadow color is chosen for the bevel based
99 * on the leftBevel and relief arguments. Normally this
100 * procedure is called first, then Tk_3DHorizontalBevel is
101 * called next to draw neat corners.
102 *
103 * Results:
104 * None.
105 *
106 * Side effects:
107 * Graphics are drawn in drawable.
108 *
109 *--------------------------------------------------------------
110 */
111
112 void
Tk_3DVerticalBevel(tkwin,drawable,border,x,y,width,height,leftBevel,relief)113 Tk_3DVerticalBevel(tkwin, drawable, border, x, y, width, height,
114 leftBevel, relief)
115 Tk_Window tkwin; /* Window for which border was allocated. */
116 Drawable drawable; /* X window or pixmap in which to draw. */
117 Tk_3DBorder border; /* Token for border to draw. */
118 int x, y, width, height; /* Area of vertical bevel. */
119 int leftBevel; /* Non-zero means this bevel forms the
120 * left side of the object; 0 means it
121 * forms the right side. */
122 int relief; /* Kind of bevel to draw. For example,
123 * TK_RELIEF_RAISED means interior of
124 * object should appear higher than
125 * exterior. */
126 {
127 TkBorder *borderPtr = (TkBorder *) border;
128 GC left, right;
129 Display *display = Tk_Display(tkwin);
130
131 if ((borderPtr->lightGC == None) && (relief != TK_RELIEF_FLAT)) {
132 TkpGetShadows(borderPtr, tkwin);
133 }
134
135 if (relief == TK_RELIEF_RAISED) {
136 XFillRectangle(display, drawable,
137 (leftBevel) ? borderPtr->lightGC : borderPtr->darkGC,
138 x, y, (unsigned) width, (unsigned) height);
139 } else if (relief == TK_RELIEF_SUNKEN) {
140 XFillRectangle(display, drawable,
141 (leftBevel) ? borderPtr->darkGC : borderPtr->lightGC,
142 x, y, (unsigned) width, (unsigned) height);
143 } else if (relief == TK_RELIEF_RIDGE) {
144 int half;
145
146 left = borderPtr->lightGC;
147 right = borderPtr->darkGC;
148 ridgeGroove:
149 half = width/2;
150 if (!leftBevel && (width & 1)) {
151 half++;
152 }
153 XFillRectangle(display, drawable, left, x, y, (unsigned) half,
154 (unsigned) height);
155 XFillRectangle(display, drawable, right, x+half, y,
156 (unsigned) (width-half), (unsigned) height);
157 } else if (relief == TK_RELIEF_GROOVE) {
158 left = borderPtr->darkGC;
159 right = borderPtr->lightGC;
160 goto ridgeGroove;
161 } else if (relief == TK_RELIEF_FLAT) {
162 XFillRectangle(display, drawable, borderPtr->bgGC, x, y,
163 (unsigned) width, (unsigned) height);
164 } else if (relief == TK_RELIEF_SOLID) {
165 UnixBorder *unixBorderPtr = (UnixBorder *) borderPtr;
166 if (unixBorderPtr->solidGC == None) {
167 XGCValues gcValues;
168
169 gcValues.foreground = BlackPixelOfScreen(borderPtr->screen);
170 unixBorderPtr->solidGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
171 }
172 XFillRectangle(display, drawable, unixBorderPtr->solidGC, x, y,
173 (unsigned) width, (unsigned) height);
174 }
175 }
176
177 /*
178 *--------------------------------------------------------------
179 *
180 * Tk_3DHorizontalBevel --
181 *
182 * This procedure draws a horizontal bevel along one side of
183 * an object. The bevel has mitered corners (depending on
184 * leftIn and rightIn arguments).
185 *
186 * Results:
187 * None.
188 *
189 * Side effects:
190 * None.
191 *
192 *--------------------------------------------------------------
193 */
194
195 void
Tk_3DHorizontalBevel(tkwin,drawable,border,x,y,width,height,leftIn,rightIn,topBevel,relief)196 Tk_3DHorizontalBevel(tkwin, drawable, border, x, y, width, height,
197 leftIn, rightIn, topBevel, relief)
198 Tk_Window tkwin; /* Window for which border was allocated. */
199 Drawable drawable; /* X window or pixmap in which to draw. */
200 Tk_3DBorder border; /* Token for border to draw. */
201 int x, y, width, height; /* Bounding box of area of bevel. Height
202 * gives width of border. */
203 int leftIn, rightIn; /* Describes whether the left and right
204 * edges of the bevel angle in or out as
205 * they go down. For example, if "leftIn"
206 * is true, the left side of the bevel
207 * looks like this:
208 * ___________
209 * __________
210 * _________
211 * ________
212 */
213 int topBevel; /* Non-zero means this bevel forms the
214 * top side of the object; 0 means it
215 * forms the bottom side. */
216 int relief; /* Kind of bevel to draw. For example,
217 * TK_RELIEF_RAISED means interior of
218 * object should appear higher than
219 * exterior. */
220 {
221 TkBorder *borderPtr = (TkBorder *) border;
222 Display *display = Tk_Display(tkwin);
223 int bottom, halfway, x1, x2, x1Delta, x2Delta;
224 UnixBorder *unixBorderPtr = (UnixBorder *) borderPtr;
225 GC topGC = None, bottomGC = None;
226 /* Initializations needed only to prevent
227 * compiler warnings. */
228
229 if ((borderPtr->lightGC == None) && (relief != TK_RELIEF_FLAT) &&
230 (relief != TK_RELIEF_SOLID)) {
231 TkpGetShadows(borderPtr, tkwin);
232 }
233
234 /*
235 * Compute a GC for the top half of the bevel and a GC for the
236 * bottom half (they're the same in many cases).
237 */
238
239 switch (relief) {
240 case TK_RELIEF_FLAT:
241 topGC = bottomGC = borderPtr->bgGC;
242 break;
243 case TK_RELIEF_GROOVE:
244 topGC = borderPtr->darkGC;
245 bottomGC = borderPtr->lightGC;
246 break;
247 case TK_RELIEF_RAISED:
248 topGC = bottomGC =
249 (topBevel) ? borderPtr->lightGC : borderPtr->darkGC;
250 break;
251 case TK_RELIEF_RIDGE:
252 topGC = borderPtr->lightGC;
253 bottomGC = borderPtr->darkGC;
254 break;
255 case TK_RELIEF_SOLID:
256 if (unixBorderPtr->solidGC == None) {
257 XGCValues gcValues;
258
259 gcValues.foreground = BlackPixelOfScreen(borderPtr->screen);
260 unixBorderPtr->solidGC = Tk_GetGC(tkwin, GCForeground,
261 &gcValues);
262 }
263 XFillRectangle(display, drawable, unixBorderPtr->solidGC, x, y,
264 (unsigned) width, (unsigned) height);
265 return;
266 case TK_RELIEF_SUNKEN:
267 topGC = bottomGC =
268 (topBevel) ? borderPtr->darkGC : borderPtr->lightGC;
269 break;
270 }
271
272 /*
273 * Compute various other geometry-related stuff.
274 */
275
276 x1 = x;
277 if (!leftIn) {
278 x1 += height;
279 }
280 x2 = x+width;
281 if (!rightIn) {
282 x2 -= height;
283 }
284 x1Delta = (leftIn) ? 1 : -1;
285 x2Delta = (rightIn) ? -1 : 1;
286 halfway = y + height/2;
287 if (!topBevel && (height & 1)) {
288 halfway++;
289 }
290 bottom = y + height;
291
292 /*
293 * Draw one line for each y-coordinate covered by the bevel.
294 */
295
296 for ( ; y < bottom; y++) {
297 /*
298 * X Dimensions are 16-bit, so avoid wraparound or display errors
299 * by limiting these here.
300 */
301 if (x1 < -32767)
302 x1 = -32767;
303 if (x2 > 32767)
304 x2 = 32767;
305
306 /*
307 * In some weird cases (such as large border widths for skinny
308 * rectangles) x1 can be >= x2. Don't draw the lines
309 * in these cases.
310 */
311
312 if (x1 < x2) {
313 XFillRectangle(display, drawable,
314 (y < halfway) ? topGC : bottomGC, x1, y,
315 (unsigned) (x2-x1), (unsigned) 1);
316 }
317 x1 += x1Delta;
318 x2 += x2Delta;
319 }
320 }
321
322 /*
323 *----------------------------------------------------------------------
324 *
325 * TkpGetShadows --
326 *
327 * This procedure computes the shadow colors for a 3-D border
328 * and fills in the corresponding fields of the Border structure.
329 * It's called lazily, so that the colors aren't allocated until
330 * something is actually drawn with them. That way, if a border
331 * is only used for flat backgrounds the shadow colors will
332 * never be allocated.
333 *
334 * Results:
335 * None.
336 *
337 * Side effects:
338 * The lightGC and darkGC fields in borderPtr get filled in,
339 * if they weren't already.
340 *
341 *----------------------------------------------------------------------
342 */
343
344 void
TkpGetShadows(borderPtr,tkwin)345 TkpGetShadows(borderPtr, tkwin)
346 TkBorder *borderPtr; /* Information about border. */
347 Tk_Window tkwin; /* Window where border will be used for
348 * drawing. */
349 {
350 XColor lightColor, darkColor;
351 int stressed, tmp1, tmp2;
352 int r, g, b;
353 XGCValues gcValues;
354
355 if (borderPtr->lightGC != None) {
356 return;
357 }
358 stressed = TkpCmapStressed(tkwin, borderPtr->colormap);
359
360 /*
361 * First, handle the case of a color display with lots of colors.
362 * The shadow colors get computed using whichever formula results
363 * in the greatest change in color:
364 * 1. Lighter shadow is half-way to white, darker shadow is half
365 * way to dark.
366 * 2. Lighter shadow is 40% brighter than background, darker shadow
367 * is 40% darker than background.
368 */
369
370 if (!stressed && (Tk_Depth(tkwin) >= 6)) {
371 /*
372 * This is a color display with lots of colors. For the dark
373 * shadow, cut 40% from each of the background color components.
374 * But if the background is already very dark, make the
375 * dark color a little lighter than the background by increasing
376 * each color component 1/4th of the way to MAX_INTENSITY.
377 *
378 * For the light shadow, boost each component by 40% or half-way
379 * to white, whichever is greater (the first approach works
380 * better for unsaturated colors, the second for saturated ones).
381 * But if the background is already very bright, instead choose a
382 * slightly darker color for the light shadow by reducing each
383 * color component by 10%.
384 *
385 * Compute the colors using integers, not using lightColor.red
386 * etc.: these are shorts and may have problems with integer
387 * overflow.
388 */
389
390 /*
391 * Compute the dark shadow color
392 */
393
394 r = (int) borderPtr->bgColorPtr->red;
395 g = (int) borderPtr->bgColorPtr->green;
396 b = (int) borderPtr->bgColorPtr->blue;
397
398 if (r*0.5*r + g*1.0*g + b*0.28*b < MAX_INTENSITY*0.05*MAX_INTENSITY) {
399 darkColor.red = (MAX_INTENSITY + 3*r)/4;
400 darkColor.green = (MAX_INTENSITY + 3*g)/4;
401 darkColor.blue = (MAX_INTENSITY + 3*b)/4;
402 } else {
403 darkColor.red = (60 * r)/100;
404 darkColor.green = (60 * g)/100;
405 darkColor.blue = (60 * b)/100;
406 }
407
408 /*
409 * Allocate the dark shadow color and its GC
410 */
411
412 borderPtr->darkColorPtr = Tk_GetColorByValue(tkwin, &darkColor);
413 gcValues.foreground = borderPtr->darkColorPtr->pixel;
414 borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
415
416 /*
417 * Compute the light shadow color
418 */
419
420 if (g > MAX_INTENSITY*0.95) {
421 lightColor.red = (90 * r)/100;
422 lightColor.green = (90 * g)/100;
423 lightColor.blue = (90 * b)/100;
424 } else {
425 tmp1 = (14 * r)/10;
426 if (tmp1 > MAX_INTENSITY) {
427 tmp1 = MAX_INTENSITY;
428 }
429 tmp2 = (MAX_INTENSITY + r)/2;
430 lightColor.red = (tmp1 > tmp2) ? tmp1 : tmp2;
431 tmp1 = (14 * g)/10;
432 if (tmp1 > MAX_INTENSITY) {
433 tmp1 = MAX_INTENSITY;
434 }
435 tmp2 = (MAX_INTENSITY + g)/2;
436 lightColor.green = (tmp1 > tmp2) ? tmp1 : tmp2;
437 tmp1 = (14 * b)/10;
438 if (tmp1 > MAX_INTENSITY) {
439 tmp1 = MAX_INTENSITY;
440 }
441 tmp2 = (MAX_INTENSITY + b)/2;
442 lightColor.blue = (tmp1 > tmp2) ? tmp1 : tmp2;
443 }
444
445 /*
446 * Allocate the light shadow color and its GC
447 */
448
449 borderPtr->lightColorPtr = Tk_GetColorByValue(tkwin, &lightColor);
450 gcValues.foreground = borderPtr->lightColorPtr->pixel;
451 borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
452 return;
453 }
454
455 if (borderPtr->shadow == None) {
456 borderPtr->shadow = Tk_GetBitmap((Tcl_Interp *) NULL, tkwin,
457 Tk_GetUid("gray50"));
458 if (borderPtr->shadow == None) {
459 panic("TkpGetShadows couldn't allocate bitmap for border");
460 }
461 }
462 if (borderPtr->visual->map_entries > 2) {
463 /*
464 * This isn't a monochrome display, but the colormap either
465 * ran out of entries or didn't have very many to begin with.
466 * Generate the light shadows with a white stipple and the
467 * dark shadows with a black stipple.
468 */
469
470 gcValues.foreground = borderPtr->bgColorPtr->pixel;
471 gcValues.background = BlackPixelOfScreen(borderPtr->screen);
472 gcValues.stipple = borderPtr->shadow;
473 gcValues.fill_style = FillOpaqueStippled;
474 borderPtr->darkGC = Tk_GetGC(tkwin,
475 GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues);
476 gcValues.background = WhitePixelOfScreen(borderPtr->screen);
477 borderPtr->lightGC = Tk_GetGC(tkwin,
478 GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues);
479 return;
480 }
481
482 /*
483 * This is just a measly monochrome display, hardly even worth its
484 * existence on this earth. Make one shadow a 50% stipple and the
485 * other the opposite of the background.
486 */
487
488 gcValues.foreground = WhitePixelOfScreen(borderPtr->screen);
489 gcValues.background = BlackPixelOfScreen(borderPtr->screen);
490 gcValues.stipple = borderPtr->shadow;
491 gcValues.fill_style = FillOpaqueStippled;
492 borderPtr->lightGC = Tk_GetGC(tkwin,
493 GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues);
494 if (borderPtr->bgColorPtr->pixel
495 == WhitePixelOfScreen(borderPtr->screen)) {
496 gcValues.foreground = BlackPixelOfScreen(borderPtr->screen);
497 borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
498 } else {
499 borderPtr->darkGC = borderPtr->lightGC;
500 borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
501 }
502 }
503