source: trunk/kitgen/8.x/blt/generic/bltGrMisc.c@ 201

Last change on this file since 201 was 175, checked in by demin, 12 years ago

initial commit

File size: 48.3 KB
Line 
1
2/*
3 * bltGrMisc.c --
4 *
5 * This module implements miscellaneous routines for the BLT
6 * graph widget.
7 *
8 * Copyright 1993-1998 Lucent Technologies, Inc.
9 *
10 * Permission to use, copy, modify, and distribute this software and
11 * its documentation for any purpose and without fee is hereby
12 * granted, provided that the above copyright notice appear in all
13 * copies and that both that the copyright notice and warranty
14 * disclaimer appear in supporting documentation, and that the names
15 * of Lucent Technologies any of their entities not be used in
16 * advertising or publicity pertaining to distribution of the software
17 * without specific, written prior permission.
18 *
19 * Lucent Technologies disclaims all warranties with regard to this
20 * software, including all implied warranties of merchantability and
21 * fitness. In no event shall Lucent Technologies be liable for any
22 * special, indirect or consequential damages or any damages
23 * whatsoever resulting from loss of use, data or profits, whether in
24 * an action of contract, negligence or other tortuous action, arising
25 * out of or in connection with the use or performance of this
26 * software.
27 */
28
29#include "bltGraph.h"
30#include <X11/Xutil.h>
31
32#if defined(__STDC__)
33#include <stdarg.h>
34#else
35#include <varargs.h>
36#endif
37
38
39static Tk_OptionParseProc StringToPoint;
40static Tk_OptionPrintProc PointToString;
41static Tk_OptionParseProc StringToColorPair;
42static Tk_OptionPrintProc ColorPairToString;
43Tk_CustomOption bltPointOption =
44{
45 StringToPoint, PointToString, (ClientData)0
46};
47Tk_CustomOption bltColorPairOption =
48{
49 StringToColorPair, ColorPairToString, (ClientData)0
50};
51
52/* ----------------------------------------------------------------------
53 * Custom option parse and print procedures
54 * ----------------------------------------------------------------------
55 */
56
57/*
58 *----------------------------------------------------------------------
59 *
60 * Blt_GetXY --
61 *
62 * Converts a string in the form "@x,y" into an XPoint structure
63 * of the x and y coordinates.
64 *
65 * Results:
66 * A standard Tcl result. If the string represents a valid position
67 * *pointPtr* will contain the converted x and y coordinates and
68 * TCL_OK is returned. Otherwise, TCL_ERROR is returned and
69 * interp->result will contain an error message.
70 *
71 *----------------------------------------------------------------------
72 */
73int
74Blt_GetXY(interp, tkwin, string, xPtr, yPtr)
75 Tcl_Interp *interp;
76 Tk_Window tkwin;
77 char *string;
78 int *xPtr, *yPtr;
79{
80 char *comma;
81 int result;
82 int x, y;
83
84 if ((string == NULL) || (*string == '\0')) {
85 *xPtr = *yPtr = -SHRT_MAX;
86 return TCL_OK;
87 }
88 if (*string != '@') {
89 goto badFormat;
90 }
91 comma = strchr(string + 1, ',');
92 if (comma == NULL) {
93 goto badFormat;
94 }
95 *comma = '\0';
96 result = ((Tk_GetPixels(interp, tkwin, string + 1, &x) == TCL_OK) &&
97 (Tk_GetPixels(interp, tkwin, comma + 1, &y) == TCL_OK));
98 *comma = ',';
99 if (!result) {
100 Tcl_AppendResult(interp, ": can't parse position \"", string, "\"",
101 (char *)NULL);
102 return TCL_ERROR;
103 }
104 *xPtr = x, *yPtr = y;
105 return TCL_OK;
106
107 badFormat:
108 Tcl_AppendResult(interp, "bad position \"", string,
109 "\": should be \"@x,y\"", (char *)NULL);
110 return TCL_ERROR;
111}
112
113/*
114 *----------------------------------------------------------------------
115 *
116 * StringToPoint --
117 *
118 * Convert the string representation of a legend XY position into
119 * window coordinates. The form of the string must be "@x,y" or
120 * none.
121 *
122 * Results:
123 * A standard Tcl result. The symbol type is written into the
124 * widget record.
125 *
126 *----------------------------------------------------------------------
127 */
128/*ARGSUSED*/
129static int
130StringToPoint(clientData, interp, tkwin, string, widgRec, offset)
131 ClientData clientData; /* Not used. */
132 Tcl_Interp *interp; /* Interpreter to send results back to */
133 Tk_Window tkwin; /* Not used. */
134 char *string; /* New legend position string */
135 char *widgRec; /* Widget record */
136 int offset; /* offset to XPoint structure */
137{
138 XPoint *pointPtr = (XPoint *)(widgRec + offset);
139 int x, y;
140
141 if (Blt_GetXY(interp, tkwin, string, &x, &y) != TCL_OK) {
142 return TCL_ERROR;
143 }
144 pointPtr->x = x, pointPtr->y = y;
145 return TCL_OK;
146}
147
148/*
149 *----------------------------------------------------------------------
150 *
151 * PointToString --
152 *
153 * Convert the window coordinates into a string.
154 *
155 * Results:
156 * The string representing the coordinate position is returned.
157 *
158 *----------------------------------------------------------------------
159 */
160/*ARGSUSED*/
161static char *
162PointToString(clientData, tkwin, widgRec, offset, freeProcPtr)
163 ClientData clientData; /* Not used. */
164 Tk_Window tkwin; /* Not used. */
165 char *widgRec; /* Widget record */
166 int offset; /* offset of XPoint in record */
167 Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
168{
169 char *result;
170 XPoint *pointPtr = (XPoint *)(widgRec + offset);
171
172 result = "";
173 if ((pointPtr->x != -SHRT_MAX) && (pointPtr->y != -SHRT_MAX)) {
174 char string[200];
175
176 sprintf(string, "@%d,%d", pointPtr->x, pointPtr->y);
177 result = Blt_Strdup(string);
178 assert(result);
179 *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
180 }
181 return result;
182}
183
184/*LINTLIBRARY*/
185static int
186GetColorPair(interp, tkwin, fgStr, bgStr, pairPtr, allowDefault)
187 Tcl_Interp *interp;
188 Tk_Window tkwin;
189 char *fgStr, *bgStr;
190 ColorPair *pairPtr;
191 int allowDefault;
192{
193 unsigned int length;
194 XColor *fgColor, *bgColor;
195
196 length = strlen(fgStr);
197 if (fgStr[0] == '\0') {
198 fgColor = NULL;
199 } else if ((allowDefault) && (fgStr[0] == 'd') &&
200 (strncmp(fgStr, "defcolor", length) == 0)) {
201 fgColor = COLOR_DEFAULT;
202 } else {
203 fgColor = Tk_GetColor(interp, tkwin, Tk_GetUid(fgStr));
204 if (fgColor == NULL) {
205 return TCL_ERROR;
206 }
207 }
208 length = strlen(bgStr);
209 if (bgStr[0] == '\0') {
210 bgColor = NULL;
211 } else if ((allowDefault) && (bgStr[0] == 'd') &&
212 (strncmp(bgStr, "defcolor", length) == 0)) {
213 bgColor = COLOR_DEFAULT;
214 } else {
215 bgColor = Tk_GetColor(interp, tkwin, Tk_GetUid(bgStr));
216 if (bgColor == NULL) {
217 return TCL_ERROR;
218 }
219 }
220 pairPtr->fgColor = fgColor;
221 pairPtr->bgColor = bgColor;
222 return TCL_OK;
223}
224
225void
226Blt_FreeColorPair(pairPtr)
227 ColorPair *pairPtr;
228{
229 if ((pairPtr->bgColor != NULL) && (pairPtr->bgColor != COLOR_DEFAULT)) {
230 Tk_FreeColor(pairPtr->bgColor);
231 }
232 if ((pairPtr->fgColor != NULL) && (pairPtr->fgColor != COLOR_DEFAULT)) {
233 Tk_FreeColor(pairPtr->fgColor);
234 }
235 pairPtr->bgColor = pairPtr->fgColor = NULL;
236}
237
238/*
239 *----------------------------------------------------------------------
240 *
241 * StringToColorPair --
242 *
243 * Convert the color names into pair of XColor pointers.
244 *
245 * Results:
246 * A standard Tcl result. The color pointer is written into the
247 * widget record.
248 *
249 *----------------------------------------------------------------------
250 */
251/*ARGSUSED*/
252static int
253StringToColorPair(clientData, interp, tkwin, string, widgRec, offset)
254 ClientData clientData; /* Not used. */
255 Tcl_Interp *interp; /* Interpreter to send results back to */
256 Tk_Window tkwin; /* Not used. */
257 char *string; /* String representing color */
258 char *widgRec; /* Widget record */
259 int offset; /* Offset of color field in record */
260{
261 ColorPair *pairPtr = (ColorPair *)(widgRec + offset);
262 ColorPair sample;
263 int allowDefault = (int)clientData;
264
265 sample.fgColor = sample.bgColor = NULL;
266 if ((string != NULL) && (*string != '\0')) {
267 int nColors;
268 char **colors;
269 int result;
270
271 if (Tcl_SplitList(interp, string, &nColors, &colors) != TCL_OK) {
272 return TCL_ERROR;
273 }
274 switch (nColors) {
275 case 0:
276 result = TCL_OK;
277 break;
278 case 1:
279 result = GetColorPair(interp, tkwin, colors[0], "", &sample,
280 allowDefault);
281 break;
282 case 2:
283 result = GetColorPair(interp, tkwin, colors[0], colors[1],
284 &sample, allowDefault);
285 break;
286 default:
287 result = TCL_ERROR;
288 Tcl_AppendResult(interp, "too many names in colors list",
289 (char *)NULL);
290 }
291 Blt_Free(colors);
292 if (result != TCL_OK) {
293 return TCL_ERROR;
294 }
295 }
296 Blt_FreeColorPair(pairPtr);
297 *pairPtr = sample;
298 return TCL_OK;
299}
300
301/*
302 *----------------------------------------------------------------------
303 *
304 * NameOfColor --
305 *
306 * Convert the color option value into a string.
307 *
308 * Results:
309 * The static string representing the color option is returned.
310 *
311 *----------------------------------------------------------------------
312 */
313static char *
314NameOfColor(colorPtr)
315 XColor *colorPtr;
316{
317 if (colorPtr == NULL) {
318 return "";
319 } else if (colorPtr == COLOR_DEFAULT) {
320 return "defcolor";
321 } else {
322 return Tk_NameOfColor(colorPtr);
323 }
324}
325
326/*
327 *----------------------------------------------------------------------
328 *
329 * ColorPairToString --
330 *
331 * Convert the color pairs into color names.
332 *
333 * Results:
334 * The string representing the symbol color is returned.
335 *
336 *----------------------------------------------------------------------
337 */
338/*ARGSUSED*/
339static char *
340ColorPairToString(clientData, tkwin, widgRec, offset, freeProcPtr)
341 ClientData clientData; /* Not used. */
342 Tk_Window tkwin; /* Not used. */
343 char *widgRec; /* Element information record */
344 int offset; /* Offset of symbol type field in record */
345 Tcl_FreeProc **freeProcPtr; /* Not used. */
346{
347 ColorPair *pairPtr = (ColorPair *)(widgRec + offset);
348 Tcl_DString dString;
349 char *result;
350
351 Tcl_DStringInit(&dString);
352 Tcl_DStringAppendElement(&dString, NameOfColor(pairPtr->fgColor));
353 Tcl_DStringAppendElement(&dString, NameOfColor(pairPtr->bgColor));
354 result = Tcl_DStringValue(&dString);
355 if (result == dString.staticSpace) {
356 result = Blt_Strdup(result);
357 }
358 *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
359 return result;
360}
361
362int
363Blt_PointInSegments(samplePtr, segments, nSegments, halo)
364 Point2D *samplePtr;
365 Segment2D *segments;
366 int nSegments;
367 double halo;
368{
369 register Segment2D *segPtr, *endPtr;
370 double left, right, top, bottom;
371 Point2D p, t;
372 double dist, minDist;
373
374 minDist = DBL_MAX;
375 for (segPtr = segments, endPtr = segments + nSegments; segPtr < endPtr;
376 segPtr++) {
377 t = Blt_GetProjection((int)samplePtr->x, (int)samplePtr->y,
378 &segPtr->p, &segPtr->q);
379 if (segPtr->p.x > segPtr->q.x) {
380 right = segPtr->p.x, left = segPtr->q.x;
381 } else {
382 right = segPtr->q.x, left = segPtr->p.x;
383 }
384 if (segPtr->p.y > segPtr->q.y) {
385 bottom = segPtr->p.y, top = segPtr->q.y;
386 } else {
387 bottom = segPtr->q.y, top = segPtr->p.y;
388 }
389 p.x = BOUND(t.x, left, right);
390 p.y = BOUND(t.y, top, bottom);
391 dist = hypot(p.x - samplePtr->x, p.y - samplePtr->y);
392 if (dist < minDist) {
393 minDist = dist;
394 }
395 }
396 return (minDist < halo);
397}
398
399int
400Blt_PointInPolygon(samplePtr, points, nPoints)
401 Point2D *samplePtr;
402 Point2D *points;
403 int nPoints;
404{
405 double b;
406 register Point2D *p, *q, *endPtr;
407 register int count;
408
409 count = 0;
410 for (p = points, q = p + 1, endPtr = p + nPoints; q < endPtr; p++, q++) {
411 if (((p->y <= samplePtr->y) && (samplePtr->y < q->y)) ||
412 ((q->y <= samplePtr->y) && (samplePtr->y < p->y))) {
413 b = (q->x - p->x) * (samplePtr->y - p->y) / (q->y - p->y) + p->x;
414 if (samplePtr->x < b) {
415 count++; /* Count the number of intersections. */
416 }
417 }
418 }
419 return (count & 0x01);
420}
421
422int
423Blt_RegionInPolygon(extsPtr, points, nPoints, enclosed)
424 Extents2D *extsPtr;
425 Point2D *points;
426 int nPoints;
427 int enclosed;
428{
429 register Point2D *pointPtr, *endPtr;
430
431 if (enclosed) {
432 /*
433 * All points of the polygon must be inside the rectangle.
434 */
435 for (pointPtr = points, endPtr = points + nPoints; pointPtr < endPtr;
436 pointPtr++) {
437 if ((pointPtr->x < extsPtr->left) ||
438 (pointPtr->x > extsPtr->right) ||
439 (pointPtr->y < extsPtr->top) ||
440 (pointPtr->y > extsPtr->bottom)) {
441 return FALSE; /* One point is exterior. */
442 }
443 }
444 return TRUE;
445 } else {
446 Point2D p, q;
447
448 /*
449 * If any segment of the polygon clips the bounding region, the
450 * polygon overlaps the rectangle.
451 */
452 points[nPoints] = points[0];
453 for (pointPtr = points, endPtr = points + nPoints; pointPtr < endPtr;
454 pointPtr++) {
455 p = *pointPtr;
456 q = *(pointPtr + 1);
457 if (Blt_LineRectClip(extsPtr, &p, &q)) {
458 return TRUE;
459 }
460 }
461 /*
462 * Otherwise the polygon and rectangle are either disjoint
463 * or enclosed. Check if one corner of the rectangle is
464 * inside the polygon.
465 */
466 p.x = extsPtr->left;
467 p.y = extsPtr->top;
468 return Blt_PointInPolygon(&p, points, nPoints);
469 }
470}
471
472/*
473 *----------------------------------------------------------------------
474 *
475 * Blt_GraphExtents --
476 *
477 * Generates a bounding box representing the plotting area of
478 * the graph. This data structure is used to clip the points and
479 * line segments of the line element.
480 *
481 * The clip region is the plotting area plus such arbitrary extra
482 * space. The reason we clip with a bounding box larger than the
483 * plot area is so that symbols will be drawn even if their center
484 * point isn't in the plotting area.
485 *
486 * Results:
487 * None.
488 *
489 * Side Effects:
490 * The bounding box is filled with the dimensions of the plotting
491 * area.
492 *
493 *----------------------------------------------------------------------
494 */
495void
496Blt_GraphExtents(graphPtr, extsPtr)
497 Graph *graphPtr;
498 Extents2D *extsPtr;
499{
500 extsPtr->left = (double)(graphPtr->hOffset - graphPtr->padX.side1);
501 extsPtr->top = (double)(graphPtr->vOffset - graphPtr->padY.side1);
502 extsPtr->right = (double)(graphPtr->hOffset + graphPtr->hRange +
503 graphPtr->padX.side2);
504 extsPtr->bottom = (double)(graphPtr->vOffset + graphPtr->vRange +
505 graphPtr->padY.side2);
506}
507
508static int
509ClipTest (double ds, double dr, double *t1, double *t2)
510{
511 double t;
512
513 if (ds < 0.0) {
514 t = dr / ds;
515 if (t > *t2) {
516 return FALSE;
517 }
518 if (t > *t1) {
519 *t1 = t;
520 }
521 } else if (ds > 0.0) {
522 t = dr / ds;
523 if (t < *t1) {
524 return FALSE;
525 }
526 if (t < *t2) {
527 *t2 = t;
528 }
529 } else {
530 /* d = 0, so line is parallel to this clipping edge */
531 if (dr < 0.0) { /* Line is outside clipping edge */
532 return FALSE;
533 }
534 }
535 return TRUE;
536}
537
538/*
539 *----------------------------------------------------------------------
540 *
541 * Blt_LineRectClip --
542 *
543 * Clips the given line segment to a rectangular region. The
544 * coordinates of the clipped line segment are returned. The
545 * original coordinates are overwritten.
546 *
547 * Reference: Liang-Barsky Line Clipping Algorithm.
548 *
549 * Results:
550 * Returns if line segment is visible within the region. The
551 * coordinates of the original line segment are overwritten
552 * by the clipped coordinates.
553 *
554 *----------------------------------------------------------------------
555 */
556int
557Blt_LineRectClip(extsPtr, p, q)
558 Extents2D *extsPtr; /* Rectangular region to clip. */
559 Point2D *p, *q; /* (in/out) Coordinates of original
560 * and clipped line segment. */
561{
562 double t1, t2;
563 double dx, dy;
564
565 t1 = 0.0;
566 t2 = 1.0;
567 dx = q->x - p->x;
568 if ((ClipTest (-dx, p->x - extsPtr->left, &t1, &t2)) &&
569 (ClipTest (dx, extsPtr->right - p->x, &t1, &t2))) {
570 dy = q->y - p->y;
571 if ((ClipTest (-dy, p->y - extsPtr->top, &t1, &t2)) &&
572 (ClipTest (dy, extsPtr->bottom - p->y, &t1, &t2))) {
573 if (t2 < 1.0) {
574 q->x = p->x + t2 * dx;
575 q->y = p->y + t2 * dy;
576 }
577 if (t1 > 0.0) {
578 p->x += t1 * dx;
579 p->y += t1 * dy;
580 }
581 return TRUE;
582 }
583 }
584 return FALSE;
585}
586
587/*
588 *----------------------------------------------------------------------
589 *
590 * Blt_PolyRectClip --
591 *
592 * Clips the given polygon to a rectangular region. The resulting
593 * polygon is returned. Note that the resulting polyon may be
594 * complex, connected by zero width/height segments. The drawing
595 * routine (such as XFillPolygon) will not draw a connecting
596 * segment.
597 *
598 * Reference: Liang-Barsky Polygon Clipping Algorithm
599 *
600 * Results:
601 * Returns the number of points in the clipped polygon. The
602 * points of the clipped polygon are stored in *outputPts*.
603 *
604 *----------------------------------------------------------------------
605 */
606#define EPSILON FLT_EPSILON
607#define AddVertex(vx, vy) r->x=(vx), r->y=(vy), r++, count++
608#define LastVertex(vx, vy) r->x=(vx), r->y=(vy), count++
609
610int
611Blt_PolyRectClip(extsPtr, points, nPoints, clipPts)
612 Extents2D *extsPtr;
613 Point2D *points;
614 int nPoints;
615 Point2D *clipPts;
616{
617 Point2D *endPtr;
618 double dx, dy;
619 double tin1, tin2;
620 double tinx, tiny;
621 double xin, yin, xout, yout;
622 int count;
623 register Point2D *p; /* First vertex of input polygon edge. */
624 register Point2D *q; /* Last vertex of input polygon edge. */
625 register Point2D *r;
626
627 r = clipPts;
628 count = 0; /* Counts # of vertices in output polygon. */
629
630 points[nPoints] = points[0];
631
632 for (p = points, q = p + 1, endPtr = p + nPoints; p < endPtr; p++, q++) {
633 dx = q->x - p->x; /* X-direction */
634 dy = q->y - p->y; /* Y-direction */
635
636 if (FABS(dx) < EPSILON) {
637 dx = (p->x > extsPtr->left) ? -EPSILON : EPSILON ;
638 }
639 if (FABS(dy) < EPSILON) {
640 dy = (p->y > extsPtr->top) ? -EPSILON : EPSILON ;
641 }
642
643 if (dx > 0.0) { /* Left */
644 xin = extsPtr->left;
645 xout = extsPtr->right + 1.0;
646 } else { /* Right */
647 xin = extsPtr->right + 1.0;
648 xout = extsPtr->left;
649 }
650 if (dy > 0.0) { /* Top */
651 yin = extsPtr->top;
652 yout = extsPtr->bottom + 1.0;
653 } else { /* Bottom */
654 yin = extsPtr->bottom + 1.0;
655 yout = extsPtr->top;
656 }
657
658 tinx = (xin - p->x) / dx;
659 tiny = (yin - p->y) / dy;
660
661 if (tinx < tiny) { /* Hits x first */
662 tin1 = tinx;
663 tin2 = tiny;
664 } else { /* Hits y first */
665 tin1 = tiny;
666 tin2 = tinx;
667 }
668
669 if (tin1 <= 1.0) {
670 if (tin1 > 0.0) {
671 AddVertex(xin, yin);
672 }
673 if (tin2 <= 1.0) {
674 double toutx, touty, tout1;
675
676 toutx = (xout - p->x) / dx;
677 touty = (yout - p->y) / dy;
678 tout1 = MIN(toutx, touty);
679
680 if ((tin2 > 0.0) || (tout1 > 0.0)) {
681 if (tin2 <= tout1) {
682 if (tin2 > 0.0) {
683 if (tinx > tiny) {
684 AddVertex(xin, p->y + tinx * dy);
685 } else {
686 AddVertex(p->x + tiny * dx, yin);
687 }
688 }
689 if (tout1 < 1.0) {
690 if (toutx < touty) {
691 AddVertex(xout, p->y + toutx * dy);
692 } else {
693 AddVertex(p->x + touty * dx, yout);
694 }
695 } else {
696 AddVertex(q->x, q->y);
697 }
698 } else {
699 if (tinx > tiny) {
700 AddVertex(xin, yout);
701 } else {
702 AddVertex(xout, yin);
703 }
704 }
705 }
706 }
707 }
708 }
709 if (count > 0) {
710 LastVertex(clipPts[0].x, clipPts[0].y);
711 }
712 return count;
713}
714
715/*
716 *----------------------------------------------------------------------
717 *
718 * Blt_GetProjection --
719 *
720 * Computes the projection of a point on a line. The line (given
721 * by two points), is assumed the be infinite.
722 *
723 * Compute the slope (angle) of the line and rotate it 90 degrees.
724 * Using the slope-intercept method (we know the second line from
725 * the sample test point and the computed slope), then find the
726 * intersection of both lines. This will be the projection of the
727 * sample point on the first line.
728 *
729 * Results:
730 * Returns the coordinates of the projection on the line.
731 *
732 *----------------------------------------------------------------------
733 */
734Point2D
735Blt_GetProjection(x, y, p, q)
736 int x, y; /* Screen coordinates of the sample point. */
737 Point2D *p, *q; /* Line segment to project point onto */
738{
739 double dx, dy;
740 Point2D t;
741
742 dx = p->x - q->x;
743 dy = p->y - q->y;
744
745 /* Test for horizontal and vertical lines */
746 if (FABS(dx) < DBL_EPSILON) {
747 t.x = p->x, t.y = (double)y;
748 } else if (FABS(dy) < DBL_EPSILON) {
749 t.x = (double)x, t.y = p->y;
750 } else {
751 double m1, m2; /* Slope of both lines */
752 double b1, b2; /* y-intercepts */
753 double midX, midY; /* Midpoint of line segment. */
754 double ax, ay, bx, by;
755
756 /* Compute the slop and intercept of the line segment. */
757 m1 = (dy / dx);
758 b1 = p->y - (p->x * m1);
759
760 /*
761 * Compute the slope and intercept of a second line segment:
762 * one that intersects through sample X-Y coordinate with a
763 * slope perpendicular to original line.
764 */
765
766 /* Find midpoint of original segment. */
767 midX = (p->x + q->x) * 0.5;
768 midY = (p->y + q->y) * 0.5;
769
770 /* Rotate the line 90 degrees */
771 ax = midX - (0.5 * dy);
772 ay = midY - (0.5 * -dx);
773 bx = midX + (0.5 * dy);
774 by = midY + (0.5 * -dx);
775
776 m2 = (ay - by) / (ax - bx);
777 b2 = y - (x * m2);
778
779 /*
780 * Given the equations of two lines which contain the same point,
781 *
782 * y = m1 * x + b1
783 * y = m2 * x + b2
784 *
785 * solve for the intersection.
786 *
787 * x = (b2 - b1) / (m1 - m2)
788 * y = m1 * x + b1
789 *
790 */
791
792 t.x = (b2 - b1) / (m1 - m2);
793 t.y = m1 * t.x + b1;
794 }
795 return t;
796}
797
798typedef struct {
799 double hue, sat, val;
800} HSV;
801
802#define SetColor(c,r,g,b) ((c)->red = (int)((r) * 65535.0), \
803 (c)->green = (int)((g) * 65535.0), \
804 (c)->blue = (int)((b) * 65535.0))
805
806void
807Blt_XColorToHSV(colorPtr, hsvPtr)
808 XColor *colorPtr;
809 HSV *hsvPtr;
810{
811 unsigned short max, min;
812 double range;
813 unsigned short *colorValues;
814
815 /* Find the minimum and maximum RGB intensities */
816 colorValues = (unsigned short *)&colorPtr->red;
817 max = MAX3(colorValues[0], colorValues[1], colorValues[2]);
818 min = MIN3(colorValues[0], colorValues[1], colorValues[2]);
819
820 hsvPtr->val = (double)max / 65535.0;
821 hsvPtr->hue = hsvPtr->sat = 0.0;
822
823 range = (double)(max - min);
824 if (max != min) {
825 hsvPtr->sat = range / (double)max;
826 }
827 if (hsvPtr->sat > 0.0) {
828 double red, green, blue;
829
830 /* Normalize the RGB values */
831 red = (double)(max - colorPtr->red) / range;
832 green = (double)(max - colorPtr->green) / range;
833 blue = (double)(max - colorPtr->blue) / range;
834
835 if (colorPtr->red == max) {
836 hsvPtr->hue = (blue - green);
837 } else if (colorPtr->green == max) {
838 hsvPtr->hue = 2 + (red - blue);
839 } else if (colorPtr->blue == max) {
840 hsvPtr->hue = 4 + (green - red);
841 }
842 hsvPtr->hue *= 60.0;
843 } else {
844 hsvPtr->sat = 0.5;
845 }
846 if (hsvPtr->hue < 0.0) {
847 hsvPtr->hue += 360.0;
848 }
849}
850
851void
852Blt_HSVToXColor(hsvPtr, colorPtr)
853 HSV *hsvPtr;
854 XColor *colorPtr;
855{
856 double hue, p, q, t;
857 double frac;
858 int quadrant;
859
860 if (hsvPtr->val < 0.0) {
861 hsvPtr->val = 0.0;
862 } else if (hsvPtr->val > 1.0) {
863 hsvPtr->val = 1.0;
864 }
865 if (hsvPtr->sat == 0.0) {
866 SetColor(colorPtr, hsvPtr->val, hsvPtr->val, hsvPtr->val);
867 return;
868 }
869 hue = FMOD(hsvPtr->hue, 360.0) / 60.0;
870 quadrant = (int)floor(hue);
871 frac = hsvPtr->hue - quadrant;
872 p = hsvPtr->val * (1 - (hsvPtr->sat));
873 q = hsvPtr->val * (1 - (hsvPtr->sat * frac));
874 t = hsvPtr->val * (1 - (hsvPtr->sat * (1 - frac)));
875
876 switch (quadrant) {
877 case 0:
878 SetColor(colorPtr, hsvPtr->val, t, p);
879 break;
880 case 1:
881 SetColor(colorPtr, q, hsvPtr->val, p);
882 break;
883 case 2:
884 SetColor(colorPtr, p, hsvPtr->val, t);
885 break;
886 case 3:
887 SetColor(colorPtr, p, q, hsvPtr->val);
888 break;
889 case 4:
890 SetColor(colorPtr, t, p, hsvPtr->val);
891 break;
892 case 5:
893 SetColor(colorPtr, hsvPtr->val, p, q);
894 break;
895 }
896}
897
898/*
899 *----------------------------------------------------------------------
900 *
901 * Blt_AdjustViewport --
902 *
903 * Adjusts the offsets of the viewport according to the scroll mode.
904 * This is to accommodate both "listbox" and "canvas" style scrolling.
905 *
906 * "canvas" The viewport scrolls within the range of world
907 * coordinates. This way the viewport always displays
908 * a full page of the world. If the world is smaller
909 * than the viewport, then (bizarrely) the world and
910 * viewport are inverted so that the world moves up
911 * and down within the viewport.
912 *
913 * "listbox" The viewport can scroll beyond the range of world
914 * coordinates. Every entry can be displayed at the
915 * top of the viewport. This also means that the
916 * scrollbar thumb weirdly shrinks as the last entry
917 * is scrolled upward.
918 *
919 * Results:
920 * The corrected offset is returned.
921 *
922 *----------------------------------------------------------------------
923 */
924int
925Blt_AdjustViewport(offset, worldSize, windowSize, scrollUnits, scrollMode)
926 int offset, worldSize, windowSize;
927 int scrollUnits;
928 int scrollMode;
929{
930 switch (scrollMode) {
931 case BLT_SCROLL_MODE_CANVAS:
932
933 /*
934 * Canvas-style scrolling allows the world to be scrolled
935 * within the window.
936 */
937
938 if (worldSize < windowSize) {
939 if ((worldSize - offset) > windowSize) {
940 offset = worldSize - windowSize;
941 }
942 if (offset > 0) {
943 offset = 0;
944 }
945 } else {
946 if ((offset + windowSize) > worldSize) {
947 offset = worldSize - windowSize;
948 }
949 if (offset < 0) {
950 offset = 0;
951 }
952 }
953 break;
954
955 case BLT_SCROLL_MODE_LISTBOX:
956 if (offset < 0) {
957 offset = 0;
958 }
959 if (offset >= worldSize) {
960 offset = worldSize - scrollUnits;
961 }
962 break;
963
964 case BLT_SCROLL_MODE_HIERBOX:
965
966 /*
967 * Hierbox-style scrolling allows the world to be scrolled
968 * within the window.
969 */
970 if ((offset + windowSize) > worldSize) {
971 offset = worldSize - windowSize;
972 }
973 if (offset < 0) {
974 offset = 0;
975 }
976 break;
977 }
978 return offset;
979}
980
981int
982Blt_GetScrollInfo(interp, argc, argv, offsetPtr, worldSize, windowSize,
983 scrollUnits, scrollMode)
984 Tcl_Interp *interp;
985 int argc;
986 char **argv;
987 int *offsetPtr;
988 int worldSize, windowSize;
989 int scrollUnits;
990 int scrollMode;
991{
992 char c;
993 unsigned int length;
994 int offset;
995 int count;
996 double fract;
997
998 offset = *offsetPtr;
999 c = argv[0][0];
1000 length = strlen(argv[0]);
1001 if ((c == 's') && (strncmp(argv[0], "scroll", length) == 0)) {
1002 if (argc != 3) {
1003 return TCL_ERROR;
1004 }
1005 /* scroll number unit/page */
1006 if (Tcl_GetInt(interp, argv[1], &count) != TCL_OK) {
1007 return TCL_ERROR;
1008 }
1009 c = argv[2][0];
1010 length = strlen(argv[2]);
1011 if ((c == 'u') && (strncmp(argv[2], "units", length) == 0)) {
1012 fract = (double)count *scrollUnits;
1013 } else if ((c == 'p') && (strncmp(argv[2], "pages", length) == 0)) {
1014 /* A page is 90% of the view-able window. */
1015 fract = (double)count *windowSize * 0.9;
1016 } else {
1017 Tcl_AppendResult(interp, "unknown \"scroll\" units \"", argv[2],
1018 "\"", (char *)NULL);
1019 return TCL_ERROR;
1020 }
1021 offset += (int)fract;
1022 } else if ((c == 'm') && (strncmp(argv[0], "moveto", length) == 0)) {
1023 if (argc != 2) {
1024 return TCL_ERROR;
1025 }
1026 /* moveto fraction */
1027 if (Tcl_GetDouble(interp, argv[1], &fract) != TCL_OK) {
1028 return TCL_ERROR;
1029 }
1030 offset = (int)(worldSize * fract);
1031 } else {
1032 /* Treat like "scroll units" */
1033 if (Tcl_GetInt(interp, argv[0], &count) != TCL_OK) {
1034 return TCL_ERROR;
1035 }
1036 fract = (double)count *scrollUnits;
1037 offset += (int)fract;
1038 }
1039 *offsetPtr = Blt_AdjustViewport(offset, worldSize, windowSize, scrollUnits,
1040 scrollMode);
1041 return TCL_OK;
1042}
1043
1044#if (TCL_MAJOR_VERSION >= 8)
1045int
1046Blt_GetScrollInfoFromObj(interp, objc, objv, offsetPtr, worldSize, windowSize,
1047 scrollUnits, scrollMode)
1048 Tcl_Interp *interp;
1049 int objc;
1050 Tcl_Obj *CONST *objv;
1051 int *offsetPtr;
1052 int worldSize, windowSize;
1053 int scrollUnits;
1054 int scrollMode;
1055{
1056 char c;
1057 unsigned int length;
1058 int offset;
1059 int count;
1060 double fract;
1061 char *string;
1062
1063 offset = *offsetPtr;
1064
1065 string = Tcl_GetString(objv[0]);
1066 c = string[0];
1067 length = strlen(string);
1068 if ((c == 's') && (strncmp(string, "scroll", length) == 0)) {
1069 if (objc != 3) {
1070 return TCL_ERROR;
1071 }
1072 /* scroll number unit/page */
1073 if (Tcl_GetIntFromObj(interp, objv[1], &count) != TCL_OK) {
1074 return TCL_ERROR;
1075 }
1076 string = Tcl_GetString(objv[2]);
1077 c = string[0];
1078 length = strlen(string);
1079 if ((c == 'u') && (strncmp(string, "units", length) == 0)) {
1080 fract = (double)count *scrollUnits;
1081 } else if ((c == 'p') && (strncmp(string, "pages", length) == 0)) {
1082 /* A page is 90% of the view-able window. */
1083 fract = (double)count *windowSize * 0.9;
1084 } else {
1085 Tcl_AppendResult(interp, "unknown \"scroll\" units \"",
1086 Tcl_GetString(objv[2]), "\"", (char *)NULL);
1087 return TCL_ERROR;
1088 }
1089 offset += (int)fract;
1090 } else if ((c == 'm') && (strncmp(string, "moveto", length) == 0)) {
1091 if (objc != 2) {
1092 return TCL_ERROR;
1093 }
1094 /* moveto fraction */
1095 if (Tcl_GetDoubleFromObj(interp, objv[1], &fract) != TCL_OK) {
1096 return TCL_ERROR;
1097 }
1098 offset = (int)(worldSize * fract);
1099 } else {
1100 /* Treat like "scroll units" */
1101 if (Tcl_GetIntFromObj(interp, objv[0], &count) != TCL_OK) {
1102 return TCL_ERROR;
1103 }
1104 fract = (double)count *scrollUnits;
1105 offset += (int)fract;
1106 }
1107 *offsetPtr = Blt_AdjustViewport(offset, worldSize, windowSize, scrollUnits,
1108 scrollMode);
1109 return TCL_OK;
1110}
1111#endif /* TCL_MAJOR_VERSION >= 8 */
1112
1113/*
1114 * ----------------------------------------------------------------------
1115 *
1116 * Blt_UpdateScrollbar --
1117 *
1118 * Invoke a Tcl command to the scrollbar, defining the new
1119 * position and length of the scroll. See the Tk documentation
1120 * for further information on the scrollbar. It is assumed the
1121 * scrollbar command prefix is valid.
1122 *
1123 * Results:
1124 * None.
1125 *
1126 * Side Effects:
1127 * Scrollbar is commanded to change position and/or size.
1128 *
1129 * ----------------------------------------------------------------------
1130 */
1131void
1132Blt_UpdateScrollbar(interp, scrollCmd, firstFract, lastFract)
1133 Tcl_Interp *interp;
1134 char *scrollCmd; /* scrollbar command */
1135 double firstFract, lastFract;
1136{
1137 char string[200];
1138 Tcl_DString dString;
1139
1140 Tcl_DStringInit(&dString);
1141 Tcl_DStringAppend(&dString, scrollCmd, -1);
1142 sprintf(string, " %f %f", firstFract, lastFract);
1143 Tcl_DStringAppend(&dString, string, -1);
1144 if (Tcl_GlobalEval(interp, Tcl_DStringValue(&dString)) != TCL_OK) {
1145 Tcl_BackgroundError(interp);
1146 }
1147 Tcl_DStringFree(&dString);
1148}
1149
1150/* -------------- */
1151/*
1152 *----------------------------------------------------------------------
1153 *
1154 * Blt_GetPrivateGCFromDrawable --
1155 *
1156 * Like Tk_GetGC, but doesn't share the GC with any other widget.
1157 * This is needed because the certain GC parameters (like dashes)
1158 * can not be set via XCreateGC, therefore there is no way for
1159 * Tk's hashing mechanism to recognize that two such GCs differ.
1160 *
1161 * Results:
1162 * A new GC is returned.
1163 *
1164 *----------------------------------------------------------------------
1165 */
1166GC
1167Blt_GetPrivateGCFromDrawable(display, drawable, gcMask, valuePtr)
1168 Display *display;
1169 Drawable drawable;
1170 unsigned long gcMask;
1171 XGCValues *valuePtr;
1172{
1173 GC newGC;
1174
1175#ifdef WIN32
1176 newGC = Blt_EmulateXCreateGC(display, drawable, gcMask, valuePtr);
1177#else
1178 newGC = XCreateGC(display, drawable, gcMask, valuePtr);
1179#endif
1180 return newGC;
1181}
1182
1183/*
1184 *----------------------------------------------------------------------
1185 *
1186 * Blt_GetPrivateGC --
1187 *
1188 * Like Tk_GetGC, but doesn't share the GC with any other widget.
1189 * This is needed because the certain GC parameters (like dashes)
1190 * can not be set via XCreateGC, therefore there is no way for
1191 * Tk's hashing mechanism to recognize that two such GCs differ.
1192 *
1193 * Results:
1194 * A new GC is returned.
1195 *
1196 *----------------------------------------------------------------------
1197 */
1198GC
1199Blt_GetPrivateGC(tkwin, gcMask, valuePtr)
1200 Tk_Window tkwin;
1201 unsigned long gcMask;
1202 XGCValues *valuePtr;
1203{
1204 GC gc;
1205 Pixmap pixmap;
1206 Drawable drawable;
1207 Display *display;
1208
1209 pixmap = None;
1210 drawable = Tk_WindowId(tkwin);
1211 display = Tk_Display(tkwin);
1212
1213 if (drawable == None) {
1214 Drawable root;
1215 int depth;
1216
1217 root = RootWindow(display, Tk_ScreenNumber(tkwin));
1218 depth = Tk_Depth(tkwin);
1219
1220 if (depth == DefaultDepth(display, Tk_ScreenNumber(tkwin))) {
1221 drawable = root;
1222 } else {
1223 pixmap = Tk_GetPixmap(display, root, 1, 1, depth);
1224 drawable = pixmap;
1225 }
1226 }
1227 gc = Blt_GetPrivateGCFromDrawable(display, drawable, gcMask, valuePtr);
1228 if (pixmap != None) {
1229 Tk_FreePixmap(display, pixmap);
1230 }
1231 return gc;
1232}
1233
1234void
1235Blt_FreePrivateGC(display, gc)
1236 Display *display;
1237 GC gc;
1238{
1239 Tk_FreeXId(display, (XID) XGContextFromGC(gc));
1240 XFreeGC(display, gc);
1241}
1242
1243#ifndef WIN32
1244void
1245Blt_SetDashes(display, gc, dashesPtr)
1246 Display *display;
1247 GC gc;
1248 Blt_Dashes *dashesPtr;
1249{
1250 XSetDashes(display, gc, dashesPtr->offset,
1251 (CONST char *)dashesPtr->values, strlen((char *)dashesPtr->values));
1252}
1253#endif
1254
1255
1256static double
1257FindSplit(points, i, j, split)
1258 Point2D points[];
1259 int i, j; /* Indices specifying the range of points. */
1260 int *split; /* (out) Index of next split. */
1261{ double maxDist;
1262
1263 maxDist = -1.0;
1264 if ((i + 1) < j) {
1265 register int k;
1266 double a, b, c;
1267 double sqDist;
1268
1269 /*
1270 *
1271 * sqDist P(k) = | 1 P(i).x P(i).y |
1272 * | 1 P(j).x P(j).y |
1273 * | 1 P(k).x P(k).y |
1274 * ---------------------------
1275 * (P(i).x - P(j).x)^2 + (P(i).y - P(j).y)^2
1276 */
1277
1278 a = points[i].y - points[j].y;
1279 b = points[j].x - points[i].x;
1280 c = (points[i].x * points[j].y) - (points[i].y * points[j].x);
1281 for (k = (i + 1); k < j; k++) {
1282 sqDist = (points[k].x * a) + (points[k].y * b) + c;
1283 if (sqDist < 0.0) {
1284 sqDist = -sqDist;
1285 }
1286 if (sqDist > maxDist) {
1287 maxDist = sqDist; /* Track the maximum. */
1288 *split = k;
1289 }
1290 }
1291 /* Correction for segment length---should be redone if can == 0 */
1292 maxDist *= maxDist / (a * a + b * b);
1293 }
1294 return maxDist;
1295}
1296
1297
1298/* Douglas-Peucker line simplification algorithm */
1299int
1300Blt_SimplifyLine(inputPts, low, high, tolerance, indices)
1301 Point2D inputPts[];
1302 int low, high;
1303 double tolerance;
1304 int indices[];
1305{
1306#define StackPush(a) s++, stack[s] = (a)
1307#define StackPop(a) (a) = stack[s], s--
1308#define StackEmpty() (s < 0)
1309#define StackTop() stack[s]
1310 int *stack;
1311 int split = -1;
1312 double sqDist, sqTolerance;
1313 int s = -1; /* Points to top stack item. */
1314 int count;
1315
1316 stack = Blt_Malloc(sizeof(int) * (high - low + 1));
1317 StackPush(high);
1318 count = 0;
1319 indices[count++] = 0;
1320 sqTolerance = tolerance * tolerance;
1321 while (!StackEmpty()) {
1322 sqDist = FindSplit(inputPts, low, StackTop(), &split);
1323 if (sqDist > sqTolerance) {
1324 StackPush(split);
1325 } else {
1326 indices[count++] = StackTop();
1327 StackPop(low);
1328 }
1329 }
1330 Blt_Free(stack);
1331 return count;
1332}
1333
1334void
1335Blt_Draw2DSegments(display, drawable, gc, segPtr, nSegments)
1336 Display *display;
1337 Drawable drawable;
1338 GC gc;
1339 register Segment2D *segPtr;
1340 int nSegments;
1341{
1342 XSegment *xSegPtr, *xSegArr;
1343 Segment2D *endPtr;
1344
1345 xSegArr = Blt_Malloc(nSegments * sizeof(XSegment));
1346 if (xSegArr == NULL) {
1347 return;
1348 }
1349 xSegPtr = xSegArr;
1350 for (endPtr = segPtr + nSegments; segPtr < endPtr; segPtr++) {
1351 xSegPtr->x1 = (short int)segPtr->p.x;
1352 xSegPtr->y1 = (short int)segPtr->p.y;
1353 xSegPtr->x2 = (short int)segPtr->q.x;
1354 xSegPtr->y2 = (short int)segPtr->q.y;
1355 xSegPtr++;
1356 }
1357 XDrawSegments(display, drawable, gc, xSegArr, nSegments);
1358 Blt_Free(xSegArr);
1359}
1360
1361void
1362Blt_DrawArrow(display, drawable, gc, x, y, arrowHeight, orientation)
1363 Display *display;
1364 Drawable drawable;
1365 GC gc;
1366 int x, y;
1367 int arrowHeight;
1368 int orientation;
1369{
1370 XPoint arrow[5];
1371 int a, b;
1372
1373 a = arrowHeight / 2 + 1;
1374 b = arrowHeight;
1375 switch (orientation) {
1376 case ARROW_UP:
1377 /*
1378 * 0
1379 * +
1380 * / \
1381 * / \
1382 * / \ a
1383 * / \
1384 * x,y / \
1385 * +-----------+
1386 * 1 b 2
1387 */
1388 arrow[0].x = x;
1389 arrow[0].y = y - a;
1390 arrow[1].x = arrow[0].x - b;
1391 arrow[1].y = arrow[0].y + b;
1392 arrow[2].x = arrow[0].x + b;
1393 arrow[2].y = arrow[0].y + b;
1394 arrow[3].x = arrow[0].x;
1395 arrow[3].y = arrow[0].y;
1396 break;
1397
1398 case ARROW_DOWN:
1399 /*
1400 * 1 b 2
1401 * +-----------+
1402 * \ /
1403 * \ x,y /
1404 * \ / a
1405 * \ /
1406 * \ /
1407 * +
1408 * 0
1409 */
1410 arrow[0].x = x;
1411 arrow[0].y = y + a;
1412 arrow[1].x = arrow[0].x - b;
1413 arrow[1].y = arrow[0].y - b;
1414 arrow[2].x = arrow[0].x + b;
1415 arrow[2].y = arrow[0].y - b;
1416 arrow[3].x = arrow[0].x;
1417 arrow[3].y = arrow[0].y;
1418 break;
1419
1420 case ARROW_RIGHT:
1421 /*
1422 * 2
1423 * +
1424 * |\
1425 * | \
1426 * | \
1427 * | \
1428 * | \
1429 * | x,y + 0
1430 * | /
1431 * | /
1432 * | /
1433 * | /
1434 * |/
1435 * +
1436 * 1
1437 */
1438 arrow[0].x = x + a;
1439 arrow[0].y = y;
1440 arrow[1].x = arrow[0].x - b;
1441 arrow[1].y = arrow[0].y + b;
1442 arrow[2].x = arrow[0].x - b;
1443 arrow[2].y = arrow[0].y - b;
1444 arrow[3].x = arrow[0].x;
1445 arrow[3].y = arrow[0].y;
1446 break;
1447
1448 case ARROW_LEFT:
1449 /*
1450 * 2
1451 * +
1452 * /|
1453 * / |
1454 * / |
1455 * / |
1456 * / |
1457 * 0+ x,y |
1458 * \ |
1459 * \ |
1460 * \ |
1461 * \ |
1462 * \|
1463 * +
1464 * 1
1465 */
1466 arrow[0].x = x - a;
1467 arrow[0].y = y;
1468 arrow[1].x = arrow[0].x + b;
1469 arrow[1].y = arrow[0].y + b;
1470 arrow[2].x = arrow[0].x + b;
1471 arrow[2].y = arrow[0].y - b;
1472 arrow[3].x = arrow[0].x;
1473 arrow[3].y = arrow[0].y;
1474 break;
1475
1476 }
1477 XFillPolygon(display, drawable, gc, arrow, 4, Convex, CoordModeOrigin);
1478 XDrawLines(display, drawable, gc, arrow, 4, CoordModeOrigin);
1479}
1480
1481int
1482Blt_MaxRequestSize(Display *display, unsigned int elemSize)
1483{
1484 long size;
1485
1486#ifdef HAVE_XEXTENDEDMAXREQUESTSIZE
1487 size = XExtendedMaxRequestSize(display);
1488 if (size == 0) {
1489 size = XMaxRequestSize(display);
1490 }
1491#else
1492 size = XMaxRequestSize(display);
1493#endif
1494 size -= 4;
1495 return ((size * 4) / elemSize);
1496}
1497
1498#undef Blt_Fill3DRectangle
1499void
1500Blt_Fill3DRectangle(tkwin, drawable, border, x, y, width,
1501 height, borderWidth, relief)
1502 Tk_Window tkwin; /* Window for which border was allocated. */
1503 Drawable drawable; /* X window or pixmap in which to draw. */
1504 Tk_3DBorder border; /* Token for border to draw. */
1505 int x, y, width, height; /* Outside area of rectangular region. */
1506 int borderWidth; /* Desired width for border, in
1507 * pixels. Border will be *inside* region. */
1508 int relief; /* Indicates 3D effect: TK_RELIEF_FLAT,
1509 * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
1510{
1511#ifndef notdef
1512 if ((borderWidth > 1) && (width > 2) && (height > 2) &&
1513 ((relief == TK_RELIEF_SUNKEN) || (relief == TK_RELIEF_RAISED))) {
1514 GC lightGC, darkGC;
1515 int x2, y2;
1516
1517 x2 = x + width - 1;
1518 y2 = y + height - 1;
1519#define TK_3D_LIGHT2_GC TK_3D_DARK_GC+1
1520#define TK_3D_DARK2_GC TK_3D_DARK_GC+2
1521 if (relief == TK_RELIEF_RAISED) {
1522 lightGC = Tk_3DBorderGC(tkwin, border, TK_3D_FLAT_GC);
1523#ifdef WIN32
1524 darkGC = Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC);
1525#else
1526 darkGC = DefaultGC(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
1527#endif
1528 } else {
1529#ifdef WIN32
1530 lightGC = Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC);
1531#else
1532 lightGC = DefaultGC(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
1533#endif
1534 darkGC = Tk_3DBorderGC(tkwin, border, TK_3D_FLAT_GC);
1535 }
1536 XDrawLine(Tk_Display(tkwin), drawable, lightGC, x, y, x2, y);
1537 XDrawLine(Tk_Display(tkwin), drawable, darkGC, x2, y2, x2, y);
1538 XDrawLine(Tk_Display(tkwin), drawable, darkGC, x2, y2, x, y2);
1539 XDrawLine(Tk_Display(tkwin), drawable, lightGC, x, y, x, y2);
1540 x++, y++, width -= 2, height -= 2, borderWidth--;
1541 }
1542#endif
1543 Tk_Fill3DRectangle(tkwin, drawable, border, x, y, width, height,
1544 borderWidth, relief);
1545}
1546
1547
1548#undef Blt_Draw3DRectangle
1549void
1550Blt_Draw3DRectangle(tkwin, drawable, border, x, y, width,
1551 height, borderWidth, relief)
1552 Tk_Window tkwin; /* Window for which border was allocated. */
1553 Drawable drawable; /* X window or pixmap in which to draw. */
1554 Tk_3DBorder border; /* Token for border to draw. */
1555 int x, y, width, height; /* Outside area of rectangular region. */
1556 int borderWidth; /* Desired width for border, in
1557 * pixels. Border will be *inside* region. */
1558 int relief; /* Indicates 3D effect: TK_RELIEF_FLAT,
1559 * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
1560{
1561#ifndef notdef
1562 if ((borderWidth > 1) && (width > 2) && (height > 2) &&
1563 ((relief == TK_RELIEF_SUNKEN) || (relief == TK_RELIEF_RAISED))) {
1564 GC lightGC, darkGC;
1565 int x2, y2;
1566
1567 x2 = x + width - 1;
1568 y2 = y + height - 1;
1569 if (relief == TK_RELIEF_RAISED) {
1570 lightGC = Tk_3DBorderGC(tkwin, border, TK_3D_FLAT_GC);
1571#ifdef WIN32
1572 darkGC = Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC);
1573#else
1574 darkGC = DefaultGC(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
1575#endif
1576 } else {
1577#ifdef WIN32
1578 lightGC = Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC);
1579#else
1580 lightGC = DefaultGC(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
1581#endif
1582 darkGC = Tk_3DBorderGC(tkwin, border, TK_3D_FLAT_GC);
1583 }
1584 XDrawLine(Tk_Display(tkwin), drawable, darkGC, x2, y2, x2, y);
1585 XDrawLine(Tk_Display(tkwin), drawable, lightGC, x, y, x2, y);
1586 XDrawLine(Tk_Display(tkwin), drawable, darkGC, x2, y2, x, y2);
1587 XDrawLine(Tk_Display(tkwin), drawable, lightGC, x, y, x, y2);
1588 x++, y++, width -= 2, height -= 2, borderWidth--;
1589 }
1590#endif
1591 Tk_Draw3DRectangle(tkwin, drawable, border, x, y, width, height,
1592 borderWidth, relief);
1593}
1594
1595#ifdef notdef
1596typedef struct {
1597 Screen *screen;
1598 Visual *visual;
1599 Colormap colormap;
1600 Tk_Uid nameUid;
1601} BorderKey;
1602
1603typedef struct {
1604 Screen *screen; /* Screen on which the border will be used. */
1605 Visual *visual; /* Visual for all windows and pixmaps using
1606 * the border. */
1607 int depth; /* Number of bits per pixel of drawables where
1608 * the border will be used. */
1609 Colormap colormap; /* Colormap out of which pixels are
1610 * allocated. */
1611 int refCount; /* Number of active uses of this color
1612 * (each active use corresponds to a
1613 * call to Blt_Get3DBorder). If this
1614 * count is 0, then this structure is
1615 * no longer valid and it isn't
1616 * present in borderTable: it is being
1617 * kept around only because there are
1618 * objects referring to it. The
1619 * structure is freed when refCount is
1620 * 0. */
1621
1622 XColor *bgColorPtr; /* Color of face. */
1623 XColor *shadows[4];
1624
1625 Pixmap darkStipple; /* Stipple pattern to use for drawing
1626 * shadows areas. Used for displays with
1627 * <= 64 colors or where colormap has filled
1628 * up. */
1629 Pixmap lightStipple; /* Stipple pattern to use for drawing
1630 * shadows areas. Used for displays with
1631 * <= 64 colors or where colormap has filled
1632 * up. */
1633 GC bgGC; /* Used (if necessary) to draw areas in
1634 * the background color. */
1635 GC lightGC, darkGC;
1636 Tcl_HashEntry *hashPtr; /* Entry in borderTable (needed in
1637 * order to delete structure). */
1638
1639 struct Blt_3DBorderStruct *nextPtr;
1640} Border, *Blt_3DBorder;
1641
1642
1643void
1644Blt_Draw3DRectangle(tkwin, drawable, border, x, y, width,
1645 height, borderWidth, relief)
1646 Tk_Window tkwin; /* Window for which border was allocated. */
1647 Drawable drawable; /* X window or pixmap in which to draw. */
1648 Blt_3DBorder *borderPtr; /* Border to draw. */
1649 int x, y, width, height; /* Outside area of rectangular region. */
1650 int borderWidth; /* Desired width for border, in
1651 * pixels. Border will be *inside* region. */
1652 int relief; /* Indicates 3D effect: TK_RELIEF_FLAT,
1653 * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
1654{
1655 if ((width > (2 * borderWidth)) && (height > (2 * borderWidth))) {
1656 int x2, y2;
1657 int i;
1658
1659 x2 = x + width - 1;
1660 y2 = y + height - 1;
1661
1662 XSetForeground(borderPtr->lightGC, borderPtr->shadows[0]);
1663 XSetForeground(borderPtr->darkGC, borderPtr->shadows[3]);
1664 XDrawLine(Tk_Display(tkwin), drawable, borderPtr->lightGC,
1665 x, y, x2, y);
1666 XDrawLine(Tk_Display(tkwin), drawable, borderPtr->lightGC,
1667 x, y, x, y2);
1668 XDrawLine(Tk_Display(tkwin), drawable, borderPtr->darkGC,
1669 x2, y, x2, y2);
1670 XDrawLine(Tk_Display(tkwin), drawable, borderPtr->darkGC,
1671 x2, y2, x, y2);
1672 XSetForeground(borderPtr->lightGC, borderPtr->shadows[1]);
1673 XSetForeground(borderPtr->darkGC, borderPtr->shadows[2]);
1674 for (i = 1; i < (borderWidth - 1); i++) {
1675
1676 /*
1677 * +---------
1678 * |+-------
1679 * ||+-----
1680 * |||
1681 * |||
1682 * ||
1683 * |
1684 */
1685 x++, y++, x2--, y2--;
1686 XDrawLine(Tk_Display(tkwin), drawable, borderPtr->lightGC,
1687 x, y, x2, y);
1688 XDrawLine(Tk_Display(tkwin), drawable, borderPtr->lightGC,
1689 x, y, x, y2);
1690 XDrawLine(Tk_Display(tkwin), drawable, borderPtr->darkGC,
1691 x2, y, x2, y2);
1692 XDrawLine(Tk_Display(tkwin), drawable, borderPtr->darkGC,
1693 x2, y2, x, y2);
1694 }
1695 }
1696}
1697
1698void
1699Blt_Fill3DRectangle(tkwin, drawable, border, x, y, width, height, borderWidth,
1700 relief)
1701 Tk_Window tkwin; /* Window for which border was allocated. */
1702 Drawable drawable; /* X window or pixmap in which to draw. */
1703 Tk_3DBorder border; /* Token for border to draw. */
1704 int x, y, width, height; /* Outside area of rectangular region. */
1705 int borderWidth; /* Desired width for border, in
1706 * pixels. Border will be *inside* region. */
1707 int relief; /* Indicates 3D effect: TK_RELIEF_FLAT,
1708 * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
1709{
1710 Blt_3DBorder *borderPtr;
1711
1712 XFillRectangle(Tk_Display(tkwin), drawable, borderPtr->bgGC, x, y, width,
1713 height);
1714 if ((borderWidth > 0) && (relief != BLT_RELIEF_FLAT)) {
1715 Blt_Draw3DRectangle(tkwin, drawable, borderPtr, x, y, width, height,
1716 borderWidth, relief);
1717 }
1718}
1719
1720
1721void
1722FreeBorder(display, borderPtr)
1723 Display *display;
1724 Border *borderPtr;
1725{
1726 int i;
1727
1728 if (borderPtr->bgColorPtr != NULL) {
1729 Tk_FreeColor(display, borderPtr->bgColorPtr);
1730 }
1731 for (i = 0; i < 4; i++) {
1732 Tk_FreeColor(display, borderPtr->shadows[i]);
1733 }
1734 if (borderPtr->tile != NULL) {
1735 Blt_FreeTile(tile);
1736 }
1737 if (borderPtr->darkGC != NULL) {
1738 Blt_FreePrivateGC(display, borderPtr->darkGC);
1739 }
1740 if (borderPtr->lightGC != NULL) {
1741 Blt_FreePrivateGC(tkwin, borderPtr->lightGC);
1742 }
1743 if (borderPtr->bgGC != NULL) {
1744 Blt_FreePrivateGC(tkwin, borderPtr->bgGC);
1745 }
1746 Blt_Free(borderPtr);
1747}
1748
1749void
1750Blt_Free3DBorder(display, border)
1751 Display *display;
1752 Blt_3DBorder border;
1753{
1754 Border *borderPtr = (Border *)border;
1755
1756 borderPtr->refCount--;
1757 if (borderPtr->refCount >= 0) {
1758 /* Search for the border in the bucket. Start at the head. */
1759 headPtr = Blt_GetHashValue(borderPtr->hashPtr);
1760 lastPtr = NULL;
1761 while ((headPtr != borderPtr) && (headPtr != NULL)) {
1762 lastPtr = headPtr;
1763 headPtr = headPtr->next;
1764 }
1765 if (headPtr == NULL) {
1766 return; /* This can't happen. It means that
1767 * we could not find the border. */
1768 }
1769 if (lastPtr != NULL) {
1770 lastPtr->next = borderPtr->next;
1771 } else {
1772 Tcl_DeleteHashEntry(borderPtr->hashPtr);
1773 }
1774 FreeBorder(display, borderPtr);
1775 }
1776}
1777
1778Blt_3DBorder *
1779Blt_Get3DBorder(interp, tkwin, borderName)
1780 Tcl_Interp *interp;
1781 Tk_Window tkwin;
1782 char *borderName;
1783{
1784 Blt_3DBorder *borderPtr, *lastBorderPtr;
1785 Blt_HashEntry *hPtr;
1786 Blt_Tile tile;
1787 XColor *bgColorPtr;
1788 char **argv;
1789 char *colorName;
1790 int argc;
1791 int isNew;
1792
1793 lastBorderPtr = NULL;
1794 hPtr = Tcl_CreateHashEntry(&dataPtr->borderTable, borderName, &isNew);
1795 if (!isNew) {
1796 borderPtr = lastBorderPtr = Blt_GetHashValue(hPtr);
1797 while (borderPtr != NULL) {
1798 if ((Tk_Screen(tkwin) == borderPtr->screen) &&
1799 (Tk_Colormap(tkwin) == borderPtr->colormap)) {
1800 borderPtr->refCount++;
1801 return borderPtr;
1802 }
1803 borderPtr = borderPtr->nextPtr;
1804 }
1805 }
1806 /* Create a new border. */
1807 argv = NULL;
1808 bgColorPtr = NULL;
1809 tile = NULL;
1810
1811 if (Tcl_SplitList(interp, borderName, &argc, &argv) != TCL_OK) {
1812 goto error;
1813 }
1814 colorName = borderName;
1815 if ((argc == 2) && (Blt_GetTile(interp, tkwin, argv[0], &tile) == TCL_OK)) {
1816 colorName = argv[1];
1817 }
1818 bgColorPtr = Tk_GetColor(interp, tkwin, colorName);
1819 if (bgColorPtr == NULL) {
1820 goto error;
1821 }
1822
1823 /* Create a new border */
1824 borderPtr = Blt_Calloc(1, sizeof(Blt_3DBorder));
1825 assert(borderPtr);
1826 borderPtr->screen = Tk_Screen(tkwin);
1827 borderPtr->visual = Tk_Visual(tkwin);
1828 borderPtr->depth = Tk_Depth(tkwin);
1829 borderPtr->colormap = Tk_Colormap(tkwin);
1830 borderPtr->refCount = 1;
1831 borderPtr->bgColorPtr = bgColorPtr;
1832 borderPtr->tile = tile;
1833 borderPtr->darkGC = Blt_GetPrivateGC(tkwin, 0, NULL);
1834 borderPtr->lightGC = Blt_GetPrivateGC(tkwin, 0, NULL);
1835 borderPtr->hashPtr = lastBorderPtr->hashPtr;
1836 lastBorderPtr->nextPtr = lastBorderPtr;
1837 {
1838 HSV hsv;
1839 XColor color;
1840 double sat, sat0, diff, step, hstep;
1841 int count;
1842
1843 /* Convert the face (background) color to HSV */
1844 Blt_XColorToHSV(borderPtr->bgColorPtr, &hsv);
1845
1846 /* Using the color as the baseline intensity, pick a set of
1847 * colors around the intensity. */
1848#define UFLOOR(x,u) (floor((x)*(u))/(u))
1849 diff = hsv.sat - UFLOOR(hsv.sat, 0.2);
1850 sat = 0.1 + (diff - 0.1);
1851 sat0 = hsv.sat;
1852 count = 0;
1853 for (sat = 0.1 + (diff - 0.1); sat <= 1.0; sat += 0.2) {
1854 if (FABS(sat0 - sat) >= 0.1) {
1855 hsv.sat = sat;
1856 Blt_HSVToXColor(&hsv, &color);
1857 borderPtr->shadows[count] = Tk_GetColorByValue(tkwin, &color);
1858 count++;
1859 }
1860 }
1861 }
1862 Blt_SetHashValue(hPtr, borderPtr);
1863 if (argv != NULL) {
1864 Blt_Free(argv);
1865 }
1866 return TCL_OK;
1867
1868 error:
1869 if (argv != NULL) {
1870 Blt_Free(argv);
1871 }
1872 if (tile != NULL) {
1873 Blt_FreeTile(tile);
1874 }
1875 if (bgColorPtr != NULL) {
1876 Tk_FreeColor(bgColorPtr);
1877 }
1878 if (isNew) {
1879 Blt_DeleteHashEntry(&borderTable, hPtr);
1880 }
1881 return NULL;
1882}
1883
1884#endif
Note: See TracBrowser for help on using the repository browser.