OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [tags/] [start/] [insight/] [tk/] [generic/] [tkFont.c] - Blame information for rev 1780

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
2
 * tkFont.c --
3
 *
4
 *      This file maintains a database of fonts for the Tk toolkit.
5
 *      It also provides several utility procedures for measuring and
6
 *      displaying text.
7
 *
8
 * Copyright (c) 1990-1994 The Regents of the University of California.
9
 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
10
 *
11
 * See the file "license.terms" for information on usage and redistribution
12
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13
 *
14
 * RCS: @(#) $Id: tkFont.c,v 1.1.1.1 2002-01-16 10:25:51 markom Exp $
15
 */
16
 
17
#include "tkInt.h"
18
#include "tkFont.h"
19
 
20
/*
21
 * The following structure is used to keep track of all the fonts that
22
 * exist in the current application.  It must be stored in the
23
 * TkMainInfo for the application.
24
 */
25
 
26
typedef struct TkFontInfo {
27
    Tcl_HashTable fontCache;    /* Map a string to an existing Tk_Font.
28
                                 * Keys are CachedFontKey structs, values are
29
                                 * TkFont structs. */
30
    Tcl_HashTable namedTable;   /* Map a name to a set of attributes for a
31
                                 * font, used when constructing a Tk_Font from
32
                                 * a named font description.  Keys are
33
                                 * Tk_Uids, values are NamedFont structs. */
34
    TkMainInfo *mainPtr;        /* Application that owns this structure. */
35
    int updatePending;
36
} TkFontInfo;
37
 
38
/*
39
 * The following structure is used as a key in the fontCache.
40
 */
41
 
42
typedef struct CachedFontKey {
43
    Display *display;           /* Display for which font was constructed. */
44
    Tk_Uid string;              /* String that describes font. */
45
} CachedFontKey;
46
 
47
/*
48
 * The following data structure is used to keep track of the font attributes
49
 * for each named font that has been defined.  The named font is only deleted
50
 * when the last reference to it goes away.
51
 */
52
 
53
typedef struct NamedFont {
54
    int refCount;               /* Number of users of named font. */
55
    int deletePending;          /* Non-zero if font should be deleted when
56
                                 * last reference goes away. */
57
    TkFontAttributes fa;        /* Desired attributes for named font. */
58
} NamedFont;
59
 
60
/*
61
 * The following two structures are used to keep track of string
62
 * measurement information when using the text layout facilities.
63
 *
64
 * A LayoutChunk represents a contiguous range of text that can be measured
65
 * and displayed by low-level text calls.  In general, chunks will be
66
 * delimited by newlines and tabs.  Low-level, platform-specific things
67
 * like kerning and non-integer character widths may occur between the
68
 * characters in a single chunk, but not between characters in different
69
 * chunks.
70
 *
71
 * A TextLayout is a collection of LayoutChunks.  It can be displayed with
72
 * respect to any origin.  It is the implementation of the Tk_TextLayout
73
 * opaque token.
74
 */
75
 
76
typedef struct LayoutChunk {
77
    CONST char *start;          /* Pointer to simple string to be displayed.
78
                                 * This is a pointer into the TkTextLayout's
79
                                 * string. */
80
    int numChars;               /* The number of characters in this chunk. */
81
    int numDisplayChars;        /* The number of characters to display when
82
                                 * this chunk is displayed.  Can be less than
83
                                 * numChars if extra space characters were
84
                                 * absorbed by the end of the chunk.  This
85
                                 * will be < 0 if this is a chunk that is
86
                                 * holding a tab or newline. */
87
    int x, y;                   /* The origin of the first character in this
88
                                 * chunk with respect to the upper-left hand
89
                                 * corner of the TextLayout. */
90
    int totalWidth;             /* Width in pixels of this chunk.  Used
91
                                 * when hit testing the invisible spaces at
92
                                 * the end of a chunk. */
93
    int displayWidth;           /* Width in pixels of the displayable
94
                                 * characters in this chunk.  Can be less than
95
                                 * width if extra space characters were
96
                                 * absorbed by the end of the chunk. */
97
} LayoutChunk;
98
 
99
typedef struct TextLayout {
100
    Tk_Font tkfont;             /* The font used when laying out the text. */
101
    CONST char *string;         /* The string that was layed out. */
102
    int width;                  /* The maximum width of all lines in the
103
                                 * text layout. */
104
    int numChunks;              /* Number of chunks actually used in
105
                                 * following array. */
106
    LayoutChunk chunks[1];      /* Array of chunks.  The actual size will
107
                                 * be maxChunks.  THIS FIELD MUST BE THE LAST
108
                                 * IN THE STRUCTURE. */
109
} TextLayout;
110
 
111
/*
112
 * The following structures are used as two-way maps between the values for
113
 * the fields in the TkFontAttributes structure and the strings used in
114
 * Tcl, when parsing both option-value format and style-list format font
115
 * name strings.
116
 */
117
 
118
static TkStateMap weightMap[] = {
119
    {TK_FW_NORMAL,      "normal"},
120
    {TK_FW_BOLD,        "bold"},
121
    {TK_FW_UNKNOWN,     NULL}
122
};
123
 
124
static TkStateMap slantMap[] = {
125
    {TK_FS_ROMAN,       "roman"},
126
    {TK_FS_ITALIC,      "italic"},
127
    {TK_FS_UNKNOWN,     NULL}
128
};
129
 
130
static TkStateMap underlineMap[] = {
131
    {1,                 "underline"},
132
    {0,                  NULL}
133
};
134
 
135
static TkStateMap overstrikeMap[] = {
136
    {1,                 "overstrike"},
137
    {0,                  NULL}
138
};
139
 
140
/*
141
 * The following structures are used when parsing XLFD's into a set of
142
 * TkFontAttributes.
143
 */
144
 
145
static TkStateMap xlfdWeightMap[] = {
146
    {TK_FW_NORMAL,      "normal"},
147
    {TK_FW_NORMAL,      "medium"},
148
    {TK_FW_NORMAL,      "book"},
149
    {TK_FW_NORMAL,      "light"},
150
    {TK_FW_BOLD,        "bold"},
151
    {TK_FW_BOLD,        "demi"},
152
    {TK_FW_BOLD,        "demibold"},
153
    {TK_FW_NORMAL,      NULL}           /* Assume anything else is "normal". */
154
};
155
 
156
static TkStateMap xlfdSlantMap[] = {
157
    {TK_FS_ROMAN,       "r"},
158
    {TK_FS_ITALIC,      "i"},
159
    {TK_FS_OBLIQUE,     "o"},
160
    {TK_FS_ROMAN,       NULL}           /* Assume anything else is "roman". */
161
};
162
 
163
static TkStateMap xlfdSetwidthMap[] = {
164
    {TK_SW_NORMAL,      "normal"},
165
    {TK_SW_CONDENSE,    "narrow"},
166
    {TK_SW_CONDENSE,    "semicondensed"},
167
    {TK_SW_CONDENSE,    "condensed"},
168
    {TK_SW_UNKNOWN,     NULL}
169
};
170
 
171
static TkStateMap xlfdCharsetMap[] = {
172
    {TK_CS_NORMAL,      "iso8859"},
173
    {TK_CS_SYMBOL,      "adobe"},
174
    {TK_CS_SYMBOL,      "sun"},
175
    {TK_CS_OTHER,       NULL}
176
};
177
 
178
/*
179
 * The following structure and defines specify the valid builtin options
180
 * when configuring a set of font attributes.
181
 */
182
 
183
static char *fontOpt[] = {
184
    "-family",
185
    "-size",
186
    "-weight",
187
    "-slant",
188
    "-underline",
189
    "-overstrike",
190
    NULL
191
};
192
 
193
#define FONT_FAMILY     0
194
#define FONT_SIZE       1
195
#define FONT_WEIGHT     2
196
#define FONT_SLANT      3
197
#define FONT_UNDERLINE  4
198
#define FONT_OVERSTRIKE 5
199
#define FONT_NUMFIELDS  6           /* Length of fontOpt array. */
200
 
201
#define GetFontAttributes(tkfont) \
202
                ((CONST TkFontAttributes *) &((TkFont *) (tkfont))->fa)
203
 
204
#define GetFontMetrics(tkfont)    \
205
                ((CONST TkFontMetrics *) &((TkFont *) (tkfont))->fm)
206
 
207
 
208
static int              ConfigAttributesObj _ANSI_ARGS_((Tcl_Interp *interp,
209
                            Tk_Window tkwin, int objc, Tcl_Obj *CONST objv[],
210
                            TkFontAttributes *faPtr));
211
static int              FieldSpecified _ANSI_ARGS_((CONST char *field));
212
static int              GetAttributeInfoObj _ANSI_ARGS_((Tcl_Interp *interp,
213
                            CONST TkFontAttributes *faPtr, Tcl_Obj *objPtr));
214
static LayoutChunk *    NewChunk _ANSI_ARGS_((TextLayout **layoutPtrPtr,
215
                            int *maxPtr, CONST char *start, int numChars,
216
                            int curX, int newX, int y));
217
static int              ParseFontNameObj _ANSI_ARGS_((Tcl_Interp *interp,
218
                            Tk_Window tkwin, Tcl_Obj *objPtr,
219
                            TkFontAttributes *faPtr));
220
static void             RecomputeWidgets _ANSI_ARGS_((TkWindow *winPtr));
221
static void             TheWorldHasChanged _ANSI_ARGS_((
222
                            ClientData clientData));
223
static void             UpdateDependantFonts _ANSI_ARGS_((TkFontInfo *fiPtr,
224
                            Tk_Window tkwin, Tcl_HashEntry *namedHashPtr));
225
 
226
 
227
 
228
 
229
/*
230
 *---------------------------------------------------------------------------
231
 *
232
 * TkFontPkgInit --
233
 *
234
 *      This procedure is called when an application is created.  It
235
 *      initializes all the structures that are used by the font
236
 *      package on a per application basis.
237
 *
238
 * Results:
239
 *      Returns a token that must be stored in the TkMainInfo for this
240
 *      application.
241
 *
242
 * Side effects:
243
 *      Memory allocated.
244
 *
245
 *---------------------------------------------------------------------------
246
 */
247
void
248
TkFontPkgInit(mainPtr)
249
    TkMainInfo *mainPtr;        /* The application being created. */
250
{
251
    TkFontInfo *fiPtr;
252
 
253
    fiPtr = (TkFontInfo *) ckalloc(sizeof(TkFontInfo));
254
    Tcl_InitHashTable(&fiPtr->fontCache, sizeof(CachedFontKey) / sizeof(int));
255
    Tcl_InitHashTable(&fiPtr->namedTable, TCL_ONE_WORD_KEYS);
256
    fiPtr->mainPtr = mainPtr;
257
    fiPtr->updatePending = 0;
258
    mainPtr->fontInfoPtr = fiPtr;
259
}
260
 
261
/*
262
 *---------------------------------------------------------------------------
263
 *
264
 * TkFontPkgFree --
265
 *
266
 *      This procedure is called when an application is deleted.  It
267
 *      deletes all the structures that were used by the font package
268
 *      for this application.
269
 *
270
 * Results:
271
 *      None.
272
 *
273
 * Side effects:
274
 *      Memory freed.
275
 *
276
 *---------------------------------------------------------------------------
277
 */
278
 
279
void
280
TkFontPkgFree(mainPtr)
281
    TkMainInfo *mainPtr;        /* The application being deleted. */
282
{
283
    TkFontInfo *fiPtr;
284
    Tcl_HashEntry *hPtr;
285
    Tcl_HashSearch search;
286
 
287
    fiPtr = mainPtr->fontInfoPtr;
288
 
289
    if (fiPtr->fontCache.numEntries != 0) {
290
        panic("TkFontPkgFree: all fonts should have been freed already");
291
    }
292
    Tcl_DeleteHashTable(&fiPtr->fontCache);
293
 
294
    hPtr = Tcl_FirstHashEntry(&fiPtr->namedTable, &search);
295
    while (hPtr != NULL) {
296
        ckfree((char *) Tcl_GetHashValue(hPtr));
297
        hPtr = Tcl_NextHashEntry(&search);
298
    }
299
    Tcl_DeleteHashTable(&fiPtr->namedTable);
300
    if (fiPtr->updatePending != 0) {
301
        Tcl_CancelIdleCall(TheWorldHasChanged, (ClientData) fiPtr);
302
    }
303
    ckfree((char *) fiPtr);
304
}
305
 
306
/*
307
 *---------------------------------------------------------------------------
308
 *
309
 * Tk_FontObjCmd --
310
 *
311
 *      This procedure is implemented to process the "font" Tcl command.
312
 *      See the user documentation for details on what it does.
313
 *
314
 * Results:
315
 *      A standard Tcl result.
316
 *
317
 * Side effects:
318
 *      See the user documentation.
319
 *
320
 *----------------------------------------------------------------------
321
 */
322
 
323
int
324
Tk_FontObjCmd(clientData, interp, objc, objv)
325
    ClientData clientData;      /* Main window associated with interpreter. */
326
    Tcl_Interp *interp;         /* Current interpreter. */
327
    int objc;                   /* Number of arguments. */
328
    Tcl_Obj *CONST objv[];      /* Argument objects. */
329
{
330
    int index;
331
    Tk_Window tkwin;
332
    TkFontInfo *fiPtr;
333
    static char *optionStrings[] = {
334
        "actual",       "configure",    "create",       "delete",
335
        "families",     "measure",      "metrics",      "names",
336
        NULL
337
    };
338
    enum options {
339
        FONT_ACTUAL,    FONT_CONFIGURE, FONT_CREATE,    FONT_DELETE,
340
        FONT_FAMILIES,  FONT_MEASURE,   FONT_METRICS,   FONT_NAMES
341
    };
342
 
343
    tkwin = (Tk_Window) clientData;
344
    fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;
345
 
346
    if (objc < 2) {
347
        Tcl_WrongNumArgs(interp, 1, objv, "option ?arg?");
348
        return TCL_ERROR;
349
    }
350
    if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
351
            &index) != TCL_OK) {
352
        return TCL_ERROR;
353
    }
354
 
