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.53 96/06/27 08:15:35
14 */
15
16 #include "tkInt.h"
17
18 /*
19 * One of the following data structures is allocated for
20 * each 3-D border currently in use. Structures of this
21 * type are indexed by borderTable, so that a single
22 * structure can be shared for several uses.
23 */
24
25 typedef struct {
26 Screen *screen; /* Screen on which the border will be used. */
27 Visual *visual; /* Visual for all windows and pixmaps using
28 * the border. */
29 int depth; /* Number of bits per pixel of drawables where
30 * the border will be used. */
31 Colormap colormap; /* Colormap out of which pixels are
32 * allocated. */
33 int refCount; /* Number of different users of
34 * this border. */
35 XColor *bgColorPtr; /* Background color (intensity
36 * between lightColorPtr and
37 * darkColorPtr). */
38 XColor *darkColorPtr; /* Color for darker areas (must free when
39 * deleting structure). NULL means shadows
40 * haven't been allocated yet.*/
41 XColor *lightColorPtr; /* Color used for lighter areas of border
42 * (must free this when deleting structure).
43 * NULL means shadows haven't been allocated
44 * yet. */
45 Pixmap shadow; /* Stipple pattern to use for drawing
46 * shadows areas. Used for displays with
47 * <= 64 colors or where colormap has filled
48 * up. */
49 GC bgGC; /* Used (if necessary) to draw areas in
50 * the background color. */
51 GC darkGC; /* Used to draw darker parts of the
52 * border. None means the shadow colors
53 * haven't been allocated yet.*/
54 GC lightGC; /* Used to draw lighter parts of
55 * the border. None means the shadow colors
56 * haven't been allocated yet. */
57 Tcl_HashEntry *hashPtr; /* Entry in borderTable (needed in
58 * order to delete structure). */
59 } Border;
60
61 /*
62 * Hash table to map from a border's values (color, etc.) to a
63 * Border structure for those values.
64 */
65
66 static Tcl_HashTable borderTable;
67 typedef struct {
68 Tk_Uid colorName; /* Color for border. */
69 Colormap colormap; /* Colormap used for allocating border
70 * colors. */
71 Screen *screen; /* Screen on which border will be drawn. */
72 } BorderKey;
73
74 /*
75 * Maximum intensity for a color:
76 */
77
78 #define MAX_INTENSITY 65535
79
80
81 static int initialized = 0; /* 0 means static structures haven't
82 * been initialized yet. */
83
84 /*
85 * Forward declarations for procedures defined in this file:
86 */
87
88 static void BorderInit _ANSI_ARGS_((void));
89 static void GetShadows _ANSI_ARGS_((Border *borderPtr,
90 Tk_Window tkwin));
91 static int Intersect _ANSI_ARGS_((XPoint *a1Ptr, XPoint *a2Ptr,
92 XPoint *b1Ptr, XPoint *b2Ptr, XPoint *iPtr));
93 static void ShiftLine _ANSI_ARGS_((XPoint *p1Ptr, XPoint *p2Ptr,
94 int distance, XPoint *p3Ptr));
95
96 /*
97 *--------------------------------------------------------------
98 *
99 * Tk_Get3DBorder --
100 *
101 * Create a data structure for displaying a 3-D border.
102 *
103 * Results:
104 * The return value is a token for a data structure
105 * describing a 3-D border. This token may be passed
106 * to Tk_Draw3DRectangle and Tk_Free3DBorder. If an
107 * error prevented the border from being created then
108 * NULL is returned and an error message will be left
109 * in interp->result.
110 *
111 * Side effects:
112 * Data structures, graphics contexts, etc. are allocated.
113 * It is the caller's responsibility to eventually call
114 * Tk_Free3DBorder to release the resources.
115 *
116 *--------------------------------------------------------------
117 */
118
119 Tk_3DBorder
Tk_Get3DBorder(interp,tkwin,colorName)120 Tk_Get3DBorder(interp, tkwin, colorName)
121 Tcl_Interp *interp; /* Place to store an error message. */
122 Tk_Window tkwin; /* Token for window in which border will
123 * be drawn. */
124 Tk_Uid colorName; /* String giving name of color
125 * for window background. */
126 {
127 BorderKey key;
128 Tcl_HashEntry *hashPtr;
129 register Border *borderPtr;
130 int new;
131 XGCValues gcValues;
132
133 if (!initialized) {
134 BorderInit();
135 }
136
137 /*
138 * First, check to see if there's already a border that will work
139 * for this request.
140 */
141
142 key.colorName = colorName;
143 key.colormap = Tk_Colormap(tkwin);
144 key.screen = Tk_Screen(tkwin);
145
146 hashPtr = Tcl_CreateHashEntry(&borderTable, (char *) &key, &new);
147 if (!new) {
148 borderPtr = (Border *) Tcl_GetHashValue(hashPtr);
149 borderPtr->refCount++;
150 } else {
151
152 /*
153 * No satisfactory border exists yet. Initialize a new one.
154 */
155
156 borderPtr = (Border *) ckalloc(sizeof(Border));
157 borderPtr->screen = Tk_Screen(tkwin);
158 borderPtr->visual = Tk_Visual(tkwin);
159 borderPtr->depth = Tk_Depth(tkwin);
160 borderPtr->colormap = key.colormap;
161 borderPtr->refCount = 1;
162 borderPtr->bgColorPtr = NULL;
163 borderPtr->darkColorPtr = NULL;
164 borderPtr->lightColorPtr = NULL;
165 borderPtr->shadow = None;
166 borderPtr->bgGC = None;
167 borderPtr->darkGC = None;
168 borderPtr->lightGC = None;
169 borderPtr->hashPtr = hashPtr;
170 Tcl_SetHashValue(hashPtr, borderPtr);
171
172 /*
173 * Create the information for displaying the background color,
174 * but delay the allocation of shadows until they are actually
175 * needed for drawing.
176 */
177
178 borderPtr->bgColorPtr = Tk_GetColor(interp, tkwin, colorName);
179 if (borderPtr->bgColorPtr == NULL) {
180 goto error;
181 }
182 gcValues.foreground = borderPtr->bgColorPtr->pixel;
183 borderPtr->bgGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
184 }
185 return (Tk_3DBorder) borderPtr;
186
187 error:
188 Tk_Free3DBorder((Tk_3DBorder) borderPtr);
189 return NULL;
190 }
191
192 /*
193 *--------------------------------------------------------------
194 *
195 * Tk_3DVerticalBevel --
196 *
197 * This procedure draws a vertical bevel along one side of
198 * an object. The bevel is always rectangular in shape:
199 * |||
200 * |||
201 * |||
202 * |||
203 * |||
204 * |||
205 * An appropriate shadow color is chosen for the bevel based
206 * on the leftBevel and relief arguments. Normally this
207 * procedure is called first, then Tk_3DHorizontalBevel is
208 * called next to draw neat corners.
209 *
210 * Results:
211 * None.
212 *
213 * Side effects:
214 * Graphics are drawn in drawable.
215 *
216 *--------------------------------------------------------------
217 */
218
219 void
Tk_3DVerticalBevel(tkwin,drawable,border,x,y,width,height,leftBevel,relief)220 Tk_3DVerticalBevel(tkwin, drawable, border, x, y, width, height,
221 leftBevel, relief)
222 Tk_Window tkwin; /* Window for which border was allocated. */
223 Drawable drawable; /* X window or pixmap in which to draw. */
224 Tk_3DBorder border; /* Token for border to draw. */
225 int x, y, width, height; /* Area of vertical bevel. */
226 int leftBevel; /* Non-zero means this bevel forms the
227 * left side of the object; 0 means it
228 * forms the right side. */
229 int relief; /* Kind of bevel to draw. For example,
230 * TK_RELIEF_RAISED means interior of
231 * object should appear higher than
232 * exterior. */
233 {
234 Border *borderPtr = (Border *) border;
235 GC left, right;
236 Display *display = Tk_Display(tkwin);
237
238 if ((borderPtr->lightGC == None) && (relief != TK_RELIEF_FLAT)) {
239 GetShadows(borderPtr, tkwin);
240 }
241 if (relief == TK_RELIEF_RAISED) {
242 XFillRectangle(display, drawable,
243 (leftBevel) ? borderPtr->lightGC : borderPtr->darkGC,
244 x, y, (unsigned) width, (unsigned) height);
245 } else if (relief == TK_RELIEF_SUNKEN) {
246 XFillRectangle(display, drawable,
247 (leftBevel) ? borderPtr->darkGC : borderPtr->lightGC,
248 x, y, (unsigned) width, (unsigned) height);
249 } else if (relief == TK_RELIEF_RIDGE) {
250 int half;
251
252 left = borderPtr->lightGC;
253 right = borderPtr->darkGC;
254 ridgeGroove:
255 half = width/2;
256 if (!leftBevel && (width & 1)) {
257 half++;
258 }
259 XFillRectangle(display, drawable, left, x, y, (unsigned) half,
260 (unsigned) height);
261 XFillRectangle(display, drawable, right, x+half, y,
262 (unsigned) (width-half), (unsigned) height);
263 } else if (relief == TK_RELIEF_GROOVE) {
264 left = borderPtr->darkGC;
265 right = borderPtr->lightGC;
266 goto ridgeGroove;
267 } else if (relief == TK_RELIEF_FLAT) {
268 XFillRectangle(display, drawable, borderPtr->bgGC, x, y,
269 (unsigned) width, (unsigned) height);
270 }
271 }
272
273 /*
274 *--------------------------------------------------------------
275 *
276 * Tk_3DHorizontalBevel --
277 *
278 * This procedure draws a horizontal bevel along one side of
279 * an object. The bevel has mitered corners (depending on
280 * leftIn and rightIn arguments).
281 *
282 * Results:
283 * None.
284 *
285 * Side effects:
286 * None.
287 *
288 *--------------------------------------------------------------
289 */
290
291 void
Tk_3DHorizontalBevel(tkwin,drawable,border,x,y,width,height,leftIn,rightIn,topBevel,relief)292 Tk_3DHorizontalBevel(tkwin, drawable, border, x, y, width, height,
293 leftIn, rightIn, topBevel, relief)
294 Tk_Window tkwin; /* Window for which border was allocated. */
295 Drawable drawable; /* X window or pixmap in which to draw. */
296 Tk_3DBorder border; /* Token for border to draw. */
297 int x, y, width, height; /* Bounding box of area of bevel. Height
298 * gives width of border. */
299 int leftIn, rightIn; /* Describes whether the left and right
300 * edges of the bevel angle in or out as
301 * they go down. For example, if "leftIn"
302 * is true, the left side of the bevel
303 * looks like this:
304 * ___________
305 * __________
306 * _________
307 * ________
308 */
309 int topBevel; /* Non-zero means this bevel forms the
310 * top side of the object; 0 means it
311 * forms the bottom side. */
312 int relief; /* Kind of bevel to draw. For example,
313 * TK_RELIEF_RAISED means interior of
314 * object should appear higher than
315 * exterior. */
316 {
317 Border *borderPtr = (Border *) border;
318 Display *display = Tk_Display(tkwin);
319 int bottom, halfway, x1, x2, x1Delta, x2Delta;
320 GC topGC = None, bottomGC = None;
321 /* Initializations needed only to prevent
322 * compiler warnings. */
323
324 if ((borderPtr->lightGC == None) && (relief != TK_RELIEF_FLAT)) {
325 GetShadows(borderPtr, tkwin);
326 }
327
328 /*
329 * Compute a GC for the top half of the bevel and a GC for the
330 * bottom half (they're the same in many cases).
331 */
332
333 switch (relief) {
334 case TK_RELIEF_RAISED:
335 topGC = bottomGC =
336 (topBevel) ? borderPtr->lightGC : borderPtr->darkGC;
337 break;
338 case TK_RELIEF_SUNKEN:
339 topGC = bottomGC =
340 (topBevel) ? borderPtr->darkGC : borderPtr->lightGC;
341 break;
342 case TK_RELIEF_RIDGE:
343 topGC = borderPtr->lightGC;
344 bottomGC = borderPtr->darkGC;
345 break;
346 case TK_RELIEF_GROOVE:
347 topGC = borderPtr->darkGC;
348 bottomGC = borderPtr->lightGC;
349 break;
350 case TK_RELIEF_FLAT:
351 topGC = bottomGC = borderPtr->bgGC;
352 break;
353 }
354
355 /*
356 * Compute various other geometry-related stuff.
357 */
358
359 x1 = x;
360 if (!leftIn) {
361 x1 += height;
362 }
363 x2 = x+width;
364 if (!rightIn) {
365 x2 -= height;
366 }
367 x1Delta = (leftIn) ? 1 : -1;
368 x2Delta = (rightIn) ? -1 : 1;
369 halfway = y + height/2;
370 if (!topBevel && (height & 1)) {
371 halfway++;
372 }
373 bottom = y + height;
374
375 /*
376 * Draw one line for each y-coordinate covered by the bevel.
377 */
378
379 for ( ; y < bottom; y++) {
380 /*
381 * In some weird cases (such as large border widths for skinny
382 * rectangles) x1 can be >= x2. Don't draw the lines
383 * in these cases.
384 */
385
386 if (x1 < x2) {
387 XFillRectangle(display, drawable,
388 (y < halfway) ? topGC : bottomGC, x1, y,
389 (unsigned) (x2-x1), (unsigned) 1);
390 }
391 x1 += x1Delta;
392 x2 += x2Delta;
393 }
394 }
395
396 /*
397 *--------------------------------------------------------------
398 *
399 * Tk_Draw3DRectangle --
400 *
401 * Draw a 3-D border at a given place in a given window.
402 *
403 * Results:
404 * None.
405 *
406 * Side effects:
407 * A 3-D border will be drawn in the indicated drawable.
408 * The outside edges of the border will be determined by x,
409 * y, width, and height. The inside edges of the border
410 * will be determined by the borderWidth argument.
411 *
412 *--------------------------------------------------------------
413 */
414
415 void
Tk_Draw3DRectangle(tkwin,drawable,border,x,y,width,height,borderWidth,relief)416 Tk_Draw3DRectangle(tkwin, drawable, border, x, y, width, height,
417 borderWidth, relief)
418 Tk_Window tkwin; /* Window for which border was allocated. */
419 Drawable drawable; /* X window or pixmap in which to draw. */
420 Tk_3DBorder border; /* Token for border to draw. */
421 int x, y, width, height; /* Outside area of region in
422 * which border will be drawn. */
423 int borderWidth; /* Desired width for border, in
424 * pixels. */
425 int relief; /* Type of relief: TK_RELIEF_RAISED,
426 * TK_RELIEF_SUNKEN, TK_RELIEF_GROOVE, etc. */
427 {
428 if (width < 2*borderWidth) {
429 borderWidth = width/2;
430 }
431 if (height < 2*borderWidth) {
432 borderWidth = height/2;
433 }
434 Tk_3DVerticalBevel(tkwin, drawable, border, x, y, borderWidth, height,
435 1, relief);
436 Tk_3DVerticalBevel(tkwin, drawable, border, x+width-borderWidth, y,
437 borderWidth, height, 0, relief);
438 Tk_3DHorizontalBevel(tkwin, drawable, border, x, y, width, borderWidth,
439 1, 1, 1, relief);
440 Tk_3DHorizontalBevel(tkwin, drawable, border, x, y+height-borderWidth,
441 width, borderWidth, 0, 0, 0, relief);
442 }
443
444 /*
445 *--------------------------------------------------------------
446 *
447 * Tk_NameOf3DBorder --
448 *
449 * Given a border, return a textual string identifying the
450 * border's color.
451 *
452 * Results:
453 * The return value is the string that was used to create
454 * the border.
455 *
456 * Side effects:
457 * None.
458 *
459 *--------------------------------------------------------------
460 */
461
462 char *
Tk_NameOf3DBorder(border)463 Tk_NameOf3DBorder(border)
464 Tk_3DBorder border; /* Token for border. */
465 {
466 Border *borderPtr = (Border *) border;
467 void *ptr = borderPtr->hashPtr->key.words;
468
469 return ((BorderKey *) ptr)->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 *
Tk_3DBorderColor(border)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, returns one of the graphics contexts used to
501 * draw the border.
502 *
503 * Results:
504 * Returns the graphics context given by the "which" argument.
505 *
506 * Side effects:
507 * None.
508 *
509 *--------------------------------------------------------------------
510 */
511 GC
Tk_3DBorderGC(tkwin,border,which)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
Tk_Free3DBorder(border)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
Tk_SetBackgroundFromBorder(tkwin,border)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
Tk_GetRelief(interp,name,reliefPtr)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 *
Tk_NameOfRelief(relief)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
Tk_Draw3DPolygon(tkwin,drawable,border,pointPtr,numPoints,borderWidth,leftRelief)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
Tk_Fill3DRectangle(tkwin,drawable,border,x,y,width,height,borderWidth,relief)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
Tk_Fill3DPolygon(tkwin,drawable,border,pointPtr,numPoints,borderWidth,leftRelief)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
BorderInit()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
ShiftLine(p1Ptr,p2Ptr,distance,p3Ptr)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
Intersect(a1Ptr,a2Ptr,b1Ptr,b2Ptr,iPtr)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
GetShadows(borderPtr,tkwin)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