~ubuntu-branches/ubuntu/dapper/tk8.0/dapper-updates

« back to all changes in this revision

Viewing changes to generic/tk3d.c

  • Committer: Bazaar Package Importer
  • Author(s): Mike Markley
  • Date: 2001-07-24 21:57:40 UTC
  • Revision ID: james.westby@ubuntu.com-20010724215740-r70t25rtmbqjil2h
Tags: upstream-8.0.5
ImportĀ upstreamĀ versionĀ 8.0.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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-1997 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
 * RCS: @(#) $Id: tk3d.c,v 1.2 1998/09/14 18:23:02 stanton Exp $
 
14
 */
 
15
 
 
16
#include <tk3d.h>
 
17
 
 
18
/*
 
19
 * Hash table to map from a border's values (color, etc.) to a
 
20
 * Border structure for those values.
 
21
 */
 
22
 
 
23
static Tcl_HashTable borderTable;
 
24
typedef struct {
 
25
    Tk_Uid colorName;           /* Color for border. */
 
26
    Colormap colormap;          /* Colormap used for allocating border
 
27
                                 * colors. */
 
28
    Screen *screen;             /* Screen on which border will be drawn. */
 
29
} BorderKey;
 
30
 
 
31
static int initialized = 0;     /* 0 means static structures haven't
 
32
                                 * been initialized yet. */
 
33
 
 
34
/*
 
35
 * Forward declarations for procedures defined in this file:
 
36
 */
 
37
 
 
38
static void             BorderInit _ANSI_ARGS_((void));
 
39
static int              Intersect _ANSI_ARGS_((XPoint *a1Ptr, XPoint *a2Ptr,
 
40
                            XPoint *b1Ptr, XPoint *b2Ptr, XPoint *iPtr));
 
41
static void             ShiftLine _ANSI_ARGS_((XPoint *p1Ptr, XPoint *p2Ptr,
 
42
                            int distance, XPoint *p3Ptr));
 
43
 
 
44
/*
 
45
 *--------------------------------------------------------------
 
46
 *
 
47
 * Tk_Get3DBorder --
 
48
 *
 
49
 *      Create a data structure for displaying a 3-D border.
 
50
 *
 
51
 * Results:
 
52
 *      The return value is a token for a data structure
 
53
 *      describing a 3-D border.  This token may be passed
 
54
 *      to Tk_Draw3DRectangle and Tk_Free3DBorder.  If an
 
55
 *      error prevented the border from being created then
 
56
 *      NULL is returned and an error message will be left
 
57
 *      in interp->result.
 
58
 *
 
59
 * Side effects:
 
60
 *      Data structures, graphics contexts, etc. are allocated.
 
61
 *      It is the caller's responsibility to eventually call
 
62
 *      Tk_Free3DBorder to release the resources.
 
63
 *
 
64
 *--------------------------------------------------------------
 
65
 */
 
66
 
 
67
Tk_3DBorder
 
68
Tk_Get3DBorder(interp, tkwin, colorName)
 
69
    Tcl_Interp *interp;         /* Place to store an error message. */
 
70
    Tk_Window tkwin;            /* Token for window in which border will
 
71
                                 * be drawn. */
 
72
    Tk_Uid colorName;           /* String giving name of color
 
73
                                 * for window background. */
 
74
{
 
75
    BorderKey key;
 
76
    Tcl_HashEntry *hashPtr;
 
77
    register TkBorder *borderPtr;
 
78
    int new;
 
79
    XGCValues gcValues;
 
80
 
 
81
    if (!initialized) {
 
82
        BorderInit();
 
83
    }
 
84
 
 
85
    /*
 
86
     * First, check to see if there's already a border that will work
 
87
     * for this request.
 
88
     */
 
89
 
 
90
    key.colorName = colorName;
 
91
    key.colormap = Tk_Colormap(tkwin);
 
92
    key.screen = Tk_Screen(tkwin);
 
93
 
 
94
    hashPtr = Tcl_CreateHashEntry(&borderTable, (char *) &key, &new);
 
95
    if (!new) {
 
96
        borderPtr = (TkBorder *) Tcl_GetHashValue(hashPtr);
 
97
        borderPtr->refCount++;
 
98
    } else {
 
99
        XColor *bgColorPtr;
 
100
 
 
101
        /*
 
102
         * No satisfactory border exists yet.  Initialize a new one.
 
103
         */
 
104
    
 
105
        bgColorPtr = Tk_GetColor(interp, tkwin, colorName);
 
106
        if (bgColorPtr == NULL) {
 
107
            Tcl_DeleteHashEntry(hashPtr);
 
108
            return NULL;
 
109
        }
 
110
 
 
111
        borderPtr = TkpGetBorder();
 
112
        borderPtr->screen = Tk_Screen(tkwin);
 
113
        borderPtr->visual = Tk_Visual(tkwin);
 
114
        borderPtr->depth = Tk_Depth(tkwin);
 
115
        borderPtr->colormap = key.colormap;
 
116
        borderPtr->refCount = 1;
 
117
        borderPtr->bgColorPtr = bgColorPtr;
 
118
        borderPtr->darkColorPtr = NULL;
 
119
        borderPtr->lightColorPtr = NULL;
 
120
        borderPtr->shadow = None;
 
121
        borderPtr->bgGC = None;
 
122
        borderPtr->darkGC = None;
 
123
        borderPtr->lightGC = None;
 
124
        borderPtr->hashPtr = hashPtr;
 
125
        Tcl_SetHashValue(hashPtr, borderPtr);
 
126
    
 
127
        /*
 
128
         * Create the information for displaying the background color,
 
129
         * but delay the allocation of shadows until they are actually
 
130
         * needed for drawing.
 
131
         */
 
132
    
 
133
        gcValues.foreground = borderPtr->bgColorPtr->pixel;
 
134
        borderPtr->bgGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
 
135
    }
 
136
    return (Tk_3DBorder) borderPtr;
 
137
}
 
138
 
 
139
/*
 
140
 *--------------------------------------------------------------
 
141
 *
 
142
 * Tk_Draw3DRectangle --
 
143
 *
 
144
 *      Draw a 3-D border at a given place in a given window.
 
145
 *
 
146
 * Results:
 
147
 *      None.
 
148
 *
 
149
 * Side effects:
 
150
 *      A 3-D border will be drawn in the indicated drawable.
 
151
 *      The outside edges of the border will be determined by x,
 
152
 *      y, width, and height.  The inside edges of the border
 
153
 *      will be determined by the borderWidth argument.
 
154
 *
 
155
 *--------------------------------------------------------------
 
156
 */
 
157
 
 
158
void
 
159
Tk_Draw3DRectangle(tkwin, drawable, border, x, y, width, height,
 
160
        borderWidth, relief)
 
161
    Tk_Window tkwin;            /* Window for which border was allocated. */
 
162
    Drawable drawable;          /* X window or pixmap in which to draw. */
 
163
    Tk_3DBorder border;         /* Token for border to draw. */
 
164
    int x, y, width, height;    /* Outside area of region in
 
165
                                 * which border will be drawn. */
 
166
    int borderWidth;            /* Desired width for border, in
 
167
                                 * pixels. */
 
168
    int relief;                 /* Type of relief: TK_RELIEF_RAISED,
 
169
                                 * TK_RELIEF_SUNKEN, TK_RELIEF_GROOVE, etc. */
 
170
{
 
171
    if (width < 2*borderWidth) {
 
172
        borderWidth = width/2;
 
173
    }
 
174
    if (height < 2*borderWidth) {
 
175
        borderWidth = height/2;
 
176
    }
 
177
    Tk_3DVerticalBevel(tkwin, drawable, border, x, y, borderWidth, height,
 
178
            1, relief);
 
179
    Tk_3DVerticalBevel(tkwin, drawable, border, x+width-borderWidth, y,
 
180
            borderWidth, height, 0, relief);
 
181
    Tk_3DHorizontalBevel(tkwin, drawable, border, x, y, width, borderWidth,
 
182
            1, 1, 1, relief);
 
183
    Tk_3DHorizontalBevel(tkwin, drawable, border, x, y+height-borderWidth,
 
184
            width, borderWidth, 0, 0, 0, relief);
 
185
}
 
186
 
 
187
/*
 
188
 *--------------------------------------------------------------
 
189
 *
 
190
 * Tk_NameOf3DBorder --
 
191
 *
 
192
 *      Given a border, return a textual string identifying the
 
193
 *      border's color.
 
194
 *
 
195
 * Results:
 
196
 *      The return value is the string that was used to create
 
197
 *      the border.
 
198
 *
 
199
 * Side effects:
 
200
 *      None.
 
201
 *
 
202
 *--------------------------------------------------------------
 
203
 */
 
204
 
 
205
char *
 
206
Tk_NameOf3DBorder(border)
 
207
    Tk_3DBorder border;         /* Token for border. */
 
208
{
 
209
    TkBorder *borderPtr = (TkBorder *) border;
 
210
 
 
211
    return ((BorderKey *) borderPtr->hashPtr->key.words)->colorName;
 
212
}
 
213
 
 
214
/*
 
215
 *--------------------------------------------------------------------
 
216
 *
 
217
 * Tk_3DBorderColor --
 
218
 *
 
219
 *      Given a 3D border, return the X color used for the "flat"
 
220
 *      surfaces.
 
221
 *
 
222
 * Results:
 
223
 *      Returns the color used drawing flat surfaces with the border.
 
224
 *
 
225
 * Side effects:
 
226
 *      None.
 
227
 *
 
228
 *--------------------------------------------------------------------
 
229
 */
 
230
XColor *
 
231
Tk_3DBorderColor(border)
 
232
    Tk_3DBorder border;         /* Border whose color is wanted. */
 
233
{
 
234
    return(((TkBorder *) border)->bgColorPtr);
 
235
}
 
236
 
 
237
/*
 
238
 *--------------------------------------------------------------------
 
239
 *
 
240
 * Tk_3DBorderGC --
 
241
 *
 
242
 *      Given a 3D border, returns one of the graphics contexts used to
 
243
 *      draw the border.
 
244
 *
 
245
 * Results:
 
246
 *      Returns the graphics context given by the "which" argument.
 
247
 *
 
248
 * Side effects:
 
249
 *      None.
 
250
 *
 
251
 *--------------------------------------------------------------------
 
252
 */
 
253
GC
 
254
Tk_3DBorderGC(tkwin, border, which)
 
255
    Tk_Window tkwin;            /* Window for which border was allocated. */
 
256
    Tk_3DBorder border;         /* Border whose GC is wanted. */
 
257
    int which;                  /* Selects one of the border's 3 GC's:
 
258
                                 * TK_3D_FLAT_GC, TK_3D_LIGHT_GC, or
 
259
                                 * TK_3D_DARK_GC. */
 
260
{
 
261
    TkBorder * borderPtr = (TkBorder *) border;
 
262
 
 
263
    if ((borderPtr->lightGC == None) && (which != TK_3D_FLAT_GC)) {
 
264
        TkpGetShadows(borderPtr, tkwin);
 
265
    }
 
266
    if (which == TK_3D_FLAT_GC) {
 
267
        return borderPtr->bgGC;
 
268
    } else if (which == TK_3D_LIGHT_GC) {
 
269
        return borderPtr->lightGC;
 
270
    } else if (which == TK_3D_DARK_GC){
 
271
        return borderPtr->darkGC;
 
272
    }
 
273
    panic("bogus \"which\" value in Tk_3DBorderGC");
 
274
 
 
275
    /*
 
276
     * The code below will never be executed, but it's needed to
 
277
     * keep compilers happy.
 
278
     */
 
279
 
 
280
    return (GC) None;
 
281
}
 
282
 
 
283
/*
 
284
 *--------------------------------------------------------------
 
285
 *
 
286
 * Tk_Free3DBorder --
 
287
 *
 
288
 *      This procedure is called when a 3D border is no longer
 
289
 *      needed.  It frees the resources associated with the
 
290
 *      border.  After this call, the caller should never again
 
291
 *      use the "border" token.
 
292
 *
 
293
 * Results:
 
294
 *      None.
 
295
 *
 
296
 * Side effects:
 
297
 *      Resources are freed.
 
298
 *
 
299
 *--------------------------------------------------------------
 
300
 */
 
301
 
 
302
void
 
303
Tk_Free3DBorder(border)
 
304
    Tk_3DBorder border;         /* Token for border to be released. */
 
305
{
 
306
    register TkBorder *borderPtr = (TkBorder *) border;
 
307
    Display *display = DisplayOfScreen(borderPtr->screen);
 
308
 
 
309
    borderPtr->refCount--;
 
310
    if (borderPtr->refCount == 0) {
 
311
        TkpFreeBorder(borderPtr);
 
312
        if (borderPtr->bgColorPtr != NULL) {
 
313
            Tk_FreeColor(borderPtr->bgColorPtr);
 
314
        }
 
315
        if (borderPtr->darkColorPtr != NULL) {
 
316
            Tk_FreeColor(borderPtr->darkColorPtr);
 
317
        }
 
318
        if (borderPtr->lightColorPtr != NULL) {
 
319
            Tk_FreeColor(borderPtr->lightColorPtr);
 
320
        }
 
321
        if (borderPtr->shadow != None) {
 
322
            Tk_FreeBitmap(display, borderPtr->shadow);
 
323
        }
 
324
        if (borderPtr->bgGC != None) {
 
325
            Tk_FreeGC(display, borderPtr->bgGC);
 
326
        }
 
327
        if (borderPtr->darkGC != None) {
 
328
            Tk_FreeGC(display, borderPtr->darkGC);
 
329
        }
 
330
        if (borderPtr->lightGC != None) {
 
331
            Tk_FreeGC(display, borderPtr->lightGC);
 
332
        }
 
333
        Tcl_DeleteHashEntry(borderPtr->hashPtr);
 
334
        ckfree((char *) borderPtr);
 
335
    }
 
336
}
 
337
 
 
338
/*
 
339
 *----------------------------------------------------------------------
 
340
 *
 
341
 * Tk_SetBackgroundFromBorder --
 
342
 *
 
343
 *      Change the background of a window to one appropriate for a given
 
344
 *      3-D border.
 
345
 *
 
346
 * Results:
 
347
 *      None.
 
348
 *
 
349
 * Side effects:
 
350
 *      Tkwin's background gets modified.
 
351
 *
 
352
 *----------------------------------------------------------------------
 
353
 */
 
354
 
 
355
void
 
356
Tk_SetBackgroundFromBorder(tkwin, border)
 
357
    Tk_Window tkwin;            /* Window whose background is to be set. */
 
358
    Tk_3DBorder border;         /* Token for border. */
 
359
{
 
360
    register TkBorder *borderPtr = (TkBorder *) border;
 
361
 
 
362
    Tk_SetWindowBackground(tkwin, borderPtr->bgColorPtr->pixel);
 
363
}
 
364
 
 
365
/*
 
366
 *----------------------------------------------------------------------
 
367
 *
 
368
 * Tk_GetRelief --
 
369
 *
 
370
 *      Parse a relief description and return the corresponding
 
371
 *      relief value, or an error.
 
372
 *
 
373
 * Results:
 
374
 *      A standard Tcl return value.  If all goes well then
 
375
 *      *reliefPtr is filled in with one of the values
 
376
 *      TK_RELIEF_RAISED, TK_RELIEF_FLAT, or TK_RELIEF_SUNKEN.
 
377
 *
 
378
 * Side effects:
 
379
 *      None.
 
380
 *
 
381
 *----------------------------------------------------------------------
 
382
 */
 
383
 
 
384
int
 
385
Tk_GetRelief(interp, name, reliefPtr)
 
386
    Tcl_Interp *interp;         /* For error messages. */
 
387
    char *name;                 /* Name of a relief type. */
 
388
    int *reliefPtr;             /* Where to store converted relief. */
 
389
{
 
390
    char c;
 
391
    size_t length;
 
392
 
 
393
    c = name[0];
 
394
    length = strlen(name);
 
395
    if ((c == 'f') && (strncmp(name, "flat", length) == 0)) {
 
396
        *reliefPtr = TK_RELIEF_FLAT;
 
397
    } else if ((c == 'g') && (strncmp(name, "groove", length) == 0)
 
398
            && (length >= 2)) {
 
399
        *reliefPtr = TK_RELIEF_GROOVE;
 
400
    } else if ((c == 'r') && (strncmp(name, "raised", length) == 0)
 
401
            && (length >= 2)) {
 
402
        *reliefPtr = TK_RELIEF_RAISED;
 
403
    } else if ((c == 'r') && (strncmp(name, "ridge", length) == 0)) {
 
404
        *reliefPtr = TK_RELIEF_RIDGE;
 
405
    } else if ((c == 's') && (strncmp(name, "solid", length) == 0)) {
 
406
        *reliefPtr = TK_RELIEF_SOLID;
 
407
    } else if ((c == 's') && (strncmp(name, "sunken", length) == 0)) {
 
408
        *reliefPtr = TK_RELIEF_SUNKEN;
 
409
    } else {
 
410
        sprintf(interp->result, "bad relief type \"%.50s\": must be %s",
 
411
                name, "flat, groove, raised, ridge, solid, or sunken");
 
412
        return TCL_ERROR;
 
413
    }
 
414
    return TCL_OK;
 
415
}
 
416
 
 
417
/*
 
418
 *--------------------------------------------------------------
 
419
 *
 
420
 * Tk_NameOfRelief --
 
421
 *
 
422
 *      Given a relief value, produce a string describing that
 
423
 *      relief value.
 
424
 *
 
425
 * Results:
 
426
 *      The return value is a static string that is equivalent
 
427
 *      to relief.
 
428
 *
 
429
 * Side effects:
 
430
 *      None.
 
431
 *
 
432
 *--------------------------------------------------------------
 
433
 */
 
434
 
 
435
char *
 
436
Tk_NameOfRelief(relief)
 
437
    int relief;         /* One of TK_RELIEF_FLAT, TK_RELIEF_RAISED,
 
438
                         * or TK_RELIEF_SUNKEN. */
 
439
{
 
440
    if (relief == TK_RELIEF_FLAT) {
 
441
        return "flat";
 
442
    } else if (relief == TK_RELIEF_SUNKEN) {
 
443
        return "sunken";
 
444
    } else if (relief == TK_RELIEF_RAISED) {
 
445
        return "raised";
 
446
    } else if (relief == TK_RELIEF_GROOVE) {
 
447
        return "groove";
 
448
    } else if (relief == TK_RELIEF_RIDGE) {
 
449
        return "ridge";
 
450
    } else if (relief == TK_RELIEF_SOLID) {
 
451
        return "solid";
 
452
    } else {
 
453
        return "unknown relief";
 
454
    }
 
455
}
 
456
 
 
457
/*
 
458
 *--------------------------------------------------------------
 
459
 *
 
460
 * Tk_Draw3DPolygon --
 
461
 *
 
462
 *      Draw a border with 3-D appearance around the edge of a
 
463
 *      given polygon.
 
464
 *
 
465
 * Results:
 
466
 *      None.
 
467
 *
 
468
 * Side effects:
 
469
 *      Information is drawn in "drawable" in the form of a
 
470
 *      3-D border borderWidth units width wide on the left
 
471
 *      of the trajectory given by pointPtr and numPoints (or
 
472
 *      -borderWidth units wide on the right side, if borderWidth
 
473
 *      is negative).
 
474
 *
 
475
 *--------------------------------------------------------------
 
476
 */
 
477
 
 
478
void
 
479
Tk_Draw3DPolygon(tkwin, drawable, border, pointPtr, numPoints,
 
480
        borderWidth, leftRelief)
 
481
    Tk_Window tkwin;            /* Window for which border was allocated. */
 
482
    Drawable drawable;          /* X window or pixmap in which to draw. */
 
483
    Tk_3DBorder border;         /* Token for border to draw. */
 
484
    XPoint *pointPtr;           /* Array of points describing
 
485
                                 * polygon.  All points must be
 
486
                                 * absolute (CoordModeOrigin). */
 
487
    int numPoints;              /* Number of points at *pointPtr. */
 
488
    int borderWidth;            /* Width of border, measured in
 
489
                                 * pixels to the left of the polygon's
 
490
                                 * trajectory.   May be negative. */
 
491
    int leftRelief;             /* TK_RELIEF_RAISED or
 
492
                                 * TK_RELIEF_SUNKEN: indicates how
 
493
                                 * stuff to left of trajectory looks
 
494
                                 * relative to stuff on right. */
 
495
{
 
496
    XPoint poly[4], b1, b2, newB1, newB2;
 
497
    XPoint perp, c, shift1, shift2;     /* Used for handling parallel lines. */
 
498
    register XPoint *p1Ptr, *p2Ptr;
 
499
    TkBorder *borderPtr = (TkBorder *) border;
 
500
    GC gc;
 
501
    int i, lightOnLeft, dx, dy, parallel, pointsSeen;
 
502
    Display *display = Tk_Display(tkwin);
 
503
 
 
504
    if (borderPtr->lightGC == None) {
 
505
        TkpGetShadows(borderPtr, tkwin);
 
506
    }
 
507
 
 
508
    /*
 
509
     * Handle grooves and ridges with recursive calls.
 
510
     */
 
511
 
 
512
    if ((leftRelief == TK_RELIEF_GROOVE) || (leftRelief == TK_RELIEF_RIDGE)) {
 
513
        int halfWidth;
 
514
 
 
515
        halfWidth = borderWidth/2;
 
516
        Tk_Draw3DPolygon(tkwin, drawable, border, pointPtr, numPoints,
 
517
                halfWidth, (leftRelief == TK_RELIEF_GROOVE) ? TK_RELIEF_RAISED
 
518
                : TK_RELIEF_SUNKEN);
 
519
        Tk_Draw3DPolygon(tkwin, drawable, border, pointPtr, numPoints,
 
520
                -halfWidth, (leftRelief == TK_RELIEF_GROOVE) ? TK_RELIEF_SUNKEN
 
521
                : TK_RELIEF_RAISED);
 
522
        return;
 
523
    }
 
524
 
 
525
    /*
 
526
     * If the polygon is already closed, drop the last point from it
 
527
     * (we'll close it automatically).
 
528
     */
 
529
 
 
530
    p1Ptr = &pointPtr[numPoints-1];
 
531
    p2Ptr = &pointPtr[0];
 
532
    if ((p1Ptr->x == p2Ptr->x) && (p1Ptr->y == p2Ptr->y)) {
 
533
        numPoints--;
 
534
    }
 
535
 
 
536
    /*
 
537
     * The loop below is executed once for each vertex in the polgon.
 
538
     * At the beginning of each iteration things look like this:
 
539
     *
 
540
     *          poly[1]       /
 
541
     *             *        /
 
542
     *             |      /
 
543
     *             b1   * poly[0] (pointPtr[i-1])
 
544
     *             |    |
 
545
     *             |    |
 
546
     *             |    |
 
547
     *             |    |
 
548
     *             |    |
 
549
     *             |    | *p1Ptr            *p2Ptr
 
550
     *             b2   *--------------------*
 
551
     *             |
 
552
     *             |
 
553
     *             x-------------------------
 
554
     *
 
555
     * The job of this iteration is to do the following:
 
556
     * (a) Compute x (the border corner corresponding to
 
557
     *     pointPtr[i]) and put it in poly[2].  As part of
 
558
     *     this, compute a new b1 and b2 value for the next
 
559
     *     side of the polygon.
 
560
     * (b) Put pointPtr[i] into poly[3].
 
561
     * (c) Draw the polygon given by poly[0..3].
 
562
     * (d) Advance poly[0], poly[1], b1, and b2 for the
 
563
     *     next side of the polygon.
 
564
     */
 
565
 
 
566
    /*
 
567
     * The above situation doesn't first come into existence until
 
568
     * two points have been processed;  the first two points are
 
569
     * used to "prime the pump", so some parts of the processing
 
570
     * are ommitted for these points.  The variable "pointsSeen"
 
571
     * keeps track of the priming process;  it has to be separate
 
572
     * from i in order to be able to ignore duplicate points in the
 
573
     * polygon.
 
574
     */
 
575
 
 
576
    pointsSeen = 0;
 
577
    for (i = -2, p1Ptr = &pointPtr[numPoints-2], p2Ptr = p1Ptr+1;
 
578
            i < numPoints; i++, p1Ptr = p2Ptr, p2Ptr++) {
 
579
        if ((i == -1) || (i == numPoints-1)) {
 
580
            p2Ptr = pointPtr;
 
581
        }
 
582
        if ((p2Ptr->x == p1Ptr->x) && (p2Ptr->y == p1Ptr->y)) {
 
583
            /*
 
584
             * Ignore duplicate points (they'd cause core dumps in
 
585
             * ShiftLine calls below).
 
586
             */
 
587
            continue;
 
588
        }
 
589
        ShiftLine(p1Ptr, p2Ptr, borderWidth, &newB1);
 
590
        newB2.x = newB1.x + (p2Ptr->x - p1Ptr->x);
 
591
        newB2.y = newB1.y + (p2Ptr->y - p1Ptr->y);
 
592
        poly[3] = *p1Ptr;
 
593
        parallel = 0;
 
594
        if (pointsSeen >= 1) {
 
595
            parallel = Intersect(&newB1, &newB2, &b1, &b2, &poly[2]);
 
596
 
 
597
            /*
 
598
             * If two consecutive segments of the polygon are parallel,
 
599
             * then things get more complex.  Consider the following
 
600
             * diagram:
 
601
             *
 
602
             * poly[1]
 
603
             *    *----b1-----------b2------a
 
604
             *                                \
 
605
             *                                  \
 
606
             *         *---------*----------*    b
 
607
             *        poly[0]  *p2Ptr   *p1Ptr  /
 
608
             *                                /
 
609
             *              --*--------*----c
 
610
             *              newB1    newB2
 
611
             *
 
612
             * Instead of using x and *p1Ptr for poly[2] and poly[3], as
 
613
             * in the original diagram, use a and b as above.  Then instead
 
614
             * of using x and *p1Ptr for the new poly[0] and poly[1], use
 
615
             * b and c as above.
 
616
             *
 
617
             * Do the computation in three stages:
 
618
             * 1. Compute a point "perp" such that the line p1Ptr-perp
 
619
             *    is perpendicular to p1Ptr-p2Ptr.
 
620
             * 2. Compute the points a and c by intersecting the lines
 
621
             *    b1-b2 and newB1-newB2 with p1Ptr-perp.
 
622
             * 3. Compute b by shifting p1Ptr-perp to the right and
 
623
             *    intersecting it with p1Ptr-p2Ptr.
 
624
             */
 
625
 
 
626
            if (parallel) {
 
627
                perp.x = p1Ptr->x + (p2Ptr->y - p1Ptr->y);
 
628
                perp.y = p1Ptr->y - (p2Ptr->x - p1Ptr->x);
 
629
                (void) Intersect(p1Ptr, &perp, &b1, &b2, &poly[2]);
 
630
                (void) Intersect(p1Ptr, &perp, &newB1, &newB2, &c);
 
631
                ShiftLine(p1Ptr, &perp, borderWidth, &shift1);
 
632
                shift2.x = shift1.x + (perp.x - p1Ptr->x);
 
633
                shift2.y = shift1.y + (perp.y - p1Ptr->y);
 
634
                (void) Intersect(p1Ptr, p2Ptr, &shift1, &shift2, &poly[3]);
 
635
            }
 
636
        }
 
637
        if (pointsSeen >= 2) {
 
638
            dx = poly[3].x - poly[0].x;
 
639
            dy = poly[3].y - poly[0].y;
 
640
            if (dx > 0) {
 
641
                lightOnLeft = (dy <= dx);
 
642
            } else {
 
643
                lightOnLeft = (dy < dx);
 
644
            }
 
645
            if (lightOnLeft ^ (leftRelief == TK_RELIEF_RAISED)) {
 
646
                gc = borderPtr->lightGC;
 
647
            } else {
 
648
                gc = borderPtr->darkGC;
 
649
            }
 
650
            XFillPolygon(display, drawable, gc, poly, 4, Convex,
 
651
                    CoordModeOrigin);
 
652
        }
 
653
        b1.x = newB1.x;
 
654
        b1.y = newB1.y;
 
655
        b2.x = newB2.x;
 
656
        b2.y = newB2.y;
 
657
        poly[0].x = poly[3].x;
 
658
        poly[0].y = poly[3].y;
 
659
        if (parallel) {
 
660
            poly[1].x = c.x;
 
661
            poly[1].y = c.y;
 
662
        } else if (pointsSeen >= 1) {
 
663
            poly[1].x = poly[2].x;
 
664
            poly[1].y = poly[2].y;
 
665
        }
 
666
        pointsSeen++;
 
667
    }
 
668
}
 
669
 
 
670
/*
 
671
 *----------------------------------------------------------------------
 
672
 *
 
673
 * Tk_Fill3DRectangle --
 
674
 *
 
675
 *      Fill a rectangular area, supplying a 3D border if desired.
 
676
 *
 
677
 * Results:
 
678
 *      None.
 
679
 *
 
680
 * Side effects:
 
681
 *      Information gets drawn on the screen.
 
682
 *
 
683
 *----------------------------------------------------------------------
 
684
 */
 
685
 
 
686
void
 
687
Tk_Fill3DRectangle(tkwin, drawable, border, x, y, width,
 
688
        height, borderWidth, relief)
 
689
    Tk_Window tkwin;            /* Window for which border was allocated. */
 
690
    Drawable drawable;          /* X window or pixmap in which to draw. */
 
691
    Tk_3DBorder border;         /* Token for border to draw. */
 
692
    int x, y, width, height;    /* Outside area of rectangular region. */
 
693
    int borderWidth;            /* Desired width for border, in
 
694
                                 * pixels. Border will be *inside* region. */
 
695
    int relief;                 /* Indicates 3D effect: TK_RELIEF_FLAT,
 
696
                                 * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
 
697
{
 
698
    register TkBorder *borderPtr = (TkBorder *) border;
 
699
    int doubleBorder;
 
700
 
 
701
    /*
 
702
     * This code is slightly tricky because it only draws the background
 
703
     * in areas not covered by the 3D border. This avoids flashing
 
704
     * effects on the screen for the border region.
 
705
     */
 
706
  
 
707
    if (relief == TK_RELIEF_FLAT) {
 
708
        borderWidth = 0;
 
709
    }
 
710
    doubleBorder = 2*borderWidth;
 
711
 
 
712
    if ((width > doubleBorder) && (height > doubleBorder)) {
 
713
        XFillRectangle(Tk_Display(tkwin), drawable, borderPtr->bgGC,
 
714
                x + borderWidth, y + borderWidth,
 
715
                (unsigned int) (width - doubleBorder),
 
716
                (unsigned int) (height - doubleBorder));
 
717
    }
 
718
    if (borderWidth) {
 
719
        Tk_Draw3DRectangle(tkwin, drawable, border, x, y, width,
 
720
                height, borderWidth, relief);
 
721
    }
 
722
}
 