355
    switch ((enum options) index) {
356
        case FONT_ACTUAL: {
357
            int skip, result;
358
            Tk_Font tkfont;
359
            Tcl_Obj *objPtr;
360
            CONST TkFontAttributes *faPtr;
361
 
362
            skip = TkGetDisplayOf(interp, objc - 3, objv + 3, &tkwin);
363
            if (skip < 0) {
364
                return TCL_ERROR;
365
            }
366
            if ((objc < 3) || (objc - skip > 4)) {
367
                Tcl_WrongNumArgs(interp, 2, objv,
368
                        "font ?-displayof window? ?option?");
369
                return TCL_ERROR;
370
            }
371
            tkfont = Tk_GetFontFromObj(interp, tkwin, objv[2]);
372
            if (tkfont == NULL) {
373
                return TCL_ERROR;
374
            }
375
            objc -= skip;
376
            objv += skip;
377
            faPtr = GetFontAttributes(tkfont);
378
            objPtr = NULL;
379
            if (objc > 3) {
380
                objPtr = objv[3];
381
            }
382
            result = GetAttributeInfoObj(interp, faPtr, objPtr);
383
            Tk_FreeFont(tkfont);
384
            return result;
385
        }
386
        case FONT_CONFIGURE: {
387
            int result;
388
            char *string;
389
            Tcl_Obj *objPtr;
390
            NamedFont *nfPtr;
391
            Tcl_HashEntry *namedHashPtr;
392
 
393
            if (objc < 3) {
394
                Tcl_WrongNumArgs(interp, 2, objv, "fontname ?options?");
395
                return TCL_ERROR;
396
            }
397
            string = Tk_GetUid(Tcl_GetStringFromObj(objv[2], NULL));
398
            namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, string);
399
            nfPtr = NULL;               /* lint. */
400
            if (namedHashPtr != NULL) {
401
                nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
402
            }
403
            if ((namedHashPtr == NULL) || (nfPtr->deletePending != 0)) {
404
                Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "named font \"", string,
405
                        "\" doesn't exist", NULL);
406
                return TCL_ERROR;
407
            }
408
            if (objc == 3) {
409
                objPtr = NULL;
410
            } else if (objc == 4) {
411
                objPtr = objv[3];
412
            } else {
413
                result = ConfigAttributesObj(interp, tkwin, objc - 3,
414
                        objv + 3, &nfPtr->fa);
415
                UpdateDependantFonts(fiPtr, tkwin, namedHashPtr);
416
                return result;
417
            }
418
            return GetAttributeInfoObj(interp, &nfPtr->fa, objPtr);
419
        }
420
        case FONT_CREATE: {
421
            int skip, i;
422
            char *name;
423
            char buf[32];
424
            TkFontAttributes fa;
425
            Tcl_HashEntry *namedHashPtr;
426
 
427
            skip = 3;
428
            if (objc < 3) {
429
                name = NULL;
430
            } else {
431
                name = Tcl_GetStringFromObj(objv[2], NULL);
432
                if (name[0] == '-') {
433
                    name = NULL;
434
                }
435
            }
436
            if (name == NULL) {
437
                /*
438
                 * No font name specified.  Generate one of the form "fontX".
439
                 */
440
 
441
                for (i = 1; ; i++) {
442
                    sprintf(buf, "font%d", i);
443
                    namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable,
444
                            Tk_GetUid(buf));
445
                    if (namedHashPtr == NULL) {
446
                        break;
447
                    }
448
                }
449
                name = buf;
450
                skip = 2;
451
            }
452
            TkInitFontAttributes(&fa);
453
            if (ConfigAttributesObj(interp, tkwin, objc - skip, objv + skip,
454
                    &fa) != TCL_OK) {
455
                return TCL_ERROR;
456
            }
457
            if (TkCreateNamedFont(interp, tkwin, name, &fa) != TCL_OK) {
458
                return TCL_ERROR;
459
            }
460
            Tcl_SetStringObj(Tcl_GetObjResult(interp), name, -1);
461
            break;
462
        }
463
        case FONT_DELETE: {
464
            int i;
465
            char *string;
466
            NamedFont *nfPtr;
467
            Tcl_HashEntry *namedHashPtr;
468
 
469
            /*
470
             * Delete the named font.  If there are still widgets using this
471
             * font, then it isn't deleted right away.
472
             */
473
 
474
            if (objc < 3) {
475
                Tcl_WrongNumArgs(interp, 2, objv, "fontname ?fontname ...?");
476
                return TCL_ERROR;
477
            }
478
            for (i = 2; i < objc; i++) {
479
                string = Tk_GetUid(Tcl_GetStringFromObj(objv[i], NULL));
480
                namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, string);
481
                if (namedHashPtr == NULL) {
482
                    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "named font \"", string,
483
                            "\" doesn't exist", (char *) NULL);
484
                    return TCL_ERROR;
485
                }
486
                nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
487
                if (nfPtr->refCount != 0) {
488
                    nfPtr->deletePending = 1;
489
                } else {
490
                    Tcl_DeleteHashEntry(namedHashPtr);
491
                    ckfree((char *) nfPtr);
492
                }
493
            }
494
            break;
495
        }
496
        case FONT_FAMILIES: {
497
            int skip;
498
 
499
            skip = TkGetDisplayOf(interp, objc - 2, objv + 2, &tkwin);
500
            if (skip < 0) {
501
                return TCL_ERROR;
502
            }
503
            if (objc - skip != 2) {
504
                Tcl_WrongNumArgs(interp, 2, objv, "?-displayof window?");
505
                return TCL_ERROR;
506
            }
507
            TkpGetFontFamilies(interp, tkwin);
508
            break;
509
        }
510
        case FONT_MEASURE: {
511
            char *string;
512
            Tk_Font tkfont;
513
            int length, skip;
514
 
515
            skip = TkGetDisplayOf(interp, objc - 3, objv + 3, &tkwin);
516
            if (skip < 0) {
517
                return TCL_ERROR;
518
            }
519
            if (objc - skip != 4) {
520
                Tcl_WrongNumArgs(interp, 2, objv,
521
                        "font ?-displayof window? text");
522
                return TCL_ERROR;
523
            }
524
            tkfont = Tk_GetFontFromObj(interp, tkwin, objv[2]);
525
            if (tkfont == NULL) {
526
                return TCL_ERROR;
527
            }
528
            string = Tcl_GetStringFromObj(objv[3 + skip], &length);
529
            Tcl_SetIntObj(Tcl_GetObjResult(interp), Tk_TextWidth(tkfont, string, length));
530
            Tk_FreeFont(tkfont);
531
            break;
532
        }
533
        case FONT_METRICS: {
534
            char buf[64];
535
            Tk_Font tkfont;
536
            int skip, index, i;
537
            CONST TkFontMetrics *fmPtr;
538
            static char *switches[] = {
539
                "-ascent", "-descent", "-linespace", "-fixed", NULL
540
            };
541
 
542
            skip = TkGetDisplayOf(interp, objc - 3, objv + 3, &tkwin);
543
            if (skip < 0) {
544
                return TCL_ERROR;
545
            }
546
            if ((objc < 3) || ((objc - skip) > 4)) {
547
                Tcl_WrongNumArgs(interp, 2, objv,
548
                        "font ?-displayof window? ?option?");
549
                return TCL_ERROR;
550
            }
551
            tkfont = Tk_GetFontFromObj(interp, tkwin, objv[2]);
552
            if (tkfont == NULL) {
553
                return TCL_ERROR;
554
            }
555
            objc -= skip;
556
            objv += skip;
557
            fmPtr = GetFontMetrics(tkfont);
558
            if (objc == 3) {
559
                sprintf(buf, "-ascent %d -descent %d -linespace %d -fixed %d",
560
                        fmPtr->ascent, fmPtr->descent,
561
                        fmPtr->ascent + fmPtr->descent,
562
                        fmPtr->fixed);
563
                Tcl_SetStringObj(Tcl_GetObjResult(interp), buf, -1);
564
            } else {
565
                if (Tcl_GetIndexFromObj(interp, objv[3], switches,
566
                        "metric", 0, &index) != TCL_OK) {
567
                    Tk_FreeFont(tkfont);
568
                    return TCL_ERROR;
569
                }
570
                i = 0;                   /* Needed only to prevent compiler
571
                                         * warning. */
572
                switch (index) {
573
                    case 0: i = fmPtr->ascent;                   break;
574
                    case 1: i = fmPtr->descent;                 break;
575
                    case 2: i = fmPtr->ascent + fmPtr->descent; break;
576
                    case 3: i = fmPtr->fixed;                   break;
577
                }
578
                Tcl_SetIntObj(Tcl_GetObjResult(interp), i);
579
            }
580
            Tk_FreeFont(tkfont);
581
            break;
582
        }
583
        case FONT_NAMES: {
584
            char *string;
585
            Tcl_Obj *strPtr;
586
            NamedFont *nfPtr;
587
            Tcl_HashSearch search;
588
            Tcl_HashEntry *namedHashPtr;
589
 
590
            if (objc != 2) {
591
                Tcl_WrongNumArgs(interp, 1, objv, "names");
592
                return TCL_ERROR;
593
            }
594
            namedHashPtr = Tcl_FirstHashEntry(&fiPtr->namedTable, &search);
595
            while (namedHashPtr != NULL) {
596
                nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
597
                if (nfPtr->deletePending == 0) {
598
                    string = Tcl_GetHashKey(&fiPtr->namedTable, namedHashPtr);
599
                    strPtr = Tcl_NewStringObj(string, -1);
600
                    Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), strPtr);
601
                }
602
                namedHashPtr = Tcl_NextHashEntry(&search);
603
            }
604
            break;
605
        }
606
    }
607
    return TCL_OK;
608
}
609
 
610
/*
611
 *---------------------------------------------------------------------------
612
 *
613
 * UpdateDependantFonts, TheWorldHasChanged, RecomputeWidgets --
614
 *
615
 *      Called when the attributes of a named font changes.  Updates all
616
 *      the instantiated fonts that depend on that named font and then
617
 *      uses the brute force approach and prepares every widget to
618
 *      recompute its geometry.
619
 *
620
 * Results:
621
 *      None.
622
 *
623
 * Side effects:
624
 *      Things get queued for redisplay.
625
 *
626
 *---------------------------------------------------------------------------
627
 */
628
 
629
static void
630
UpdateDependantFonts(fiPtr, tkwin, namedHashPtr)
631
    TkFontInfo *fiPtr;          /* Info about application's fonts. */
632
    Tk_Window tkwin;            /* A window in the application. */
633
    Tcl_HashEntry *namedHashPtr;/* The named font that is changing. */
634
{
635
    Tcl_HashEntry *cacheHashPtr;
636
    Tcl_HashSearch search;
637
    TkFont *fontPtr;
638
    NamedFont *nfPtr;
639
 
640
    nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
641
    if (nfPtr->refCount == 0) {
642
        /*
643
         * Well nobody's using this named font, so don't have to tell
644
         * any widgets to recompute themselves.
645
         */
646
 
647
        return;
648
    }
649
 
650
 
651
    cacheHashPtr = Tcl_FirstHashEntry(&fiPtr->fontCache, &search);
652
    while (cacheHashPtr != NULL) {
653
        fontPtr = (TkFont *) Tcl_GetHashValue(cacheHashPtr);
654
        if (fontPtr->namedHashPtr == namedHashPtr) {
655
            TkpGetFontFromAttributes(fontPtr, tkwin, &nfPtr->fa);
656
            if (fiPtr->updatePending == 0) {
657
                fiPtr->updatePending = 1;
658
                Tcl_DoWhenIdle(TheWorldHasChanged, (ClientData) fiPtr);
659
            }
660
        }
661
        cacheHashPtr = Tcl_NextHashEntry(&search);
662
    }
663
}
664
 
665
static void
666
TheWorldHasChanged(clientData)
667
    ClientData clientData;      /* Info about application's fonts. */
668
{
669
    TkFontInfo *fiPtr;
670
 
671
    fiPtr = (TkFontInfo *) clientData;
672
    fiPtr->updatePending = 0;
673
 
674
    RecomputeWidgets(fiPtr->mainPtr->winPtr);
675
}
676
 
677
static void
678
RecomputeWidgets(winPtr)
679
    TkWindow *winPtr;           /* Window to which command is sent. */
680
{
681
    if ((winPtr->classProcsPtr != NULL)
682
            && (winPtr->classProcsPtr->geometryProc != NULL)) {
683
        (*winPtr->classProcsPtr->geometryProc)(winPtr->instanceData);
684
    }
685
    for (winPtr = winPtr->childList; winPtr != NULL; winPtr = winPtr->nextPtr) {
686
        RecomputeWidgets(winPtr);
687
    }
688
}
689
 
690
/*
691
 *---------------------------------------------------------------------------
692
 *
693
 * TkCreateNamedFont --
694
 *
695
 *      Create the specified named font with the given attributes in the
696
 *      named font table associated with the interp.
697
 *
698
 * Results:
699
 *      Returns TCL_OK if the font was successfully created, or TCL_ERROR
700
 *      if the named font already existed.  If TCL_ERROR is returned, an
701
 *      error message is left in interp->result.
702
 *
703
 * Side effects:
704
 *      Assume there used to exist a named font by the specified name, and
705
 *      that the named font had been deleted, but there were still some
706
 *      widgets using the named font at the time it was deleted.  If a
707
 *      new named font is created with the same name, all those widgets
708
 *      that were using the old named font will be redisplayed using
709
 *      the new named font's attributes.
710
 *
711
 *---------------------------------------------------------------------------
712
 */
713
 
714
int
715
TkCreateNamedFont(interp, tkwin, name, faPtr)
716
    Tcl_Interp *interp;         /* Interp for error return. */
717
    Tk_Window tkwin;            /* A window associated with interp. */
718
    CONST char *name;           /* Name for the new named font. */
719
    TkFontAttributes *faPtr;    /* Attributes for the new named font. */
720
{
721
    TkFontInfo *fiPtr;
722
    Tcl_HashEntry *namedHashPtr;
723
    int new;
724
    NamedFont *nfPtr;
725
 
726
    fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;
727
 
728
    name = Tk_GetUid(name);
729
    namedHashPtr = Tcl_CreateHashEntry(&fiPtr->namedTable, name, &new);
730
 
731
    if (new == 0) {
732
        nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
733
        if (nfPtr->deletePending == 0) {
734
            interp->result[0] = '\0';
735
            Tcl_AppendResult(interp, "font \"", name,
736
                    "\" already exists", (char *) NULL);
737
            return TCL_ERROR;
738
        }
739
 
740
        /*
741
         * Recreating a named font with the same name as a previous
742
         * named font.  Some widgets were still using that named
743
         * font, so they need to get redisplayed.
744
         */
745
 
746
        nfPtr->fa = *faPtr;
747
        nfPtr->deletePending = 0;
748
        UpdateDependantFonts(fiPtr, tkwin, namedHashPtr);
749
        return TCL_OK;
750
    }
751
 
752
    nfPtr = (NamedFont *) ckalloc(sizeof(NamedFont));
753
    nfPtr->deletePending = 0;
754
    Tcl_SetHashValue(namedHashPtr, nfPtr);
755
    nfPtr->fa = *faPtr;
756
    nfPtr->refCount = 0;
757
    nfPtr->deletePending = 0;
758
    return TCL_OK;
759
}
760
 
761
/*
762
 *---------------------------------------------------------------------------
763
 *
764
 * Tk_GetFont --
765
 *
766
 *      Given a string description of a font, map the description to a
767
 *      corresponding Tk_Font that represents the font.
768
 *
769
 * Results:
770
 *      The return value is token for the font, or NULL if an error
771
 *      prevented the font from being created.  If NULL is returned, an
772
 *      error message will be left in interp->result.
773
 *
774
 * Side effects:
775
 *      Calls Tk_GetFontFromObj(), which modifies interp's result object,
776
 *      then copies the string from the result object into interp->result.
777
 *      This procedure will go away when Tk_ConfigureWidget() is
778
 *      made into an object command.
779
 *
780
 *---------------------------------------------------------------------------
781
 */
782
 
783
Tk_Font
784
Tk_GetFont(interp, tkwin, string)
785
    Tcl_Interp *interp;         /* Interp for database and error return. */
786
    Tk_Window tkwin;            /* For display on which font will be used. */
787
    CONST char *string;         /* String describing font, as: named font,
788
                                 * native format, or parseable string. */
789
{
790
    Tcl_Obj *strPtr;
791
    Tk_Font tkfont;
792
 
793
    strPtr = Tcl_NewStringObj((char *) string, -1);
794
 
795
    tkfont = Tk_GetFontFromObj(interp, tkwin, strPtr);
796
    if (tkfont == NULL) {
797
        Tcl_SetResult(interp,
798
                Tcl_GetStringFromObj(Tcl_GetObjResult(interp), NULL),
799
                TCL_VOLATILE);
800
    }
801
 
802
    Tcl_DecrRefCount(strPtr);   /* done with object */
803
    return tkfont;
804
}
805
 
806
/*
807
 *---------------------------------------------------------------------------
808
 *
809
 * Tk_GetFontFromObj --
810
 *
811
 *      Given a string description of a font, map the description to a
812
 *      corresponding Tk_Font that represents the font.
813
 *
814
 * Results:
815
 *      The return value is token for the font, or NULL if an error
816
 *      prevented the font from being created.  If NULL is returned, an
817
 *      error message will be left in interp's result object.
818
 *
819
 * Side effects:
820
 *      The font is added to an internal database with a reference
821
 *      count.  For each call to this procedure, there should eventually
822
 *      be a call to Tk_FreeFont() so that the database is cleaned up when
823
 *      fonts aren't in use anymore.
824
 *
825
 *---------------------------------------------------------------------------
826
 */
827
 
828
Tk_Font
829
Tk_GetFontFromObj(interp, tkwin, objPtr)
830
    Tcl_Interp *interp;         /* Interp for database and error return. */
831
    Tk_Window tkwin;            /* For display on which font will be used. */
832
    Tcl_Obj *objPtr;            /* Object describing font, as: named font,
833
                                 * native format, or parseable string. */
834
{
835
    TkFontInfo *fiPtr;
836
    CachedFontKey key;
837
    Tcl_HashEntry *cacheHashPtr, *namedHashPtr;
838
    TkFont *fontPtr;
839
    int new, descent;
840
    NamedFont *nfPtr;
841
    char *string;
842
 
843
    fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;
844
    string = Tcl_GetStringFromObj(objPtr, NULL);
845
 
846
    key.display = Tk_Display(tkwin);
847
    key.string = Tk_GetUid(string);
848
    cacheHashPtr = Tcl_CreateHashEntry(&fiPtr->fontCache, (char *) &key, &new);
849
 
850
    if (new == 0) {
851
        /*
852
         * We have already constructed a font with this description for
853
         * this display.  Bump the reference count of the cached font.
854
         */
855
 
856
        fontPtr = (TkFont *) Tcl_GetHashValue(cacheHashPtr);
857
        fontPtr->refCount++;
858
        return (Tk_Font) fontPtr;
859
    }
860
 
861
    namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, key.string);
862
    if (namedHashPtr != NULL) {
863
        /*
864
         * Construct a font based on a named font.
865
         */
866
 
867
        nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
868
        nfPtr->refCount++;
869
 
870
        fontPtr = TkpGetFontFromAttributes(NULL, tkwin, &nfPtr->fa);
871
    } else {
872
        /*
873
         * Native font?
874
         */
875
 
876
        fontPtr = TkpGetNativeFont(tkwin, string);
877
        if (fontPtr == NULL) {
878
            TkFontAttributes fa;
879
 
880
            TkInitFontAttributes(&fa);
881
            if (ParseFontNameObj(interp, tkwin, objPtr, &fa) != TCL_OK) {
882
                Tcl_DeleteHashEntry(cacheHashPtr);
883
                return NULL;
884
            }
885
 
886
            /*
887
             * String contained the attributes inline.
888
             */
889
 
890
            fontPtr = TkpGetFontFromAttributes(NULL, tkwin, &fa);
891
        }
892
    }
893
    Tcl_SetHashValue(cacheHashPtr, fontPtr);
894
 
895
    fontPtr->refCount       = 1;
896
    fontPtr->cacheHashPtr   = cacheHashPtr;
897
    fontPtr->namedHashPtr   = namedHashPtr;
898
 
899
    Tk_MeasureChars((Tk_Font) fontPtr, "0", 1, 0, 0, &fontPtr->tabWidth);
900
    if (fontPtr->tabWidth == 0) {
901
        fontPtr->tabWidth = fontPtr->fm.maxWidth;
902
    }
903
    fontPtr->tabWidth *= 8;
904
 
905
    /*
906
     * Make sure the tab width isn't zero (some fonts may not have enough
907
     * information to set a reasonable tab width).
908
     */
909
 
910
    if (fontPtr->tabWidth == 0) {
911
        fontPtr->tabWidth = 1;
912
    }
913
 
914
    /*
915
     * Get information used for drawing underlines in generic code on a
916
     * non-underlined font.
917
     */
918
 
919
    descent = fontPtr->fm.descent;
920
    fontPtr->underlinePos = descent / 2;
921
    fontPtr->underlineHeight = fontPtr->fa.pointsize / 10;
922
    if (fontPtr->underlineHeight == 0) {
923
        fontPtr->underlineHeight = 1;
924
    }
925
    if (fontPtr->underlinePos + fontPtr->underlineHeight > descent) {
926
        /*
927
         * If this set of values would cause the bottom of the underline
928
         * bar to stick below the descent of the font, jack the underline
929
         * up a bit higher.
930
         */
931
 
932
        fontPtr->underlineHeight = descent - fontPtr->underlinePos;
933
        if (fontPtr->underlineHeight == 0) {
934
            fontPtr->underlinePos--;
935
            fontPtr->underlineHeight = 1;
936
        }
937
    }
938
 
939
    return (Tk_Font) fontPtr;
940
}
941
 
942
/*
943
 *---------------------------------------------------------------------------
944
 *
945
 * Tk_NameOfFont --
946
 *
947
 *      Given a font, return a textual string identifying it.
948
 *
949
 * Results:
950
 *      The return value is the description that was passed to
951
 *      Tk_GetFont() to create the font.  The storage for the returned
952
 *      string is only guaranteed to persist until the font is deleted.
953
 *      The caller should not modify this string.
954
 *
955
 * Side effects:
956
 *      None.
957
 *
958
 *---------------------------------------------------------------------------
959
 */
960
 
961
char *
962
Tk_NameOfFont(tkfont)
963
    Tk_Font tkfont;             /* Font whose name is desired. */
964
{
965
    TkFont *fontPtr;
966
    Tcl_HashEntry *hPtr;
967
    CachedFontKey *keyPtr;
968
 
969
    fontPtr = (TkFont *) tkfont;
970
    hPtr = fontPtr->cacheHashPtr;
971
 
972
    keyPtr = (CachedFontKey *) Tcl_GetHashKey(hPtr->tablePtr, hPtr);
973
    return (char *) keyPtr->string;
974
}
975
 
976
/*
977
 *---------------------------------------------------------------------------
978
 *
979
 * Tk_FreeFont --
980
 *
981
 *      Called to release a font allocated by Tk_GetFont().
982
 *
983
 * Results:
984
 *      None.
985
 *
986
 * Side effects:
987
 *      The reference count associated with font is decremented, and
988
 *      only deallocated when no one is using it.
989
 *
990
 *---------------------------------------------------------------------------
991
 */
992
 
993
void
994
Tk_FreeFont(tkfont)
995
    Tk_Font tkfont;             /* Font to be released. */
996
{
997
    TkFont *fontPtr;
998
    NamedFont *nfPtr;
999
 
1000
    if (tkfont == NULL) {
1001
        return;
1002
    }
1003
    fontPtr = (TkFont *) tkfont;
1004
    fontPtr->refCount--;
1005
    if (fontPtr->refCount == 0) {
1006
        if (fontPtr->namedHashPtr != NULL) {
1007
            /*
1008
             * The font is being deleted.  Determine if the associated named
1009
             * font definition should and/or can be deleted too.
1010
             */
1011
 
1012
            nfPtr = (NamedFont *) Tcl_GetHashValue(fontPtr->namedHashPtr);
1013
            nfPtr->refCount--;
1014
            if ((nfPtr->refCount == 0) && (nfPtr->deletePending != 0)) {
1015
                Tcl_DeleteHashEntry(fontPtr->namedHashPtr);
1016
                ckfree((char *) nfPtr);
1017
            }
1018
        }
1019
        Tcl_DeleteHashEntry(fontPtr->cacheHashPtr);
1020
        TkpDeleteFont(fontPtr);
1021
    }
1022
}
1023
 
1024
/*
1025
 *---------------------------------------------------------------------------
1026
 *
1027
 * Tk_FontId --
1028
 *
1029
 *      Given a font, return an opaque handle that should be selected
1030
 *      into the XGCValues structure in order to get the constructed
1031
 *      gc to use this font.  This procedure would go away if the
1032
 *      XGCValues structure were replaced with a TkGCValues structure.
1033
 *
1034
 * Results:
1035
 *      As above.
1036
 *
1037
 * Side effects:
1038
 *      None.
1039
 *
1040
 *---------------------------------------------------------------------------
1041
 */
1042
 
1043
Font
1044
Tk_FontId(tkfont)
1045
    Tk_Font tkfont;     /* Font that is going to be selected into GC. */
1046
{
1047
    TkFont *fontPtr;
1048
 
1049
    fontPtr = (TkFont *) tkfont;
1050
    return fontPtr->fid;
1051
}
1052
 
1053
/*
1054
 *---------------------------------------------------------------------------
1055
 *
1056
 * Tk_GetFontMetrics --
1057
 *
1058
 *      Returns overall ascent and descent metrics for the given font.
1059
 *      These values can be used to space multiple lines of text and
1060
 *      to align the baselines of text in different fonts.
1061
 *
1062
 * Results:
1063
 *      If *heightPtr is non-NULL, it is filled with the overall height
1064
 *      of the font, which is the sum of the ascent and descent.
1065
 *      If *ascentPtr or *descentPtr is non-NULL, they are filled with
1066
 *      the ascent and/or descent information for the font.
1067
 *
1068
 * Side effects:
1069
 *      None.
1070
 *
1071
 *---------------------------------------------------------------------------
1072
 */
1073
void
1074
Tk_GetFontMetrics(tkfont, fmPtr)
1075
    Tk_Font tkfont;             /* Font in which metrics are calculated. */
1076
    Tk_FontMetrics *fmPtr;      /* Pointer to structure in which font
1077
                                 * metrics for tkfont will be stored. */
1078
{
1079
    TkFont *fontPtr;
1080
 
1081
    fontPtr = (TkFont *) tkfont;
1082
    fmPtr->ascent = fontPtr->fm.ascent;
1083
    fmPtr->descent = fontPtr->fm.descent;
1084
    fmPtr->linespace = fontPtr->fm.ascent + fontPtr->fm.descent;
1085
}
1086
 
1087
/*
1088
 *---------------------------------------------------------------------------
1089
 *
1090
 * Tk_PostscriptFontName --
1091
 *
1092
 *      Given a Tk_Font, return the name of the corresponding Postscript
1093
 *      font.
1094
 *
1095
 * Results:
1096
 *      The return value is the pointsize of the given Tk_Font.
1097
 *      The name of the Postscript font is appended to dsPtr.
1098
 *
1099
 * Side effects:
1100
 *      If the font does not exist on the printer, the print job will
1101
 *      fail at print time.  Given a "reasonable" Postscript printer,
1102
 *      the following Tk_Font font families should print correctly:
1103
 *
1104
 *          Avant Garde, Arial, Bookman, Courier, Courier New, Geneva,
1105
 *          Helvetica, Monaco, New Century Schoolbook, New York,
1106
 *          Palatino, Symbol, Times, Times New Roman, Zapf Chancery,
1107
 *          and Zapf Dingbats.
1108
 *
1109
 *      Any other Tk_Font font families may not print correctly
1110
 *      because the computed Postscript font name may be incorrect.
1111
 *
1112
 *---------------------------------------------------------------------------
1113
 */
1114
 
1115
 
1116
int
1117
Tk_PostscriptFontName(tkfont, dsPtr)
1118
    Tk_Font tkfont;             /* Font in which text will be printed. */
1119
    Tcl_DString *dsPtr;         /* Pointer to an initialized Tcl_DString to
1120
                                 * which the name of the Postscript font that
1121
                                 * corresponds to tkfont will be appended. */