723
 
 
724
/*
 
725
 *----------------------------------------------------------------------
 
726
 *
 
727
 * Tk_Fill3DPolygon --
 
728
 *
 
729
 *      Fill a polygonal area, supplying a 3D border if desired.
 
730
 *
 
731
 * Results:
 
732
 *      None.
 
733
 *
 
734
 * Side effects:
 
735
 *      Information gets drawn on the screen.
 
736
 *
 
737
 *----------------------------------------------------------------------
 
738
 */
 
739
 
 
740
void
 
741
Tk_Fill3DPolygon(tkwin, drawable, border, pointPtr, numPoints,
 
742
        borderWidth, leftRelief)
 
743
    Tk_Window tkwin;            /* Window for which border was allocated. */
 
744
    Drawable drawable;          /* X window or pixmap in which to draw. */
 
745
    Tk_3DBorder border;         /* Token for border to draw. */
 
746
    XPoint *pointPtr;           /* Array of points describing
 
747
                                 * polygon.  All points must be
 
748
                                 * absolute (CoordModeOrigin). */
 
749
    int numPoints;              /* Number of points at *pointPtr. */
 
750
    int borderWidth;            /* Width of border, measured in
 
751
                                 * pixels to the left of the polygon's
 
752
                                 * trajectory.   May be negative. */
 