1122
{
1123
    TkFont *fontPtr;
1124
    char *family, *weightString, *slantString;
1125
    char *src, *dest;
1126
    int upper, len;
1127
 
1128
    len = Tcl_DStringLength(dsPtr);
1129
    fontPtr = (TkFont *) tkfont;
1130
 
1131
    /*
1132
     * Convert the case-insensitive Tk_Font family name to the
1133
     * case-sensitive Postscript family name.  Take out any spaces and
1134
     * capitalize the first letter of each word.
1135
     */
1136
 
1137
    family = fontPtr->fa.family;
1138
    if (strncasecmp(family, "itc ", 4) == 0) {
1139
        family = family + 4;
1140
    }
1141
    if ((strcasecmp(family, "Arial") == 0)
1142
            || (strcasecmp(family, "Geneva") == 0)) {
1143
        family = "Helvetica";
1144
    } else if ((strcasecmp(family, "Times New Roman") == 0)
1145
            || (strcasecmp(family, "New York") == 0)) {
1146
        family = "Times";
1147
    } else if ((strcasecmp(family, "Courier New") == 0)
1148
            || (strcasecmp(family, "Monaco") == 0)) {
1149
        family = "Courier";
1150
    } else if (strcasecmp(family, "AvantGarde") == 0) {
1151
        family = "AvantGarde";
1152
    } else if (strcasecmp(family, "ZapfChancery") == 0) {
1153
        family = "ZapfChancery";
1154
    } else if (strcasecmp(family, "ZapfDingbats") == 0) {
1155
        family = "ZapfDingbats";
1156
    } else {
1157
        /*
1158
         * Inline, capitalize the first letter of each word, lowercase the
1159
         * rest of the letters in each word, and then take out the spaces
1160
         * between the words.  This may make the DString shorter, which is
1161
         * safe to do.
1162
         */
1163
 
1164
        Tcl_DStringAppend(dsPtr, family, -1);
1165
 
1166
        src = dest = Tcl_DStringValue(dsPtr) + len;
1167
        upper = 1;
1168
        for (; *src != '\0'; src++, dest++) {
1169
            while (isspace(UCHAR(*src))) {
1170
                src++;
1171
                upper = 1;
1172
            }
1173
            *dest = *src;
1174
            if ((upper != 0) && (islower(UCHAR(*src)))) {
1175
                *dest = toupper(UCHAR(*src));
1176
            }
1177
            upper = 0;
1178
        }
1179
        *dest = '\0';
1180
        Tcl_DStringSetLength(dsPtr, dest - Tcl_DStringValue(dsPtr));
1181
        family = Tcl_DStringValue(dsPtr) + len;
1182
    }
1183
    if (family != Tcl_DStringValue(dsPtr) + len) {
1184
        Tcl_DStringAppend(dsPtr, family, -1);
1185
        family = Tcl_DStringValue(dsPtr) + len;
1186
    }
1187
 
1188
    if (strcasecmp(family, "NewCenturySchoolbook") == 0) {
1189
        Tcl_DStringSetLength(dsPtr, len);
1190
        Tcl_DStringAppend(dsPtr, "NewCenturySchlbk", -1);
1191
        family = Tcl_DStringValue(dsPtr) + len;
1192
    }
1193
 
1194
    /*
1195
     * Get the string to use for the weight.
1196
     */
1197
 
1198
    weightString = NULL;
1199
    if (fontPtr->fa.weight == TK_FW_NORMAL) {
1200
        if (strcmp(family, "Bookman") == 0) {
1201
            weightString = "Light";
1202
        } else if (strcmp(family, "AvantGarde") == 0) {
1203
            weightString = "Book";
1204
        } else if (strcmp(family, "ZapfChancery") == 0) {
1205
            weightString = "Medium";
1206
        }
1207
    } else {
1208
        if ((strcmp(family, "Bookman") == 0)
1209
                || (strcmp(family, "AvantGarde") == 0)) {
1210
            weightString = "Demi";
1211
        } else {
1212
            weightString = "Bold";
1213
        }
1214
    }
1215
 
1216
    /*
1217
     * Get the string to use for the slant.
1218
     */
1219
 
1220
    slantString = NULL;
1221
    if (fontPtr->fa.slant == TK_FS_ROMAN) {
1222
        ;
1223
    } else {
1224
        if ((strcmp(family, "Helvetica") == 0)
1225
                || (strcmp(family, "Courier") == 0)
1226
                || (strcmp(family, "AvantGarde") == 0)) {
1227
            slantString = "Oblique";
1228
        } else {
1229
            slantString = "Italic";
1230
        }
1231
    }
1232
 
1233
    /*
1234
     * The string "Roman" needs to be added to some fonts that are not bold
1235
     * and not italic.
1236
     */
1237
 
1238
    if ((slantString == NULL) && (weightString == NULL)) {
1239
        if ((strcmp(family, "Times") == 0)
1240
                || (strcmp(family, "NewCenturySchlbk") == 0)
1241
                || (strcmp(family, "Palatino") == 0)) {
1242
            Tcl_DStringAppend(dsPtr, "-Roman", -1);
1243
        }
1244
    } else {
1245
        Tcl_DStringAppend(dsPtr, "-", -1);
1246
        if (weightString != NULL) {
1247
            Tcl_DStringAppend(dsPtr, weightString, -1);
1248
        }
1249
        if (slantString != NULL) {
1250
            Tcl_DStringAppend(dsPtr, slantString, -1);
1251
        }
1252
    }
1253
 
1254
    return fontPtr->fa.pointsize;
1255
}
1256
 
1257
/*
1258
 *---------------------------------------------------------------------------
1259
 *
1260
 * Tk_TextWidth --
1261
 *
1262
 *      A wrapper function for the more complicated interface of
1263
 *      Tk_MeasureChars.  Computes how much space the given
1264
 *      simple string needs.
1265
 *
1266
 * Results:
1267
 *      The return value is the width (in pixels) of the given string.
1268
 *
1269
 * Side effects:
1270
 *      None.
1271
 *
1272
 *---------------------------------------------------------------------------
1273
 */
1274
 
1275
int
1276
Tk_TextWidth(tkfont, string, numChars)
1277
    Tk_Font tkfont;             /* Font in which text will be measured. */
1278
    CONST char *string;         /* String whose width will be computed. */
1279
    int numChars;               /* Number of characters to consider from
1280
                                 * string, or < 0 for strlen(). */
1281
{
1282
    int width;
1283
 
1284
    if (numChars < 0) {
1285
        numChars = strlen(string);
1286
    }
1287
    Tk_MeasureChars(tkfont, string, numChars, 0, 0, &width);
1288
    return width;
1289
}
1290
 
1291
/*
1292
 *---------------------------------------------------------------------------
1293
 *
1294
 * Tk_UnderlineChars --
1295
 *
1296
 *      This procedure draws an underline for a given range of characters
1297
 *      in a given string.  It doesn't draw the characters (which are
1298
 *      assumed to have been displayed previously); it just draws the
1299
 *      underline.  This procedure would mainly be used to quickly
1300
 *      underline a few characters without having to construct an
1301
 *      underlined font.  To produce properly underlined text, the
1302
 *      appropriate underlined font should be constructed and used.
1303
 *
1304
 * Results:
1305
 *      None.
1306
 *
1307
 * Side effects:
1308
 *      Information gets displayed in "drawable".
1309
 *
1310
 *----------------------------------------------------------------------
1311
 */
1312
 
1313
void
1314
Tk_UnderlineChars(display, drawable, gc, tkfont, string, x, y, firstChar,
1315
        lastChar)
1316
    Display *display;           /* Display on which to draw. */
1317
    Drawable drawable;          /* Window or pixmap in which to draw. */
1318
    GC gc;                      /* Graphics context for actually drawing
1319
                                 * line. */
1320
    Tk_Font tkfont;             /* Font used in GC;  must have been allocated
1321
                                 * by Tk_GetFont().  Used for character
1322
                                 * dimensions, etc. */
1323
    CONST char *string;         /* String containing characters to be
1324
                                 * underlined or overstruck. */
1325
    int x, y;                   /* Coordinates at which first character of
1326
                                 * string is drawn. */
1327
    int firstChar;              /* Index of first character. */
1328
    int lastChar;               /* Index of one after the last character. */
1329
{
1330
    TkFont *fontPtr;
1331
    int startX, endX;
1332
 
1333
    fontPtr = (TkFont *) tkfont;
1334
 
1335
    Tk_MeasureChars(tkfont, string, firstChar, 0, 0, &startX);
1336
    Tk_MeasureChars(tkfont, string, lastChar, 0, 0, &endX);
1337
 
1338
    XFillRectangle(display, drawable, gc, x + startX,
1339
            y + fontPtr->underlinePos, (unsigned int) (endX - startX),
1340
            (unsigned int) fontPtr->underlineHeight);
1341
}
1342
 
1343
/*
1344
 *---------------------------------------------------------------------------
1345
 *
1346
 * Tk_ComputeTextLayout --
1347
 *
1348
 *      Computes the amount of screen space needed to display a
1349
 *      multi-line, justified string of text.  Records all the
1350
 *      measurements that were done to determine to size and
1351
 *      positioning of the individual lines of text; this information
1352
 *      can be used by the Tk_DrawTextLayout() procedure to
1353
 *      display the text quickly (without remeasuring it).
1354
 *
1355
 *      This procedure is useful for simple widgets that want to
1356
 *      display single-font, multi-line text and want Tk to handle the
1357
 *      details.
1358
 *
1359
 * Results:
1360
 *      The return value is a Tk_TextLayout token that holds the
1361
 *      measurement information for the given string.  The token is
1362
 *      only valid for the given string.  If the string is freed,
1363
 *      the token is no longer valid and must also be freed.  To free
1364
 *      the token, call Tk_FreeTextLayout().
1365
 *
1366
 *      The dimensions of the screen area needed to display the text
1367
 *      are stored in *widthPtr and *heightPtr.
1368
 *
1369
 * Side effects:
1370
 *      Memory is allocated to hold the measurement information.
1371
 *
1372
 *---------------------------------------------------------------------------
1373
 */
1374
 
1375
Tk_TextLayout
1376
Tk_ComputeTextLayout(tkfont, string, numChars, wrapLength, justify, flags,
1377
        widthPtr, heightPtr)
1378
    Tk_Font tkfont;             /* Font that will be used to display text. */
1379
    CONST char *string;         /* String whose dimensions are to be
1380
                                 * computed. */
1381
    int numChars;               /* Number of characters to consider from
1382
                                 * string, or < 0 for strlen(). */
1383
    int wrapLength;             /* Longest permissible line length, in
1384
                                 * pixels.  <= 0 means no automatic wrapping:
1385
                                 * just let lines get as long as needed. */
1386
    Tk_Justify justify;         /* How to justify lines. */
1387
    int flags;                  /* Flag bits OR-ed together.
1388
                                 * TK_IGNORE_TABS means that tab characters
1389
                                 * should not be expanded.  TK_IGNORE_NEWLINES
1390
                                 * means that newline characters should not
1391
                                 * cause a line break. */
1392
    int *widthPtr;              /* Filled with width of string. */
1393
    int *heightPtr;             /* Filled with height of string. */
1394
{
1395
    TkFont *fontPtr;
1396
    CONST char *start, *end, *special;
1397
    int n, y, charsThisChunk, maxChunks;
1398
    int baseline, height, curX, newX, maxWidth;
1399
    TextLayout *layoutPtr;
1400
    LayoutChunk *chunkPtr;
1401
    CONST TkFontMetrics *fmPtr;
1402
#define MAX_LINES 50
1403
    int staticLineLengths[MAX_LINES];
1404
    int *lineLengths;
1405
    int maxLines, curLine, layoutHeight;
1406
 
1407
    lineLengths = staticLineLengths;
1408
    maxLines = MAX_LINES;
1409
 
1410
    fontPtr = (TkFont *) tkfont;
1411
    fmPtr = &fontPtr->fm;
1412
 
1413
    height = fmPtr->ascent + fmPtr->descent;
1414
 
1415
    if (numChars < 0) {
1416
        numChars = strlen(string);
1417
    }
1418
 
1419
    maxChunks = 1;
1420
 
1421
    layoutPtr = (TextLayout *) ckalloc(sizeof(TextLayout)
1422
            + (maxChunks - 1) * sizeof(LayoutChunk));
1423
    layoutPtr->tkfont       = tkfont;
1424
    layoutPtr->string       = string;
1425
    layoutPtr->numChunks    = 0;
1426
 
1427
    baseline = fmPtr->ascent;
1428
    maxWidth = 0;
1429
 
1430
    /*
1431
     * Divide the string up into simple strings and measure each string.
1432
     */
1433
 
1434
    curX = 0;
1435
 
1436
    end = string + numChars;
1437
    special = string;
1438
 
1439
    flags &= TK_IGNORE_TABS | TK_IGNORE_NEWLINES;
1440
    flags |= TK_WHOLE_WORDS | TK_AT_LEAST_ONE;
1441
    curLine = 0;
1442
    for (start = string; start < end; ) {
1443
        if (start >= special) {
1444
            /*
1445
             * Find the next special character in the string.
1446
             */
1447
 
1448
            for (special = start; special < end; special++) {
1449
                if (!(flags & TK_IGNORE_NEWLINES)) {
1450
                    if ((*special == '\n') || (*special == '\r')) {
1451
                        break;
1452
                    }
1453
                }
1454
                if (!(flags & TK_IGNORE_TABS)) {
1455
                    if (*special == '\t') {
1456
                        break;
1457
                    }
1458
                }
1459
            }
1460
        }
1461
 
1462
        /*
1463
         * Special points at the next special character (or the end of the
1464
         * string).  Process characters between start and special.
1465
         */
1466
 
1467
        chunkPtr = NULL;
1468
        if (start < special) {
1469
            charsThisChunk = Tk_MeasureChars(tkfont, start, special - start,
1470
                    wrapLength - curX, flags, &newX);
1471
            newX += curX;
1472
            flags &= ~TK_AT_LEAST_ONE;
1473
            if (charsThisChunk > 0) {
1474
                chunkPtr = NewChunk(&layoutPtr, &maxChunks, start,
1475
                        charsThisChunk, curX, newX, baseline);
1476
 
1477
                start += charsThisChunk;
1478
                curX = newX;
1479
            }
1480
        }
1481
 
1482
        if ((start == special) && (special < end)) {
1483
            /*
1484
             * Handle the special character.
1485
             */
1486
 
1487
            chunkPtr = NULL;
1488
            if (*special == '\t') {
1489
                newX = curX + fontPtr->tabWidth;
1490
                newX -= newX % fontPtr->tabWidth;
1491
                NewChunk(&layoutPtr, &maxChunks, start, 1, curX, newX,
1492
                        baseline)->numDisplayChars = -1;
1493
                start++;
1494
                if ((start < end) &&
1495
                        ((wrapLength <= 0) || (newX <= wrapLength))) {
1496
                    /*
1497
                     * More chars can still fit on this line.
1498
                     */
1499
 
1500
                    curX = newX;
1501
                    flags &= ~TK_AT_LEAST_ONE;
1502
                    continue;
1503
                }
1504
            } else {
1505
                NewChunk(&layoutPtr, &maxChunks, start, 1, curX, 1000000000,
1506
                        baseline)->numDisplayChars = -1;
1507
                start++;
1508
                goto wrapLine;
1509
            }
1510
        }
1511
 
1512
        /*
1513
         * No more characters are going to go on this line, either because
1514
         * no more characters can fit or there are no more characters left.
1515
         * Consume all extra spaces at end of line.
1516
         */
1517
 
1518
        while ((start < end) && isspace(UCHAR(*start))) {
1519
            if (!(flags & TK_IGNORE_NEWLINES)) {
1520
                if ((*start == '\n') || (*start == '\r')) {
1521
                    break;
1522
                }
1523
            }
1524
            if (!(flags & TK_IGNORE_TABS)) {
1525
                if (*start == '\t') {
1526
                    break;
1527
                }
1528
            }
1529
            start++;
1530
        }
1531
        if (chunkPtr != NULL) {
1532
            /*
1533
             * Append all the extra spaces on this line to the end of the
1534
             * last text chunk.
1535
             */
1536
            charsThisChunk = start - (chunkPtr->start + chunkPtr->numChars);
1537
            if (charsThisChunk > 0) {
1538
                chunkPtr->numChars += Tk_MeasureChars(tkfont,
1539
                        chunkPtr->start + chunkPtr->numChars, charsThisChunk,
1540
                        0, 0, &chunkPtr->totalWidth);
1541
                chunkPtr->totalWidth += curX;
1542
            }
1543
        }
1544
 
1545
        wrapLine:
1546
        flags |= TK_AT_LEAST_ONE;
1547
 
1548
        /*
1549
         * Save current line length, then move current position to start of
1550
         * next line.
1551
         */
1552
 
1553
        if (curX > maxWidth) {
1554
            maxWidth = curX;
1555
        }
1556
 
1557
        /*
1558
         * Remember width of this line, so that all chunks on this line
1559
         * can be centered or right justified, if necessary.
1560
         */
1561
 
1562
        if (curLine >= maxLines) {
1563
            int *newLengths;
1564
 
1565
            newLengths = (int *) ckalloc(2 * maxLines * sizeof(int));
1566
            memcpy((void *) newLengths, lineLengths, maxLines * sizeof(int));
1567
            if (lineLengths != staticLineLengths) {
1568
                ckfree((char *) lineLengths);
1569
            }
1570
            lineLengths = newLengths;
1571
            maxLines *= 2;
1572
        }
1573
        lineLengths[curLine] = curX;
1574
        curLine++;
1575
 
1576
        curX = 0;
1577
        baseline += height;
1578
    }
1579
 
1580
    /*
1581
     * If last line ends with a newline, then we need to make a 0 width
1582
     * chunk on the next line.  Otherwise "Hello" and "Hello\n" are the
1583
     * same height.
1584
     */
1585
 
1586
    if ((layoutPtr->numChunks > 0) && ((flags & TK_IGNORE_NEWLINES) == 0)) {
1587
        if (layoutPtr->chunks[layoutPtr->numChunks - 1].start[0] == '\n') {
1588
            chunkPtr = NewChunk(&layoutPtr, &maxChunks, start, 0, curX,
1589
                    1000000000, baseline);
1590
            chunkPtr->numDisplayChars = -1;
1591
            baseline += height;
1592
        }
1593
    }
1594
 
1595
    /*
1596
     * Using maximum line length, shift all the chunks so that the lines are
1597
     * all justified correctly.
1598
     */
1599
 
1600
    curLine = 0;
1601
    chunkPtr = layoutPtr->chunks;
1602
    y = chunkPtr->y;
1603
    for (n = 0; n < layoutPtr->numChunks; n++) {
1604
        int extra;
1605
 
1606
        if (chunkPtr->y != y) {
1607
            curLine++;
1608
            y = chunkPtr->y;
1609
        }
1610
        extra = maxWidth - lineLengths[curLine];
1611
        if (justify == TK_JUSTIFY_CENTER) {
1612
            chunkPtr->x += extra / 2;
1613
        } else if (justify == TK_JUSTIFY_RIGHT) {
1614
            chunkPtr->x += extra;
1615
        }
1616
        chunkPtr++;
1617
    }
1618
 
1619
    layoutPtr->width = maxWidth;
1620
    layoutHeight = baseline - fmPtr->ascent;
1621
    if (layoutPtr->numChunks == 0) {
1622
        layoutHeight = height;
1623
 
1624
        /*
1625
         * This fake chunk is used by the other procedures so that they can
1626
         * pretend that there is a chunk with no chars in it, which makes
1627
         * the coding simpler.
1628
         */
1629
 
1630
        layoutPtr->numChunks = 1;
1631
        layoutPtr->chunks[0].start               = string;
1632
        layoutPtr->chunks[0].numChars            = 0;
1633
        layoutPtr->chunks[0].numDisplayChars     = -1;
1634
        layoutPtr->chunks[0].x                   = 0;
1635
        layoutPtr->chunks[0].y                   = fmPtr->ascent;
1636
        layoutPtr->chunks[0].totalWidth          = 0;
1637
        layoutPtr->chunks[0].displayWidth        = 0;
1638
    }
1639
 
1640
    if (widthPtr != NULL) {
1641
        *widthPtr = layoutPtr->width;
1642
    }
1643
    if (heightPtr != NULL) {
1644
        *heightPtr = layoutHeight;
1645
    }
1646
    if (lineLengths != staticLineLengths) {
1647
        ckfree((char *) lineLengths);
1648
    }
1649
 
1650
    return (Tk_TextLayout) layoutPtr;
1651
}
1652
 
1653
/*
1654
 *---------------------------------------------------------------------------
1655
 *
1656
 * Tk_FreeTextLayout --
1657
 *
1658
 *      This procedure is called to release the storage associated with
1659
 *      a Tk_TextLayout when it is no longer needed.
1660
 *
1661
 * Results:
1662
 *      None.
1663
 *
1664
 * Side effects:
1665
 *      Memory is freed.
1666
 *
1667
 *---------------------------------------------------------------------------
1668
 */
1669
 
1670
void
1671
Tk_FreeTextLayout(textLayout)
1672
    Tk_TextLayout textLayout;   /* The text layout to be released. */
1673
{
1674
    TextLayout *layoutPtr;
1675
 
1676
    layoutPtr = (TextLayout *) textLayout;
1677
    if (layoutPtr != NULL) {
1678
        ckfree((char *) layoutPtr);
1679
    }
1680
}
1681
 
1682
/*
1683
 *---------------------------------------------------------------------------
1684
 *
1685
 * Tk_DrawTextLayout --
1686
 *
1687
 *      Use the information in the Tk_TextLayout token to display a
1688
 *      multi-line, justified string of text.
1689
 *
1690
 *      This procedure is useful for simple widgets that need to
1691
 *      display single-font, multi-line text and want Tk to handle
1692
 *      the details.
1693
 *
1694
 * Results:
1695
 *      None.
1696
 *
1697
 * Side effects:
1698
 *      Text drawn on the screen.
1699
 *
1700
 *---------------------------------------------------------------------------
1701
 */
1702
 
1703
void
1704
Tk_DrawTextLayout(display, drawable, gc, layout, x, y, firstChar, lastChar)
1705
    Display *display;           /* Display on which to draw. */
1706
    Drawable drawable;          /* Window or pixmap in which to draw. */
1707
    GC gc;                      /* Graphics context to use for drawing text. */
1708
    Tk_TextLayout layout;       /* Layout information, from a previous call
1709
                                 * to Tk_ComputeTextLayout(). */
1710
    int x, y;                   /* Upper-left hand corner of rectangle in
1711
                                 * which to draw (pixels). */
1712
    int firstChar;              /* The index of the first character to draw
1713
                                 * from the given text item.  0 specfies the
1714
                                 * beginning. */
1715
    int lastChar;               /* The index just after the last character
1716
                                 * to draw from the given text item.  A number
1717
                                 * < 0 means to draw all characters. */
1718
{
1719
    TextLayout *layoutPtr;
1720
    int i, numDisplayChars, drawX;
1721
    LayoutChunk *chunkPtr;
1722
 
1723
    layoutPtr = (TextLayout *) layout;
1724
    if (layoutPtr == NULL) {
1725
        return;
1726
    }
1727
 
1728
    if (lastChar < 0) {
1729
        lastChar = 100000000;
1730
    }
1731
    chunkPtr = layoutPtr->chunks;
1732
    for (i = 0; i < layoutPtr->numChunks; i++) {
1733
        numDisplayChars = chunkPtr->numDisplayChars;
1734
        if ((numDisplayChars > 0) && (firstChar < numDisplayChars)) {
1735
            if (firstChar <= 0) {
1736
                drawX = 0;
1737
                firstChar = 0;
1738
            } else {
1739
                Tk_MeasureChars(layoutPtr->tkfont, chunkPtr->start, firstChar,
1740
                        0, 0, &drawX);
1741
            }
1742
            if (lastChar < numDisplayChars) {
1743
                numDisplayChars = lastChar;
1744
            }
1745
            Tk_DrawChars(display, drawable, gc, layoutPtr->tkfont,
1746
                    chunkPtr->start + firstChar, numDisplayChars - firstChar,
1747
                    x + chunkPtr->x + drawX, y + chunkPtr->y);
1748
        }
1749
        firstChar -= chunkPtr->numChars;
1750
        lastChar -= chunkPtr->numChars;
1751
        if (lastChar <= 0) {
1752
            break;
1753
        }
1754
        chunkPtr++;
1755
    }
1756
}
1757
 
1758
/*
1759
 *---------------------------------------------------------------------------
1760
 *
1761
 * Tk_UnderlineTextLayout --
1762
 *
1763
 *      Use the information in the Tk_TextLayout token to display an
1764
 *      underline below an individual character.  This procedure does
1765
 *      not draw the text, just the underline.
1766
 *
1767
 *      This procedure is useful for simple widgets that need to
1768
 *      display single-font, multi-line text with an individual
1769
 *      character underlined and want Tk to handle the details.
1770
 *      To display larger amounts of underlined text, construct
1771
 *      and use an underlined font.
1772
 *
1773
 * Results:
1774
 *      None.
1775
 *
1776
 * Side effects:
1777
 *      Underline drawn on the screen.
1778
 *
1779
 *---------------------------------------------------------------------------
1780
 */
1781
 
1782
void
1783
Tk_UnderlineTextLayout(display, drawable, gc, layout, x, y, underline)
1784
    Display *display;           /* Display on which to draw. */
1785
    Drawable drawable;          /* Window or pixmap in which to draw. */
1786
    GC gc;                      /* Graphics context to use for drawing text. */
1787
    Tk_TextLayout layout;       /* Layout information, from a previous call
1788
                                 * to Tk_ComputeTextLayout(). */
1789
    int x, y;                   /* Upper-left hand corner of rectangle in
1790
                                 * which to draw (pixels). */
1791
    int underline;              /* Index of the single character to
1792
                                 * underline, or -1 for no underline. */
1793
{
1794
    TextLayout *layoutPtr;
1795
    TkFont *fontPtr;
1796
    int xx, yy, width, height;
1797
 
1798
    if ((Tk_CharBbox(layout, underline, &xx, &yy, &width, &height) != 0)
1799
            && (width != 0)) {
1800
        layoutPtr = (TextLayout *) layout;
1801
        fontPtr = (TkFont *) layoutPtr->tkfont;
1802
 
1803
        XFillRectangle(display, drawable, gc, x + xx,
1804
                y + yy + fontPtr->fm.ascent + fontPtr->underlinePos,
1805
                (unsigned int) width, (unsigned int) fontPtr->underlineHeight);
1806
    }
1807
}
1808
 
1809
/*
1810
 *---------------------------------------------------------------------------
1811
 *
1812
 * Tk_PointToChar --
1813
 *
1814
 *      Use the information in the Tk_TextLayout token to determine the
1815
 *      character closest to the given point.  The point must be
1816
 *      specified with respect to the upper-left hand corner of the
1817
 *      text layout, which is considered to be located at (0, 0).
1818
 *
1819
 *      Any point whose y-value is less that 0 will be considered closest
1820
 *      to the first character in the text layout; any point whose y-value
1821
 *      is greater than the height of the text layout will be considered
1822
 *      closest to the last character in the text layout.
1823
 *
1824
 *      Any point whose x-value is less than 0 will be considered closest
1825
 *      to the first character on that line; any point whose x-value is
1826
 *      greater than the width of the text layout will be considered
1827
 *      closest to the last character on that line.
1828
 *
1829
 * Results:
1830
 *      The return value is the index of the character that was
1831
 *      closest to the point.  Given a text layout with no characters,
1832
 *      the value 0 will always be returned, referring to a hypothetical
1833
 *      zero-width placeholder character.
1834
 *
1835
 * Side effects:
1836
 *      None.
1837
 *
1838
 *---------------------------------------------------------------------------
1839
 */
1840
 
1841
int
1842
Tk_PointToChar(layout, x, y)
1843
    Tk_TextLayout layout;       /* Layout information, from a previous call
1844
                                 * to Tk_ComputeTextLayout(). */
1845
    int x, y;                   /* Coordinates of point to check, with
1846
                                 * respect to the upper-left corner of the
1847
                                 * text layout. */