753
    int leftRelief;                     /* Indicates 3D effect of left side of
 
754
                                 * trajectory relative to right:
 
755
                                 * TK_RELIEF_FLAT, TK_RELIEF_RAISED,
 
756
                                 * or TK_RELIEF_SUNKEN. */
 
757
{
 
758
    register TkBorder *borderPtr = (TkBorder *) border;
 
759
 
 
760
    XFillPolygon(Tk_Display(tkwin), drawable, borderPtr->bgGC,
 
761
            pointPtr, numPoints, Complex, CoordModeOrigin);
 
762
    if (leftRelief != TK_RELIEF_FLAT) {
 
763
        Tk_Draw3DPolygon(tkwin, drawable, border, pointPtr, numPoints,
 
764
                borderWidth, leftRelief);
 
765
    }
 
766
}
 
767
 
 
768
/*
 
769
 *--------------------------------------------------------------
 
770
 *
 
771
 * BorderInit --
 
772
 *
 
773
 *      Initialize the structures used for border management.
 
774
 *
 
775
 * Results:
 
776
 *      None.
 
777
 *
 
778
 * Side effects:
 
779
 *      Read the code.
 
780
 *
 
781
 *-------------------------------------------------------------
 
782
 */
 
783
 
 
784
static void
 
785
BorderInit()
 