1848
{
1849
    TextLayout *layoutPtr;
1850
    LayoutChunk *chunkPtr, *lastPtr;
1851
    TkFont *fontPtr;
1852
    int i, n, dummy, baseline, pos;
1853
 
1854
    if (y < 0) {
1855
        /*
1856
         * Point lies above any line in this layout.  Return the index of
1857
         * the first char.
1858
         */
1859
 
1860
        return 0;
1861
    }
1862
 
1863
    /*
1864
     * Find which line contains the point.
1865
     */
1866
 
1867
    layoutPtr = (TextLayout *) layout;
1868
    fontPtr = (TkFont *) layoutPtr->tkfont;
1869
    lastPtr = chunkPtr = layoutPtr->chunks;
1870
    for (i = 0; i < layoutPtr->numChunks; i++) {
1871
        baseline = chunkPtr->y;
1872
        if (y < baseline + fontPtr->fm.descent) {
1873
            if (x < chunkPtr->x) {
1874
                /*
1875
                 * Point is to the left of all chunks on this line.  Return
1876
                 * the index of the first character on this line.
1877
                 */
1878
 
1879
                return chunkPtr->start - layoutPtr->string;
1880
            }
1881
            if (x >= layoutPtr->width) {
1882
                /*
1883
                 * If point lies off right side of the text layout, return
1884
                 * the last char in the last chunk on this line.  Without
1885
                 * this, it might return the index of the first char that
1886
                 * was located outside of the text layout.
1887
                 */
1888
 
1889
                x = INT_MAX;
1890
            }
1891
 
1892
            /*
1893
             * Examine all chunks on this line to see which one contains
1894
             * the specified point.
1895
             */
1896
 
1897
            lastPtr = chunkPtr;
1898
            while ((i < layoutPtr->numChunks) && (chunkPtr->y == baseline))  {
1899
                if (x < chunkPtr->x + chunkPtr->totalWidth) {
1900
                    /*
1901
                     * Point falls on one of the characters in this chunk.
1902
                     */
1903
 
1904
                    if (chunkPtr->numDisplayChars < 0) {
1905
                        /*
1906
                         * This is a special chunk that encapsulates a single
1907
                         * tab or newline char.
1908
                         */
1909
 
1910
                        return chunkPtr->start - layoutPtr->string;
1911
                    }
1912
                    n = Tk_MeasureChars((Tk_Font) fontPtr, chunkPtr->start,
1913
                            chunkPtr->numChars, x + 1 - chunkPtr->x,
1914
                            TK_PARTIAL_OK, &dummy);
1915
                    return (chunkPtr->start + n - 1) - layoutPtr->string;
1916
                }
1917
                lastPtr = chunkPtr;
1918
                chunkPtr++;
1919
                i++;
1920
            }
1921
 
1922
            /*
1923
             * Point is to the right of all chars in all the chunks on this
1924
             * line.  Return the index just past the last char in the last
1925
             * chunk on this line.
1926
             */
1927
 
1928
            pos = (lastPtr->start + lastPtr->numChars) - layoutPtr->string;
1929
            if (i < layoutPtr->numChunks) {
1930
                pos--;
1931
            }
1932
            return pos;
1933
        }
1934
        lastPtr = chunkPtr;
1935
        chunkPtr++;
1936
    }
1937
 
1938
    /*
1939
     * Point lies below any line in this text layout.  Return the index
1940
     * just past the last char.
1941
     */
1942
 
1943
    return (lastPtr->start + lastPtr->numChars) - layoutPtr->string;
1944
}
1945
 
1946
/*
1947
 *---------------------------------------------------------------------------
1948
 *
1949
 * Tk_CharBbox --
1950
 *
1951
 *      Use the information in the Tk_TextLayout token to return the
1952
 *      bounding box for the character specified by index.
1953
 *
1954
 *      The width of the bounding box is the advance width of the
1955
 *      character, and does not include and left- or right-bearing.
1956
 *      Any character that extends partially outside of the
1957
 *      text layout is considered to be truncated at the edge.  Any
1958
 *      character which is located completely outside of the text
1959
 *      layout is considered to be zero-width and pegged against
1960
 *      the edge.
1961
 *
1962
 *      The height of the bounding box is the line height for this font,
1963
 *      extending from the top of the ascent to the bottom of the
1964
 *      descent.  Information about the actual height of the individual
1965
 *      letter is not available.
1966
 *
1967
 *      A text layout that contains no characters is considered to
1968
 *      contain a single zero-width placeholder character.
1969
 *
1970
 * Results:
1971
 *      The return value is 0 if the index did not specify a character
1972
 *      in the text layout, or non-zero otherwise.  In that case,
1973
 *      *bbox is filled with the bounding box of the character.
1974
 *
1975
 * Side effects:
1976
 *      None.
1977
 *
1978
 *---------------------------------------------------------------------------
1979
 */
1980
 
1981
int
1982
Tk_CharBbox(layout, index, xPtr, yPtr, widthPtr, heightPtr)
1983
    Tk_TextLayout layout;   /* Layout information, from a previous call to
1984
                             * Tk_ComputeTextLayout(). */
1985
    int index;              /* The index of the character whose bbox is
1986
                             * desired. */
1987
    int *xPtr, *yPtr;       /* Filled with the upper-left hand corner, in
1988
                             * pixels, of the bounding box for the character
1989
                             * specified by index, if non-NULL. */
1990
    int *widthPtr, *heightPtr;
1991
                            /* Filled with the width and height of the
1992
                             * bounding box for the character specified by
1993
                             * index, if non-NULL. */
1994
{
1995
    TextLayout *layoutPtr;
1996
    LayoutChunk *chunkPtr;
1997
    int i, x, w;
1998
    Tk_Font tkfont;
1999
    TkFont *fontPtr;
2000
 
2001
    if (index < 0) {
2002
        if (xPtr)
2003
                *xPtr = 0;
2004
        if (yPtr)
2005
                *yPtr = 0;
2006
        if (widthPtr)
2007
                *widthPtr = 0;
2008
        if (heightPtr)
2009
                *heightPtr = 0;
2010
        return 0;
2011
    }
2012
 
2013
    layoutPtr = (TextLayout *) layout;
2014
    chunkPtr = layoutPtr->chunks;
2015
    tkfont = layoutPtr->tkfont;
2016
    fontPtr = (TkFont *) tkfont;
2017
 
2018
    for (i = 0; i < layoutPtr->numChunks; i++) {
2019
        if (chunkPtr->numDisplayChars < 0) {
2020
            if (index == 0) {
2021
                x = chunkPtr->x;
2022
                w = chunkPtr->totalWidth;
2023
                goto check;
2024
            }
2025
        } else if (index < chunkPtr->numChars) {
2026
            if (xPtr != NULL) {
2027
                Tk_MeasureChars(tkfont, chunkPtr->start, index, 0, 0, &x);
2028
                x += chunkPtr->x;
2029
            }
2030
            if (widthPtr != NULL) {
2031
                Tk_MeasureChars(tkfont, chunkPtr->start + index, 1, 0, 0, &w);
2032
            }
2033
            goto check;
2034
        }
2035
        index -= chunkPtr->numChars;
2036
        chunkPtr++;
2037
    }
2038
    if (index == 0) {
2039
        /*
2040
         * Special case to get location just past last char in layout.
2041
         */
2042
 
2043
        chunkPtr--;
2044
        x = chunkPtr->x + chunkPtr->totalWidth;
2045
        w = 0;
2046
    } else {
2047
        return 0;
2048
    }
2049
 
2050
    /*
2051
     * Ensure that the bbox lies within the text layout.  This forces all
2052
     * chars that extend off the right edge of the text layout to have
2053
     * truncated widths, and all chars that are completely off the right
2054
     * edge of the text layout to peg to the edge and have 0 width.
2055
     */
2056
    check:
2057
    if (yPtr != NULL) {
2058
        *yPtr = chunkPtr->y - fontPtr->fm.ascent;
2059
    }
2060
    if (heightPtr != NULL) {
2061
        *heightPtr = fontPtr->fm.ascent + fontPtr->fm.descent;
2062
    }
2063
 
2064
    if (x > layoutPtr->width) {
2065
        x = layoutPtr->width;
2066
    }
2067
    if (xPtr != NULL) {
2068
        *xPtr = x;
2069
    }
2070
    if (widthPtr != NULL) {
2071
        if (x + w > layoutPtr->width) {
2072
            w = layoutPtr->width - x;
2073
        }
2074
        *widthPtr = w;
2075
    }
2076
 
2077
    return 1;
2078
}
2079
 
2080
/*
2081
 *---------------------------------------------------------------------------
2082
 *
2083
 * Tk_DistanceToTextLayout --
2084
 *
2085
 *      Computes the distance in pixels from the given point to the
2086
 *      given text layout.  Non-displaying space characters that occur
2087
 *      at the end of individual lines in the text layout are ignored
2088
 *      for hit detection purposes.
2089
 *
2090
 * Results:
2091
 *      The return value is 0 if the point (x, y) is inside the text
2092
 *      layout.  If the point isn't inside the text layout then the
2093
 *      return value is the distance in pixels from the point to the
2094
 *      text item.
2095
 *
2096
 * Side effects:
2097
 *      None.
2098
 *
2099
 *---------------------------------------------------------------------------
2100
 */
2101
 
2102
int
2103
Tk_DistanceToTextLayout(layout, x, y)
2104
    Tk_TextLayout layout;       /* Layout information, from a previous call
2105
                                 * to Tk_ComputeTextLayout(). */
2106
    int x, y;                   /* Coordinates of point to check, with
2107
                                 * respect to the upper-left corner of the
2108
                                 * text layout (in pixels). */
2109
{
2110
    int i, x1, x2, y1, y2, xDiff, yDiff, dist, minDist, ascent, descent;
2111
    LayoutChunk *chunkPtr;
2112
    TextLayout *layoutPtr;
2113
    TkFont *fontPtr;
2114
 
2115
    layoutPtr = (TextLayout *) layout;
2116
    fontPtr = (TkFont *) layoutPtr->tkfont;
2117
    ascent = fontPtr->fm.ascent;
2118
    descent = fontPtr->fm.descent;
2119
 
2120
    minDist = 0;
2121
    chunkPtr = layoutPtr->chunks;
2122
    for (i = 0; i < layoutPtr->numChunks; i++) {
2123
        if (chunkPtr->start[0] == '\n') {
2124
            /*
2125
             * Newline characters are not counted when computing distance
2126
             * (but tab characters would still be considered).
2127
             */
2128
 
2129
            chunkPtr++;
2130
            continue;
2131
        }
2132
 
2133
        x1 = chunkPtr->x;
2134
        y1 = chunkPtr->y - ascent;
2135
        x2 = chunkPtr->x + chunkPtr->displayWidth;
2136
        y2 = chunkPtr->y + descent;
2137
 
2138
        if (x < x1) {
2139
            xDiff = x1 - x;
2140
        } else if (x >= x2) {
2141
            xDiff = x - x2 + 1;
2142
        } else {
2143
            xDiff = 0;
2144
        }
2145
 
2146
        if (y < y1) {
2147
            yDiff = y1 - y;
2148
        } else if (y >= y2) {
2149
            yDiff = y - y2 + 1;
2150
        } else {
2151
            yDiff = 0;
2152
        }
2153
        if ((xDiff == 0) && (yDiff == 0)) {
2154
            return 0;
2155
        }
2156
        dist = (int) hypot((double) xDiff, (double) yDiff);
2157
        if ((dist < minDist) || (minDist == 0)) {
2158
            minDist = dist;
2159
        }
2160
        chunkPtr++;
2161
    }
2162
    return minDist;
2163
}
2164
 
2165
/*
2166
 *---------------------------------------------------------------------------
2167
 *
2168
 * Tk_IntersectTextLayout --
2169
 *
2170
 *      Determines whether a text layout lies entirely inside,
2171
 *      entirely outside, or overlaps a given rectangle.  Non-displaying
2172
 *      space characters that occur at the end of individual lines in
2173
 *      the text layout are ignored for intersection calculations.
2174
 *
2175
 * Results:
2176
 *      The return value is -1 if the text layout is entirely outside of
2177
 *      the rectangle, 0 if it overlaps, and 1 if it is entirely inside
2178
 *      of the rectangle.
2179
 *
2180
 * Side effects:
2181
 *      None.
2182
 *
2183
 *---------------------------------------------------------------------------
2184
 */
2185
 
2186
int
2187
Tk_IntersectTextLayout(layout, x, y, width, height)
2188
    Tk_TextLayout layout;       /* Layout information, from a previous call
2189
                                 * to Tk_ComputeTextLayout(). */
2190
    int x, y;                   /* Upper-left hand corner, in pixels, of
2191
                                 * rectangular area to compare with text
2192
                                 * layout.  Coordinates are with respect to
2193
                                 * the upper-left hand corner of the text
2194
                                 * layout itself. */
2195
    int width, height;          /* The width and height of the above
2196
                                 * rectangular area, in pixels. */
2197
{
2198
    int result, i, x1, y1, x2, y2;
2199
    TextLayout *layoutPtr;
2200
    LayoutChunk *chunkPtr;
2201
    TkFont *fontPtr;
2202
    int left, top, right, bottom;
2203
 
2204
    /*
2205
     * Scan the chunks one at a time, seeing whether each is entirely in,
2206
     * entirely out, or overlapping the rectangle.  If an overlap is
2207
     * detected, return immediately; otherwise wait until all chunks have
2208
     * been processed and see if they were all inside or all outside.
2209
     */
2210
 
2211
    layoutPtr = (TextLayout *) layout;
2212
    chunkPtr = layoutPtr->chunks;
2213
    fontPtr = (TkFont *) layoutPtr->tkfont;
2214
 
2215
    left    = x;
2216
    top     = y;
2217
    right   = x + width;
2218
    bottom  = y + height;
2219
 
2220
    result = 0;
2221
    for (i = 0; i < layoutPtr->numChunks; i++) {
2222
        if (chunkPtr->start[0] == '\n') {
2223
            /*
2224
             * Newline characters are not counted when computing area
2225
             * intersection (but tab characters would still be considered).
2226
             */
2227
 
2228
            chunkPtr++;
2229
            continue;
2230
        }
2231
 
2232
        x1 = chunkPtr->x;
2233
        y1 = chunkPtr->y - fontPtr->fm.ascent;
2234
        x2 = chunkPtr->x + chunkPtr->displayWidth;
2235
        y2 = chunkPtr->y + fontPtr->fm.descent;
2236
 
2237
        if ((right < x1) || (left >= x2)
2238
                || (bottom < y1) || (top >= y2)) {
2239
            if (result == 1) {
2240
                return 0;
2241
            }
2242
            result = -1;
2243
        } else if ((x1 < left) || (x2 >= right)
2244
                || (y1 < top) || (y2 >= bottom)) {
2245
            return 0;
2246
        } else if (result == -1) {
2247
            return 0;
2248
        } else {
2249
            result = 1;
2250
        }
2251
        chunkPtr++;
2252
    }
2253
    return result;
2254
}
2255
 
2256
/*
2257
 *---------------------------------------------------------------------------
2258
 *
2259
 * Tk_TextLayoutToPostscript --
2260
 *
2261
 *      Outputs the contents of a text layout in Postscript format.
2262
 *      The set of lines in the text layout will be rendered by the user
2263
 *      supplied Postscript function.  The function should be of the form:
2264
 *
2265
 *          justify x y string  function  --
2266
 *
2267
 *      Justify is -1, 0, or 1, depending on whether the following string
2268
 *      should be left, center, or right justified, x and y is the
2269
 *      location for the origin of the string, string is the sequence
2270
 *      of characters to be printed, and function is the name of the
2271
 *      caller-provided function; the function should leave nothing
2272
 *      on the stack.
2273
 *
2274
 *      The meaning of the origin of the string (x and y) depends on
2275
 *      the justification.  For left justification, x is where the
2276
 *      left edge of the string should appear.  For center justification,
2277
 *      x is where the center of the string should appear.  And for right
2278
 *      justification, x is where the right edge of the string should
2279
 *      appear.  This behavior is necessary because, for example, right
2280
 *      justified text on the screen is justified with screen metrics.
2281
 *      The same string needs to be justified with printer metrics on
2282
 *      the printer to appear in the correct place with respect to other
2283
 *      similarly justified strings.  In all circumstances, y is the
2284
 *      location of the baseline for the string.
2285
 *
2286
 * Results:
2287
 *      Interp->result is modified to hold the Postscript code that
2288
 *      will render the text layout.
2289
 *
2290
 * Side effects:
2291
 *      None.
2292
 *
2293
 *---------------------------------------------------------------------------
2294
 */
2295
 
2296
void
2297
Tk_TextLayoutToPostscript(interp, layout)
2298
    Tcl_Interp *interp;         /* Filled with Postscript code. */
2299
    Tk_TextLayout layout;       /* The layout to be rendered. */
2300
{
2301
#define MAXUSE 128
2302
    char buf[MAXUSE+10];
2303
    LayoutChunk *chunkPtr;
2304
    int i, j, used, c, baseline;
2305
    TextLayout *layoutPtr;
2306
 
2307
    layoutPtr = (TextLayout *) layout;
2308
    chunkPtr = layoutPtr->chunks;
2309
    baseline = chunkPtr->y;
2310
    used = 0;
2311
    buf[used++] = '(';
2312
    for (i = 0; i < layoutPtr->numChunks; i++) {
2313
        if (baseline != chunkPtr->y) {
2314
            buf[used++] = ')';
2315
            buf[used++] = '\n';
2316
            buf[used++] = '(';
2317
            baseline = chunkPtr->y;
2318
        }
2319
        if (chunkPtr->numDisplayChars <= 0) {
2320
            if (chunkPtr->start[0] == '\t') {
2321
                buf[used++] = '\\';
2322
                buf[used++] = 't';
2323
            }
2324
        } else {
2325
            for (j = 0; j < chunkPtr->numDisplayChars; j++) {
2326
                c = UCHAR(chunkPtr->start[j]);
2327
                if ((c == '(') || (c == ')') || (c == '\\') || (c < 0x20)
2328
                        || (c >= UCHAR(0x7f))) {
2329
                    /*
2330
                     * Tricky point:  the "03" is necessary in the sprintf
2331
                     * below, so that a full three digits of octal are
2332
                     * always generated.  Without the "03", a number
2333
                     * following this sequence could be interpreted by
2334
                     * Postscript as part of this sequence.
2335
                     */
2336
 
2337
                    sprintf(buf + used, "\\%03o", c);
2338
                    used += 4;
2339
                } else {
2340
                    buf[used++] = c;
2341
                }
2342
                if (used >= MAXUSE) {
2343
                    buf[used] = '\0';
2344
                    Tcl_AppendResult(interp, buf, (char *) NULL);
2345
                    used = 0;
2346
                }
2347
            }
2348
        }
2349
        if (used >= MAXUSE) {
2350
            /*
2351
             * If there are a whole bunch of returns or tabs in a row,
2352
             * then buf[] could get filled up.
2353
             */
2354
 
2355
            buf[used] = '\0';
2356
            Tcl_AppendResult(interp, buf, (char *) NULL);
2357
            used = 0;
2358
        }
2359
        chunkPtr++;
2360
    }
2361
    buf[used++] = ')';
2362
    buf[used++] = '\n';
2363
    buf[used] = '\0';
2364
    Tcl_AppendResult(interp, buf, (char *) NULL);
2365
}
2366
 
2367
/*
2368
 *---------------------------------------------------------------------------
2369
 *
2370
 * TkInitFontAttributes --
2371
 *
2372
 *      Initialize the font attributes structure to contain sensible
2373
 *      values.  This must be called before using any other font
2374
 *      attributes functions.
2375
 *
2376
 * Results:
2377
 *      None.
2378
 *
2379
 * Side effects.
2380
 *      None.
2381
 *
2382
 *---------------------------------------------------------------------------
2383
 */
2384
 
2385
void
2386
TkInitFontAttributes(faPtr)
2387
    TkFontAttributes *faPtr;    /* The attributes structure to initialize. */
2388
{
2389
    faPtr->family       = NULL;
2390
    faPtr->pointsize    = 0;
2391
    faPtr->weight       = TK_FW_NORMAL;
2392
    faPtr->slant        = TK_FS_ROMAN;
2393
    faPtr->underline    = 0;
2394
    faPtr->overstrike   = 0;
2395
}
2396
 
2397
/*
2398
 *---------------------------------------------------------------------------
2399
 *
2400
 * ConfigAttributesObj --
2401
 *
2402
 *      Process command line options to fill in fields of a properly
2403
 *      initialized font attributes structure.
2404
 *
2405
 * Results:
2406
 *      A standard Tcl return value.  If TCL_ERROR is returned, an
2407
 *      error message will be left in interp's result object.
2408
 *
2409
 * Side effects:
2410
 *      The fields of the font attributes structure get filled in with
2411
 *      information from argc/argv.  If an error occurs while parsing,
2412
 *      the font attributes structure will contain all modifications
2413
 *      specified in the command line options up to the point of the
2414
 *      error.
2415
 *
2416
 *---------------------------------------------------------------------------
2417
 */
2418
 
2419
static int
2420
ConfigAttributesObj(interp, tkwin, objc, objv, faPtr)
2421
    Tcl_Interp *interp;         /* Interp for error return. */
2422
    Tk_Window tkwin;            /* For display on which font will be used. */
2423
    int objc;                   /* Number of elements in argv. */
2424
    Tcl_Obj *CONST objv[];      /* Command line options. */
2425
    TkFontAttributes *faPtr;    /* Font attributes structure whose fields
2426
                                 * are to be modified.  Structure must already
2427
                                 * be properly initialized. */
2428
{
2429
    int i, n, index;
2430
    Tcl_Obj *value;
2431
    char *option, *string;
2432
 
2433
    if (objc & 1) {
2434
        string = Tcl_GetStringFromObj(objv[objc - 1], NULL);
2435
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "missing value for \"",
2436
                string, "\" option", (char *) NULL);
2437
        return TCL_ERROR;
2438
    }
2439
 
2440
    for (i = 0; i < objc; i += 2) {
2441
        option = Tcl_GetStringFromObj(objv[i], NULL);
2442
        value = objv[i + 1];
2443
 
2444
        if (Tcl_GetIndexFromObj(interp, objv[i], fontOpt, "option", 1,
2445
                &index) != TCL_OK) {
2446
            return TCL_ERROR;
2447
        }
2448
        switch (index) {
2449
            case FONT_FAMILY:
2450
                string = Tcl_GetStringFromObj(value, NULL);
2451
                faPtr->family = Tk_GetUid(string);
2452
                break;
2453
 
2454
            case FONT_SIZE:
2455
                if (Tcl_GetIntFromObj(interp, value, &n) != TCL_OK) {
2456
                    return TCL_ERROR;
2457
                }
2458
                faPtr->pointsize = n;
2459
                break;
2460
 
2461
            case FONT_WEIGHT:
2462
                string = Tcl_GetStringFromObj(value, NULL);
2463
                n = TkFindStateNum(interp, option, weightMap, string);
2464
                if (n == TK_FW_UNKNOWN) {
2465
                    return TCL_ERROR;
2466
                }
2467
                faPtr->weight = n;
2468
                break;
2469
 
2470
            case FONT_SLANT:
2471
                string = Tcl_GetStringFromObj(value, NULL);
2472
                n = TkFindStateNum(interp, option, slantMap, string);
2473
                if (n == TK_FS_UNKNOWN) {
2474
                    return TCL_ERROR;
2475
                }
2476
                faPtr->slant = n;
2477
                break;
2478
 
2479
            case FONT_UNDERLINE:
2480
                if (Tcl_GetBooleanFromObj(interp, value, &n) != TCL_OK) {
2481
                    return TCL_ERROR;
2482
                }
2483
                faPtr->underline = n;
2484
                break;
2485
 
2486
            case FONT_OVERSTRIKE:
2487
                if (Tcl_GetBooleanFromObj(interp, value, &n) != TCL_OK) {
2488
                    return TCL_ERROR;
2489
                }
2490
                faPtr->overstrike = n;
2491
                break;
2492
        }
2493
    }
2494
    return TCL_OK;
2495
}
2496
 
2497
/*
2498
 *---------------------------------------------------------------------------
2499
 *
2500
 * GetAttributeInfoObj --
2501
 *
2502
 *      Return information about the font attributes as a Tcl list.
2503
 *
2504
 * Results:
2505
 *      The return value is TCL_OK if the objPtr was non-NULL and
2506
 *      specified a valid font attribute, TCL_ERROR otherwise.  If TCL_OK
2507
 *      is returned, the interp's result object is modified to hold a
2508
 *      description of either the current value of a single option, or a
2509
 *      list of all options and their current values for the given font
2510
 *      attributes.  If TCL_ERROR is returned, the interp's result is
2511
 *      set to an error message describing that the objPtr did not refer
2512
 *      to a valid option.
2513
 *
2514
 * Side effects:
2515
 *      None.
2516
 *
2517
 *---------------------------------------------------------------------------
2518
 */
2519
 
2520
static int
2521
GetAttributeInfoObj(interp, faPtr, objPtr)
2522
    Tcl_Interp *interp;                 /* Interp to hold result. */
2523
    CONST TkFontAttributes *faPtr;      /* The font attributes to inspect. */
2524
    Tcl_Obj *objPtr;                    /* If non-NULL, indicates the single
2525
                                         * option whose value is to be
2526
                                         * returned. Otherwise
2527
                                         * information is returned for
2528
                                         * all options. */
2529
{
2530
    int i, index, start, end, num;
2531
    char *str;
2532
    Tcl_Obj *newPtr;
2533
 
2534
    start = 0;
2535
    end = FONT_NUMFIELDS;
2536
    if (objPtr != NULL) {
2537
        if (Tcl_GetIndexFromObj(interp, objPtr, fontOpt, "option", 1,
2538
                &index) != TCL_OK) {
2539
            return TCL_ERROR;
2540
        }
2541
        start = index;
2542
        end = index + 1;
2543
    }
2544
 
2545
    for (i = start; i < end; i++) {
2546
        str = NULL;
2547
        num = 0;                 /* Needed only to prevent compiler
2548
                                         * warning. */
2549
        switch (i) {
2550
            case FONT_FAMILY:
2551
                str = faPtr->family;
2552
                if (str == NULL) {
2553
                    str = "";
2554
                }
2555
                break;
2556
 
2557
            case FONT_SIZE:
2558
                num = faPtr->pointsize;
2559
                break;
2560
 
2561
            case FONT_WEIGHT:
2562
                str = TkFindStateString(weightMap, faPtr->weight);
2563
                break;
2564
 
2565
            case FONT_SLANT:
2566
                str = TkFindStateString(slantMap, faPtr->slant);
2567
                break;
2568
 
2569
            case FONT_UNDERLINE:
2570
                num = faPtr->underline;
2571
                break;
2572
 
2573
            case FONT_OVERSTRIKE:
2574
                num = faPtr->overstrike;
2575
                break;
2576
        }
2577
        if (objPtr == NULL) {
2578
            Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp),
2579
                    Tcl_NewStringObj(fontOpt[i], -1));
2580
            if (str != NULL) {
2581
                newPtr = Tcl_NewStringObj(str, -1);
2582
            } else {
2583
                newPtr = Tcl_NewIntObj(num);
2584
            }
2585
            Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp),
2586
                    newPtr);
2587
        } else {
2588
            if (str != NULL) {
2589
                Tcl_SetStringObj(Tcl_GetObjResult(interp), str, -1);
2590
            } else {
2591
                Tcl_SetIntObj(Tcl_GetObjResult(interp), num);
2592
            }
2593
        }
2594
    }
2595
    return TCL_OK;
2596
}
2597
 
2598
/*
2599
 *---------------------------------------------------------------------------
2600
 *
2601
 * ParseFontNameObj --
2602
 *
2603
 *      Converts a object into a set of font attributes that can be used
2604
 *      to construct a font.
2605
 *
2606
 *      The string rep of the object can be one of the following forms:
2607
 *              XLFD (see X documentation)
2608
 *              "Family [size [style] [style ...]]"
2609
 *              "-option value [-option value ...]"
2610
 *
2611
 * Results:
2612
 *      The return value is TCL_ERROR if the object was syntactically
2613
 *      invalid.  In that case an error message is left in interp's
2614
 *      result object.  Otherwise, fills the font attribute buffer with
2615
 *      the values parsed from the string and returns TCL_OK;
2616
 *
2617
 * Side effects:
2618
 *      None.
2619
 *
2620
 *---------------------------------------------------------------------------
2621
 */
2622
 
2623
static int
2624
ParseFontNameObj(interp, tkwin, objPtr, faPtr)
2625
    Tcl_Interp *interp;         /* Interp for error return. */
2626
    Tk_Window tkwin;            /* For display on which font is used. */
2627
    Tcl_Obj *objPtr;            /* Parseable font description object. */
2628
    TkFontAttributes *faPtr;    /* Font attributes structure whose fields
2629
                                 * are to be modified.  Structure must already
2630
                                 * be properly initialized. */
2631
{
2632
    char *dash;
2633
    int objc, result, i, n;
2634
    Tcl_Obj **objv;
2635
    TkXLFDAttributes xa;
2636
    char *string;
2637
 
2638
    string = Tcl_GetStringFromObj(objPtr, NULL);
2639
    if (*string == '-') {
2640
        /*
2641
         * This may be an XLFD or an "-option value" string.
2642
         *
2643
         * If the string begins with "-*" or a "-foundry-family-*" pattern,
2644
         * then consider it an XLFD.
2645
         */
2646
 
2647
        if (string[1] == '*') {
2648
            goto xlfd;
2649
        }
2650
        dash = strchr(string + 1, '-');
2651
        if ((dash != NULL) && (!isspace(UCHAR(dash[-1])))) {
2652
            goto xlfd;
2653
        }
2654
 
2655
        if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
2656
            return TCL_ERROR;
2657
        }
2658
 
2659
        return ConfigAttributesObj(interp, tkwin, objc, objv, faPtr);
2660
    }
2661
 
2662
    if (*string == '*') {
2663
        /*
2664
         * This appears to be an XLFD.
2665
         */
2666
 
2667
        xlfd:
2668
        xa.fa = *faPtr;
2669
        result = TkParseXLFD(string, &xa);
2670
        if (result == TCL_OK) {
2671
            *faPtr = xa.fa;
2672
            return result;
2673
        }
2674
    }