786
{
 
787
    initialized = 1;
 
788
    Tcl_InitHashTable(&borderTable, sizeof(BorderKey)/sizeof(int));
 
789
}
 
790
 
 
791
/*
 
792
 *--------------------------------------------------------------
 
793
 *
 
794
 * ShiftLine --
 
795
 *
 
796
 *      Given two points on a line, compute a point on a
 
797
 *      new line that is parallel to the given line and
 
798
 *      a given distance away from it.
 
799
 *
 
800
 * Results:
 
801
 *      None.
 
802
 *
 
803
 * Side effects:
 
804
 *      None.
 
805
 *
 
806
 *--------------------------------------------------------------
 
807
 */
 
808
 
 
809
static void
 
810
ShiftLine(p1Ptr, p2Ptr, distance, p3Ptr)
 
811
    XPoint *p1Ptr;              /* First point on line. */
 
812
    XPoint *p2Ptr;              /* Second point on line. */
 
813
    int distance;               /* New line is to be this many
 
814
                                 * units to the left of original
 
815
                                 * line, when looking from p1 to
 
816
                                 * p2.  May be negative. */
 
817
    XPoint *p3Ptr;              /* Store coords of point on new
 
818
                                 * line here. */
 
819
{
 
820
    int dx, dy, dxNeg, dyNeg;
 
821
 
 
822
    /*
 
823
     * The table below is used for a quick approximation in
 
824
     * computing the new point.  An index into the table
 
825
     * is 128 times the slope of the original line (the slope
 
826
     * must always be between 0 and 1).  The value of the table
 
827
     * entry is 128 times the amount to displace the new line
 
828
     * in y for each unit of perpendicular distance.  In other
 
829
     * words, the table maps from the tangent of an angle to
 
830
     * the inverse of its cosine.  If the slope of the original
 
831
     * line is greater than 1, then the displacement is done in
 
832
     * x rather than in y.
 
833
     */
 
834
 
 
835
    static int shiftTable[129];
 
836
 
 
837
    /*
 
838
     * Initialize the table if this is the first time it is
 
839
     * used.
 
840
     */
 
841
 
 
842
    if (shiftTable[0] == 0) {
 
843
        int i;
 
844
        double tangent, cosine;
 
845
 
 
846
        for (i = 0; i <= 128; i++) {
 
847
            tangent = i/128.0;
 
848
            cosine = 128/cos(atan(tangent)) + .5;
 
849
            shiftTable[i] = (int) cosine;
 
850
        }
 
851
    }
 
852
 
 
853
    *p3Ptr = *p1Ptr;
 
854
    dx = p2Ptr->x - p1Ptr->x;
 
855
    dy = p2Ptr->y - p1Ptr->y;
 
856
    if (dy < 0) {
 
857
        dyNeg = 1;
 
858
        dy = -dy;
 
859
    } else {
 
860
        dyNeg = 0;
 
861
    }
 
862
    if (dx < 0) {
 
863
        dxNeg = 1;
 
864
        dx = -dx;
 
865
    } else {
 
866
        dxNeg = 0;
 
867
    }
 
868
    if (dy <= dx) {
 
869
        dy = ((distance * shiftTable[(dy<<7)/dx]) + 64) >> 7;
 
870
        if (!dxNeg) {
 
871
            dy = -dy;
 
872
        }
 
873
        p3Ptr->y += dy;
 
874
    } else {
 
875
        dx = ((distance * shiftTable[(dx<<7)/dy]) + 64) >> 7;
 
876
        if (dyNeg) {
 
877
            dx = -dx;
 
878
        }
 
879
        p3Ptr->x += dx;
 
880
    }
 
881
}
 