2675
 
2676
    /*
2677
     * Wasn't an XLFD or "-option value" string.  Try it as a
2678
     * "font size style" list.
2679
     */
2680
 
2681
    if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
2682
        return TCL_ERROR;
2683
    }
2684
    if (objc < 1) {
2685
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "font \"", string,
2686
                "\" doesn't exist", (char *) NULL);
2687
        return TCL_ERROR;
2688
    }
2689
 
2690
    faPtr->family = Tk_GetUid(Tcl_GetStringFromObj(objv[0], NULL));
2691
    if (objc > 1) {
2692
        if (Tcl_GetIntFromObj(interp, objv[1], &n) != TCL_OK) {
2693
            return TCL_ERROR;
2694
        }
2695
        faPtr->pointsize = n;
2696
    }
2697
 
2698
    i = 2;
2699
    if (objc == 3) {
2700
        if (Tcl_ListObjGetElements(interp, objv[2], &objc, &objv) != TCL_OK) {
2701
            return TCL_ERROR;
2702
        }
2703
        i = 0;
2704
    }
2705
    for ( ; i < objc; i++) {
2706
        string = Tcl_GetStringFromObj(objv[i], NULL);
2707
        n = TkFindStateNum(NULL, NULL, weightMap, string);
2708
        if (n != TK_FW_UNKNOWN) {
2709
            faPtr->weight = n;
2710
            continue;
2711
        }
2712
        n = TkFindStateNum(NULL, NULL, slantMap, string);
2713
        if (n != TK_FS_UNKNOWN) {
2714
            faPtr->slant = n;
2715
            continue;
2716
        }
2717
        n = TkFindStateNum(NULL, NULL, underlineMap, string);
2718
        if (n != 0) {
2719
            faPtr->underline = n;
2720
            continue;
2721
        }
2722
        n = TkFindStateNum(NULL, NULL, overstrikeMap, string);
2723
        if (n != 0) {
2724
            faPtr->overstrike = n;
2725
            continue;
2726
        }
2727
 
2728
        /*
2729
         * Unknown style.
2730
         */
2731
 
2732
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
2733
                "unknown font style \"", string, "\"",
2734
                (char *) NULL);
2735
        return TCL_ERROR;
2736
    }
2737
    return TCL_OK;
2738
}
2739
 
2740
/*
2741
 *---------------------------------------------------------------------------
2742
 *
2743
 * TkParseXLFD --
2744
 *
2745
 *      Break up a fully specified XLFD into a set of font attributes.
2746
 *
2747
 * Results:
2748
 *      Return value is TCL_ERROR if string was not a fully specified XLFD.
2749
 *      Otherwise, fills font attribute buffer with the values parsed
2750
 *      from the XLFD and returns TCL_OK.
2751
 *
2752
 * Side effects:
2753
 *      None.
2754
 *
2755
 *---------------------------------------------------------------------------
2756
 */
2757
 
2758
int
2759
TkParseXLFD(string, xaPtr)
2760
    CONST char *string;         /* Parseable font description string. */
2761
    TkXLFDAttributes *xaPtr;    /* XLFD attributes structure whose fields
2762
                                 * are to be modified.  Structure must already
2763
                                 * be properly initialized. */
2764
{
2765
    char *src;
2766
    CONST char *str;
2767
    int i, j;
2768
    char *field[XLFD_NUMFIELDS + 2];
2769
    Tcl_DString ds;
2770
 
2771
    memset(field, '\0', sizeof(field));
2772
 
2773
    str = string;
2774
    if (*str == '-') {
2775
        str++;
2776
    }
2777
 
2778
    Tcl_DStringInit(&ds);
2779
    Tcl_DStringAppend(&ds, (char *) str, -1);
2780
    src = Tcl_DStringValue(&ds);
2781
 
2782
    field[0] = src;
2783
    for (i = 0; *src != '\0'; src++) {
2784
        if (isupper(UCHAR(*src))) {
2785
            *src = tolower(UCHAR(*src));
2786
        }
2787
        if (*src == '-') {
2788
            i++;
2789
            if (i > XLFD_NUMFIELDS) {
2790
                break;
2791
            }
2792
            *src = '\0';
2793
            field[i] = src + 1;
2794
        }
2795
    }
2796
 
2797
    /*
2798
     * An XLFD of the form -adobe-times-medium-r-*-12-*-* is pretty common,
2799
     * but it is (strictly) malformed, because the first * is eliding both
2800
     * the Setwidth and the Addstyle fields.  If the Addstyle field is a
2801
     * number, then assume the above incorrect form was used and shift all
2802
     * the rest of the fields up by one, so the number gets interpreted
2803
     * as a pixelsize.  This fix is so that we don't get a million reports
2804
     * that "it works under X, but gives a syntax error under Windows".
2805
     */
2806
 
2807
    if ((i > XLFD_ADD_STYLE) && (FieldSpecified(field[XLFD_ADD_STYLE]))) {
2808
        if (atoi(field[XLFD_ADD_STYLE]) != 0) {
2809
            for (j = XLFD_NUMFIELDS - 1; j >= XLFD_ADD_STYLE; j--) {
2810
                field[j + 1] = field[j];
2811
            }
2812
            field[XLFD_ADD_STYLE] = NULL;
2813
            i++;
2814
        }
2815
    }
2816
 
2817
    /*
2818
     * Bail if we don't have enough of the fields (up to pointsize).
2819
     */
2820
 
2821
    if (i < XLFD_FAMILY) {
2822
        Tcl_DStringFree(&ds);
2823
        return TCL_ERROR;
2824
    }
2825
 
2826
    if (FieldSpecified(field[XLFD_FOUNDRY])) {
2827
        xaPtr->foundry = Tk_GetUid(field[XLFD_FOUNDRY]);
2828
    }
2829
 
2830
    if (FieldSpecified(field[XLFD_FAMILY])) {
2831
        xaPtr->fa.family = Tk_GetUid(field[XLFD_FAMILY]);
2832
    }
2833
    if (FieldSpecified(field[XLFD_WEIGHT])) {
2834
        xaPtr->fa.weight = TkFindStateNum(NULL, NULL, xlfdWeightMap,
2835
                field[XLFD_WEIGHT]);
2836
    }
2837
    if (FieldSpecified(field[XLFD_SLANT])) {
2838
        xaPtr->slant = TkFindStateNum(NULL, NULL, xlfdSlantMap,
2839
                field[XLFD_SLANT]);
2840
        if (xaPtr->slant == TK_FS_ROMAN) {
2841
            xaPtr->fa.slant = TK_FS_ROMAN;
2842
        } else {
2843
            xaPtr->fa.slant = TK_FS_ITALIC;
2844
        }
2845
    }
2846
    if (FieldSpecified(field[XLFD_SETWIDTH])) {
2847
        xaPtr->setwidth = TkFindStateNum(NULL, NULL, xlfdSetwidthMap,
2848
                field[XLFD_SETWIDTH]);
2849
    }
2850
 
2851
    /* XLFD_ADD_STYLE ignored. */
2852
 
2853
    /*
2854
     * Pointsize in tenths of a point, but treat it as tenths of a pixel.
2855
     */
2856
 
2857
    if (FieldSpecified(field[XLFD_POINT_SIZE])) {
2858
        if (field[XLFD_POINT_SIZE][0] == '[') {
2859
            /*
2860
             * Some X fonts have the point size specified as follows:
2861
             *
2862
             *      [ N1 N2 N3 N4 ]
2863
             *
2864
             * where N1 is the point size (in points, not decipoints!), and
2865
             * N2, N3, and N4 are some additional numbers that I don't know
2866
             * the purpose of, so I ignore them.
2867
             */
2868
 
2869
            xaPtr->fa.pointsize = atoi(field[XLFD_POINT_SIZE] + 1);
2870
        } else if (Tcl_GetInt(NULL, field[XLFD_POINT_SIZE],
2871
                &xaPtr->fa.pointsize) == TCL_OK) {
2872
            xaPtr->fa.pointsize /= 10;
2873
        } else {
2874
            return TCL_ERROR;
2875
        }
2876
    }
2877
 
2878
    /*
2879
     * Pixel height of font.  If specified, overrides pointsize.
2880
     */
2881
 
2882
    if (FieldSpecified(field[XLFD_PIXEL_SIZE])) {
2883
        if (field[XLFD_PIXEL_SIZE][0] == '[') {
2884
            /*
2885
             * Some X fonts have the pixel size specified as follows:
2886
             *
2887
             *      [ N1 N2 N3 N4 ]
2888
             *
2889
             * where N1 is the pixel size, and where N2, N3, and N4
2890
             * are some additional numbers that I don't know
2891
             * the purpose of, so I ignore them.
2892
             */
2893
 
2894
            xaPtr->fa.pointsize = atoi(field[XLFD_PIXEL_SIZE] + 1);
2895
        } else if (Tcl_GetInt(NULL, field[XLFD_PIXEL_SIZE],
2896
                &xaPtr->fa.pointsize) != TCL_OK) {
2897
            return TCL_ERROR;
2898
        }
2899
    }
2900
 
2901
    xaPtr->fa.pointsize = -xaPtr->fa.pointsize;
2902
 
2903
    /* XLFD_RESOLUTION_X ignored. */
2904
 
2905
    /* XLFD_RESOLUTION_Y ignored. */
2906
 
2907
    /* XLFD_SPACING ignored. */
2908
 
2909
    /* XLFD_AVERAGE_WIDTH ignored. */
2910
 
2911
    if (FieldSpecified(field[XLFD_REGISTRY])) {
2912
        xaPtr->charset = TkFindStateNum(NULL, NULL, xlfdCharsetMap,
2913
                field[XLFD_REGISTRY]);
2914
    }
2915
    if (FieldSpecified(field[XLFD_ENCODING])) {
2916
        xaPtr->encoding = atoi(field[XLFD_ENCODING]);
2917
    }
2918
 
2919
    Tcl_DStringFree(&ds);
2920
    return TCL_OK;
2921
}
2922
 
2923
/*
2924
 *---------------------------------------------------------------------------
2925
 *
2926
 * FieldSpecified --
2927
 *
2928
 *      Helper function for TkParseXLFD().  Determines if a field in the
2929
 *      XLFD was set to a non-null, non-don't-care value.
2930
 *
2931
 * Results:
2932
 *      The return value is 0 if the field in the XLFD was not set and
2933
 *      should be ignored, non-zero otherwise.
2934
 *
2935
 * Side effects:
2936
 *      None.
2937
 *
2938
 *---------------------------------------------------------------------------
2939
 */
2940
 
2941
static int
2942
FieldSpecified(field)
2943
    CONST char *field;  /* The field of the XLFD to check.  Strictly
2944
                         * speaking, only when the string is "*" does it mean
2945
                         * don't-care.  However, an unspecified or question
2946
                         * mark is also interpreted as don't-care. */
2947
{
2948
    char ch;
2949
 
2950
    if (field == NULL) {
2951
        return 0;
2952
    }
2953
    ch = field[0];
2954
    return (ch != '*' && ch != '?');
2955
}
2956
 
2957
/*
2958
 *---------------------------------------------------------------------------
2959
 *
2960
 * NewChunk --
2961
 *
2962
 *      Helper function for Tk_ComputeTextLayout().  Encapsulates a
2963
 *      measured set of characters in a chunk that can be quickly
2964
 *      drawn.
2965
 *
2966
 * Results:
2967
 *      A pointer to the new chunk in the text layout.
2968
 *
2969
 * Side effects:
2970
 *      The text layout is reallocated to hold more chunks as necessary.
2971
 *
2972
 *      Currently, Tk_ComputeTextLayout() stores contiguous ranges of
2973
 *      "normal" characters in a chunk, along with individual tab
2974
 *      and newline chars in their own chunks.  All characters in the
2975
 *      text layout are accounted for.
2976
 *
2977
 *---------------------------------------------------------------------------
2978
 */
2979
static LayoutChunk *
2980
NewChunk(layoutPtrPtr, maxPtr, start, numChars, curX, newX, y)
2981
    TextLayout **layoutPtrPtr;
2982
    int *maxPtr;
2983
    CONST char *start;
2984
    int numChars;
2985
    int curX;
2986
    int newX;
2987
    int y;
2988
{
2989
    TextLayout *layoutPtr;
2990
    LayoutChunk *chunkPtr;
2991
    int maxChunks;
2992
    size_t s;
2993
 
2994
    layoutPtr = *layoutPtrPtr;
2995
    maxChunks = *maxPtr;
2996
    if (layoutPtr->numChunks == maxChunks) {
2997
        maxChunks *= 2;
2998
        s = sizeof(TextLayout) + ((maxChunks - 1) * sizeof(LayoutChunk));
2999
        layoutPtr = (TextLayout *) ckrealloc((char *) layoutPtr, s);
3000
 
3001
        *layoutPtrPtr = layoutPtr;
3002
        *maxPtr = maxChunks;
3003
    }
3004
    chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks];
3005
    chunkPtr->start             = start;
3006
    chunkPtr->numChars          = numChars;
3007
    chunkPtr->numDisplayChars   = numChars;
3008
    chunkPtr->x                 = curX;
3009
    chunkPtr->y                 = y;
3010
    chunkPtr->totalWidth        = newX - curX;
3011
    chunkPtr->displayWidth      = newX - curX;
3012
    layoutPtr->numChunks++;
3013
 
3014
    return chunkPtr;
3015
}
3016
 
3017
/* CYGNUS LOCAL: This routine is called on Windows to update a named
3018
   font to a possibly new set of font attributes.  */
3019
 
3020
void
3021
TkUpdateFonts(tkwin, changed)
3022
    Tk_Window tkwin;
3023
    int (*changed) _ANSI_ARGS_((TkFontAttributes *faPtr));
3024
{
3025
    TkFontInfo *fiPtr;
3026
    Tcl_HashEntry *namedHashPtr;
3027
    Tcl_HashSearch search;
3028
    NamedFont *nfPtr;
3029
 
3030
    fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;
3031
 
3032
    namedHashPtr = Tcl_FirstHashEntry(&fiPtr->namedTable, &search);
3033
    while (namedHashPtr != NULL) {
3034
        nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
3035
        if ((*changed)(&nfPtr->fa)) {
3036
            UpdateDependantFonts(fiPtr, tkwin, namedHashPtr);
3037
        }
3038
        namedHashPtr = Tcl_NextHashEntry(&search);
3039
    }
3040
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.