882
 
 
883
/*
 
884
 *--------------------------------------------------------------
 
885
 *
 
886
 * Intersect --
 
887
 *
 
888
 *      Find the intersection point between two lines.
 
889
 *
 
890
 * Results:
 
891
 *      Under normal conditions 0 is returned and the point
 
892
 *      at *iPtr is filled in with the intersection between
 
893
 *      the two lines.  If the two lines are parallel, then
 
894
 *      -1 is returned and *iPtr isn't modified.
 
895
 *
 
896
 * Side effects:
 
897
 *      None.
 
898
 *
 
899
 *--------------------------------------------------------------
 
900
 */
 
901
 
 
902
static int
 
903
Intersect(a1Ptr, a2Ptr, b1Ptr, b2Ptr, iPtr)
 
904
    XPoint *a1Ptr;              /* First point of first line. */
 
905
    XPoint *a2Ptr;              /* Second point of first line. */
 
906
    XPoint *b1Ptr;              /* First point of second line. */
 
907
    XPoint *b2Ptr;              /* Second point of second line. */
 
908
    XPoint *iPtr;               /* Filled in with intersection point. */
 
909
{
 
910
    int dxadyb, dxbdya, dxadxb, dyadyb, p, q;
 
911
 
 
912
    /*
 
913
     * The code below is just a straightforward manipulation of two
 
914
     * equations of the form y = (x-x1)*(y2-y1)/(x2-x1) + y1 to solve
 
915
     * for the x-coordinate of intersection, then the y-coordinate.
 
916
     */
 
917
 
 
918
    dxadyb = (a2Ptr->x - a1Ptr->x)*(b2Ptr->y - b1Ptr->y);
 
919
    dxbdya = (b2Ptr->x - b1Ptr->x)*(a2Ptr->y - a1Ptr->y);
 
920
    dxadxb = (a2Ptr->x - a1Ptr->x)*(b2Ptr->x - b1Ptr->x);
 
921
    dyadyb = (a2Ptr->y - a1Ptr->y)*(b2Ptr->y - b1Ptr->y);
 
922
 
 
923
    if (dxadyb == dxbdya) {
 
924
        return -1;
 
925
    }
 
926
    p = (a1Ptr->x*dxbdya - b1Ptr->x*dxadyb + (b1Ptr->y - a1Ptr->y)*dxadxb);
 
927
    q = dxbdya - dxadyb;
 
928
    if (q < 0) {
 
929
        p = -p;
 
930
        q = -q;
 
931
    }
 
932
    if (p < 0) {
 
933
        iPtr->x = - ((-p + q/2)/q);
 
934
    } else {
 
935
        iPtr->x = (p + q/2)/q;
 
936
    }
 
937
    p = (a1Ptr->y*dxadyb - b1Ptr->y*dxbdya + (b1Ptr->x - a1Ptr->x)*dyadyb);
 
938
    q = dxadyb - dxbdya;
 
939
    if (q < 0) {
 
940
        p = -p;
 
941
        q = -q;
 
942
    }
 
943
    if (p < 0) {
 
944
        iPtr->y = - ((-p + q/2)/q);
 
945
    } else {
 
946
        iPtr->y = (p + q/2)/q;
 
947
    }
 
948
    return 0;
 
949
}