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

Subversion Repositories or1k

[/] [or1k/] [tags/] [start/] [insight/] [tk/] [generic/] [tkTextDisp.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
 * tkTextDisp.c --
3
 *
4
 *      This module provides facilities to display text widgets.  It is
5
 *      the only place where information is kept about the screen layout
6
 *      of text widgets.
7
 *
8
 * Copyright (c) 1992-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: tkTextDisp.c,v 1.1.1.1 2002-01-16 10:25:53 markom Exp $
15
 */
16
 
17
#include "tkPort.h"
18
#include "tkInt.h"
19
#include "tkText.h"
20
 
21
/*
22
 * The following structure describes how to display a range of characters.
23
 * The information is generated by scanning all of the tags associated
24
 * with the characters and combining that with default information for
25
 * the overall widget.  These structures form the hash keys for
26
 * dInfoPtr->styleTable.
27
 */
28
 
29
typedef struct StyleValues {
30
    Tk_3DBorder border;         /* Used for drawing background under text.
31
                                 * NULL means use widget background. */
32
    int borderWidth;            /* Width of 3-D border for background. */
33
    int relief;                 /* 3-D relief for background. */
34
    Pixmap bgStipple;           /* Stipple bitmap for background.  None
35
                                 * means draw solid. */
36
    XColor *fgColor;            /* Foreground color for text. */
37
    Tk_Font tkfont;             /* Font for displaying text. */
38
    Pixmap fgStipple;           /* Stipple bitmap for text and other
39
                                 * foreground stuff.   None means draw
40
                                 * solid.*/
41
    int justify;                /* Justification style for text. */
42
    int lMargin1;               /* Left margin, in pixels, for first display
43
                                 * line of each text line. */
44
    int lMargin2;               /* Left margin, in pixels, for second and
45
                                 * later display lines of each text line. */
46
    int offset;                 /* Offset in pixels of baseline, relative to
47
                                 * baseline of line. */
48
    int overstrike;             /* Non-zero means draw overstrike through
49
                                 * text. */
50
    int rMargin;                /* Right margin, in pixels. */
51
    int spacing1;               /* Spacing above first dline in text line. */
52
    int spacing2;               /* Spacing between lines of dline. */
53
    int spacing3;               /* Spacing below last dline in text line. */
54
    TkTextTabArray *tabArrayPtr;/* Locations and types of tab stops (may
55
                                 * be NULL). */
56
    int underline;              /* Non-zero means draw underline underneath
57
                                 * text. */
58
    Tk_Uid wrapMode;            /* How to handle wrap-around for this tag.
59
                                 * One of tkTextCharUid, tkTextNoneUid,
60
                                 * or tkTextWordUid. */
61
} StyleValues;
62
 
63
/*
64
 * The following structure extends the StyleValues structure above with
65
 * graphics contexts used to actually draw the characters.  The entries
66
 * in dInfoPtr->styleTable point to structures of this type.
67
 */
68
 
69
typedef struct TextStyle {
70
    int refCount;               /* Number of times this structure is
71
                                 * referenced in Chunks. */
72
    GC bgGC;                    /* Graphics context for background.  None
73
                                 * means use widget background. */
74
    GC fgGC;                    /* Graphics context for foreground. */
75
    StyleValues *sValuePtr;     /* Raw information from which GCs were
76
                                 * derived. */
77
    Tcl_HashEntry *hPtr;        /* Pointer to entry in styleTable.  Used
78
                                 * to delete entry. */
79
} TextStyle;
80
 
81
/*
82
 * The following macro determines whether two styles have the same
83
 * background so that, for example, no beveled border should be drawn
84
 * between them.
85
 */
86
 
87
#define SAME_BACKGROUND(s1, s2) \
88
    (((s1)->sValuePtr->border == (s2)->sValuePtr->border) \
89
        && ((s1)->sValuePtr->borderWidth == (s2)->sValuePtr->borderWidth) \
90
        && ((s1)->sValuePtr->relief == (s2)->sValuePtr->relief) \
91
        && ((s1)->sValuePtr->bgStipple == (s2)->sValuePtr->bgStipple))
92
 
93
/*
94
 * The following structure describes one line of the display, which may
95
 * be either part or all of one line of the text.
96
 */
97
 
98
typedef struct DLine {
99
    TkTextIndex index;          /* Identifies first character in text
100
                                 * that is displayed on this line. */
101
    int count;                  /* Number of characters accounted for by this
102
                                 * display line, including a trailing space
103
                                 * or newline that isn't actually displayed. */
104
    int y;                      /* Y-position at which line is supposed to
105
                                 * be drawn (topmost pixel of rectangular
106
                                 * area occupied by line). */
107
    int oldY;                   /* Y-position at which line currently
108
                                 * appears on display.  -1 means line isn't
109
                                 * currently visible on display and must be
110
                                 * redrawn.  This is used to move lines by
111
                                 * scrolling rather than re-drawing. */
112
    int height;                 /* Height of line, in pixels. */
113
    int baseline;               /* Offset of text baseline from y, in
114
                                 * pixels. */
115
    int spaceAbove;             /* How much extra space was added to the
116
                                 * top of the line because of spacing
117
                                 * options.  This is included in height
118
                                 * and baseline. */
119
    int spaceBelow;             /* How much extra space was added to the
120
                                 * bottom of the line because of spacing
121
                                 * options.  This is included in height. */
122
    int length;                 /* Total length of line, in pixels. */
123
    TkTextDispChunk *chunkPtr;  /* Pointer to first chunk in list of all
124
                                 * of those that are displayed on this
125
                                 * line of the screen. */
126
    struct DLine *nextPtr;      /* Next in list of all display lines for
127
                                 * this window.   The list is sorted in
128
                                 * order from top to bottom.  Note:  the
129
                                 * next DLine doesn't always correspond
130
                                 * to the next line of text:  (a) can have
131
                                 * multiple DLines for one text line, and
132
                                 * (b) can have gaps where DLine's have been
133
                                 * deleted because they're out of date. */
134
    int flags;                  /* Various flag bits:  see below for values. */
135
} DLine;
136
 
137
/*
138
 * Flag bits for DLine structures:
139
 *
140
 * HAS_3D_BORDER -              Non-zero means that at least one of the
141
 *                              chunks in this line has a 3D border, so
142
 *                              it potentially interacts with 3D borders
143
 *                              in neighboring lines (see
144
 *                              DisplayLineBackground).
145
 * NEW_LAYOUT -                 Non-zero means that the line has been
146
 *                              re-layed out since the last time the
147
 *                              display was updated.
148
 * TOP_LINE -                   Non-zero means that this was the top line
149
 *                              in the window the last time that the window
150
 *                              was laid out.  This is important because
151
 *                              a line may be displayed differently if its
152
 *                              at the top or bottom than if it's in the
153
 *                              middle (e.g. beveled edges aren't displayed
154
 *                              for middle lines if the adjacent line has
155
 *                              a similar background).
156
 * BOTTOM_LINE -                Non-zero means that this was the bottom line
157
 *                              in the window the last time that the window
158
 *                              was laid out.
159
 */
160
 
161
#define HAS_3D_BORDER   1
162
#define NEW_LAYOUT      2
163
#define TOP_LINE        4
164
#define BOTTOM_LINE     8
165
 
166
/*
167
 * Overall display information for a text widget:
168
 */
169
 
170
typedef struct TextDInfo {
171
    Tcl_HashTable styleTable;   /* Hash table that maps from StyleValues
172
                                 * to TextStyles for this widget. */
173
    DLine *dLinePtr;            /* First in list of all display lines for
174
                                 * this widget, in order from top to bottom. */
175
    GC copyGC;                  /* Graphics context for copying from off-
176
                                 * screen pixmaps onto screen. */
177
    GC scrollGC;                /* Graphics context for copying from one place
178
                                 * in the window to another (scrolling):
179
                                 * differs from copyGC in that we need to get
180
                                 * GraphicsExpose events. */
181
    int x;                      /* First x-coordinate that may be used for
182
                                 * actually displaying line information.
183
                                 * Leaves space for border, etc. */
184
    int y;                      /* First y-coordinate that may be used for
185
                                 * actually displaying line information.
186
                                 * Leaves space for border, etc. */
187
    int maxX;                   /* First x-coordinate to right of available
188
                                 * space for displaying lines. */
189
    int maxY;                   /* First y-coordinate below available
190
                                 * space for displaying lines. */
191
    int topOfEof;               /* Top-most pixel (lowest y-value) that has
192
                                 * been drawn in the appropriate fashion for
193
                                 * the portion of the window after the last
194
                                 * line of the text.  This field is used to
195
                                 * figure out when to redraw part or all of
196
                                 * the eof field. */
197
 
198
    /*
199
     * Information used for scrolling:
200
     */
201
 
202
    int newCharOffset;          /* Desired x scroll position, measured as the
203
                                 * number of average-size characters off-screen
204
                                 * to the left for a line with no left
205
                                 * margin. */
206
    int curPixelOffset;         /* Actual x scroll position, measured as the
207
                                 * number of pixels off-screen to the left. */
208
    int maxLength;              /* Length in pixels of longest line that's
209
                                 * visible in window (length may exceed window
210
                                 * size).  If there's no wrapping, this will
211
                                 * be zero. */
212
    double xScrollFirst, xScrollLast;
213
                                /* Most recent values reported to horizontal
214
                                 * scrollbar;  used to eliminate unnecessary
215
                                 * reports. */
216
    double yScrollFirst, yScrollLast;
217
                                /* Most recent values reported to vertical
218
                                 * scrollbar;  used to eliminate unnecessary
219
                                 * reports. */
220
 
221
    /*
222
     * The following information is used to implement scanning:
223
     */
224
 
225
    int scanMarkChar;           /* Character that was at the left edge of
226
                                 * the window when the scan started. */
227
    int scanMarkX;              /* X-position of mouse at time scan started. */
228
    int scanTotalScroll;        /* Total scrolling (in screen lines) that has
229
                                 * occurred since scanMarkY was set. */
230
    int scanMarkY;              /* Y-position of mouse at time scan started. */
231
 
232
    /*
233
     * Miscellaneous information:
234
     */
235
 
236
    int dLinesInvalidated;      /* This value is set to 1 whenever something
237
                                 * happens that invalidates information in
238
                                 * DLine structures;  if a redisplay
239
                                 * is in progress, it will see this and
240
                                 * abort the redisplay.  This is needed
241
                                 * because, for example, an embedded window
242
                                 * could change its size when it is first
243
                                 * displayed, invalidating the DLine that
244
                                 * is currently being displayed.  If redisplay
245
                                 * continues, it will use freed memory and
246
                                 * could dump core. */
247
    int flags;                  /* Various flag values:  see below for
248
                                 * definitions. */
249
} TextDInfo;
250
 
251
/*
252
 * In TkTextDispChunk structures for character segments, the clientData
253
 * field points to one of the following structures:
254
 */
255
 
256
typedef struct CharInfo {
257
    int numChars;               /* Number of characters to display. */
258
    char chars[4];              /* Characters to display.  Actual size
259
                                 * will be numChars, not 4.  THIS MUST BE
260
                                 * THE LAST FIELD IN THE STRUCTURE. */
261
} CharInfo;
262
 
263
/*
264
 * Flag values for TextDInfo structures:
265
 *
266
 * DINFO_OUT_OF_DATE:           Non-zero means that the DLine structures
267
 *                              for this window are partially or completely
268
 *                              out of date and need to be recomputed.
269
 * REDRAW_PENDING:              Means that a when-idle handler has been
270
 *                              scheduled to update the display.
271
 * REDRAW_BORDERS:              Means window border or pad area has
272
 *                              potentially been damaged and must be redrawn.
273
 * REPICK_NEEDED:               1 means that the widget has been modified
274
 *                              in a way that could change the current
275
 *                              character (a different character might be
276
 *                              under the mouse cursor now).  Need to
277
 *                              recompute the current character before
278
 *                              the next redisplay.
279
 */
280
 
281
#define DINFO_OUT_OF_DATE       1
282
#define REDRAW_PENDING          2
283
#define REDRAW_BORDERS          4
284
#define REPICK_NEEDED           8
285
 
286
/*
287
 * The following counters keep statistics about redisplay that can be
288
 * checked to see how clever this code is at reducing redisplays.
289
 */
290
 
291
static int numRedisplays;       /* Number of calls to DisplayText. */
292
static int linesRedrawn;        /* Number of calls to DisplayDLine. */
293
static int numCopies;           /* Number of calls to XCopyArea to copy part
294
                                 * of the screen. */
295
 
296
/*
297
 * Forward declarations for procedures defined later in this file:
298
 */
299
 
300
static void             AdjustForTab _ANSI_ARGS_((TkText *textPtr,
301
                            TkTextTabArray *tabArrayPtr, int index,
302
                            TkTextDispChunk *chunkPtr));
303
static void             CharBboxProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
304
                            int index, int y, int lineHeight, int baseline,
305
                            int *xPtr, int *yPtr, int *widthPtr,
306
                            int *heightPtr));
307
static void             CharDisplayProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
308
                            int x, int y, int height, int baseline,
309
                            Display *display, Drawable dst, int screenY));
310
static int              CharMeasureProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
311
                            int x));
312
static void             CharUndisplayProc _ANSI_ARGS_((TkText *textPtr,
313
                            TkTextDispChunk *chunkPtr));
314
static void             DisplayDLine _ANSI_ARGS_((TkText *textPtr,
315
                            DLine *dlPtr, DLine *prevPtr, Pixmap pixmap));
316
static void             DisplayLineBackground _ANSI_ARGS_((TkText *textPtr,
317
                            DLine *dlPtr, DLine *prevPtr, Pixmap pixmap));
318
static void             DisplayText _ANSI_ARGS_((ClientData clientData));
319
static DLine *          FindDLine _ANSI_ARGS_((DLine *dlPtr,
320
                            TkTextIndex *indexPtr));
321
static void             FreeDLines _ANSI_ARGS_((TkText *textPtr,
322
                            DLine *firstPtr, DLine *lastPtr, int unlink));
323
static void             FreeStyle _ANSI_ARGS_((TkText *textPtr,
324
                            TextStyle *stylePtr));
325
static TextStyle *      GetStyle _ANSI_ARGS_((TkText *textPtr,
326
                            TkTextIndex *indexPtr));
327
static void             GetXView _ANSI_ARGS_((Tcl_Interp *interp,
328
                            TkText *textPtr, int report));
329
static void             GetYView _ANSI_ARGS_((Tcl_Interp *interp,
330
                            TkText *textPtr, int report));
331
static DLine *          LayoutDLine _ANSI_ARGS_((TkText *textPtr,
332
                            TkTextIndex *indexPtr));
333
static int              MeasureChars _ANSI_ARGS_((Tk_Font tkfont,
334
                            CONST char *source, int maxChars, int startX,
335
                            int maxX, int tabOrigin, int *nextXPtr));
336
static void             MeasureUp _ANSI_ARGS_((TkText *textPtr,
337
                            TkTextIndex *srcPtr, int distance,
338
                            TkTextIndex *dstPtr));
339
static int              NextTabStop _ANSI_ARGS_((TkText *textPtr, Tk_Font tkfont, int x,
340
                            int tabOrigin));
341
static void             UpdateDisplayInfo _ANSI_ARGS_((TkText *textPtr));
342
static void             ScrollByLines _ANSI_ARGS_((TkText *textPtr,
343
                            int offset));
344
static int              SizeOfTab _ANSI_ARGS_((TkText *textPtr,
345
                            TkTextTabArray *tabArrayPtr, int index, int x,
346
                            int maxX));
347
static void             TextInvalidateRegion _ANSI_ARGS_((TkText *textPtr,
348
                            TkRegion region));
349
 
350
 
351
/*
352
 *----------------------------------------------------------------------
353
 *
354
 * TkTextCreateDInfo --
355
 *
356
 *      This procedure is called when a new text widget is created.
357
 *      Its job is to set up display-related information for the widget.
358
 *
359
 * Results:
360
 *      None.
361
 *
362
 * Side effects:
363
 *      A TextDInfo data structure is allocated and initialized and attached
364
 *      to textPtr.
365
 *
366
 *----------------------------------------------------------------------
367
 */
368
 
369
void
370
TkTextCreateDInfo(textPtr)
371
    TkText *textPtr;            /* Overall information for text widget. */
372
{
373
    register TextDInfo *dInfoPtr;
374
    XGCValues gcValues;
375
 
376
    dInfoPtr = (TextDInfo *) ckalloc(sizeof(TextDInfo));
377
    Tcl_InitHashTable(&dInfoPtr->styleTable, sizeof(StyleValues)/sizeof(int));
378
    dInfoPtr->dLinePtr = NULL;
379
    dInfoPtr->copyGC = None;
380
    gcValues.graphics_exposures = True;
381
    dInfoPtr->scrollGC = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures,
382
            &gcValues);
383
    dInfoPtr->topOfEof = 0;
384
    dInfoPtr->newCharOffset = 0;
385
    dInfoPtr->curPixelOffset = 0;
386
    dInfoPtr->maxLength = 0;
387
    dInfoPtr->xScrollFirst = -1;
388
    dInfoPtr->xScrollLast = -1;
389
    dInfoPtr->yScrollFirst = -1;
390
    dInfoPtr->yScrollLast = -1;
391
    dInfoPtr->scanMarkChar = 0;
392
    dInfoPtr->scanMarkX = 0;
393
    dInfoPtr->scanTotalScroll = 0;
394
    dInfoPtr->scanMarkY = 0;
395
    dInfoPtr->dLinesInvalidated = 0;
396
    dInfoPtr->flags = DINFO_OUT_OF_DATE;
397
    textPtr->dInfoPtr = dInfoPtr;
398
}
399
 
400
/*
401
 *----------------------------------------------------------------------
402
 *
403
 * TkTextFreeDInfo --
404
 *
405
 *      This procedure is called to free up all of the private display
406
 *      information kept by this file for a text widget.
407
 *
408
 * Results:
409
 *      None.
410
 *
411
 * Side effects:
412
 *      Lots of resources get freed.
413
 *
414
 *----------------------------------------------------------------------
415
 */
416
 
417
void
418
TkTextFreeDInfo(textPtr)
419
    TkText *textPtr;            /* Overall information for text widget. */
420
{
421
    register TextDInfo *dInfoPtr = textPtr->dInfoPtr;
422
 
423
    /*
424
     * Be careful to free up styleTable *after* freeing up all the
425
     * DLines, so that the hash table is still intact to free up the
426
     * style-related information from the lines.  Once the lines are
427
     * all free then styleTable will be empty.
428
     */
429
 
430
    FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
431
    Tcl_DeleteHashTable(&dInfoPtr->styleTable);
432
    if (dInfoPtr->copyGC != None) {
433
        Tk_FreeGC(textPtr->display, dInfoPtr->copyGC);
434
    }
435
    Tk_FreeGC(textPtr->display, dInfoPtr->scrollGC);
436
    if (dInfoPtr->flags & REDRAW_PENDING) {
437
        Tcl_CancelIdleCall(DisplayText, (ClientData) textPtr);
438
    }
439
    ckfree((char *) dInfoPtr);
440
}
441
 
442
/*
443
 *----------------------------------------------------------------------
444
 *
445
 * GetStyle --
446
 *
447
 *      This procedure creates all the information needed to display
448
 *      text at a particular location.
449
 *
450
 * Results:
451
 *      The return value is a pointer to a TextStyle structure that
452
 *      corresponds to *sValuePtr.
453
 *
454
 * Side effects:
455
 *      A new entry may be created in the style table for the widget.
456
 *
457
 *----------------------------------------------------------------------
458
 */
459
 
460
static TextStyle *
461
GetStyle(textPtr, indexPtr)
462
    TkText *textPtr;            /* Overall information about text widget. */
463
    TkTextIndex *indexPtr;      /* The character in the text for which
464
                                 * display information is wanted. */
465
{
466
    TkTextTag **tagPtrs;
467
    register TkTextTag *tagPtr;
468
    StyleValues styleValues;
469
    TextStyle *stylePtr;
470
    Tcl_HashEntry *hPtr;
471
    int numTags, new, i;
472
    XGCValues gcValues;
473
    unsigned long mask;
474
 
475
    /*
476
     * The variables below keep track of the highest-priority specification
477
     * that has occurred for each of the various fields of the StyleValues.
478
     */
479
 
480
    int borderPrio, borderWidthPrio, reliefPrio, bgStipplePrio;
481
    int fgPrio, fontPrio, fgStipplePrio;
482
    int underlinePrio, justifyPrio, offsetPrio;
483
    int lMargin1Prio, lMargin2Prio, rMarginPrio;
484
    int spacing1Prio, spacing2Prio, spacing3Prio;
485
    int overstrikePrio, tabPrio, wrapPrio;
486
 
487
    /*
488
     * Find out what tags are present for the character, then compute
489
     * a StyleValues structure corresponding to those tags (scan
490
     * through all of the tags, saving information for the highest-
491
     * priority tag).
492
     */
493
 
494
    tagPtrs = TkBTreeGetTags(indexPtr, &numTags);
495
    borderPrio = borderWidthPrio = reliefPrio = bgStipplePrio = -1;
496
    fgPrio = fontPrio = fgStipplePrio = -1;
497
    underlinePrio = justifyPrio = offsetPrio = -1;
498
    lMargin1Prio = lMargin2Prio = rMarginPrio = -1;
499
    spacing1Prio = spacing2Prio = spacing3Prio = -1;
500
    overstrikePrio = tabPrio = wrapPrio = -1;
501
    memset((VOID *) &styleValues, 0, sizeof(StyleValues));
502
    styleValues.relief = TK_RELIEF_FLAT;
503
    styleValues.fgColor = textPtr->fgColor;
504
    styleValues.tkfont = textPtr->tkfont;
505
    styleValues.justify = TK_JUSTIFY_LEFT;
506
    styleValues.spacing1 = textPtr->spacing1;
507
    styleValues.spacing2 = textPtr->spacing2;
508
    styleValues.spacing3 = textPtr->spacing3;
509
    styleValues.tabArrayPtr = textPtr->tabArrayPtr;
510
    styleValues.wrapMode = textPtr->wrapMode;
511
    for (i = 0 ; i < numTags; i++) {
512
        tagPtr = tagPtrs[i];
513
 
514
        /*
515
         * On Windows and Mac, we need to skip the selection tag if
516
         * we don't have focus.
517
         */
518
 
519
#ifndef ALWAYS_SHOW_SELECTION
520
        if ((tagPtr == textPtr->selTagPtr) && !(textPtr->flags & GOT_FOCUS)) {
521
            continue;
522
        }
523
#endif
524
 
525
        if ((tagPtr->border != NULL) && (tagPtr->priority > borderPrio)) {
526
            styleValues.border = tagPtr->border;
527
            borderPrio = tagPtr->priority;
528
        }
529
        if ((tagPtr->bdString != NULL)
530
                && (tagPtr->priority > borderWidthPrio)) {
531
            styleValues.borderWidth = tagPtr->borderWidth;
532
            borderWidthPrio = tagPtr->priority;
533
        }
534
        if ((tagPtr->reliefString != NULL)
535
                && (tagPtr->priority > reliefPrio)) {
536
            if (styleValues.border == NULL) {
537
                styleValues.border = textPtr->border;
538
            }
539
            styleValues.relief = tagPtr->relief;
540
            reliefPrio = tagPtr->priority;
541
        }
542
        if ((tagPtr->bgStipple != None)
543
                && (tagPtr->priority > bgStipplePrio)) {
544
            styleValues.bgStipple = tagPtr->bgStipple;
545
            bgStipplePrio = tagPtr->priority;
546
        }
547
        if ((tagPtr->fgColor != None) && (tagPtr->priority > fgPrio)) {
548
            styleValues.fgColor = tagPtr->fgColor;
549
            fgPrio = tagPtr->priority;
550
        }
551
        if ((tagPtr->tkfont != None) && (tagPtr->priority > fontPrio)) {
552
            styleValues.tkfont = tagPtr->tkfont;
553
            fontPrio = tagPtr->priority;
554
        }
555
        if ((tagPtr->fgStipple != None)
556
                && (tagPtr->priority > fgStipplePrio)) {
557
            styleValues.fgStipple = tagPtr->fgStipple;
558
            fgStipplePrio = tagPtr->priority;
559
        }
560
        if ((tagPtr->justifyString != NULL)
561
                && (tagPtr->priority > justifyPrio)) {
562
            styleValues.justify = tagPtr->justify;
563
            justifyPrio = tagPtr->priority;
564
        }
565
        if ((tagPtr->lMargin1String != NULL)
566
                && (tagPtr->priority > lMargin1Prio)) {
567
            styleValues.lMargin1 = tagPtr->lMargin1;
568
            lMargin1Prio = tagPtr->priority;
569
        }
570
        if ((tagPtr->lMargin2String != NULL)
571
                && (tagPtr->priority > lMargin2Prio)) {
572
            styleValues.lMargin2 = tagPtr->lMargin2;
573
            lMargin2Prio = tagPtr->priority;
574
        }
575
        if ((tagPtr->offsetString != NULL)
576
                && (tagPtr->priority > offsetPrio)) {
577
            styleValues.offset = tagPtr->offset;
578
            offsetPrio = tagPtr->priority;
579
        }
580
        if ((tagPtr->overstrikeString != NULL)
581
                && (tagPtr->priority > overstrikePrio)) {
582
            styleValues.overstrike = tagPtr->overstrike;
583
            overstrikePrio = tagPtr->priority;
584
        }
585
        if ((tagPtr->rMarginString != NULL)
586
                && (tagPtr->priority > rMarginPrio)) {
587
            styleValues.rMargin = tagPtr->rMargin;
588
            rMarginPrio = tagPtr->priority;
589
        }
590
        if ((tagPtr->spacing1String != NULL)
591
                && (tagPtr->priority > spacing1Prio)) {
592
            styleValues.spacing1 = tagPtr->spacing1;
593
            spacing1Prio = tagPtr->priority;
594
        }
595
        if ((tagPtr->spacing2String != NULL)
596
                && (tagPtr->priority > spacing2Prio)) {
597
            styleValues.spacing2 = tagPtr->spacing2;
598
            spacing2Prio = tagPtr->priority;
599
        }
600
        if ((tagPtr->spacing3String != NULL)
601
                && (tagPtr->priority > spacing3Prio)) {
602
            styleValues.spacing3 = tagPtr->spacing3;
603
            spacing3Prio = tagPtr->priority;
604
        }
605
        if ((tagPtr->tabString != NULL)
606
                && (tagPtr->priority > tabPrio)) {
607
            styleValues.tabArrayPtr = tagPtr->tabArrayPtr;
608
            tabPrio = tagPtr->priority;
609
        }
610
        if ((tagPtr->underlineString != NULL)
611
                && (tagPtr->priority > underlinePrio)) {
612
            styleValues.underline = tagPtr->underline;
613
            underlinePrio = tagPtr->priority;
614
        }
615
        if ((tagPtr->wrapMode != NULL)
616
                && (tagPtr->priority > wrapPrio)) {
617
            styleValues.wrapMode = tagPtr->wrapMode;
618
            wrapPrio = tagPtr->priority;
619
        }
620
    }
621
    if (tagPtrs != NULL) {
622
        ckfree((char *) tagPtrs);
623
    }
624
 
625
    /*
626
     * Use an existing style if there's one around that matches.
627
     */
628
 
629
    hPtr = Tcl_CreateHashEntry(&textPtr->dInfoPtr->styleTable,
630
            (char *) &styleValues, &new);
631
    if (!new) {
632
        stylePtr = (TextStyle *) Tcl_GetHashValue(hPtr);
633
        stylePtr->refCount++;
634
        return stylePtr;
635
    }
636
 
637
    /*
638
     * No existing style matched.  Make a new one.
639
     */
640
 
641
    stylePtr = (TextStyle *) ckalloc(sizeof(TextStyle));
642
    stylePtr->refCount = 1;
643
    if (styleValues.border != NULL) {
644
        gcValues.foreground = Tk_3DBorderColor(styleValues.border)->pixel;
645
        mask = GCForeground;
646
        if (styleValues.bgStipple != None) {
647
            gcValues.stipple = styleValues.bgStipple;
648
            gcValues.fill_style = FillStippled;
649
            mask |= GCStipple|GCFillStyle;
650
        }
651
        stylePtr->bgGC = Tk_GetGCColor(textPtr->tkwin, mask, &gcValues,
652
                                       Tk_3DBorderColor(styleValues.border),
653
                                       NULL);
654
    } else {
655
        stylePtr->bgGC = None;
656
    }
657
    mask = GCForeground|GCFont;
658
    gcValues.foreground = styleValues.fgColor->pixel;
659
    gcValues.font = Tk_FontId(styleValues.tkfont);
660
    if (styleValues.fgStipple != None) {
661
        gcValues.stipple = styleValues.fgStipple;
662
        gcValues.fill_style = FillStippled;
663
        mask |= GCStipple|GCFillStyle;
664
    }
665
    stylePtr->fgGC = Tk_GetGCColor(textPtr->tkwin, mask, &gcValues,
666
                                   styleValues.fgColor, NULL);
667
    stylePtr->sValuePtr = (StyleValues *)
668
            Tcl_GetHashKey(&textPtr->dInfoPtr->styleTable, hPtr);
669
    stylePtr->hPtr = hPtr;
670
    Tcl_SetHashValue(hPtr, stylePtr);
671
    return stylePtr;
672
}
673
 
674
/*
675
 *----------------------------------------------------------------------
676
 *
677
 * FreeStyle --
678
 *
679
 *      This procedure is called when a TextStyle structure is no longer
680
 *      needed.  It decrements the reference count and frees up the
681
 *      space for the style structure if the reference count is 0.
682
 *
683
 * Results:
684
 *      None.
685
 *
686
 * Side effects:
687
 *      The storage and other resources associated with the style
688
 *      are freed up if no-one's still using it.
689
 *
690
 *----------------------------------------------------------------------
691
 */
692
 
693
static void
694
FreeStyle(textPtr, stylePtr)
695
    TkText *textPtr;                    /* Information about overall widget. */
696
    register TextStyle *stylePtr;       /* Information about style to free. */
697
 
698
{
699
    stylePtr->refCount--;
700
    if (stylePtr->refCount == 0) {
701
        if (stylePtr->bgGC != None) {
702
            Tk_FreeGC(textPtr->display, stylePtr->bgGC);
703
        }
704
        Tk_FreeGC(textPtr->display, stylePtr->fgGC);
705
        Tcl_DeleteHashEntry(stylePtr->hPtr);
706
        ckfree((char *) stylePtr);
707
    }
708
}
709
 
710
/*
711
 *----------------------------------------------------------------------
712
 *
713
 * LayoutDLine --
714
 *
715
 *      This procedure generates a single DLine structure for a display
716
 *      line whose leftmost character is given by indexPtr.
717
 *
718
 * Results:
719
 *      The return value is a pointer to a DLine structure desribing the
720
 *      display line.  All fields are filled in and correct except for
721
 *      y and nextPtr.
722
 *
723
 * Side effects:
724
 *      Storage is allocated for the new DLine.
725
 *
726
 *----------------------------------------------------------------------
727
 */
728
 
729
static DLine *
730
LayoutDLine(textPtr, indexPtr)
731
    TkText *textPtr;            /* Overall information about text widget. */
732
    TkTextIndex *indexPtr;      /* Beginning of display line.  May not
733
                                 * necessarily point to a character segment. */
734
{
735
    register DLine *dlPtr;              /* New display line. */
736
    TkTextSegment *segPtr;              /* Current segment in text. */
737
    TkTextDispChunk *lastChunkPtr;      /* Last chunk allocated so far
738
                                         * for line. */
739
    TkTextDispChunk *chunkPtr;          /* Current chunk. */
740
    TkTextIndex curIndex;
741
    TkTextDispChunk *breakChunkPtr;     /* Chunk containing best word break
742
                                         * point, if any. */
743
    TkTextIndex breakIndex;             /* Index of first character in
744
                                         * breakChunkPtr. */
745
    int breakCharOffset;                /* Character within breakChunkPtr just
746
                                         * to right of best break point. */
747
    int noCharsYet;                     /* Non-zero means that no characters
748
                                         * have been placed on the line yet. */
749
    int justify;                        /* How to justify line: taken from
750
                                         * style for first character in line. */
751
    int jIndent;                        /* Additional indentation (beyond
752
                                         * margins) due to justification. */
753
    int rMargin;                        /* Right margin width for line. */
754
    Tk_Uid wrapMode;                    /* Wrap mode to use for this line. */
755
    int x = 0, maxX = 0;          /* Initializations needed only to
756
                                         * stop compiler warnings. */
757
    int wholeLine;                      /* Non-zero means this display line
758
                                         * runs to the end of the text line. */
759
    int tabIndex;                       /* Index of the current tab stop. */
760
    int gotTab;                         /* Non-zero means the current chunk
761
                                         * contains a tab. */
762
    TkTextDispChunk *tabChunkPtr;       /* Pointer to the chunk containing
763
                                         * the previous tab stop. */
764
    int maxChars;                       /* Maximum number of characters to
765
                                         * include in this chunk. */
766
    TkTextTabArray *tabArrayPtr;        /* Tab stops for line;  taken from
767
                                         * style for first character on line. */
768
    int tabSize;                        /* Number of pixels consumed by current
769
                                         * tab stop. */
770
    TkTextDispChunk *lastCharChunkPtr;  /* Pointer to last chunk in display
771
                                         * lines with numChars > 0.  Used to
772
                                         * drop 0-sized chunks from the end
773
                                         * of the line. */
774
    int offset, ascent, descent, code;
775
    StyleValues *sValuePtr;
776
 
777
    /*
778
     * Create and initialize a new DLine structure.
779
     */
780
 
781
    dlPtr = (DLine *) ckalloc(sizeof(DLine));
782
    dlPtr->index = *indexPtr;
783
    dlPtr->count = 0;
784
    dlPtr->y = 0;
785
    dlPtr->oldY = -1;
786
    dlPtr->height = 0;
787
    dlPtr->baseline = 0;
788
    dlPtr->chunkPtr = NULL;
789
    dlPtr->nextPtr = NULL;
790
    dlPtr->flags = NEW_LAYOUT;
791
 
792
    /*
793
     * Each iteration of the loop below creates one TkTextDispChunk for
794
     * the new display line.  The line will always have at least one
795
     * chunk (for the newline character at the end, if there's nothing
796
     * else available).
797
     */
798
 
799
    curIndex = *indexPtr;
800
    lastChunkPtr = NULL;
801
    chunkPtr = NULL;
802
    noCharsYet = 1;
803
    breakChunkPtr = NULL;
804
    breakCharOffset = 0;
805
    justify = TK_JUSTIFY_LEFT;
806
    tabIndex = -1;
807
    tabChunkPtr = NULL;
808
    tabArrayPtr = NULL;
809
    rMargin = 0;
810
    wrapMode = tkTextCharUid;
811
    tabSize = 0;
812
    lastCharChunkPtr = NULL;
813
 
814
    /*
815
     * Find the first segment to consider for the line.  Can't call
816
     * TkTextIndexToSeg for this because it won't return a segment
817
     * with zero size (such as the insertion cursor's mark).
818
     */
819
 
820
    for (offset = curIndex.charIndex, segPtr = curIndex.linePtr->segPtr;
821
            (offset > 0) && (offset >= segPtr->size);
822
            offset -= segPtr->size, segPtr = segPtr->nextPtr) {
823
        /* Empty loop body. */
824
    }
825
 
826
    while (segPtr != NULL) {
827
        if (segPtr->typePtr->layoutProc == NULL) {
828
            segPtr = segPtr->nextPtr;
829
            offset = 0;
830
            continue;
831
        }
832
        if (chunkPtr == NULL) {
833
            chunkPtr = (TkTextDispChunk *) ckalloc(sizeof(TkTextDispChunk));
834
            chunkPtr->nextPtr = NULL;
835
        }
836
        chunkPtr->stylePtr = GetStyle(textPtr, &curIndex);
837
 
838
        /*
839
         * Save style information such as justification and indentation,
840
         * up until the first character is encountered, then retain that
841
         * information for the rest of the line.
842
         */
843
 
844
        if (noCharsYet) {
845
            tabArrayPtr = chunkPtr->stylePtr->sValuePtr->tabArrayPtr;
846
            justify = chunkPtr->stylePtr->sValuePtr->justify;
847
            rMargin = chunkPtr->stylePtr->sValuePtr->rMargin;
848
            wrapMode = chunkPtr->stylePtr->sValuePtr->wrapMode;
849
            x = ((curIndex.charIndex == 0)
850
                    ? chunkPtr->stylePtr->sValuePtr->lMargin1
851
                    : chunkPtr->stylePtr->sValuePtr->lMargin2);
852
            if (wrapMode == tkTextNoneUid) {
853
                maxX = INT_MAX;
854
            } else {
855
                maxX = textPtr->dInfoPtr->maxX - textPtr->dInfoPtr->x
856
                        - rMargin;
857
                if (maxX < x) {
858
                    maxX = x;
859
                }
860
            }
861
        }
862
 
863
        /*
864
         * See if there is a tab in the current chunk; if so, only
865
         * layout characters up to (and including) the tab.
866
         */
867
 
868
        gotTab = 0;
869
        maxChars = segPtr->size - offset;
870
        if (justify == TK_JUSTIFY_LEFT) {
871
            if (segPtr->typePtr == &tkTextCharType) {
872
                char *p;
873
 
874
                for (p = segPtr->body.chars  + offset; *p != 0; p++) {
875
                    if (*p == '\t') {
876
                        maxChars = (p + 1 - segPtr->body.chars) - offset;
877
                        gotTab = 1;
878
                        break;
879
                    }
880
                }
881
            }
882
        }
883
 
884
        chunkPtr->x = x;
885
        code = (*segPtr->typePtr->layoutProc)(textPtr, &curIndex, segPtr,
886
                offset, maxX-tabSize, maxChars, noCharsYet, wrapMode,
887
                chunkPtr);
888
        if (code <= 0) {
889
            FreeStyle(textPtr, chunkPtr->stylePtr);
890
            if (code < 0) {
891
                /*
892
                 * This segment doesn't wish to display itself (e.g. most
893
                 * marks).
894
                 */
895
 
896
                segPtr = segPtr->nextPtr;
897
                offset = 0;
898
                continue;
899
            }
900
 
901
            /*
902
             * No characters from this segment fit in the window: this
903
             * means we're at the end of the display line.
904
             */
905
 
906
            if (chunkPtr != NULL) {
907
                ckfree((char *) chunkPtr);
908
            }
909
            break;
910
        }
911
        if (chunkPtr->numChars > 0) {
912
            noCharsYet = 0;
913
            lastCharChunkPtr = chunkPtr;
914
        }
915
        if (lastChunkPtr == NULL) {
916
            dlPtr->chunkPtr = chunkPtr;
917
        } else {
918
            lastChunkPtr->nextPtr = chunkPtr;
919
        }
920
        lastChunkPtr = chunkPtr;
921
        x += chunkPtr->width;
922
        if (chunkPtr->breakIndex > 0) {
923
            breakCharOffset = chunkPtr->breakIndex;
924
            breakIndex = curIndex;
925
            breakChunkPtr = chunkPtr;
926
        }
927
        if (chunkPtr->numChars != maxChars) {
928
            break;
929
        }
930
 
931
        /*
932
         * If we're at a new tab, adjust the layout for all the chunks
933
         * pertaining to the previous tab.  Also adjust the amount of
934
         * space left in the line to account for space that will be eaten
935
         * up by the tab.
936
         */
937
 
938
        if (gotTab) {
939
            if (tabIndex >= 0) {
940
                AdjustForTab(textPtr, tabArrayPtr, tabIndex, tabChunkPtr);
941
                x = chunkPtr->x + chunkPtr->width;
942
            }
943
            tabIndex++;
944
            tabChunkPtr = chunkPtr;
945
            tabSize = SizeOfTab(textPtr, tabArrayPtr, tabIndex, x, maxX);
946
            if (tabSize >= (maxX - x)) {
947
                break;
948
            }
949
        }
950
        curIndex.charIndex += chunkPtr->numChars;
951
        offset += chunkPtr->numChars;
952
        if (offset >= segPtr->size) {
953
            offset = 0;
954
            segPtr = segPtr->nextPtr;
955
        }
956
        chunkPtr = NULL;
957
    }
958
    if (noCharsYet) {
959
        panic("LayoutDLine couldn't place any characters on a line");
960
    }
961
    wholeLine = (segPtr == NULL);
962
 
963
    /*
964
     * We're at the end of the display line.  Throw away everything
965
     * after the most recent word break, if there is one;  this may
966
     * potentially require the last chunk to be layed out again.
967
     */
968
 
969
    if (breakChunkPtr == NULL) {
970
        /*
971
         * This code makes sure that we don't accidentally display
972
         * chunks with no characters at the end of the line (such as
973
         * the insertion cursor).  These chunks belong on the next
974
         * line.  So, throw away everything after the last chunk that
975
         * has characters in it.
976
         */
977
 
978
        breakChunkPtr = lastCharChunkPtr;
979
        breakCharOffset = breakChunkPtr->numChars;
980
    }
981
    if ((breakChunkPtr != NULL) && ((lastChunkPtr != breakChunkPtr)
982
            || (breakCharOffset != lastChunkPtr->numChars))) {
983
        while (1) {
984
            chunkPtr = breakChunkPtr->nextPtr;
985
            if (chunkPtr == NULL) {
986
                break;
987
            }
988
            FreeStyle(textPtr, chunkPtr->stylePtr);
989
            breakChunkPtr->nextPtr = chunkPtr->nextPtr;
990
            (*chunkPtr->undisplayProc)(textPtr, chunkPtr);
991
            ckfree((char *) chunkPtr);
992
        }
993
        if (breakCharOffset != breakChunkPtr->numChars) {
994
            (*breakChunkPtr->undisplayProc)(textPtr, breakChunkPtr);
995
            segPtr = TkTextIndexToSeg(&breakIndex, &offset);
996
            (*segPtr->typePtr->layoutProc)(textPtr, &breakIndex,
997
                    segPtr, offset, maxX, breakCharOffset, 0,
998
                    wrapMode, breakChunkPtr);
999
        }
1000
        lastChunkPtr = breakChunkPtr;
1001
        wholeLine = 0;
1002
    }
1003
 
1004
    /*
1005
     * Make tab adjustments for the last tab stop, if there is one.
1006
     */
1007
 
1008
    if ((tabIndex >= 0) && (tabChunkPtr != NULL)) {
1009
        AdjustForTab(textPtr, tabArrayPtr, tabIndex, tabChunkPtr);
1010
    }
1011
 
1012
    /*
1013
     * Make one more pass over the line to recompute various things
1014
     * like its height, length, and total number of characters.  Also
1015
     * modify the x-locations of chunks to reflect justification.
1016
     * If we're not wrapping, I'm not sure what is the best way to
1017
     * handle left and center justification:  should the total length,
1018
     * for purposes of justification, be (a) the window width, (b)
1019
     * the length of the longest line in the window, or (c) the length
1020
     * of the longest line in the text?  (c) isn't available, (b) seems
1021
     * weird, since it can change with vertical scrolling, so (a) is
1022
     * what is implemented below.
1023
     */
1024
 
1025
    if (wrapMode == tkTextNoneUid) {
1026
        maxX = textPtr->dInfoPtr->maxX - textPtr->dInfoPtr->x - rMargin;
1027
    }
1028
    dlPtr->length = lastChunkPtr->x + lastChunkPtr->width;
1029
    if (justify == TK_JUSTIFY_LEFT) {
1030
        jIndent = 0;
1031
    } else if (justify == TK_JUSTIFY_RIGHT) {
1032
        jIndent = maxX - dlPtr->length;
1033
    } else {
1034
        jIndent = (maxX - dlPtr->length)/2;
1035
    }
1036
    ascent = descent = 0;
1037
    for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL;
1038
            chunkPtr = chunkPtr->nextPtr) {
1039
        chunkPtr->x += jIndent;
1040
        dlPtr->count += chunkPtr->numChars;
1041
        if (chunkPtr->minAscent > ascent) {
1042
            ascent = chunkPtr->minAscent;
1043
        }
1044
        if (chunkPtr->minDescent > descent) {
1045
            descent = chunkPtr->minDescent;
1046
        }
1047
        if (chunkPtr->minHeight > dlPtr->height) {
1048
            dlPtr->height = chunkPtr->minHeight;
1049
        }
1050
        sValuePtr = chunkPtr->stylePtr->sValuePtr;
1051
        if ((sValuePtr->borderWidth > 0)
1052
                && (sValuePtr->relief != TK_RELIEF_FLAT)) {
1053
            dlPtr->flags |= HAS_3D_BORDER;
1054
        }
1055
    }
1056
    if (dlPtr->height < (ascent + descent)) {
1057
        dlPtr->height = ascent + descent;
1058
        dlPtr->baseline = ascent;
1059
    } else {
1060
        dlPtr->baseline = ascent + (dlPtr->height - ascent - descent)/2;
1061
    }
1062
    sValuePtr = dlPtr->chunkPtr->stylePtr->sValuePtr;
1063
    if (dlPtr->index.charIndex == 0) {
1064
        dlPtr->spaceAbove = sValuePtr->spacing1;
1065
    } else {
1066
        dlPtr->spaceAbove = sValuePtr->spacing2 - sValuePtr->spacing2/2;
1067
    }
1068
    if (wholeLine) {
1069
        dlPtr->spaceBelow = sValuePtr->spacing3;
1070
    } else {
1071
        dlPtr->spaceBelow = sValuePtr->spacing2/2;
1072
    }
1073
    dlPtr->height += dlPtr->spaceAbove + dlPtr->spaceBelow;
1074
    dlPtr->baseline += dlPtr->spaceAbove;
1075
 
1076
    /*
1077
     * Recompute line length:  may have changed because of justification.
1078
     */
1079
 
1080
    dlPtr->length = lastChunkPtr->x + lastChunkPtr->width;
1081
    return dlPtr;
1082
}
1083
 
1084
/*
1085
 *----------------------------------------------------------------------
1086
 *
1087
 * UpdateDisplayInfo --
1088
 *
1089
 *      This procedure is invoked to recompute some or all of the
1090
 *      DLine structures for a text widget.  At the time it is called
1091
 *      the DLine structures still left in the widget are guaranteed
1092
 *      to be correct except that (a) the y-coordinates aren't
1093
 *      necessarily correct, (b) there may be missing structures
1094
 *      (the DLine structures get removed as soon as they are potentially
1095
 *      out-of-date), and (c) DLine structures that don't start at the
1096
 *      beginning of a line may be incorrect if previous information in
1097
 *      the same line changed size in a way that moved a line boundary
1098
 *      (DLines for any info that changed will have been deleted, but
1099
 *      not DLines for unchanged info in the same text line).
1100
 *
1101
 * Results:
1102
 *      None.
1103
 *
1104
 * Side effects:
1105
 *      Upon return, the DLine information for textPtr correctly reflects
1106
 *      the positions where characters will be displayed.  However, this
1107
 *      procedure doesn't actually bring the display up-to-date.
1108
 *
1109
 *----------------------------------------------------------------------
1110
 */
1111
 
1112
static void
1113
UpdateDisplayInfo(textPtr)
1114
    TkText *textPtr;                    /* Text widget to update. */
1115
{
1116
    register TextDInfo *dInfoPtr = textPtr->dInfoPtr;
1117
    register DLine *dlPtr, *prevPtr;
1118
    TkTextIndex index;
1119
    TkTextLine *lastLinePtr;
1120
    int y, maxY, pixelOffset, maxOffset;
1121
 
1122
    if (!(dInfoPtr->flags & DINFO_OUT_OF_DATE)) {
1123
        return;
1124
    }
1125
    dInfoPtr->flags &= ~DINFO_OUT_OF_DATE;
1126
 
1127
    /*
1128
     * Delete any DLines that are now above the top of the window.
1129
     */
1130
 
1131
    index = textPtr->topIndex;
1132
    dlPtr = FindDLine(dInfoPtr->dLinePtr, &index);
1133
    if ((dlPtr != NULL) && (dlPtr != dInfoPtr->dLinePtr)) {
1134
        FreeDLines(textPtr, dInfoPtr->dLinePtr, dlPtr, 1);
1135
    }
1136
 
1137
    /*
1138
     *--------------------------------------------------------------
1139
     * Scan through the contents of the window from top to bottom,
1140
     * recomputing information for lines that are missing.
1141
     *--------------------------------------------------------------
1142
     */
1143
 
1144
    lastLinePtr = TkBTreeFindLine(textPtr->tree,
1145
            TkBTreeNumLines(textPtr->tree));
1146
    dlPtr = dInfoPtr->dLinePtr;
1147
    prevPtr = NULL;
1148
    y = dInfoPtr->y;
1149
    maxY = dInfoPtr->maxY;
1150
    while (1) {
1151
        register DLine *newPtr;
1152
 
1153
        if (index.linePtr == lastLinePtr) {
1154
            break;
1155
        }
1156
 
1157
        /*
1158
         * There are three possibilities right now:
1159
         * (a) the next DLine (dlPtr) corresponds exactly to the next
1160
         *     information we want to display: just use it as-is.
1161
         * (b) the next DLine corresponds to a different line, or to
1162
         *     a segment that will be coming later in the same line:
1163
         *     leave this DLine alone in the hopes that we'll be able
1164
         *     to use it later, then create a new DLine in front of
1165
         *     it.
1166
         * (c) the next DLine corresponds to a segment in the line we
1167
         *     want, but it's a segment that has already been processed
1168
         *     or will never be processed.  Delete the DLine and try
1169
         *     again.
1170
         *
1171
         * One other twist on all this.  It's possible for 3D borders
1172
         * to interact between lines (see DisplayLineBackground) so if
1173
         * a line is relayed out and has styles with 3D borders, its
1174
         * neighbors have to be redrawn if they have 3D borders too,
1175
         * since the interactions could have changed (the neighbors
1176
         * don't have to be relayed out, just redrawn).
1177
         */
1178
 
1179
        if ((dlPtr == NULL) || (dlPtr->index.linePtr != index.linePtr)) {
1180
            /*
1181
             * Case (b) -- must make new DLine.
1182
             */
1183
 
1184
            makeNewDLine:
1185
            if (tkTextDebug) {
1186
                char string[TK_POS_CHARS];
1187
 
1188
                /*
1189
                 * Debugging is enabled, so keep a log of all the lines
1190
                 * that were re-layed out.  The test suite uses this
1191
                 * information.
1192
                 */
1193
 
1194
                TkTextPrintIndex(&index, string);
1195
                Tcl_SetVar2(textPtr->interp, "tk_textRelayout", (char *) NULL,
1196
                        string,
1197
                        TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
1198
            }
1199
            newPtr = LayoutDLine(textPtr, &index);
1200
            if (prevPtr == NULL) {
1201
                dInfoPtr->dLinePtr = newPtr;
1202
            } else {
1203
                prevPtr->nextPtr = newPtr;
1204
                if (prevPtr->flags & HAS_3D_BORDER) {
1205
                    prevPtr->oldY = -1;
1206
                }
1207
            }
1208
            newPtr->nextPtr = dlPtr;
1209
            dlPtr = newPtr;
1210
        } else {
1211
            /*
1212
             * DlPtr refers to the line we want.  Next check the
1213
             * index within the line.
1214
             */
1215
 
1216
            if (index.charIndex == dlPtr->index.charIndex) {
1217
                /*
1218
                 * Case (a) -- can use existing display line as-is.
1219
                 */
1220
 
1221
                if ((dlPtr->flags & HAS_3D_BORDER) && (prevPtr != NULL)
1222
                        && (prevPtr->flags & (NEW_LAYOUT))) {
1223
                    dlPtr->oldY = -1;
1224
                }
1225
                goto lineOK;
1226
            }
1227
            if (index.charIndex < dlPtr->index.charIndex) {
1228
                goto makeNewDLine;
1229
            }
1230
 
1231
            /*
1232
             * Case (c) -- dlPtr is useless.  Discard it and start
1233
             * again with the next display line.
1234
             */
1235
 
1236
            newPtr = dlPtr->nextPtr;
1237
            FreeDLines(textPtr, dlPtr, newPtr, 0);
1238
            dlPtr = newPtr;
1239
            if (prevPtr != NULL) {
1240
                prevPtr->nextPtr = newPtr;
1241
            } else {
1242
                dInfoPtr->dLinePtr = newPtr;
1243
            }
1244
            continue;
1245
        }
1246
 
1247
        /*
1248
         * Advance to the start of the next line.
1249
         */
1250
 
1251
        lineOK:
1252
        dlPtr->y = y;
1253
        y += dlPtr->height;
1254
        TkTextIndexForwChars(&index, dlPtr->count, &index);
1255
        prevPtr = dlPtr;
1256
        dlPtr = dlPtr->nextPtr;
1257
 
1258
        /*
1259
         * If we switched text lines, delete any DLines left for the
1260
         * old text line.
1261
         */
1262
 
1263
        if (index.linePtr != prevPtr->index.linePtr) {
1264
            register DLine *nextPtr;
1265
 
1266
            nextPtr = dlPtr;
1267
            while ((nextPtr != NULL)
1268
                    && (nextPtr->index.linePtr == prevPtr->index.linePtr)) {
1269
                nextPtr = nextPtr->nextPtr;
1270
            }
1271
            if (nextPtr != dlPtr) {
1272
                FreeDLines(textPtr, dlPtr, nextPtr, 0);
1273
                prevPtr->nextPtr = nextPtr;
1274
                dlPtr = nextPtr;
1275
            }
1276
        }
1277
 
1278
        /*
1279
         * It's important to have the following check here rather than in
1280
         * the while statement for the loop, so that there's always at least
1281
         * one DLine generated, regardless of how small the window is.  This
1282
         * keeps a lot of other code from breaking.
1283
         */
1284
 
1285
        if (y >= maxY) {
1286
            break;
1287
        }
1288
    }
1289
 
1290
    /*
1291
     * Delete any DLine structures that don't fit on the screen.
1292
     */
1293
 
1294
    FreeDLines(textPtr, dlPtr, (DLine *) NULL, 1);
1295
 
1296
    /*
1297
     *--------------------------------------------------------------
1298
     * If there is extra space at the bottom of the window (because
1299
     * we've hit the end of the text), then bring in more lines at
1300
     * the top of the window, if there are any, to fill in the view.
1301
     *--------------------------------------------------------------
1302
     */
1303
 
1304
    if (y < maxY) {
1305
        int lineNum, spaceLeft, charsToCount;
1306
        DLine *lowestPtr;
1307
 
1308
        /*
1309
         * Layout an entire text line (potentially > 1 display line),
1310
         * then link in as many display lines as fit without moving
1311
         * the bottom line out of the window.  Repeat this until
1312
         * all the extra space has been used up or we've reached the
1313
         * beginning of the text.
1314
         */
1315
 
1316
        spaceLeft = maxY - y;
1317
        lineNum = TkBTreeLineIndex(dInfoPtr->dLinePtr->index.linePtr);
1318
        charsToCount = dInfoPtr->dLinePtr->index.charIndex;
1319
        if (charsToCount == 0) {
1320
            charsToCount = INT_MAX;
1321
            lineNum--;
1322
        }
1323
        for ( ; (lineNum >= 0) && (spaceLeft > 0); lineNum--) {
1324
            index.linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
1325
            index.charIndex = 0;
1326
            lowestPtr = NULL;
1327
            do {
1328
                dlPtr = LayoutDLine(textPtr, &index);
1329
                dlPtr->nextPtr = lowestPtr;
1330
                lowestPtr = dlPtr;
1331
                TkTextIndexForwChars(&index, dlPtr->count, &index);
1332
                charsToCount -= dlPtr->count;
1333
            } while ((charsToCount > 0)
1334
                    && (index.linePtr == lowestPtr->index.linePtr));
1335
 
1336
            /*
1337
             * Scan through the display lines from the bottom one up to
1338
             * the top one.
1339
             */
1340
 
1341
            while (lowestPtr != NULL) {
1342
                dlPtr = lowestPtr;
1343
                spaceLeft -= dlPtr->height;
1344
                if (spaceLeft < 0) {
1345
                    break;
1346
                }
1347
                lowestPtr = dlPtr->nextPtr;
1348
                dlPtr->nextPtr = dInfoPtr->dLinePtr;
1349
                dInfoPtr->dLinePtr = dlPtr;
1350
                if (tkTextDebug) {
1351
                    char string[TK_POS_CHARS];
1352
 
1353
                    TkTextPrintIndex(&dlPtr->index, string);
1354
                    Tcl_SetVar2(textPtr->interp, "tk_textRelayout",
1355
                            (char *) NULL, string,
1356
                            TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
1357
                }
1358
            }
1359
            FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
1360
            charsToCount = INT_MAX;
1361
        }
1362
 
1363
        /*
1364
         * Now we're all done except that the y-coordinates in all the
1365
         * DLines are wrong and the top index for the text is wrong.
1366
         * Update them.
1367
         */
1368
 
1369
        textPtr->topIndex = dInfoPtr->dLinePtr->index;
1370
        y = dInfoPtr->y;
1371
        for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
1372
                dlPtr = dlPtr->nextPtr) {
1373
            if (y > dInfoPtr->maxY) {
1374
                panic("Added too many new lines in UpdateDisplayInfo");
1375
            }
1376
            dlPtr->y = y;
1377
            y += dlPtr->height;
1378
        }
1379
    }
1380
 
1381
    /*
1382
     *--------------------------------------------------------------
1383
     * If the old top or bottom line has scrolled elsewhere on the
1384
     * screen, we may not be able to re-use its old contents by
1385
     * copying bits (e.g., a beveled edge that was drawn when it was
1386
     * at the top or bottom won't be drawn when the line is in the
1387
     * middle and its neighbor has a matching background).  Similarly,
1388
     * if the new top or bottom line came from somewhere else on the
1389
     * screen, we may not be able to copy the old bits.
1390
     *--------------------------------------------------------------
1391
     */
1392
 
1393
    dlPtr = dInfoPtr->dLinePtr;
1394
    if ((dlPtr->flags & HAS_3D_BORDER) && !(dlPtr->flags & TOP_LINE)) {
1395
        dlPtr->oldY = -1;
1396
    }
1397
    while (1) {
1398
        if ((dlPtr->flags & TOP_LINE) && (dlPtr != dInfoPtr->dLinePtr)
1399
                && (dlPtr->flags & HAS_3D_BORDER)) {
1400
            dlPtr->oldY = -1;
1401
        }
1402
        if ((dlPtr->flags & BOTTOM_LINE) && (dlPtr->nextPtr != NULL)
1403
                && (dlPtr->flags & HAS_3D_BORDER)) {
1404
            dlPtr->oldY = -1;
1405
        }
1406
        if (dlPtr->nextPtr == NULL) {
1407
            if ((dlPtr->flags & HAS_3D_BORDER)
1408
                    && !(dlPtr->flags & BOTTOM_LINE)) {
1409
                dlPtr->oldY = -1;
1410
            }
1411
            dlPtr->flags &= ~TOP_LINE;
1412
            dlPtr->flags |= BOTTOM_LINE;
1413
            break;
1414
        }
1415
        dlPtr->flags &= ~(TOP_LINE|BOTTOM_LINE);
1416
        dlPtr = dlPtr->nextPtr;
1417
    }
1418
    dInfoPtr->dLinePtr->flags |= TOP_LINE;
1419
 
1420
    /*
1421
     * Arrange for scrollbars to be updated.
1422
     */
1423
 
1424
    textPtr->flags |= UPDATE_SCROLLBARS;
1425
 
1426
    /*
1427
     *--------------------------------------------------------------
1428
     * Deal with horizontal scrolling:
1429
     * 1. If there's empty space to the right of the longest line,
1430
     *    shift the screen to the right to fill in the empty space.
1431
     * 2. If the desired horizontal scroll position has changed,
1432
     *    force a full redisplay of all the lines in the widget.
1433
     * 3. If the wrap mode isn't "none" then re-scroll to the base
1434
     *    position.
1435
     *--------------------------------------------------------------
1436
     */
1437
 
1438
    dInfoPtr->maxLength = 0;
1439
    for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
1440
            dlPtr = dlPtr->nextPtr) {
1441
        if (dlPtr->length > dInfoPtr->maxLength) {
1442
            dInfoPtr->maxLength = dlPtr->length;
1443
        }
1444
    }
1445
    maxOffset = (dInfoPtr->maxLength - (dInfoPtr->maxX - dInfoPtr->x)
1446
            + textPtr->charWidth - 1)/textPtr->charWidth;
1447
    if (dInfoPtr->newCharOffset > maxOffset) {
1448
        dInfoPtr->newCharOffset = maxOffset;
1449
    }
1450
    if (dInfoPtr->newCharOffset < 0) {
1451
        dInfoPtr->newCharOffset = 0;
1452
    }
1453
    pixelOffset = dInfoPtr->newCharOffset * textPtr->charWidth;
1454
    if (pixelOffset != dInfoPtr->curPixelOffset) {
1455
        dInfoPtr->curPixelOffset = pixelOffset;
1456
        for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
1457
                dlPtr = dlPtr->nextPtr) {
1458
            dlPtr->oldY = -1;
1459
        }
1460
    }
1461
}
1462
 
1463
/*
1464
 *----------------------------------------------------------------------
1465
 *
1466
 * FreeDLines --
1467
 *
1468
 *      This procedure is called to free up all of the resources
1469
 *      associated with one or more DLine structures.
1470
 *
1471
 * Results:
1472
 *      None.
1473
 *
1474
 * Side effects:
1475
 *      Memory gets freed and various other resources are released.
1476
 *
1477
 *----------------------------------------------------------------------
1478
 */
1479
 
1480
static void
1481
FreeDLines(textPtr, firstPtr, lastPtr, unlink)
1482
    TkText *textPtr;                    /* Information about overall text
1483
                                         * widget. */
1484
    register DLine *firstPtr;           /* Pointer to first DLine to free up. */
1485
    DLine *lastPtr;                     /* Pointer to DLine just after last
1486
                                         * one to free (NULL means everything
1487
                                         * starting with firstPtr). */
1488
    int unlink;                         /* 1 means DLines are currently linked
1489
                                         * into the list rooted at
1490
                                         * textPtr->dInfoPtr->dLinePtr and
1491
                                         * they have to be unlinked.  0 means
1492
                                         * just free without unlinking. */
1493
{
1494
    register TkTextDispChunk *chunkPtr, *nextChunkPtr;
1495
    register DLine *nextDLinePtr;
1496
 
1497
    if (unlink) {
1498
        if (textPtr->dInfoPtr->dLinePtr == firstPtr) {
1499
            textPtr->dInfoPtr->dLinePtr = lastPtr;
1500
        } else {
1501
            register DLine *prevPtr;
1502
            for (prevPtr = textPtr->dInfoPtr->dLinePtr;
1503
                    prevPtr->nextPtr != firstPtr; prevPtr = prevPtr->nextPtr) {
1504
                /* Empty loop body. */
1505
            }
1506
            prevPtr->nextPtr = lastPtr;
1507
        }
1508
    }
1509
    while (firstPtr != lastPtr) {
1510
        nextDLinePtr = firstPtr->nextPtr;
1511
        for (chunkPtr = firstPtr->chunkPtr; chunkPtr != NULL;
1512
                chunkPtr = nextChunkPtr) {
1513
            if (chunkPtr->undisplayProc != NULL) {
1514
                (*chunkPtr->undisplayProc)(textPtr, chunkPtr);
1515
            }
1516
            FreeStyle(textPtr, chunkPtr->stylePtr);
1517
            nextChunkPtr = chunkPtr->nextPtr;
1518
            ckfree((char *) chunkPtr);
1519
        }
1520
        ckfree((char *) firstPtr);
1521
        firstPtr = nextDLinePtr;
1522
    }
1523
    textPtr->dInfoPtr->dLinesInvalidated = 1;
1524
}
1525
 
1526
/*
1527
 *----------------------------------------------------------------------
1528
 *
1529
 * DisplayDLine --
1530
 *
1531
 *      This procedure is invoked to draw a single line on the
1532
 *      screen.
1533
 *
1534
 * Results:
1535
 *      None.
1536
 *
1537
 * Side effects:
1538
 *      The line given by dlPtr is drawn at its correct position in
1539
 *      textPtr's window.  Note that this is one *display* line, not
1540
 *      one *text* line.
1541
 *
1542
 *----------------------------------------------------------------------
1543
 */
1544
 
1545
static void
1546
DisplayDLine(textPtr, dlPtr, prevPtr, pixmap)
1547
    TkText *textPtr;            /* Text widget in which to draw line. */
1548
    register DLine *dlPtr;      /* Information about line to draw. */
1549
    DLine *prevPtr;             /* Line just before one to draw, or NULL
1550
                                 * if dlPtr is the top line. */
1551
    Pixmap pixmap;              /* Pixmap to use for double-buffering.
1552
                                 * Caller must make sure it's large enough
1553
                                 * to hold line. */
1554
{
1555
    register TkTextDispChunk *chunkPtr;
1556
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
1557
    Display *display;
1558
    int height, x;
1559
 
1560
    /*
1561
     * First, clear the area of the line to the background color for the
1562
     * text widget.
1563
     */
1564
 
1565
    display = Tk_Display(textPtr->tkwin);
1566
    Tk_Fill3DRectangle(textPtr->tkwin, pixmap, textPtr->border, 0, 0,
1567
            Tk_Width(textPtr->tkwin), dlPtr->height, 0, TK_RELIEF_FLAT);
1568
 
1569
    /*
1570
     * Next, draw background information for the whole line.
1571
     */
1572
 
1573
    DisplayLineBackground(textPtr, dlPtr, prevPtr, pixmap);
1574
 
1575
    /*
1576
     * Make another pass through all of the chunks to redraw the
1577
     * insertion cursor, if it is visible on this line.  Must do
1578
     * it here rather than in the foreground pass below because
1579
     * otherwise a wide insertion cursor will obscure the character
1580
     * to its left.
1581
     */
1582
 
1583
#ifndef __WIN32__
1584
    /* CYGNUS LOCAL: On Windows, display the cursor even for disabled
1585
       text widgets.  */
1586
    if (textPtr->state == tkNormalUid) {
1587
#endif /* __WIN32__ */
1588
        for (chunkPtr = dlPtr->chunkPtr; (chunkPtr != NULL);
1589
                chunkPtr = chunkPtr->nextPtr) {
1590
            x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curPixelOffset;
1591
            if (chunkPtr->displayProc == TkTextInsertDisplayProc) {
1592
                (*chunkPtr->displayProc)(chunkPtr, x, dlPtr->spaceAbove,
1593
                        dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
1594
                        dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
1595
                        dlPtr->y + dlPtr->spaceAbove);
1596
            }
1597
        }
1598
#ifndef __WIN32__
1599
    }
1600
#endif /* __WIN32__ */
1601
 
1602
    /*
1603
     * Make yet another pass through all of the chunks to redraw all of
1604
     * foreground information.  Note:  we have to call the displayProc
1605
     * even for chunks that are off-screen.  This is needed, for
1606
     * example, so that embedded windows can be unmapped in this case.
1607
     * Conve
1608
     */
1609
 
1610
    for (chunkPtr = dlPtr->chunkPtr; (chunkPtr != NULL);
1611
            chunkPtr = chunkPtr->nextPtr) {
1612
        if (chunkPtr->displayProc == TkTextInsertDisplayProc) {
1613
            /*
1614
             * Already displayed the insertion cursor above.  Don't
1615
             * do it again here.
1616
             */
1617
 
1618
            continue;
1619
        }
1620
        x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curPixelOffset;
1621
        if ((x + chunkPtr->width <= 0) || (x >= dInfoPtr->maxX)) {
1622
            /*
1623
             * Note:  we have to call the displayProc even for chunks
1624
             * that are off-screen.  This is needed, for example, so
1625
             * that embedded windows can be unmapped in this case.
1626
             * Display the chunk at a coordinate that can be clearly
1627
             * identified by the displayProc as being off-screen to
1628
             * the left (the displayProc may not be able to tell if
1629
             * something is off to the right).
1630
             */
1631
 
1632
            (*chunkPtr->displayProc)(chunkPtr, -chunkPtr->width,
1633
                    dlPtr->spaceAbove,
1634
                    dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
1635
                    dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
1636
                    dlPtr->y + dlPtr->spaceAbove);
1637
        } else {
1638
            (*chunkPtr->displayProc)(chunkPtr, x, dlPtr->spaceAbove,
1639
                    dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
1640
                    dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
1641
                    dlPtr->y + dlPtr->spaceAbove);
1642
        }
1643
        if (dInfoPtr->dLinesInvalidated) {
1644
            return;
1645
        }
1646
    }
1647
 
1648
    /*
1649
     * Copy the pixmap onto the screen.  If this is the last line on
1650
     * the screen then copy a piece of the line, so that it doesn't
1651
     * overflow into the border area.  Another special trick:  copy the
1652
     * padding area to the left of the line;  this is because the
1653
     * insertion cursor sometimes overflows onto that area and we want
1654
     * to get as much of the cursor as possible.
1655
     */
1656
 
1657
    height = dlPtr->height;
1658
    if ((height + dlPtr->y) > dInfoPtr->maxY) {
1659
        height = dInfoPtr->maxY - dlPtr->y;
1660
    }
1661
    XCopyArea(display, pixmap, Tk_WindowId(textPtr->tkwin), dInfoPtr->copyGC,
1662
            dInfoPtr->x, 0, (unsigned) (dInfoPtr->maxX - dInfoPtr->x),
1663
            (unsigned) height, dInfoPtr->x, dlPtr->y);
1664
    linesRedrawn++;
1665
}
1666
 
1667
/*
1668
 *--------------------------------------------------------------
1669
 *
1670
 * DisplayLineBackground --
1671
 *
1672
 *      This procedure is called to fill in the background for
1673
 *      a display line.  It draws 3D borders cleverly so that
1674
 *      adjacent chunks with the same style (whether on the same
1675
 *      line or different lines) have a single 3D border around
1676
 *      the whole region.
1677
 *
1678
 * Results:
1679
 *      There is no return value.  Pixmap is filled in with background
1680
 *      information for dlPtr.
1681
 *
1682
 * Side effects:
1683
 *      None.
1684
 *
1685
 *--------------------------------------------------------------
1686
 */
1687
 
1688
static void
1689
DisplayLineBackground(textPtr, dlPtr, prevPtr, pixmap)
1690
    TkText *textPtr;            /* Text widget containing line. */
1691
    register DLine *dlPtr;      /* Information about line to draw. */
1692
    DLine *prevPtr;             /* Line just above dlPtr, or NULL if dlPtr
1693
                                 * is the top-most line in the window. */
1694
    Pixmap pixmap;              /* Pixmap to use for double-buffering.
1695
                                 * Caller must make sure it's large enough
1696
                                 * to hold line.  Caller must also have
1697
                                 * filled it with the background color for
1698
                                 * the widget. */
1699
{
1700
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
1701
    TkTextDispChunk *chunkPtr;  /* Pointer to chunk in the current line. */
1702
    TkTextDispChunk *chunkPtr2; /* Pointer to chunk in the line above or
1703
                                 * below the current one.  NULL if we're to
1704
                                 * the left of or to the right of the chunks
1705
                                 * in the line. */
1706
    TkTextDispChunk *nextPtr2;  /* Next chunk after chunkPtr2 (it's not the
1707
                                 * same as chunkPtr2->nextPtr in the case
1708
                                 * where chunkPtr2 is NULL because the line
1709
                                 * is indented). */
1710
    int leftX;                  /* The left edge of the region we're
1711
                                 * currently working on. */
1712
    int leftXIn;                /* 1 means beveled edge at leftX slopes right
1713
                                 * as it goes down, 0 means it slopes left
1714
                                 * as it goes down. */
1715
    int rightX;                 /* Right edge of chunkPtr. */
1716
    int rightX2;                /* Right edge of chunkPtr2. */
1717
    int matchLeft;              /* Does the style of this line match that
1718
                                 * of its neighbor just to the left of
1719
                                 * the current x coordinate? */
1720
    int matchRight;             /* Does line's style match its neighbor
1721
                                 * just to the right of the current x-coord? */
1722
    int minX, maxX, xOffset;
1723
    StyleValues *sValuePtr;
1724
    Display *display;
1725
 
1726
    /*
1727
     * Pass 1: scan through dlPtr from left to right.  For each range of
1728
     * chunks with the same style, draw the main background for the style
1729
     * plus the vertical parts of the 3D borders (the left and right
1730
     * edges).
1731
     */
1732
 
1733
    display = Tk_Display(textPtr->tkwin);
1734
    minX = dInfoPtr->curPixelOffset;
1735
    xOffset = dInfoPtr->x - minX;
1736
    maxX = minX + dInfoPtr->maxX - dInfoPtr->x;
1737
    chunkPtr = dlPtr->chunkPtr;
1738
 
1739
    /*
1740
     * Note A: in the following statement, and a few others later in
1741
     * this file marked with "See Note A above", the right side of the
1742
     * assignment was replaced with 0 on 6/18/97.  This has the effect
1743
     * of highlighting the empty space to the left of a line whenever
1744
     * the leftmost character of the line is highlighted.  This way,
1745
     * multi-line highlights always line up along their left edges.
1746
     * However, this may look funny in the case where a single word is
1747
     * highlighted. To undo the change, replace "leftX = 0" with "leftX
1748
     * = chunkPtr->x" and "rightX2 = 0" with "rightX2 = nextPtr2->x"
1749
     * here and at all the marked points below.  This restores the old
1750
     * behavior where empty space to the left of a line is not
1751
     * highlighted, leaving a ragged left edge for multi-line
1752
     * highlights.
1753
     */
1754
 
1755
    leftX = 0;
1756
    for (; leftX < maxX; chunkPtr = chunkPtr->nextPtr) {
1757
        if ((chunkPtr->nextPtr != NULL)
1758
                && SAME_BACKGROUND(chunkPtr->nextPtr->stylePtr,
1759
                chunkPtr->stylePtr)) {
1760
            continue;
1761
        }
1762
        sValuePtr = chunkPtr->stylePtr->sValuePtr;
1763
        rightX = chunkPtr->x + chunkPtr->width;
1764
        if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
1765
            rightX = maxX;
1766
        }
1767
        if (chunkPtr->stylePtr->bgGC != None) {
1768
            XFillRectangle(display, pixmap, chunkPtr->stylePtr->bgGC,
1769
                    leftX + xOffset, 0, (unsigned int) (rightX - leftX),
1770
                    (unsigned int) dlPtr->height);
1771
            if (sValuePtr->relief != TK_RELIEF_FLAT) {
1772
                Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
1773
                        leftX + xOffset, 0, sValuePtr->borderWidth,
1774
                        dlPtr->height, 1, sValuePtr->relief);
1775
                Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
1776
                        rightX - sValuePtr->borderWidth + xOffset,
1777
                        0, sValuePtr->borderWidth, dlPtr->height, 0,
1778
                        sValuePtr->relief);
1779
            }
1780
        }
1781
        leftX = rightX;
1782
    }
1783
 
1784
    /*
1785
     * Pass 2: draw the horizontal bevels along the top of the line.  To
1786
     * do this, scan through dlPtr from left to right while simultaneously
1787
     * scanning through the line just above dlPtr.  ChunkPtr2 and nextPtr2
1788
     * refer to two adjacent chunks in the line above.
1789
     */
1790
 
1791
    chunkPtr = dlPtr->chunkPtr;
1792
    leftX = 0;                           /* See Note A above. */
1793
    leftXIn = 1;
1794
    rightX = chunkPtr->x + chunkPtr->width;
1795
    if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
1796
        rightX = maxX;
1797
    }
1798
    chunkPtr2 = NULL;
1799
    if (prevPtr != NULL) {
1800
        /*
1801
         * Find the chunk in the previous line that covers leftX.
1802
         */
1803
 
1804
        nextPtr2 = prevPtr->chunkPtr;
1805
        rightX2 = 0;                     /* See Note A above. */
1806
        while (rightX2 <= leftX) {
1807
            chunkPtr2 = nextPtr2;
1808
            if (chunkPtr2 == NULL) {
1809
                break;
1810
            }
1811
            nextPtr2 = chunkPtr2->nextPtr;
1812
            rightX2 = chunkPtr2->x + chunkPtr2->width;
1813
            if (nextPtr2 == NULL) {
1814
                rightX2 = INT_MAX;
1815
            }
1816
        }
1817
    } else {
1818
        nextPtr2 = NULL;
1819
        rightX2 = INT_MAX;
1820
    }
1821
 
1822
    while (leftX < maxX) {
1823
        matchLeft = (chunkPtr2 != NULL)
1824
                && SAME_BACKGROUND(chunkPtr2->stylePtr, chunkPtr->stylePtr);
1825
        sValuePtr = chunkPtr->stylePtr->sValuePtr;
1826
        if (rightX <= rightX2) {
1827
            /*
1828
             * The chunk in our line is about to end.  If its style
1829
             * changes then draw the bevel for the current style.
1830
             */
1831
 
1832
            if ((chunkPtr->nextPtr == NULL)
1833
                    || !SAME_BACKGROUND(chunkPtr->stylePtr,
1834
                    chunkPtr->nextPtr->stylePtr)) {
1835
                if (!matchLeft && (sValuePtr->relief != TK_RELIEF_FLAT)) {
1836
                    Tk_3DHorizontalBevel(textPtr->tkwin, pixmap,
1837
                            sValuePtr->border, leftX + xOffset, 0,
1838
                            rightX - leftX, sValuePtr->borderWidth, leftXIn,
1839
                            1, 1, sValuePtr->relief);
1840
                }
1841
                leftX = rightX;
1842
                leftXIn = 1;
1843
 
1844
                /*
1845
                 * If the chunk in the line above is also ending at
1846
                 * the same point then advance to the next chunk in
1847
                 * that line.
1848
                 */
1849
 
1850
                if ((rightX == rightX2) && (chunkPtr2 != NULL)) {
1851
                    goto nextChunk2;
1852
                }
1853
            }
1854
            chunkPtr = chunkPtr->nextPtr;
1855
            if (chunkPtr == NULL) {
1856
                break;
1857
            }
1858
            rightX = chunkPtr->x + chunkPtr->width;
1859
            if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
1860
                rightX = maxX;
1861
            }
1862
            continue;
1863
        }
1864
 
1865
        /*
1866
         * The chunk in the line above is ending at an x-position where
1867
         * there is no change in the style of the current line.  If the
1868
         * style above matches the current line on one side of the change
1869
         * but not on the other, we have to draw an L-shaped piece of
1870
         * bevel.
1871
         */
1872
 
1873
        matchRight = (nextPtr2 != NULL)
1874
                && SAME_BACKGROUND(nextPtr2->stylePtr, chunkPtr->stylePtr);
1875
        if (matchLeft && !matchRight) {
1876
            if (sValuePtr->relief != TK_RELIEF_FLAT) {
1877
                Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
1878
                        rightX2 - sValuePtr->borderWidth + xOffset, 0,
1879
                        sValuePtr->borderWidth, sValuePtr->borderWidth, 0,
1880
                        sValuePtr->relief);
1881
            }
1882
            leftX = rightX2 - sValuePtr->borderWidth;
1883
            leftXIn = 0;
1884
        } else if (!matchLeft && matchRight
1885
                && (sValuePtr->relief != TK_RELIEF_FLAT)) {
1886
            Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
1887
                    rightX2 + xOffset, 0, sValuePtr->borderWidth,
1888
                    sValuePtr->borderWidth, 1, sValuePtr->relief);
1889
            Tk_3DHorizontalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
1890
                    leftX + xOffset, 0, rightX2 + sValuePtr->borderWidth -leftX,
1891
                    sValuePtr->borderWidth, leftXIn, 0, 1,
1892
                    sValuePtr->relief);
1893
        }
1894
 
1895
        nextChunk2:
1896
        chunkPtr2 = nextPtr2;
1897
        if (chunkPtr2 == NULL) {
1898
            rightX2 = INT_MAX;
1899
        } else {
1900
            nextPtr2 = chunkPtr2->nextPtr;
1901
            rightX2 = chunkPtr2->x + chunkPtr2->width;
1902
            if (nextPtr2 == NULL) {
1903
                rightX2 = INT_MAX;
1904
            }
1905
        }
1906
    }
1907
    /*
1908
     * Pass 3: draw the horizontal bevels along the bottom of the line.
1909
     * This uses the same approach as pass 2.
1910
     */
1911
 
1912
    chunkPtr = dlPtr->chunkPtr;
1913
    leftX = 0;                           /* See Note A above. */
1914
    leftXIn = 0;
1915
    rightX = chunkPtr->x + chunkPtr->width;
1916
    if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
1917
        rightX = maxX;
1918
    }
1919
    chunkPtr2 = NULL;
1920
    if (dlPtr->nextPtr != NULL) {
1921
        /*
1922
         * Find the chunk in the previous line that covers leftX.
1923
         */
1924
 
1925
        nextPtr2 = dlPtr->nextPtr->chunkPtr;
1926
        rightX2 = 0;                     /* See Note A above. */
1927
        while (rightX2 <= leftX) {
1928
            chunkPtr2 = nextPtr2;
1929
            if (chunkPtr2 == NULL) {
1930
                break;
1931
            }
1932
            nextPtr2 = chunkPtr2->nextPtr;
1933
            rightX2 = chunkPtr2->x + chunkPtr2->width;
1934
            if (nextPtr2 == NULL) {
1935
                rightX2 = INT_MAX;
1936
            }
1937
        }
1938
    } else {
1939
        nextPtr2 = NULL;
1940
        rightX2 = INT_MAX;
1941
    }
1942
 
1943
    while (leftX < maxX) {
1944
        matchLeft = (chunkPtr2 != NULL)
1945
                && SAME_BACKGROUND(chunkPtr2->stylePtr, chunkPtr->stylePtr);
1946
        sValuePtr = chunkPtr->stylePtr->sValuePtr;
1947
        if (rightX <= rightX2) {
1948
            if ((chunkPtr->nextPtr == NULL)
1949
                    || !SAME_BACKGROUND(chunkPtr->stylePtr,
1950
                    chunkPtr->nextPtr->stylePtr)) {
1951
                if (!matchLeft && (sValuePtr->relief != TK_RELIEF_FLAT)) {
1952
                    Tk_3DHorizontalBevel(textPtr->tkwin, pixmap,
1953
                            sValuePtr->border, leftX + xOffset,
1954
                            dlPtr->height - sValuePtr->borderWidth,
1955
                            rightX - leftX, sValuePtr->borderWidth, leftXIn,
1956
                            0, 0, sValuePtr->relief);
1957
                }
1958
                leftX = rightX;
1959
                leftXIn = 0;
1960
                if ((rightX == rightX2) && (chunkPtr2 != NULL)) {
1961
                    goto nextChunk2b;
1962
                }
1963
            }
1964
            chunkPtr = chunkPtr->nextPtr;
1965
            if (chunkPtr == NULL) {
1966
                break;
1967
            }
1968
            rightX = chunkPtr->x + chunkPtr->width;
1969
            if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
1970
                rightX = maxX;
1971
            }
1972
            continue;
1973
        }
1974
 
1975
        matchRight = (nextPtr2 != NULL)
1976
                && SAME_BACKGROUND(nextPtr2->stylePtr, chunkPtr->stylePtr);
1977
        if (matchLeft && !matchRight) {
1978
            if (sValuePtr->relief != TK_RELIEF_FLAT) {
1979
                Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
1980
                        rightX2 - sValuePtr->borderWidth + xOffset,
1981
                        dlPtr->height - sValuePtr->borderWidth,
1982
                        sValuePtr->borderWidth, sValuePtr->borderWidth, 0,
1983
                        sValuePtr->relief);
1984
            }
1985
            leftX = rightX2 - sValuePtr->borderWidth;
1986
            leftXIn = 1;
1987
        } else if (!matchLeft && matchRight
1988
                && (sValuePtr->relief != TK_RELIEF_FLAT)) {
1989
            Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
1990
                    rightX2 + xOffset, dlPtr->height - sValuePtr->borderWidth,
1991
                    sValuePtr->borderWidth, sValuePtr->borderWidth,
1992
                    1, sValuePtr->relief);
1993
            Tk_3DHorizontalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
1994
                    leftX + xOffset, dlPtr->height - sValuePtr->borderWidth,
1995
                    rightX2 + sValuePtr->borderWidth - leftX,
1996
                    sValuePtr->borderWidth, leftXIn, 1, 0, sValuePtr->relief);
1997
        }
1998
 
1999
        nextChunk2b:
2000
        chunkPtr2 = nextPtr2;
2001
        if (chunkPtr2 == NULL) {
2002
            rightX2 = INT_MAX;
2003
        } else {
2004
            nextPtr2 = chunkPtr2->nextPtr;
2005
            rightX2 = chunkPtr2->x + chunkPtr2->width;
2006
            if (nextPtr2 == NULL) {
2007
                rightX2 = INT_MAX;
2008
            }
2009
        }
2010
    }
2011
}
2012
 
2013
/*
2014
 *----------------------------------------------------------------------
2015
 *
2016
 * DisplayText --
2017
 *
2018
 *      This procedure is invoked as a when-idle handler to update the
2019
 *      display.  It only redisplays the parts of the text widget that
2020
 *      are out of date.
2021
 *
2022
 * Results:
2023
 *      None.
2024
 *
2025
 * Side effects:
2026
 *      Information is redrawn on the screen.
2027
 *
2028
 *----------------------------------------------------------------------
2029
 */
2030
 
2031
static void
2032
DisplayText(clientData)
2033
    ClientData clientData;      /* Information about widget. */
2034
{
2035
    register TkText *textPtr = (TkText *) clientData;
2036
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
2037
    Tk_Window tkwin;
2038
    register DLine *dlPtr;
2039
    DLine *prevPtr;
2040
    Pixmap pixmap;
2041
    int maxHeight, borders;
2042
    int bottomY = 0;             /* Initialization needed only to stop
2043
                                 * compiler warnings. */
2044
    Tcl_Interp *interp;
2045
 
2046
    if (textPtr->tkwin == NULL) {
2047
 
2048
        /*
2049
         * The widget has been deleted.  Don't do anything.
2050
         */
2051
 
2052
        return;
2053
    }
2054
 
2055
    interp = textPtr->interp;
2056
    Tcl_Preserve((ClientData) interp);
2057
 
2058
    if (tkTextDebug) {
2059
        Tcl_SetVar2(interp, "tk_textRelayout", (char *) NULL, "",
2060
                TCL_GLOBAL_ONLY);
2061
    }
2062
 
2063
    if (textPtr->tkwin == NULL) {
2064
 
2065
        /*
2066
         * The widget has been deleted.  Don't do anything.
2067
         */
2068
 
2069
        goto end;
2070
    }
2071
 
2072
    if (!Tk_IsMapped(textPtr->tkwin) || (dInfoPtr->maxX <= dInfoPtr->x)
2073
            || (dInfoPtr->maxY <= dInfoPtr->y)) {
2074
        UpdateDisplayInfo(textPtr);
2075
        dInfoPtr->flags &= ~REDRAW_PENDING;
2076
        goto doScrollbars;
2077
    }
2078
    numRedisplays++;
2079
    if (tkTextDebug) {
2080
        Tcl_SetVar2(interp, "tk_textRedraw", (char *) NULL, "",
2081
                TCL_GLOBAL_ONLY);
2082
    }
2083
 
2084
    if (textPtr->tkwin == NULL) {
2085
 
2086
        /*
2087
         * The widget has been deleted.  Don't do anything.
2088
         */
2089
 
2090
        goto end;
2091
    }
2092
 
2093
    /*
2094
     * Choose a new current item if that is needed (this could cause
2095
     * event handlers to be invoked, hence the preserve/release calls
2096
     * and the loop, since the handlers could conceivably necessitate
2097
     * yet another current item calculation).  The tkwin check is because
2098
     * the whole window could go away in the Tcl_Release call.
2099
     */
2100
 
2101
    while (dInfoPtr->flags & REPICK_NEEDED) {
2102
        Tcl_Preserve((ClientData) textPtr);
2103
        dInfoPtr->flags &= ~REPICK_NEEDED;
2104
        TkTextPickCurrent(textPtr, &textPtr->pickEvent);
2105
        tkwin = textPtr->tkwin;
2106
        Tcl_Release((ClientData) textPtr);
2107
        if (tkwin == NULL) {
2108
            goto end;
2109
        }
2110
    }
2111
 
2112
    /*
2113
     * First recompute what's supposed to be displayed.
2114
     */
2115
 
2116
    UpdateDisplayInfo(textPtr);
2117
    dInfoPtr->dLinesInvalidated = 0;
2118
 
2119
    /*
2120
     * See if it's possible to bring some parts of the screen up-to-date
2121
     * by scrolling (copying from other parts of the screen).
2122
     */
2123
 
2124
    for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
2125
        register DLine *dlPtr2;
2126
        int offset, height, y, oldY;
2127
        TkRegion damageRgn;
2128
 
2129
        if ((dlPtr->oldY == -1) || (dlPtr->y == dlPtr->oldY)
2130
                || ((dlPtr->oldY + dlPtr->height) > dInfoPtr->maxY)) {
2131
            continue;
2132
        }
2133
 
2134
        /*
2135
         * This line is already drawn somewhere in the window so it only
2136
         * needs to be copied to its new location.  See if there's a group
2137
         * of lines that can all be copied together.
2138
         */
2139
 
2140
        offset = dlPtr->y - dlPtr->oldY;
2141
        height = dlPtr->height;
2142
        y = dlPtr->y;
2143
        for (dlPtr2 = dlPtr->nextPtr; dlPtr2 != NULL;
2144
                dlPtr2 = dlPtr2->nextPtr) {
2145
            if ((dlPtr2->oldY == -1)
2146
                    || ((dlPtr2->oldY + offset) != dlPtr2->y)
2147
                    || ((dlPtr2->oldY + dlPtr2->height) > dInfoPtr->maxY)) {
2148
                break;
2149
            }
2150
            height += dlPtr2->height;
2151
        }
2152
 
2153
        /*
2154
         * Reduce the height of the area being copied if necessary to
2155
         * avoid overwriting the border area.
2156
         */
2157
 
2158
        if ((y + height) > dInfoPtr->maxY) {
2159
            height = dInfoPtr->maxY -y;
2160
        }
2161
        oldY = dlPtr->oldY;
2162
 
2163
        /*
2164
         * Update the lines we are going to scroll to show that they
2165
         * have been copied.
2166
         */
2167
 
2168
        while (1) {
2169
            dlPtr->oldY = dlPtr->y;
2170
            if (dlPtr->nextPtr == dlPtr2) {
2171
                break;
2172
            }
2173
            dlPtr = dlPtr->nextPtr;
2174
        }
2175
 
2176
        /*
2177
         * Scan through the lines following the copied ones to see if
2178
         * we are going to overwrite them with the copy operation.
2179
         * If so, mark them for redisplay.
2180
         */
2181
 
2182
        for ( ; dlPtr2 != NULL; dlPtr2 = dlPtr2->nextPtr) {
2183
            if ((dlPtr2->oldY != -1)
2184
                    && ((dlPtr2->oldY + dlPtr2->height) > y)
2185
                    && (dlPtr2->oldY < (y + height))) {
2186
                dlPtr2->oldY = -1;
2187
            }
2188
        }
2189
 
2190
        /*
2191
         * Now scroll the lines.  This may generate damage which we
2192
         * handle by calling TextInvalidateRegion to mark the display
2193
         * blocks as stale.
2194
         */
2195
 
2196
        damageRgn = TkCreateRegion();
2197
        if (TkScrollWindow(textPtr->tkwin, dInfoPtr->scrollGC,
2198
                dInfoPtr->x, oldY,
2199
                (dInfoPtr->maxX - dInfoPtr->x), height,
2200
                0, y - oldY, damageRgn)) {
2201
            TextInvalidateRegion(textPtr, damageRgn);
2202
        }
2203
        numCopies++;
2204
        TkDestroyRegion(damageRgn);
2205
    }
2206
 
2207
    /*
2208
     * Clear the REDRAW_PENDING flag here.  This is actually pretty
2209
     * tricky.  We want to wait until *after* doing the scrolling,
2210
     * since that could generate more areas to redraw and don't
2211
     * want to reschedule a redisplay for them.  On the other hand,
2212
     * we can't wait until after all the redisplaying, because the
2213
     * act of redisplaying could actually generate more redisplays
2214
     * (e.g. in the case of a nested window with event bindings triggered
2215
     * by redisplay).
2216
     */
2217
 
2218
    dInfoPtr->flags &= ~REDRAW_PENDING;
2219
 
2220
    /*
2221
     * Redraw the borders if that's needed.
2222
     */
2223
 
2224
    if (dInfoPtr->flags & REDRAW_BORDERS) {
2225
        if (tkTextDebug) {
2226
            Tcl_SetVar2(interp, "tk_textRedraw", (char *) NULL, "borders",
2227
                    TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
2228
        }
2229
 
2230
        if (textPtr->tkwin == NULL) {
2231
 
2232
            /*
2233
             * The widget has been deleted.  Don't do anything.
2234
             */
2235
 
2236
            goto end;
2237
        }
2238
 
2239
        Tk_Draw3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
2240
                textPtr->border, textPtr->highlightWidth,
2241
                textPtr->highlightWidth,
2242
                Tk_Width(textPtr->tkwin) - 2*textPtr->highlightWidth,
2243
                Tk_Height(textPtr->tkwin) - 2*textPtr->highlightWidth,
2244
                textPtr->borderWidth, textPtr->relief);
2245
        if (textPtr->highlightWidth != 0) {
2246
            GC gc;
2247
 
2248
            if (textPtr->flags & GOT_FOCUS) {
2249
                gc = Tk_GCForColor(textPtr->highlightColorPtr,
2250
                        Tk_WindowId(textPtr->tkwin));
2251
            } else {
2252
                gc = Tk_GCForColor(textPtr->highlightBgColorPtr,
2253
                        Tk_WindowId(textPtr->tkwin));
2254
            }
2255
            Tk_DrawFocusHighlight(textPtr->tkwin, gc, textPtr->highlightWidth,
2256
                    Tk_WindowId(textPtr->tkwin));
2257
        }
2258
        borders = textPtr->borderWidth + textPtr->highlightWidth;
2259
        if (textPtr->padY > 0) {
2260
            Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
2261
                    textPtr->border, borders, borders,
2262
                    Tk_Width(textPtr->tkwin) - 2*borders, textPtr->padY,
2263
                    0, TK_RELIEF_FLAT);
2264
            Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
2265
                    textPtr->border, borders,
2266
                    Tk_Height(textPtr->tkwin) - borders - textPtr->padY,
2267
                    Tk_Width(textPtr->tkwin) - 2*borders,
2268
                    textPtr->padY, 0, TK_RELIEF_FLAT);
2269
        }
2270
        if (textPtr->padX > 0) {
2271
            Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
2272
                    textPtr->border, borders, borders + textPtr->padY,
2273
                    textPtr->padX,
2274
                    Tk_Height(textPtr->tkwin) - 2*borders -2*textPtr->padY,
2275
                    0, TK_RELIEF_FLAT);
2276
            Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
2277
                    textPtr->border,
2278
                    Tk_Width(textPtr->tkwin) - borders - textPtr->padX,
2279
                    borders + textPtr->padY, textPtr->padX,
2280
                    Tk_Height(textPtr->tkwin) - 2*borders -2*textPtr->padY,
2281
                    0, TK_RELIEF_FLAT);
2282
        }
2283
        dInfoPtr->flags &= ~REDRAW_BORDERS;
2284
    }
2285
 
2286
    /*
2287
     * Now we have to redraw the lines that couldn't be updated by
2288
     * scrolling.  First, compute the height of the largest line and
2289
     * allocate an off-screen pixmap to use for double-buffered
2290
     * displays.
2291
     */
2292
 
2293
    maxHeight = -1;
2294
    for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
2295
            dlPtr = dlPtr->nextPtr) {
2296
        if ((dlPtr->height > maxHeight) && (dlPtr->oldY != dlPtr->y)) {
2297
            maxHeight = dlPtr->height;
2298
        }
2299
        bottomY = dlPtr->y + dlPtr->height;
2300
    }
2301
    if (maxHeight > dInfoPtr->maxY) {
2302
        maxHeight = dInfoPtr->maxY;
2303
    }
2304
    if (maxHeight > 0) {
2305
        pixmap = Tk_GetPixmap(Tk_Display(textPtr->tkwin),
2306
                Tk_WindowId(textPtr->tkwin), Tk_Width(textPtr->tkwin),
2307
                maxHeight, Tk_Depth(textPtr->tkwin));
2308
        for (prevPtr = NULL, dlPtr = textPtr->dInfoPtr->dLinePtr;
2309
                (dlPtr != NULL) && (dlPtr->y < dInfoPtr->maxY);
2310
                prevPtr = dlPtr, dlPtr = dlPtr->nextPtr) {
2311
            if (dlPtr->oldY != dlPtr->y) {
2312
                if (tkTextDebug) {
2313
                    char string[TK_POS_CHARS];
2314
                    TkTextPrintIndex(&dlPtr->index, string);
2315
                    Tcl_SetVar2(textPtr->interp, "tk_textRedraw",
2316
                            (char *) NULL, string,
2317
                            TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
2318
                }
2319
                DisplayDLine(textPtr, dlPtr, prevPtr, pixmap);
2320
                if (dInfoPtr->dLinesInvalidated) {
2321
                    Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap);
2322
                    return;
2323
                }
2324
                dlPtr->oldY = dlPtr->y;
2325
                dlPtr->flags &= ~NEW_LAYOUT;
2326
            }
2327
        }
2328
        Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap);
2329
    }
2330
 
2331
    /*
2332
     * See if we need to refresh the part of the window below the
2333
     * last line of text (if there is any such area).  Refresh the
2334
     * padding area on the left too, since the insertion cursor might
2335
     * have been displayed there previously).
2336
     */
2337
 
2338
    if (dInfoPtr->topOfEof > dInfoPtr->maxY) {
2339
        dInfoPtr->topOfEof = dInfoPtr->maxY;
2340
    }
2341
    if (bottomY < dInfoPtr->topOfEof) {
2342
        if (tkTextDebug) {
2343
            Tcl_SetVar2(textPtr->interp, "tk_textRedraw",
2344
                    (char *) NULL, "eof",
2345
                    TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
2346
        }
2347
 
2348
        if (textPtr->tkwin == NULL) {
2349
 
2350
            /*
2351
             * The widget has been deleted.  Don't do anything.
2352
             */
2353
 
2354
            goto end;
2355
        }
2356
 
2357
        Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
2358
                textPtr->border, dInfoPtr->x - textPtr->padX, bottomY,
2359
                dInfoPtr->maxX - (dInfoPtr->x - textPtr->padX),
2360
                dInfoPtr->topOfEof-bottomY, 0, TK_RELIEF_FLAT);
2361
    }
2362
    dInfoPtr->topOfEof = bottomY;
2363
 
2364
    doScrollbars:
2365
 
2366
    /*
2367
     * Update the vertical scrollbar, if there is one.  Note:  it's
2368
     * important to clear REDRAW_PENDING here, just in case the
2369
     * scroll procedure does something that requires redisplay.
2370
     */
2371
 
2372
    if (textPtr->flags & UPDATE_SCROLLBARS) {
2373
        textPtr->flags &= ~UPDATE_SCROLLBARS;
2374
        if (textPtr->yScrollCmd != NULL) {
2375
            GetYView(textPtr->interp, textPtr, 1);
2376
        }
2377
 
2378
        if (textPtr->tkwin == NULL) {
2379
 
2380
            /*
2381
             * The widget has been deleted.  Don't do anything.
2382
             */
2383
 
2384
            goto end;
2385
        }
2386
 
2387
        /*
2388
         * Update the horizontal scrollbar, if any.
2389
         */
2390
 
2391
        if (textPtr->xScrollCmd != NULL) {
2392
            GetXView(textPtr->interp, textPtr, 1);
2393
        }
2394
    }
2395
 
2396
end:
2397
    Tcl_Release((ClientData) interp);
2398
}
2399
 
2400
/*
2401
 *----------------------------------------------------------------------
2402
 *
2403
 * TkTextEventuallyRepick --
2404
 *
2405
 *      This procedure is invoked whenever something happens that
2406
 *      could change the current character or the tags associated
2407
 *      with it.
2408
 *
2409
 * Results:
2410
 *      None.
2411
 *
2412
 * Side effects:
2413
 *      A repick is scheduled as an idle handler.
2414
 *
2415
 *----------------------------------------------------------------------
2416
 */
2417
 
2418
        /* ARGSUSED */
2419
void
2420
TkTextEventuallyRepick(textPtr)
2421
    TkText *textPtr;            /* Widget record for text widget. */
2422
{
2423
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
2424
 
2425
    dInfoPtr->flags |= REPICK_NEEDED;
2426
    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
2427
        dInfoPtr->flags |= REDRAW_PENDING;
2428
        Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
2429
    }
2430
}
2431
 
2432
/*
2433
 *----------------------------------------------------------------------
2434
 *
2435
 * TkTextRedrawRegion --
2436
 *
2437
 *      This procedure is invoked to schedule a redisplay for a given
2438
 *      region of a text widget.  The redisplay itself may not occur
2439
 *      immediately:  it's scheduled as a when-idle handler.
2440
 *
2441
 * Results:
2442
 *      None.
2443
 *
2444
 * Side effects:
2445
 *      Information will eventually be redrawn on the screen.
2446
 *
2447
 *----------------------------------------------------------------------
2448
 */
2449
 
2450
        /* ARGSUSED */
2451
void
2452
TkTextRedrawRegion(textPtr, x, y, width, height)
2453
    TkText *textPtr;            /* Widget record for text widget. */
2454
    int x, y;                   /* Coordinates of upper-left corner of area
2455
                                 * to be redrawn, in pixels relative to
2456
                                 * textPtr's window. */
2457
    int width, height;          /* Width and height of area to be redrawn. */
2458
{
2459
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
2460
    TkRegion damageRgn = TkCreateRegion();
2461
    XRectangle rect;
2462
 
2463
    rect.x = x;
2464
    rect.y = y;
2465
    rect.width = width;
2466
    rect.height = height;
2467
    TkUnionRectWithRegion(&rect, damageRgn, damageRgn);
2468
 
2469
    TextInvalidateRegion(textPtr, damageRgn);
2470
 
2471
    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
2472
        dInfoPtr->flags |= REDRAW_PENDING;
2473
        Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
2474
    }
2475
    TkDestroyRegion(damageRgn);
2476
}
2477
 
2478
/*
2479
 *----------------------------------------------------------------------
2480
 *
2481
 * TextInvalidateRegion --
2482
 *
2483
 *      Mark a region of text as invalid.
2484
 *
2485
 * Results:
2486
 *      None.
2487
 *
2488
 * Side effects:
2489
 *      Updates the display information for the text widget.
2490
 *
2491
 *----------------------------------------------------------------------
2492
 */
2493
 
2494
static void
2495
TextInvalidateRegion(textPtr, region)
2496
    TkText *textPtr;            /* Widget record for text widget. */
2497
    TkRegion region;            /* Region of area to redraw. */
2498
{
2499
    register DLine *dlPtr;
2500
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
2501
    int maxY, inset;
2502
    XRectangle rect;
2503
 
2504
    /*
2505
     * Find all lines that overlap the given region and mark them for
2506
     * redisplay.
2507
     */
2508
 
2509
    TkClipBox(region, &rect);
2510
    maxY = rect.y + rect.height;
2511
    for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
2512
            dlPtr = dlPtr->nextPtr) {
2513
        if ((dlPtr->oldY != -1) && (TkRectInRegion(region, rect.x, dlPtr->y,
2514
                rect.width, (unsigned int) dlPtr->height) != RectangleOut)) {
2515
            dlPtr->oldY = -1;
2516
        }
2517
    }
2518
    if (dInfoPtr->topOfEof < maxY) {
2519
        dInfoPtr->topOfEof = maxY;
2520
    }
2521
 
2522
    /*
2523
     * Schedule the redisplay operation if there isn't one already
2524
     * scheduled.
2525
     */
2526
 
2527
    inset = textPtr->borderWidth + textPtr->highlightWidth;
2528
    if ((rect.x < (inset + textPtr->padX))
2529
            || (rect.y < (inset + textPtr->padY))
2530
            || ((int) (rect.x + rect.width) > (Tk_Width(textPtr->tkwin)
2531
                    - inset - textPtr->padX))
2532
            || (maxY > (Tk_Height(textPtr->tkwin) - inset - textPtr->padY))) {
2533
        dInfoPtr->flags |= REDRAW_BORDERS;
2534
    }
2535
}
2536
 
2537
/*
2538
 *----------------------------------------------------------------------
2539
 *
2540
 * TkTextChanged --
2541
 *
2542
 *      This procedure is invoked when info in a text widget is about
2543
 *      to be modified in a way that changes how it is displayed (e.g.
2544
 *      characters were inserted or deleted, or tag information was
2545
 *      changed).  This procedure must be called *before* a change is
2546
 *      made, so that indexes in the display information are still
2547
 *      valid.
2548
 *
2549
 * Results:
2550
 *      None.
2551
 *
2552
 * Side effects:
2553
 *      The range of character between index1Ptr (inclusive) and
2554
 *      index2Ptr (exclusive) will be redisplayed at some point in the
2555
 *      future (the actual redisplay is scheduled as a when-idle handler).
2556
 *
2557
 *----------------------------------------------------------------------
2558
 */
2559
 
2560
void
2561
TkTextChanged(textPtr, index1Ptr, index2Ptr)
2562
    TkText *textPtr;            /* Widget record for text widget. */
2563
    TkTextIndex *index1Ptr;     /* Index of first character to redisplay. */
2564
    TkTextIndex *index2Ptr;     /* Index of character just after last one
2565
                                 * to redisplay. */
2566
{
2567
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
2568
    DLine *firstPtr, *lastPtr;
2569
    TkTextIndex rounded;
2570
 
2571
    /*
2572
     * Schedule both a redisplay and a recomputation of display information.
2573
     * It's done here rather than the end of the procedure for two reasons:
2574
     *
2575
     * 1. If there are no display lines to update we'll want to return
2576
     *    immediately, well before the end of the procedure.
2577
     * 2. It's important to arrange for the redisplay BEFORE calling
2578
     *    FreeDLines.  The reason for this is subtle and has to do with
2579
     *    embedded windows.  The chunk delete procedure for an embedded
2580
     *    window will schedule an idle handler to unmap the window.
2581
     *    However, we want the idle handler for redisplay to be called
2582
     *    first, so that it can put the embedded window back on the screen
2583
     *    again (if appropriate).  This will prevent the window from ever
2584
     *    being unmapped, and thereby avoid flashing.
2585
     */
2586
 
2587
    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
2588
        Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
2589
    }
2590
    dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
2591
 
2592
    /*
2593
     * Find the DLines corresponding to index1Ptr and index2Ptr.  There
2594
     * is one tricky thing here, which is that we have to relayout in
2595
     * units of whole text lines:  round index1Ptr back to the beginning
2596
     * of its text line, and include all the display lines after index2,
2597
     * up to the end of its text line.  This is necessary because the
2598
     * indices stored in the display lines will no longer be valid.  It's
2599
     * also needed because any edit could change the way lines wrap.
2600
     */
2601
 
2602
    rounded = *index1Ptr;
2603
    rounded.charIndex = 0;
2604
    firstPtr = FindDLine(dInfoPtr->dLinePtr, &rounded);
2605
    if (firstPtr == NULL) {
2606
        return;
2607
    }
2608
    lastPtr = FindDLine(dInfoPtr->dLinePtr, index2Ptr);
2609
    while ((lastPtr != NULL)
2610
            && (lastPtr->index.linePtr == index2Ptr->linePtr)) {
2611
        lastPtr = lastPtr->nextPtr;
2612
    }
2613
 
2614
    /*
2615
     * Delete all the DLines from firstPtr up to but not including lastPtr.
2616
     */
2617
 
2618
    FreeDLines(textPtr, firstPtr, lastPtr, 1);
2619
}
2620
 
2621
/*
2622
 *----------------------------------------------------------------------
2623
 *
2624
 * TkTextRedrawTag --
2625
 *
2626
 *      This procedure is invoked to request a redraw of all characters
2627
 *      in a given range that have a particular tag on or off.  It's
2628
 *      called, for example, when tag options change.
2629
 *
2630
 * Results:
2631
 *      None.
2632
 *
2633
 * Side effects:
2634
 *      Information on the screen may be redrawn, and the layout of
2635
 *      the screen may change.
2636
 *
2637
 *----------------------------------------------------------------------
2638
 */
2639
 
2640
void
2641
TkTextRedrawTag(textPtr, index1Ptr, index2Ptr, tagPtr, withTag)
2642
    TkText *textPtr;            /* Widget record for text widget. */
2643
    TkTextIndex *index1Ptr;     /* First character in range to consider
2644
                                 * for redisplay.  NULL means start at
2645
                                 * beginning of text. */
2646
    TkTextIndex *index2Ptr;     /* Character just after last one to consider
2647
                                 * for redisplay.  NULL means process all
2648
                                 * the characters in the text. */
2649
    TkTextTag *tagPtr;          /* Information about tag. */
2650
    int withTag;                /* 1 means redraw characters that have the
2651
                                 * tag, 0 means redraw those without. */
2652
{
2653
    register DLine *dlPtr;
2654
    DLine *endPtr;
2655
    int tagOn;
2656
    TkTextSearch search;
2657
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
2658
    TkTextIndex *curIndexPtr;
2659
    TkTextIndex endOfText, *endIndexPtr;
2660
 
2661
    /*
2662
     * Round up the starting position if it's before the first line
2663
     * visible on the screen (we only care about what's on the screen).
2664
     */
2665
 
2666
    dlPtr = dInfoPtr->dLinePtr;
2667
    if (dlPtr == NULL) {
2668
        return;
2669
    }
2670
    if ((index1Ptr == NULL) || (TkTextIndexCmp(&dlPtr->index, index1Ptr) > 0)) {
2671
        index1Ptr = &dlPtr->index;
2672
    }
2673
 
2674
    /*
2675
     * Set the stopping position if it wasn't specified.
2676
     */
2677
 
2678
    if (index2Ptr == NULL) {
2679
        index2Ptr = TkTextMakeIndex(textPtr->tree,
2680
                TkBTreeNumLines(textPtr->tree), 0, &endOfText);
2681
    }
2682
 
2683
    /*
2684
     * Initialize a search through all transitions on the tag, starting
2685
     * with the first transition where the tag's current state is different
2686
     * from what it will eventually be.
2687
     */
2688
 
2689
    TkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, &search);
2690
    /*
2691
     * Make our own curIndex because at this point search.curIndex
2692
     * may not equal index1Ptr->curIndex in the case the first tag toggle
2693
     * comes after index1Ptr (See the use of FindTagStart in TkBTreeStartSearch)
2694
     */
2695
    curIndexPtr = index1Ptr;
2696
    tagOn = TkBTreeCharTagged(index1Ptr, tagPtr);
2697
    if (tagOn != withTag) {
2698
        if (!TkBTreeNextTag(&search)) {
2699
            return;
2700
        }
2701
        curIndexPtr = &search.curIndex;
2702
    }
2703
 
2704
    /*
2705
     * Schedule a redisplay and layout recalculation if they aren't
2706
     * already pending.  This has to be done before calling FreeDLines,
2707
     * for the reason given in TkTextChanged.
2708
     */
2709
 
2710
    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
2711
        Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
2712
    }
2713
    dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
2714
 
2715
    /*
2716
     * Each loop through the loop below is for one range of characters
2717
     * where the tag's current state is different than its eventual
2718
     * state.  At the top of the loop, search contains information about
2719
     * the first character in the range.
2720
     */
2721
 
2722
    while (1) {
2723
        /*
2724
         * Find the first DLine structure in the range.  Note: if the
2725
         * desired character isn't the first in its text line, then look
2726
         * for the character just before it instead.  This is needed to
2727
         * handle the case where the first character of a wrapped
2728
         * display line just got smaller, so that it now fits on the
2729
         * line before:  need to relayout the line containing the
2730
         * previous character.
2731
         */
2732
 
2733
        if (curIndexPtr->charIndex == 0) {
2734
            dlPtr = FindDLine(dlPtr, curIndexPtr);
2735
        } else {
2736
            TkTextIndex tmp;
2737
 
2738
            tmp = *curIndexPtr;
2739
            tmp.charIndex -= 1;
2740
            dlPtr = FindDLine(dlPtr, &tmp);
2741
        }
2742
        if (dlPtr == NULL) {
2743
            break;
2744
        }
2745
 
2746
        /*
2747
         * Find the first DLine structure that's past the end of the range.
2748
         */
2749
 
2750
        if (!TkBTreeNextTag(&search)) {
2751
            endIndexPtr = index2Ptr;
2752
        } else {
2753
            curIndexPtr = &search.curIndex;
2754
            endIndexPtr = curIndexPtr;
2755
        }
2756
        endPtr = FindDLine(dlPtr, endIndexPtr);
2757
        if ((endPtr != NULL) && (endPtr->index.linePtr == endIndexPtr->linePtr)
2758
                && (endPtr->index.charIndex < endIndexPtr->charIndex)) {
2759
            endPtr = endPtr->nextPtr;
2760
        }
2761
 
2762
        /*
2763
         * Delete all of the display lines in the range, so that they'll
2764
         * be re-layed out and redrawn.
2765
         */
2766
 
2767
        FreeDLines(textPtr, dlPtr, endPtr, 1);
2768
        dlPtr = endPtr;
2769
 
2770
        /*
2771
         * Find the first text line in the next range.
2772
         */
2773
 
2774
        if (!TkBTreeNextTag(&search)) {
2775
            break;
2776
        }
2777
    }
2778
}
2779
 
2780
/*
2781
 *----------------------------------------------------------------------
2782
 *
2783
 * TkTextRelayoutWindow --
2784
 *
2785
 *      This procedure is called when something has happened that
2786
 *      invalidates the whole layout of characters on the screen, such
2787
 *      as a change in a configuration option for the overall text
2788
 *      widget or a change in the window size.  It causes all display
2789
 *      information to be recomputed and the window to be redrawn.
2790
 *
2791
 * Results:
2792
 *      None.
2793
 *
2794
 * Side effects:
2795
 *      All the display information will be recomputed for the window
2796
 *      and the window will be redrawn.
2797
 *
2798
 *----------------------------------------------------------------------
2799
 */
2800
 
2801
void
2802
TkTextRelayoutWindow(textPtr)
2803
    TkText *textPtr;            /* Widget record for text widget. */
2804
{
2805
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
2806
    GC new;
2807
    XGCValues gcValues;
2808
 
2809
    /*
2810
     * Schedule the window redisplay.  See TkTextChanged for the
2811
     * reason why this has to be done before any calls to FreeDLines.
2812
     */
2813
 
2814
    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
2815
        Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
2816
    }
2817
    dInfoPtr->flags |= REDRAW_PENDING|REDRAW_BORDERS|DINFO_OUT_OF_DATE
2818
            |REPICK_NEEDED;
2819
 
2820
    /*
2821
     * (Re-)create the graphics context for drawing the traversal
2822
     * highlight.
2823
     */
2824
 
2825
    gcValues.graphics_exposures = False;
2826
    new = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures, &gcValues);
2827
    if (dInfoPtr->copyGC != None) {
2828
        Tk_FreeGC(textPtr->display, dInfoPtr->copyGC);
2829
    }
2830
    dInfoPtr->copyGC = new;
2831
 
2832
    /*
2833
     * Throw away all the current layout information.
2834
     */
2835
 
2836
    FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
2837
    dInfoPtr->dLinePtr = NULL;
2838
 
2839
    /*
2840
     * Recompute some overall things for the layout.  Even if the
2841
     * window gets very small, pretend that there's at least one
2842
     * pixel of drawing space in it.
2843
     */
2844
 
2845
    if (textPtr->highlightWidth < 0) {
2846
        textPtr->highlightWidth = 0;
2847
    }
2848
    dInfoPtr->x = textPtr->highlightWidth + textPtr->borderWidth
2849
            + textPtr->padX;
2850
    dInfoPtr->y = textPtr->highlightWidth + textPtr->borderWidth
2851
            + textPtr->padY;
2852
    dInfoPtr->maxX = Tk_Width(textPtr->tkwin) - textPtr->highlightWidth
2853
            - textPtr->borderWidth - textPtr->padX;
2854
    if (dInfoPtr->maxX <= dInfoPtr->x) {
2855
        dInfoPtr->maxX = dInfoPtr->x + 1;
2856
    }
2857
    dInfoPtr->maxY = Tk_Height(textPtr->tkwin) - textPtr->highlightWidth
2858
            - textPtr->borderWidth - textPtr->padY;
2859
    if (dInfoPtr->maxY <= dInfoPtr->y) {
2860
        dInfoPtr->maxY = dInfoPtr->y + 1;
2861
    }
2862
    dInfoPtr->topOfEof = dInfoPtr->maxY;
2863
 
2864
    /*
2865
     * If the upper-left character isn't the first in a line, recompute
2866
     * it.  This is necessary because a change in the window's size
2867
     * or options could change the way lines wrap.
2868
     */
2869
 
2870
    if (textPtr->topIndex.charIndex != 0) {
2871
        MeasureUp(textPtr, &textPtr->topIndex, 0, &textPtr->topIndex);
2872
    }
2873
 
2874
    /*
2875
     * Invalidate cached scrollbar positions, so that scrollbars
2876
     * sliders will be udpated.
2877
     */
2878
 
2879
    dInfoPtr->xScrollFirst = dInfoPtr->xScrollLast = -1;
2880
    dInfoPtr->yScrollFirst = dInfoPtr->yScrollLast = -1;
2881
}
2882
 
2883
/*
2884
 *----------------------------------------------------------------------
2885
 *
2886
 * TkTextSetYView --
2887
 *
2888
 *      This procedure is called to specify what lines are to be
2889
 *      displayed in a text widget.
2890
 *
2891
 * Results:
2892
 *      None.
2893
 *
2894
 * Side effects:
2895
 *      The display will (eventually) be updated so that the position
2896
 *      given by "indexPtr" is visible on the screen at the position
2897
 *      determined by "pickPlace".
2898
 *
2899
 *----------------------------------------------------------------------
2900
 */
2901
 
2902
void
2903
TkTextSetYView(textPtr, indexPtr, pickPlace)
2904
    TkText *textPtr;            /* Widget record for text widget. */
2905
    TkTextIndex *indexPtr;      /* Position that is to appear somewhere
2906
                                 * in the view. */
2907
    int pickPlace;              /* 0 means topLine must appear at top of
2908
                                 * screen.  1 means we get to pick where it
2909
                                 * appears:  minimize screen motion or else
2910
                                 * display line at center of screen. */
2911
{
2912
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
2913
    register DLine *dlPtr;
2914
    int bottomY, close, lineIndex;
2915
    TkTextIndex tmpIndex, rounded;
2916
    Tk_FontMetrics fm;
2917
 
2918
    /*
2919
     * If the specified position is the extra line at the end of the
2920
     * text, round it back to the last real line.
2921
     */
2922
 
2923
    lineIndex = TkBTreeLineIndex(indexPtr->linePtr);
2924
    if (lineIndex == TkBTreeNumLines(indexPtr->tree)) {
2925
        TkTextIndexBackChars(indexPtr, 1, &rounded);
2926
        indexPtr = &rounded;
2927
    }
2928
 
2929
    if (!pickPlace) {
2930
        /*
2931
         * The specified position must go at the top of the screen.
2932
         * Just leave all the DLine's alone: we may be able to reuse
2933
         * some of the information that's currently on the screen
2934
         * without redisplaying it all.
2935
         */
2936
 
2937
        if (indexPtr->charIndex == 0) {
2938
            textPtr->topIndex = *indexPtr;
2939
        } else {
2940
            MeasureUp(textPtr, indexPtr, 0, &textPtr->topIndex);
2941
        }
2942
        goto scheduleUpdate;
2943
    }
2944
 
2945
    /*
2946
     * We have to pick where to display the index.  First, bring
2947
     * the display information up to date and see if the index will be
2948
     * completely visible in the current screen configuration.  If so
2949
     * then there's nothing to do.
2950
     */
2951
 
2952
    if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
2953
        UpdateDisplayInfo(textPtr);
2954
    }
2955
    dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
2956
    if (dlPtr != NULL) {
2957
        if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
2958
            /*
2959
             * Part of the line hangs off the bottom of the screen;
2960
             * pretend the whole line is off-screen.
2961
             */
2962
 
2963
            dlPtr = NULL;
2964
        } else if ((dlPtr->index.linePtr == indexPtr->linePtr)
2965
                && (dlPtr->index.charIndex <= indexPtr->charIndex)) {
2966
            return;
2967
        }
2968
    }
2969
 
2970
    /*
2971
     * The desired line isn't already on-screen.  Figure out what
2972
     * it means to be "close" to the top or bottom of the screen.
2973
     * Close means within 1/3 of the screen height or within three
2974
     * lines, whichever is greater.  Add one extra line also, to
2975
     * account for the way MeasureUp rounds.
2976
     */
2977
 
2978
    Tk_GetFontMetrics(textPtr->tkfont, &fm);
2979
    bottomY = (dInfoPtr->y + dInfoPtr->maxY + fm.linespace)/2;
2980
    close = (dInfoPtr->maxY - dInfoPtr->y)/3;
2981
    if (close < 3*fm.linespace) {
2982
        close = 3*fm.linespace;
2983
    }
2984
    close += fm.linespace;
2985
    if (dlPtr != NULL) {
2986
        /*
2987
         * The desired line is above the top of screen.  If it is
2988
         * "close" to the top of the window then make it the top
2989
         * line on the screen.
2990
         */
2991
 
2992
        MeasureUp(textPtr, &textPtr->topIndex, close, &tmpIndex);
2993
        if (TkTextIndexCmp(&tmpIndex, indexPtr) <= 0) {
2994
            MeasureUp(textPtr, indexPtr, 0, &textPtr->topIndex);
2995
            goto scheduleUpdate;
2996
        }
2997
    } else {
2998
        /*
2999
         * The desired line is below the bottom of the screen.  If it is
3000
         * "close" to the bottom of the screen then position it at the
3001
         * bottom of the screen.
3002
         */
3003
 
3004
        MeasureUp(textPtr, indexPtr, close, &tmpIndex);
3005
        if (FindDLine(dInfoPtr->dLinePtr, &tmpIndex) != NULL) {
3006
            bottomY = dInfoPtr->maxY - dInfoPtr->y;
3007
        }
3008
    }
3009
 
3010
    /*
3011
     * Our job now is to arrange the display so that indexPtr appears
3012
     * as low on the screen as possible but with its bottom no lower
3013
     * than bottomY.  BottomY is the bottom of the window if the
3014
     * desired line is just below the current screen, otherwise it
3015
     * is a half-line lower than the center of the window.
3016
     */
3017
 
3018
    MeasureUp(textPtr, indexPtr, bottomY, &textPtr->topIndex);
3019
 
3020
    scheduleUpdate:
3021
    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
3022
        Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
3023
    }
3024
    dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
3025
}
3026
 
3027
/*
3028
 *--------------------------------------------------------------
3029
 *
3030
 * MeasureUp --
3031
 *
3032
 *      Given one index, find the index of the first character
3033
 *      on the highest display line that would be displayed no more
3034
 *      than "distance" pixels above the given index.
3035
 *
3036
 * Results:
3037
 *      *dstPtr is filled in with the index of the first character
3038
 *      on a display line.  The display line is found by measuring
3039
 *      up "distance" pixels above the pixel just below an imaginary
3040
 *      display line that contains srcPtr.  If the display line
3041
 *      that covers this coordinate actually extends above the
3042
 *      coordinate, then return the index of the next lower line
3043
 *      instead (i.e. the returned index will be completely visible
3044
 *      at or below the given y-coordinate).
3045
 *
3046
 * Side effects:
3047
 *      None.
3048
 *
3049
 *--------------------------------------------------------------
3050
 */
3051
 
3052
static void
3053
MeasureUp(textPtr, srcPtr, distance, dstPtr)
3054
    TkText *textPtr;            /* Text widget in which to measure. */
3055
    TkTextIndex *srcPtr;        /* Index of character from which to start
3056
                                 * measuring. */
3057
    int distance;               /* Vertical distance in pixels measured
3058
                                 * from the pixel just below the lowest
3059
                                 * one in srcPtr's line. */
3060
    TkTextIndex *dstPtr;        /* Index to fill in with result. */
3061
{
3062
    int lineNum;                /* Number of current line. */
3063
    int charsToCount;           /* Maximum number of characters to measure
3064
                                 * in current line. */
3065
    TkTextIndex bestIndex;      /* Best candidate seen so far for result. */
3066
    TkTextIndex index;
3067
    DLine *dlPtr, *lowestPtr;
3068
    int noBestYet;              /* 1 means bestIndex hasn't been set. */
3069
 
3070
    noBestYet = 1;
3071
    charsToCount = srcPtr->charIndex + 1;
3072
    index.tree = srcPtr->tree;
3073
    for (lineNum = TkBTreeLineIndex(srcPtr->linePtr); lineNum >= 0;
3074
            lineNum--) {
3075
        /*
3076
         * Layout an entire text line (potentially > 1 display line).
3077
         * For the first line, which contains srcPtr, only layout the
3078
         * part up through srcPtr (charsToCount is non-infinite to
3079
         * accomplish this).  Make a list of all the display lines
3080
         * in backwards order (the lowest DLine on the screen is first
3081
         * in the list).
3082
         */
3083
 
3084
        index.linePtr = TkBTreeFindLine(srcPtr->tree, lineNum);
3085
        index.charIndex = 0;
3086
        lowestPtr = NULL;
3087
        do {
3088
            dlPtr = LayoutDLine(textPtr, &index);
3089
            dlPtr->nextPtr = lowestPtr;
3090
            lowestPtr = dlPtr;
3091
            TkTextIndexForwChars(&index, dlPtr->count, &index);
3092
            charsToCount -= dlPtr->count;
3093
        } while ((charsToCount > 0) && (index.linePtr == dlPtr->index.linePtr));
3094
 
3095
        /*
3096
         * Scan through the display lines to see if we've covered enough
3097
         * vertical distance.  If so, save the starting index for the
3098
         * line at the desired location.
3099
         */
3100
 
3101
        for (dlPtr = lowestPtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
3102
            distance -= dlPtr->height;
3103
            if (distance < 0) {
3104
                *dstPtr = (noBestYet) ? dlPtr->index : bestIndex;
3105
                break;
3106
            }
3107
            bestIndex = dlPtr->index;
3108
            noBestYet = 0;
3109
        }
3110
 
3111
        /*
3112
         * Discard the display lines, then either return or prepare
3113
         * for the next display line to lay out.
3114
         */
3115
 
3116
        FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
3117
        if (distance < 0) {
3118
            return;
3119
        }
3120
        charsToCount = INT_MAX;         /* Consider all chars. in next line. */
3121
    }
3122
 
3123
    /*
3124
     * Ran off the beginning of the text.  Return the first character
3125
     * in the text.
3126
     */
3127
 
3128
    TkTextMakeIndex(textPtr->tree, 0, 0, dstPtr);
3129
}
3130
 
3131
/*
3132
 *--------------------------------------------------------------
3133
 *
3134
 * TkTextSeeCmd --
3135
 *
3136
 *      This procedure is invoked to process the "see" option for
3137
 *      the widget command for text widgets. See the user documentation
3138
 *      for details on what it does.
3139
 *
3140
 * Results:
3141
 *      A standard Tcl result.
3142
 *
3143
 * Side effects:
3144
 *      See the user documentation.
3145
 *
3146
 *--------------------------------------------------------------
3147
 */
3148
 
3149
int
3150
TkTextSeeCmd(textPtr, interp, argc, argv)
3151
    TkText *textPtr;            /* Information about text widget. */
3152
    Tcl_Interp *interp;         /* Current interpreter. */
3153
    int argc;                   /* Number of arguments. */
3154
    char **argv;                /* Argument strings.  Someone else has already
3155
                                 * parsed this command enough to know that
3156
                                 * argv[1] is "see". */
3157
{
3158
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
3159
    TkTextIndex index;
3160
    int x, y, width, height, lineWidth, charCount, oneThird, delta;
3161
    DLine *dlPtr;
3162
    TkTextDispChunk *chunkPtr;
3163
 
3164
    if (argc != 3) {
3165
        Tcl_AppendResult(interp, "wrong # args: should be \"",
3166
                argv[0], " see index\"", (char *) NULL);
3167
        return TCL_ERROR;
3168
    }
3169
 
3170
    if (TkTextGetIndex(interp, textPtr, argv[2], &index) != TCL_OK) {
3171
        return TCL_ERROR;
3172
    }
3173
 
3174
    /*
3175
     * If the specified position is the extra line at the end of the
3176
     * text, round it back to the last real line.
3177
     */
3178
 
3179
    if (TkBTreeLineIndex(index.linePtr) == TkBTreeNumLines(index.tree)) {
3180
        TkTextIndexBackChars(&index, 1, &index);
3181
    }
3182
 
3183
    /*
3184
     * First get the desired position into the vertical range of the window.
3185
     */
3186
 
3187
    TkTextSetYView(textPtr, &index, 1);
3188
 
3189
    /*
3190
     * Now make sure that the character is in view horizontally.
3191
     */
3192
 
3193
    if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
3194
        UpdateDisplayInfo(textPtr);
3195
    }
3196
    lineWidth = dInfoPtr->maxX - dInfoPtr->x;
3197
    if (dInfoPtr->maxLength < lineWidth) {
3198
        return TCL_OK;
3199
    }
3200
 
3201
    /*
3202
     * Find the chunk that contains the desired index.
3203
     */
3204
 
3205
    dlPtr = FindDLine(dInfoPtr->dLinePtr, &index);
3206
 
3207
    /*
3208
     * CYGNUS LOCAL: I can sometimes get FindDLine to return a null
3209
     * pointer.  I have not been able to find a simple test case,
3210
     * it happens in Gdbtk when you change the font for the debug window.
3211
     * Since you should not have to catch the see command, I have made
3212
     * the error silent...
3213
     */
3214
 
3215
    if (dlPtr == NULL) {
3216
        Tcl_AppendResult(interp, "got a null dlinePtr from FindDLine in the see command.",
3217
                (char *) NULL);
3218
        return TCL_OK;
3219
    }
3220
 
3221
    charCount = index.charIndex - dlPtr->index.charIndex;
3222
    for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
3223
        if (charCount < chunkPtr->numChars) {
3224
            break;
3225
        }
3226
        charCount -= chunkPtr->numChars;
3227
    }
3228
 
3229
    /*
3230
     * Call a chunk-specific procedure to find the horizontal range of
3231
     * the character within the chunk.
3232
     */
3233
 
3234
    (*chunkPtr->bboxProc)(chunkPtr, charCount, dlPtr->y + dlPtr->spaceAbove,
3235
            dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
3236
            dlPtr->baseline - dlPtr->spaceAbove, &x, &y, &width,
3237
            &height);
3238
    delta = x - dInfoPtr->curPixelOffset;
3239
    oneThird = lineWidth/3;
3240
    if (delta < 0) {
3241
        if (delta < -oneThird) {
3242
            dInfoPtr->newCharOffset = (x - lineWidth/2)/textPtr->charWidth;
3243
        } else {
3244
            dInfoPtr->newCharOffset -= ((-delta) + textPtr->charWidth - 1)
3245
                / textPtr->charWidth;
3246
        }
3247
    } else {
3248
        delta -= (lineWidth - width);
3249
        if (delta > 0) {
3250
            if (delta > oneThird) {
3251
                dInfoPtr->newCharOffset = (x - lineWidth/2)/textPtr->charWidth;
3252
            } else {
3253
                dInfoPtr->newCharOffset += (delta + textPtr->charWidth - 1)
3254
                    / textPtr->charWidth;
3255
            }
3256
        } else {
3257
            return TCL_OK;
3258
        }
3259
    }
3260
    dInfoPtr->flags |= DINFO_OUT_OF_DATE;
3261
    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
3262
        dInfoPtr->flags |= REDRAW_PENDING;
3263
        Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
3264
    }
3265
    return TCL_OK;
3266
}
3267
 
3268
/*
3269
 *--------------------------------------------------------------
3270
 *
3271
 * TkTextXviewCmd --
3272
 *
3273
 *      This procedure is invoked to process the "xview" option for
3274
 *      the widget command for text widgets. See the user documentation
3275
 *      for details on what it does.
3276
 *
3277
 * Results:
3278
 *      A standard Tcl result.
3279
 *
3280
 * Side effects:
3281
 *      See the user documentation.
3282
 *
3283
 *--------------------------------------------------------------
3284
 */
3285
 
3286
int
3287
TkTextXviewCmd(textPtr, interp, argc, argv)
3288
    TkText *textPtr;            /* Information about text widget. */
3289
    Tcl_Interp *interp;         /* Current interpreter. */
3290
    int argc;                   /* Number of arguments. */
3291
    char **argv;                /* Argument strings.  Someone else has already
3292
                                 * parsed this command enough to know that
3293
                                 * argv[1] is "xview". */
3294
{
3295
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
3296
    int type, charsPerPage, count, newOffset;
3297
    double fraction;
3298
 
3299
    if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
3300
        UpdateDisplayInfo(textPtr);
3301
    }
3302
 
3303
    if (argc == 2) {
3304
        GetXView(interp, textPtr, 0);
3305
        return TCL_OK;
3306
    }
3307
 
3308
    newOffset = dInfoPtr->newCharOffset;
3309
    type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
3310
    switch (type) {
3311
        case TK_SCROLL_ERROR:
3312
            return TCL_ERROR;
3313
        case TK_SCROLL_MOVETO:
3314
            if (fraction > 1.0) {
3315
                fraction = 1.0;
3316
            }
3317
            if (fraction < 0) {
3318
                fraction = 0;
3319
            }
3320
            newOffset = (int) (((fraction * dInfoPtr->maxLength) / textPtr->charWidth)
3321
                    + 0.5);
3322
            break;
3323
        case TK_SCROLL_PAGES:
3324
            charsPerPage = ((dInfoPtr->maxX - dInfoPtr->x) / textPtr->charWidth)
3325
                    - 2;
3326
            if (charsPerPage < 1) {
3327
                charsPerPage = 1;
3328
            }
3329
            newOffset += charsPerPage*count;
3330
            break;
3331
        case TK_SCROLL_UNITS:
3332
            newOffset += count;
3333
            break;
3334
    }
3335
 
3336
    dInfoPtr->newCharOffset = newOffset;
3337
    dInfoPtr->flags |= DINFO_OUT_OF_DATE;
3338
    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
3339
        dInfoPtr->flags |= REDRAW_PENDING;
3340
        Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
3341
    }
3342
    return TCL_OK;
3343
}
3344
 
3345
/*
3346
 *----------------------------------------------------------------------
3347
 *
3348
 * ScrollByLines --
3349
 *
3350
 *      This procedure is called to scroll a text widget up or down
3351
 *      by a given number of lines.
3352
 *
3353
 * Results:
3354
 *      None.
3355
 *
3356
 * Side effects:
3357
 *      The view in textPtr's window changes to reflect the value
3358
 *      of "offset".
3359
 *
3360
 *----------------------------------------------------------------------
3361
 */
3362
 
3363
static void
3364
ScrollByLines(textPtr, offset)
3365
    TkText *textPtr;            /* Widget to scroll. */
3366
    int offset;                 /* Amount by which to scroll, in *screen*
3367
                                 * lines.  Positive means that information
3368
                                 * later in text becomes visible, negative
3369
                                 * means that information earlier in the
3370
                                 * text becomes visible. */
3371
{
3372
    int i, charsToCount, lineNum;
3373
    TkTextIndex new, index;
3374
    TkTextLine *lastLinePtr;
3375
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
3376
    DLine *dlPtr, *lowestPtr;
3377
 
3378
    if (offset < 0) {
3379
        /*
3380
         * Must scroll up (to show earlier information in the text).
3381
         * The code below is similar to that in MeasureUp, except that
3382
         * it counts lines instead of pixels.
3383
         */
3384
 
3385
        charsToCount = textPtr->topIndex.charIndex + 1;
3386
        index.tree = textPtr->tree;
3387
        offset--;                       /* Skip line containing topIndex. */
3388
        for (lineNum = TkBTreeLineIndex(textPtr->topIndex.linePtr);
3389
                lineNum >= 0; lineNum--) {
3390
            index.linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
3391
            index.charIndex = 0;
3392
            lowestPtr = NULL;
3393
            do {
3394
                dlPtr = LayoutDLine(textPtr, &index);
3395
                dlPtr->nextPtr = lowestPtr;
3396
                lowestPtr = dlPtr;
3397
                TkTextIndexForwChars(&index, dlPtr->count, &index);
3398
                charsToCount -= dlPtr->count;
3399
            } while ((charsToCount > 0)
3400
                    && (index.linePtr == dlPtr->index.linePtr));
3401
 
3402
            for (dlPtr = lowestPtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
3403
                offset++;
3404
                if (offset == 0) {
3405
                    textPtr->topIndex = dlPtr->index;
3406
                    break;
3407
                }
3408
            }
3409
 
3410
            /*
3411
             * Discard the display lines, then either return or prepare
3412
             * for the next display line to lay out.
3413
             */
3414
 
3415
            FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
3416
            if (offset >= 0) {
3417
                goto scheduleUpdate;
3418
            }
3419
            charsToCount = INT_MAX;
3420
        }
3421
 
3422
        /*
3423
         * Ran off the beginning of the text.  Return the first character
3424
         * in the text.
3425
         */
3426
 
3427
        TkTextMakeIndex(textPtr->tree, 0, 0, &textPtr->topIndex);
3428
    } else {
3429
        /*
3430
         * Scrolling down, to show later information in the text.
3431
         * Just count lines from the current top of the window.
3432
         */
3433
 
3434
        lastLinePtr = TkBTreeFindLine(textPtr->tree,
3435
                TkBTreeNumLines(textPtr->tree));
3436
        for (i = 0; i < offset; i++) {
3437
            dlPtr = LayoutDLine(textPtr, &textPtr->topIndex);
3438
            dlPtr->nextPtr = NULL;
3439
            TkTextIndexForwChars(&textPtr->topIndex, dlPtr->count, &new);
3440
            FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
3441
            if (new.linePtr == lastLinePtr) {
3442
                break;
3443
            }
3444
            textPtr->topIndex = new;
3445
        }
3446
    }
3447
 
3448
    scheduleUpdate:
3449
    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
3450
        Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
3451
    }
3452
    dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
3453
}
3454
 
3455
/*
3456
 *--------------------------------------------------------------
3457
 *
3458
 * TkTextYviewCmd --
3459
 *
3460
 *      This procedure is invoked to process the "yview" option for
3461
 *      the widget command for text widgets. See the user documentation
3462
 *      for details on what it does.
3463
 *
3464
 * Results:
3465
 *      A standard Tcl result.
3466
 *
3467
 * Side effects:
3468
 *      See the user documentation.
3469
 *
3470
 *--------------------------------------------------------------
3471
 */
3472
 
3473
int
3474
TkTextYviewCmd(textPtr, interp, argc, argv)
3475
    TkText *textPtr;            /* Information about text widget. */
3476
    Tcl_Interp *interp;         /* Current interpreter. */
3477
    int argc;                   /* Number of arguments. */
3478
    char **argv;                /* Argument strings.  Someone else has already
3479
                                 * parsed this command enough to know that
3480
                                 * argv[1] is "yview". */
3481
{
3482
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
3483
    int pickPlace, lineNum, type, charsInLine;
3484
    Tk_FontMetrics fm;
3485
    int pixels, count;
3486
    size_t switchLength;
3487
    double fraction;
3488
    TkTextIndex index, new;
3489
    TkTextLine *lastLinePtr;
3490
    DLine *dlPtr;
3491
 
3492
    if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
3493
        UpdateDisplayInfo(textPtr);
3494
    }
3495
 
3496
    if (argc == 2) {
3497
        GetYView(interp, textPtr, 0);
3498
        return TCL_OK;
3499
    }
3500
 
3501
    /*
3502
     * Next, handle the old syntax: "pathName yview ?-pickplace? where"
3503
     */
3504
 
3505
    pickPlace = 0;
3506
    if (argv[2][0] == '-') {
3507
        switchLength = strlen(argv[2]);
3508
        if ((switchLength >= 2)
3509
                && (strncmp(argv[2], "-pickplace", switchLength) == 0)) {
3510
            pickPlace = 1;
3511
            if (argc != 4) {
3512
                Tcl_AppendResult(interp, "wrong # args: should be \"",
3513
                        argv[0], " yview -pickplace lineNum|index\"",
3514
                        (char *) NULL);
3515
                return TCL_ERROR;
3516
            }
3517
        }
3518
    }
3519
    if ((argc == 3) || pickPlace) {
3520
        if (Tcl_GetInt(interp, argv[2+pickPlace], &lineNum) == TCL_OK) {
3521
            TkTextMakeIndex(textPtr->tree, lineNum, 0, &index);
3522
            TkTextSetYView(textPtr, &index, 0);
3523
            return TCL_OK;
3524
        }
3525
 
3526
        /*
3527
         * The argument must be a regular text index.
3528
         */
3529
 
3530
        Tcl_ResetResult(interp);
3531
        if (TkTextGetIndex(interp, textPtr, argv[2+pickPlace],
3532
                &index) != TCL_OK) {
3533
            return TCL_ERROR;
3534
        }
3535
        TkTextSetYView(textPtr, &index, pickPlace);
3536
        return TCL_OK;
3537
    }
3538
 
3539
    /*
3540
     * New syntax: dispatch based on argv[2].
3541
     */
3542
 
3543
    type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
3544
    switch (type) {
3545
        case TK_SCROLL_ERROR:
3546
            return TCL_ERROR;
3547
        case TK_SCROLL_MOVETO:
3548
            if (fraction > 1.0) {
3549
                fraction = 1.0;
3550
            }
3551
            if (fraction < 0) {
3552
                fraction = 0;
3553
            }
3554
            fraction *= TkBTreeNumLines(textPtr->tree);
3555
            lineNum = (int) fraction;
3556
            TkTextMakeIndex(textPtr->tree, lineNum, 0, &index);
3557
            charsInLine = TkBTreeCharsInLine(index.linePtr);
3558
            index.charIndex = (int)((charsInLine * (fraction-lineNum)) + 0.5);
3559
            if (index.charIndex >= charsInLine) {
3560
                TkTextMakeIndex(textPtr->tree, lineNum+1, 0, &index);
3561
            }
3562
            TkTextSetYView(textPtr, &index, 0);
3563
            break;
3564
        case TK_SCROLL_PAGES:
3565
            /*
3566
             * Scroll up or down by screenfuls.  Actually, use the
3567
             * window height minus two lines, so that there's some
3568
             * overlap between adjacent pages.
3569
             */
3570
 
3571
            Tk_GetFontMetrics(textPtr->tkfont, &fm);
3572
            if (count < 0) {
3573
                pixels = (dInfoPtr->maxY - 2*fm.linespace - dInfoPtr->y)*(-count)
3574
                        + fm.linespace;
3575
                MeasureUp(textPtr, &textPtr->topIndex, pixels, &new);
3576
                if (TkTextIndexCmp(&textPtr->topIndex, &new) == 0) {
3577
                    /*
3578
                     * A page of scrolling ended up being less than one line.
3579
                     * Scroll one line anyway.
3580
                     */
3581
 
3582
                    count = -1;
3583
                    goto scrollByLines;
3584
                }
3585
                textPtr->topIndex = new;
3586
            } else {
3587
                /*
3588
                 * Scrolling down by pages.  Layout lines starting at the
3589
                 * top index and count through the desired vertical distance.
3590
                 */
3591
 
3592
                pixels = (dInfoPtr->maxY - 2*fm.linespace - dInfoPtr->y)*count;
3593
                lastLinePtr = TkBTreeFindLine(textPtr->tree,
3594
                        TkBTreeNumLines(textPtr->tree));
3595
                do {
3596
                    dlPtr = LayoutDLine(textPtr, &textPtr->topIndex);
3597
                    dlPtr->nextPtr = NULL;
3598
                    TkTextIndexForwChars(&textPtr->topIndex, dlPtr->count,
3599
                            &new);
3600
                    pixels -= dlPtr->height;
3601
                    FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
3602
                    if (new.linePtr == lastLinePtr) {
3603
                        break;
3604
                    }
3605
                    textPtr->topIndex = new;
3606
                } while (pixels > 0);
3607
            }
3608
            if (!(dInfoPtr->flags & REDRAW_PENDING)) {
3609
                Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
3610
            }
3611
            dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
3612
            break;
3613
        case TK_SCROLL_UNITS:
3614
            scrollByLines:
3615
            ScrollByLines(textPtr, count);
3616
            break;
3617
    }
3618
    return TCL_OK;
3619
}
3620
 
3621
/*
3622
 *--------------------------------------------------------------
3623
 *
3624
 * TkTextScanCmd --
3625
 *
3626
 *      This procedure is invoked to process the "scan" option for
3627
 *      the widget command for text widgets. See the user documentation
3628
 *      for details on what it does.
3629
 *
3630
 * Results:
3631
 *      A standard Tcl result.
3632
 *
3633
 * Side effects:
3634
 *      See the user documentation.
3635
 *
3636
 *--------------------------------------------------------------
3637
 */
3638
 
3639
int
3640
TkTextScanCmd(textPtr, interp, argc, argv)
3641
    register TkText *textPtr;   /* Information about text widget. */
3642
    Tcl_Interp *interp;         /* Current interpreter. */
3643
    int argc;                   /* Number of arguments. */
3644
    char **argv;                /* Argument strings.  Someone else has already
3645
                                 * parsed this command enough to know that
3646
                                 * argv[1] is "scan". */
3647
{
3648
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
3649
    TkTextIndex index;
3650
    int c, x, y, totalScroll, newChar, maxChar;
3651
    Tk_FontMetrics fm;
3652
    size_t length;
3653
 
3654
    if (argc != 5) {
3655
        Tcl_AppendResult(interp, "wrong # args: should be \"",
3656
                argv[0], " scan mark|dragto x y\"", (char *) NULL);
3657
        return TCL_ERROR;
3658
    }
3659
    if (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) {
3660
        return TCL_ERROR;
3661
    }
3662
    if (Tcl_GetInt(interp, argv[4], &y) != TCL_OK) {
3663
        return TCL_ERROR;
3664
    }
3665
    c = argv[2][0];
3666
    length = strlen(argv[2]);
3667
    if ((c == 'd') && (strncmp(argv[2], "dragto", length) == 0)) {
3668
        /*
3669
         * Amplify the difference between the current position and the
3670
         * mark position to compute how much the view should shift, then
3671
         * update the mark position to correspond to the new view.  If we
3672
         * run off the edge of the text, reset the mark point so that the
3673
         * current position continues to correspond to the edge of the
3674
         * window.  This means that the picture will start dragging as
3675
         * soon as the mouse reverses direction (without this reset, might
3676
         * have to slide mouse a long ways back before the picture starts
3677
         * moving again).
3678
         */
3679
 
3680
        newChar = dInfoPtr->scanMarkChar + (10*(dInfoPtr->scanMarkX - x))
3681
                / (textPtr->charWidth);
3682
        maxChar = 1 + (dInfoPtr->maxLength - (dInfoPtr->maxX - dInfoPtr->x)
3683
                + textPtr->charWidth - 1)/textPtr->charWidth;
3684
        if (newChar < 0) {
3685
            dInfoPtr->scanMarkChar = newChar = 0;
3686
            dInfoPtr->scanMarkX = x;
3687
        } else if (newChar > maxChar) {
3688
            dInfoPtr->scanMarkChar = newChar = maxChar;
3689
            dInfoPtr->scanMarkX = x;
3690
        }
3691
        dInfoPtr->newCharOffset = newChar;
3692
 
3693
        Tk_GetFontMetrics(textPtr->tkfont, &fm);
3694
        totalScroll = (10*(dInfoPtr->scanMarkY - y)) / fm.linespace;
3695
        if (totalScroll != dInfoPtr->scanTotalScroll) {
3696
            index = textPtr->topIndex;
3697
            ScrollByLines(textPtr, totalScroll-dInfoPtr->scanTotalScroll);
3698
            dInfoPtr->scanTotalScroll = totalScroll;
3699
            if ((index.linePtr == textPtr->topIndex.linePtr) &&
3700
                    (index.charIndex == textPtr->topIndex.charIndex)) {
3701
                dInfoPtr->scanTotalScroll = 0;
3702
                dInfoPtr->scanMarkY = y;
3703
            }
3704
        }
3705
    } else if ((c == 'm') && (strncmp(argv[2], "mark", length) == 0)) {
3706
        dInfoPtr->scanMarkChar = dInfoPtr->newCharOffset;
3707
        dInfoPtr->scanMarkX = x;
3708
        dInfoPtr->scanTotalScroll = 0;
3709
        dInfoPtr->scanMarkY = y;
3710
    } else {
3711
        Tcl_AppendResult(interp, "bad scan option \"", argv[2],
3712
                "\": must be mark or dragto", (char *) NULL);
3713
        return TCL_ERROR;
3714
    }
3715
    dInfoPtr->flags |= DINFO_OUT_OF_DATE;
3716
    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
3717
        dInfoPtr->flags |= REDRAW_PENDING;
3718
        Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
3719
    }
3720
    return TCL_OK;
3721
}
3722
 
3723
/*
3724
 *----------------------------------------------------------------------
3725
 *
3726
 * GetXView --
3727
 *
3728
 *      This procedure computes the fractions that indicate what's
3729
 *      visible in a text window and, optionally, evaluates a
3730
 *      Tcl script to report them to the text's associated scrollbar.
3731
 *
3732
 * Results:
3733
 *      If report is zero, then interp->result is filled in with
3734
 *      two real numbers separated by a space, giving the position of
3735
 *      the left and right edges of the window as fractions from 0 to
3736
 *      1, where 0 means the left edge of the text and 1 means the right
3737
 *      edge.  If report is non-zero, then interp->result isn't modified
3738
 *      directly, but instead a script is evaluated in interp to report
3739
 *      the new horizontal scroll position to the scrollbar (if the scroll
3740
 *      position hasn't changed then no script is invoked).
3741
 *
3742
 * Side effects:
3743
 *      None.
3744
 *
3745
 *----------------------------------------------------------------------
3746
 */
3747
 
3748
static void
3749
GetXView(interp, textPtr, report)
3750
    Tcl_Interp *interp;                 /* If "report" is FALSE, string
3751
                                         * describing visible range gets
3752
                                         * stored in interp->result. */
3753
    TkText *textPtr;                    /* Information about text widget. */
3754
    int report;                         /* Non-zero means report info to
3755
                                         * scrollbar if it has changed. */
3756
{
3757
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
3758
    char buffer[200];
3759
    double first, last;
3760
    int code;
3761
 
3762
    if (dInfoPtr->maxLength > 0) {
3763
        first = ((double) dInfoPtr->curPixelOffset)
3764
                / dInfoPtr->maxLength;
3765
        last = first + ((double) (dInfoPtr->maxX - dInfoPtr->x))
3766
                / dInfoPtr->maxLength;
3767
        if (last > 1.0) {
3768
            last = 1.0;
3769
        }
3770
    } else {
3771
        first = 0;
3772
        last = 1.0;
3773
    }
3774
    if (!report) {
3775
        sprintf(interp->result, "%g %g", first, last);
3776
        return;
3777
    }
3778
    if ((first == dInfoPtr->xScrollFirst) && (last == dInfoPtr->xScrollLast)) {
3779
        return;
3780
    }
3781
    dInfoPtr->xScrollFirst = first;
3782
    dInfoPtr->xScrollLast = last;
3783
    sprintf(buffer, " %g %g", first, last);
3784
    code = Tcl_VarEval(interp, textPtr->xScrollCmd,
3785
            buffer, (char *) NULL);
3786
    if (code != TCL_OK) {
3787
        Tcl_AddErrorInfo(interp,
3788
                "\n    (horizontal scrolling command executed by text)");
3789
        Tcl_BackgroundError(interp);
3790
    }
3791
}
3792
 
3793
/*
3794
 *----------------------------------------------------------------------
3795
 *
3796
 * GetYView --
3797
 *
3798
 *      This procedure computes the fractions that indicate what's
3799
 *      visible in a text window and, optionally, evaluates a
3800
 *      Tcl script to report them to the text's associated scrollbar.
3801
 *
3802
 * Results:
3803
 *      If report is zero, then interp->result is filled in with
3804
 *      two real numbers separated by a space, giving the position of
3805
 *      the top and bottom of the window as fractions from 0 to 1, where
3806
 *      0 means the beginning of the text and 1 means the end.  If
3807
 *      report is non-zero, then interp->result isn't modified directly,
3808
 *      but a script is evaluated in interp to report the new scroll
3809
 *      position to the scrollbar (if the scroll position hasn't changed
3810
 *      then no script is invoked).
3811
 *
3812
 * Side effects:
3813
 *      None.
3814
 *
3815
 *----------------------------------------------------------------------
3816
 */
3817
 
3818
static void
3819
GetYView(interp, textPtr, report)
3820
    Tcl_Interp *interp;                 /* If "report" is FALSE, string
3821
                                         * describing visible range gets
3822
                                         * stored in interp->result. */
3823
    TkText *textPtr;                    /* Information about text widget. */
3824
    int report;                         /* Non-zero means report info to
3825
                                         * scrollbar if it has changed. */
3826
{
3827
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
3828
    char buffer[200];
3829
    double first, last;
3830
    DLine *dlPtr;
3831
    int totalLines, code, count;
3832
 
3833
    dlPtr = dInfoPtr->dLinePtr;
3834
    totalLines = TkBTreeNumLines(textPtr->tree);
3835
    first = ((double) TkBTreeLineIndex(dlPtr->index.linePtr))
3836
            + ((double) dlPtr->index.charIndex)
3837
            / (TkBTreeCharsInLine(dlPtr->index.linePtr));
3838
    first /= totalLines;
3839
    while (1) {
3840
        if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
3841
            /*
3842
             * The last line is only partially visible, so don't
3843
             * count its characters in what's visible.
3844
             */
3845
            count = 0;
3846
            break;
3847
        }
3848
        if (dlPtr->nextPtr == NULL) {
3849
            count = dlPtr->count;
3850
            break;
3851
        }
3852
        dlPtr = dlPtr->nextPtr;
3853
    }
3854
    last = ((double) TkBTreeLineIndex(dlPtr->index.linePtr))
3855
            + ((double) (dlPtr->index.charIndex + count))
3856
            / (TkBTreeCharsInLine(dlPtr->index.linePtr));
3857
    last /= totalLines;
3858
    if (!report) {
3859
        sprintf(interp->result, "%g %g", first, last);
3860
        return;
3861
    }
3862
    if ((first == dInfoPtr->yScrollFirst) && (last == dInfoPtr->yScrollLast)) {
3863
        return;
3864
    }
3865
    dInfoPtr->yScrollFirst = first;
3866
    dInfoPtr->yScrollLast = last;
3867
    sprintf(buffer, " %g %g", first, last);
3868
    code = Tcl_VarEval(interp, textPtr->yScrollCmd,
3869
            buffer, (char *) NULL);
3870
    if (code != TCL_OK) {
3871
        Tcl_AddErrorInfo(interp,
3872
                "\n    (vertical scrolling command executed by text)");
3873
        Tcl_BackgroundError(interp);
3874
    }
3875
}
3876
 
3877
/*
3878
 *----------------------------------------------------------------------
3879
 *
3880
 * FindDLine --
3881
 *
3882
 *      This procedure is called to find the DLine corresponding to a
3883
 *      given text index.
3884
 *
3885
 * Results:
3886
 *      The return value is a pointer to the first DLine found in the
3887
 *      list headed by dlPtr that displays information at or after the
3888
 *      specified position.  If there is no such line in the list then
3889
 *      NULL is returned.
3890
 *
3891
 * Side effects:
3892
 *      None.
3893
 *
3894
 *----------------------------------------------------------------------
3895
 */
3896
 
3897
static DLine *
3898
FindDLine(dlPtr, indexPtr)
3899
    register DLine *dlPtr;      /* Pointer to first in list of DLines
3900
                                 * to search. */
3901
    TkTextIndex *indexPtr;      /* Index of desired character. */
3902
{
3903
    TkTextLine *linePtr;
3904
 
3905
    if (dlPtr == NULL) {
3906
        return NULL;
3907
    }
3908
    if (TkBTreeLineIndex(indexPtr->linePtr)
3909
            < TkBTreeLineIndex(dlPtr->index.linePtr)) {
3910
        /*
3911
         * The first display line is already past the desired line.
3912
         */
3913
        return dlPtr;
3914
    }
3915
 
3916
    /*
3917
     * Find the first display line that covers the desired text line.
3918
     */
3919
 
3920
    linePtr = dlPtr->index.linePtr;
3921
    while (linePtr != indexPtr->linePtr) {
3922
        while (dlPtr->index.linePtr == linePtr) {
3923
            dlPtr = dlPtr->nextPtr;
3924
            if (dlPtr == NULL) {
3925
                return NULL;
3926
            }
3927
        }
3928
        linePtr = TkBTreeNextLine(linePtr);
3929
        if (linePtr == NULL) {
3930
            panic("FindDLine reached end of text");
3931
        }
3932
    }
3933
    if (indexPtr->linePtr != dlPtr->index.linePtr) {
3934
        return dlPtr;
3935
    }
3936
 
3937
    /*
3938
     * Now get to the right position within the text line.
3939
     */
3940
 
3941
    while (indexPtr->charIndex >= (dlPtr->index.charIndex + dlPtr->count)) {
3942
        dlPtr = dlPtr->nextPtr;
3943
        if ((dlPtr == NULL) || (dlPtr->index.linePtr != indexPtr->linePtr)) {
3944
            break;
3945
        }
3946
    }
3947
    return dlPtr;
3948
}
3949
 
3950
/*
3951
 *----------------------------------------------------------------------
3952
 *
3953
 * TkTextPixelIndex --
3954
 *
3955
 *      Given an (x,y) coordinate on the screen, find the location of
3956
 *      the character closest to that location.
3957
 *
3958
 * Results:
3959
 *      The index at *indexPtr is modified to refer to the character
3960
 *      on the display that is closest to (x,y).
3961
 *
3962
 * Side effects:
3963
 *      None.
3964
 *
3965
 *----------------------------------------------------------------------
3966
 */
3967
 
3968
void
3969
TkTextPixelIndex(textPtr, x, y, indexPtr)
3970
    TkText *textPtr;            /* Widget record for text widget. */
3971
    int x, y;                   /* Pixel coordinates of point in widget's
3972
                                 * window. */
3973
    TkTextIndex *indexPtr;      /* This index gets filled in with the
3974
                                 * index of the character nearest to (x,y). */
3975
{
3976
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
3977
    register DLine *dlPtr;
3978
    register TkTextDispChunk *chunkPtr;
3979
 
3980
    /*
3981
     * Make sure that all of the layout information about what's
3982
     * displayed where on the screen is up-to-date.
3983
     */
3984
 
3985
    if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
3986
        UpdateDisplayInfo(textPtr);
3987
    }
3988
 
3989
    /*
3990
     * If the coordinates are above the top of the window, then adjust
3991
     * them to refer to the upper-right corner of the window.  If they're
3992
     * off to one side or the other, then adjust to the closest side.
3993
     */
3994
 
3995
    if (y < dInfoPtr->y) {
3996
        y = dInfoPtr->y;
3997
        x = dInfoPtr->x;
3998
    }
3999
    if (x >= dInfoPtr->maxX) {
4000
        x = dInfoPtr->maxX - 1;
4001
    }
4002
    if (x < dInfoPtr->x) {
4003
        x = dInfoPtr->x;
4004
    }
4005
 
4006
    /*
4007
     * Find the display line containing the desired y-coordinate.
4008
     */
4009
 
4010
    for (dlPtr = dInfoPtr->dLinePtr; y >= (dlPtr->y + dlPtr->height);
4011
            dlPtr = dlPtr->nextPtr) {
4012
        if (dlPtr->nextPtr == NULL) {
4013
            /*
4014
             * Y-coordinate is off the bottom of the displayed text.
4015
             * Use the last character on the last line.
4016
             */
4017
 
4018
            x = dInfoPtr->maxX - 1;
4019
            break;
4020
        }
4021
    }
4022
 
4023
    /*
4024
     * Scan through the line's chunks to find the one that contains
4025
     * the desired x-coordinate.  Before doing this, translate the
4026
     * x-coordinate from the coordinate system of the window to the
4027
     * coordinate system of the line (to take account of x-scrolling).
4028
     */
4029
 
4030
    *indexPtr = dlPtr->index;
4031
    x = x - dInfoPtr->x + dInfoPtr->curPixelOffset;
4032
    for (chunkPtr = dlPtr->chunkPtr; x >= (chunkPtr->x + chunkPtr->width);
4033
            indexPtr->charIndex += chunkPtr->numChars,
4034
            chunkPtr = chunkPtr->nextPtr) {
4035
        if (chunkPtr->nextPtr == NULL) {
4036
            indexPtr->charIndex += chunkPtr->numChars - 1;
4037
            return;
4038
        }
4039
    }
4040
 
4041
    /*
4042
     * If the chunk has more than one character in it, ask it which
4043
     * character is at the desired location.
4044
     */
4045
 
4046
    if (chunkPtr->numChars > 1) {
4047
        indexPtr->charIndex += (*chunkPtr->measureProc)(chunkPtr, x);
4048
    }
4049
}
4050
 
4051
/*
4052
 *----------------------------------------------------------------------
4053
 *
4054
 * TkTextCharBbox --
4055
 *
4056
 *      Given an index, find the bounding box of the screen area
4057
 *      occupied by that character.
4058
 *
4059
 * Results:
4060
 *      Zero is returned if the character is on the screen.  -1
4061
 *      means the character isn't on the screen.  If the return value
4062
 *      is 0, then the bounding box of the part of the character that's
4063
 *      visible on the screen is returned to *xPtr, *yPtr, *widthPtr,
4064
 *      and *heightPtr.
4065
 *
4066
 * Side effects:
4067
 *      None.
4068
 *
4069
 *----------------------------------------------------------------------
4070
 */
4071
 
4072
int
4073
TkTextCharBbox(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr)
4074
    TkText *textPtr;            /* Widget record for text widget. */
4075
    TkTextIndex *indexPtr;      /* Index of character whose bounding
4076
                                 * box is desired. */
4077
    int *xPtr, *yPtr;           /* Filled with character's upper-left
4078
                                 * coordinate. */
4079
    int *widthPtr, *heightPtr;  /* Filled in with character's dimensions. */
4080
{
4081
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
4082
    DLine *dlPtr;
4083
    register TkTextDispChunk *chunkPtr;
4084
    int index;
4085
 
4086
    /*
4087
     * Make sure that all of the screen layout information is up to date.
4088
     */
4089
 
4090
    if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
4091
        UpdateDisplayInfo(textPtr);
4092
    }
4093
 
4094
    /*
4095
     * Find the display line containing the desired index.
4096
     */
4097
 
4098
    dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
4099
    if ((dlPtr == NULL) || (TkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) {
4100
        return -1;
4101
    }
4102
 
4103
    /*
4104
     * Find the chunk within the line that contains the desired
4105
     * index.
4106
     */
4107
 
4108
    index = indexPtr->charIndex - dlPtr->index.charIndex;
4109
    for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
4110
        if (chunkPtr == NULL) {
4111
            return -1;
4112
        }
4113
        if (index < chunkPtr->numChars) {
4114
            break;
4115
        }
4116
        index -= chunkPtr->numChars;
4117
    }
4118
 
4119
    /*
4120
     * Call a chunk-specific procedure to find the horizontal range of
4121
     * the character within the chunk, then fill in the vertical range.
4122
     * The x-coordinate returned by bboxProc is a coordinate within a
4123
     * line, not a coordinate on the screen.  Translate it to reflect
4124
     * horizontal scrolling.
4125
     */
4126
 
4127
    (*chunkPtr->bboxProc)(chunkPtr, index, dlPtr->y + dlPtr->spaceAbove,
4128
            dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
4129
            dlPtr->baseline - dlPtr->spaceAbove, xPtr, yPtr, widthPtr,
4130
            heightPtr);
4131
    *xPtr = *xPtr + dInfoPtr->x - dInfoPtr->curPixelOffset;
4132
    if ((index == (chunkPtr->numChars-1)) && (chunkPtr->nextPtr == NULL)) {
4133
        /*
4134
         * Last character in display line.  Give it all the space up to
4135
         * the line.
4136
         */
4137
 
4138
        if (*xPtr > dInfoPtr->maxX) {
4139
            *xPtr = dInfoPtr->maxX;
4140
        }
4141
        *widthPtr = dInfoPtr->maxX - *xPtr;
4142
    }
4143
    if ((*xPtr + *widthPtr) <= dInfoPtr->x) {
4144
        return -1;
4145
    }
4146
    if ((*xPtr + *widthPtr) > dInfoPtr->maxX) {
4147
        *widthPtr = dInfoPtr->maxX - *xPtr;
4148
        if (*widthPtr <= 0) {
4149
            return -1;
4150
        }
4151
    }
4152
    if ((*yPtr + *heightPtr) > dInfoPtr->maxY) {
4153
        *heightPtr = dInfoPtr->maxY - *yPtr;
4154
        if (*heightPtr <= 0) {
4155
            return -1;
4156
        }
4157
    }
4158
    return 0;
4159
}
4160
 
4161
/*
4162
 *----------------------------------------------------------------------
4163
 *
4164
 * TkTextDLineInfo --
4165
 *
4166
 *      Given an index, return information about the display line
4167
 *      containing that character.
4168
 *
4169
 * Results:
4170
 *      Zero is returned if the character is on the screen.  -1
4171
 *      means the character isn't on the screen.  If the return value
4172
 *      is 0, then information is returned in the variables pointed
4173
 *      to by xPtr, yPtr, widthPtr, heightPtr, and basePtr.
4174
 *
4175
 * Side effects:
4176
 *      None.
4177
 *
4178
 *----------------------------------------------------------------------
4179
 */
4180
 
4181
int
4182
TkTextDLineInfo(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr, basePtr)
4183
    TkText *textPtr;            /* Widget record for text widget. */
4184
    TkTextIndex *indexPtr;      /* Index of character whose bounding
4185
                                 * box is desired. */
4186
    int *xPtr, *yPtr;           /* Filled with line's upper-left
4187
                                 * coordinate. */
4188
    int *widthPtr, *heightPtr;  /* Filled in with line's dimensions. */
4189
    int *basePtr;               /* Filled in with the baseline position,
4190
                                 * measured as an offset down from *yPtr. */
4191
{
4192
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
4193
    DLine *dlPtr;
4194
 
4195
    /*
4196
     * Make sure that all of the screen layout information is up to date.
4197
     */
4198
 
4199
    if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
4200
        UpdateDisplayInfo(textPtr);
4201
    }
4202
 
4203
    /*
4204
     * Find the display line containing the desired index.
4205
     */
4206
 
4207
    dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
4208
    if ((dlPtr == NULL) || (TkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) {
4209
        return -1;
4210
    }
4211
 
4212
    *xPtr = dInfoPtr->x - dInfoPtr->curPixelOffset + dlPtr->chunkPtr->x;
4213
    *widthPtr = dlPtr->length - dlPtr->chunkPtr->x;
4214
    *yPtr = dlPtr->y;
4215
    if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
4216
        *heightPtr = dInfoPtr->maxY - dlPtr->y;
4217
    } else {
4218
        *heightPtr = dlPtr->height;
4219
    }
4220
    *basePtr = dlPtr->baseline;
4221
    return 0;
4222
}
4223
 
4224
/*
4225
 *--------------------------------------------------------------
4226
 *
4227
 * TkTextCharLayoutProc --
4228
 *
4229
 *      This procedure is the "layoutProc" for character segments.
4230
 *
4231
 * Results:
4232
 *      If there is something to display for the chunk then a
4233
 *      non-zero value is returned and the fields of chunkPtr
4234
 *      will be filled in (see the declaration of TkTextDispChunk
4235
 *      in tkText.h for details).  If zero is returned it means
4236
 *      that no characters from this chunk fit in the window.
4237
 *      If -1 is returned it means that this segment just doesn't
4238
 *      need to be displayed (never happens for text).
4239
 *
4240
 * Side effects:
4241
 *      Memory is allocated to hold additional information about
4242
 *      the chunk.
4243
 *
4244
 *--------------------------------------------------------------
4245
 */
4246
 
4247
int
4248
TkTextCharLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars,
4249
        noCharsYet, wrapMode, chunkPtr)
4250
    TkText *textPtr;            /* Text widget being layed out. */
4251
    TkTextIndex *indexPtr;      /* Index of first character to lay out
4252
                                 * (corresponds to segPtr and offset). */
4253
    TkTextSegment *segPtr;      /* Segment being layed out. */
4254
    int offset;                 /* Offset within segment of first character
4255
                                 * to consider. */
4256
    int maxX;                   /* Chunk must not occupy pixels at this
4257
                                 * position or higher. */
4258
    int maxChars;               /* Chunk must not include more than this
4259
                                 * many characters. */
4260
    int noCharsYet;             /* Non-zero means no characters have been
4261
                                 * assigned to this display line yet. */
4262
    Tk_Uid wrapMode;            /* How to handle line wrapping: tkTextCharUid,
4263
                                 * tkTextNoneUid, or tkTextWordUid. */
4264
    register TkTextDispChunk *chunkPtr;
4265
                                /* Structure to fill in with information
4266
                                 * about this chunk.  The x field has already
4267
                                 * been set by the caller. */
4268
{
4269
    Tk_Font tkfont;
4270
    int nextX, charsThatFit, count;
4271
    CharInfo *ciPtr;
4272
    char *p;
4273
    TkTextSegment *nextPtr;
4274
    Tk_FontMetrics fm;
4275
 
4276
    /*
4277
     * Figure out how many characters will fit in the space we've got.
4278
     * Include the next character, even though it won't fit completely,
4279
     * if any of the following is true:
4280
     *   (a) the chunk contains no characters and the display line contains
4281
     *       no characters yet (i.e. the line isn't wide enough to hold
4282
     *       even a single character).
4283
     *   (b) at least one pixel of the character is visible, we haven't
4284
     *       already exceeded the character limit, and the next character
4285
     *       is a white space character.
4286
     */
4287
 
4288
    p = segPtr->body.chars + offset;
4289
    tkfont = chunkPtr->stylePtr->sValuePtr->tkfont;
4290
    charsThatFit = MeasureChars(tkfont, p, maxChars, chunkPtr->x, maxX, 0,
4291
            &nextX);
4292
    if (charsThatFit < maxChars) {
4293
        if ((charsThatFit == 0) && noCharsYet) {
4294
            charsThatFit = 1;
4295
            MeasureChars(tkfont, p, 1, chunkPtr->x, INT_MAX, 0, &nextX);
4296
        }
4297
        if ((nextX < maxX) && ((p[charsThatFit] == ' ')
4298
                || (p[charsThatFit] == '\t'))) {
4299
            /*
4300
             * Space characters are funny, in that they are considered
4301
             * to fit if there is at least one pixel of space left on the
4302
             * line.  Just give the space character whatever space is left.
4303
             */
4304
 
4305
            nextX = maxX;
4306
            charsThatFit++;
4307
        }
4308
        if (p[charsThatFit] == '\n') {
4309
            /*
4310
             * A newline character takes up no space, so if the previous
4311
             * character fits then so does the newline.
4312
             */
4313
 
4314
            charsThatFit++;
4315
        }
4316
        if (charsThatFit == 0) {
4317
            return 0;
4318
        }
4319
    }
4320
 
4321
    Tk_GetFontMetrics(tkfont, &fm);
4322
 
4323
    /*
4324
     * Fill in the chunk structure and allocate and initialize a
4325
     * CharInfo structure.  If the last character is a newline
4326
     * then don't bother to display it.
4327
     */
4328
 
4329
    chunkPtr->displayProc = CharDisplayProc;
4330
    chunkPtr->undisplayProc = CharUndisplayProc;
4331
    chunkPtr->measureProc = CharMeasureProc;
4332
    chunkPtr->bboxProc = CharBboxProc;
4333
    chunkPtr->numChars = charsThatFit;
4334
    chunkPtr->minAscent = fm.ascent + chunkPtr->stylePtr->sValuePtr->offset;
4335
    chunkPtr->minDescent = fm.descent - chunkPtr->stylePtr->sValuePtr->offset;
4336
    chunkPtr->minHeight = 0;
4337
    chunkPtr->width = nextX - chunkPtr->x;
4338
    chunkPtr->breakIndex = -1;
4339
    ciPtr = (CharInfo *) ckalloc((unsigned)
4340
            (sizeof(CharInfo) - 3 + charsThatFit));
4341
    chunkPtr->clientData = (ClientData) ciPtr;
4342
    ciPtr->numChars = charsThatFit;
4343
    strncpy(ciPtr->chars, p, (size_t) charsThatFit);
4344
    if (p[charsThatFit-1] == '\n') {
4345
        ciPtr->numChars--;
4346
    }
4347
 
4348
    /*
4349
     * Compute a break location.  If we're in word wrap mode, a
4350
     * break can occur after any space character, or at the end of
4351
     * the chunk if the next segment (ignoring those with zero size)
4352
     * is not a character segment.
4353
     */
4354
 
4355
    if (wrapMode != tkTextWordUid) {
4356
        chunkPtr->breakIndex = chunkPtr->numChars;
4357
    } else {
4358
        for (count = charsThatFit, p += charsThatFit-1; count > 0;
4359
                count--, p--) {
4360
            if (isspace(UCHAR(*p))) {
4361
                chunkPtr->breakIndex = count;
4362
                break;
4363
            }
4364
        }
4365
        if ((charsThatFit+offset) == segPtr->size) {
4366
            for (nextPtr = segPtr->nextPtr; nextPtr != NULL;
4367
                    nextPtr = nextPtr->nextPtr) {
4368
                if (nextPtr->size != 0) {
4369
                    if (nextPtr->typePtr != &tkTextCharType) {
4370
                        chunkPtr->breakIndex = chunkPtr->numChars;
4371
                    }
4372
                    break;
4373
                }
4374
            }
4375
        }
4376
    }
4377
    return 1;
4378
}
4379
 
4380
/*
4381
 *--------------------------------------------------------------
4382
 *
4383
 * CharDisplayProc --
4384
 *
4385
 *      This procedure is called to display a character chunk on
4386
 *      the screen or in an off-screen pixmap.
4387
 *
4388
 * Results:
4389
 *      None.
4390
 *
4391
 * Side effects:
4392
 *      Graphics are drawn.
4393
 *
4394
 *--------------------------------------------------------------
4395
 */
4396
 
4397
static void
4398
CharDisplayProc(chunkPtr, x, y, height, baseline, display, dst, screenY)
4399
    TkTextDispChunk *chunkPtr;          /* Chunk that is to be drawn. */
4400
    int x;                              /* X-position in dst at which to
4401
                                         * draw this chunk (may differ from
4402
                                         * the x-position in the chunk because
4403
                                         * of scrolling). */
4404
    int y;                              /* Y-position at which to draw this
4405
                                         * chunk in dst. */
4406
    int height;                         /* Total height of line. */
4407
    int baseline;                       /* Offset of baseline from y. */
4408
    Display *display;                   /* Display to use for drawing. */
4409
    Drawable dst;                       /* Pixmap or window in which to draw
4410
                                         * chunk. */
4411
    int screenY;                        /* Y-coordinate in text window that
4412
                                         * corresponds to y. */
4413
{
4414
    CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
4415
    TextStyle *stylePtr;
4416
    StyleValues *sValuePtr;
4417
    int offsetChars, offsetX;
4418
 
4419
    if ((x + chunkPtr->width) <= 0) {
4420
        /*
4421
         * The chunk is off-screen.
4422
         */
4423
 
4424
        return;
4425
    }
4426
 
4427
    stylePtr = chunkPtr->stylePtr;
4428
    sValuePtr = stylePtr->sValuePtr;
4429
 
4430
    /*
4431
     * If the text sticks out way to the left of the window, skip
4432
     * over the characters that aren't in the visible part of the
4433
     * window.  This is essential if x is very negative (such as
4434
     * less than 32K);  otherwise overflow problems will occur
4435
     * in servers that use 16-bit arithmetic, like X.
4436
     */
4437
 
4438
    offsetX = x;
4439
    offsetChars = 0;
4440
    if (x < 0) {
4441
        offsetChars = MeasureChars(sValuePtr->tkfont, ciPtr->chars,
4442
            ciPtr->numChars, x, 0, x - chunkPtr->x, &offsetX);
4443
    }
4444
 
4445
    /*
4446
     * Draw the text, underline, and overstrike for this chunk.
4447
     */
4448
 
4449
    if (ciPtr->numChars > offsetChars) {
4450
        int numChars = ciPtr->numChars - offsetChars;
4451
        char *string = ciPtr->chars + offsetChars;
4452
 
4453
        if ((numChars > 0) && (string[numChars - 1] == '\t')) {
4454
            numChars--;
4455
        }
4456
        Tk_DrawChars(display, dst, stylePtr->fgGC, sValuePtr->tkfont, string,
4457
                numChars, offsetX, y + baseline - sValuePtr->offset);
4458
        if (sValuePtr->underline) {
4459
            Tk_UnderlineChars(display, dst, stylePtr->fgGC, sValuePtr->tkfont,
4460
                    ciPtr->chars + offsetChars, offsetX,
4461
                    y + baseline - sValuePtr->offset,
4462
                    0, numChars);
4463
 
4464
        }
4465
        if (sValuePtr->overstrike) {
4466
            Tk_FontMetrics fm;
4467
 
4468
            Tk_GetFontMetrics(sValuePtr->tkfont, &fm);
4469
            Tk_UnderlineChars(display, dst, stylePtr->fgGC, sValuePtr->tkfont,
4470
                    ciPtr->chars + offsetChars, offsetX,
4471
                    y + baseline - sValuePtr->offset
4472
                            - fm.descent - (fm.ascent * 3) / 10,
4473
                    0, numChars);
4474
        }
4475
    }
4476
}
4477
 
4478
/*
4479
 *--------------------------------------------------------------
4480
 *
4481
 * CharUndisplayProc --
4482
 *
4483
 *      This procedure is called when a character chunk is no
4484
 *      longer going to be displayed.  It frees up resources
4485
 *      that were allocated to display the chunk.
4486
 *
4487
 * Results:
4488
 *      None.
4489
 *
4490
 * Side effects:
4491
 *      Memory and other resources get freed.
4492
 *
4493
 *--------------------------------------------------------------
4494
 */
4495
 
4496
static void
4497
CharUndisplayProc(textPtr, chunkPtr)
4498
    TkText *textPtr;                    /* Overall information about text
4499
                                         * widget. */
4500
    TkTextDispChunk *chunkPtr;          /* Chunk that is about to be freed. */
4501
{
4502
    CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
4503
 
4504
    ckfree((char *) ciPtr);
4505
}
4506
 
4507
/*
4508
 *--------------------------------------------------------------
4509
 *
4510
 * CharMeasureProc --
4511
 *
4512
 *      This procedure is called to determine which character in
4513
 *      a character chunk lies over a given x-coordinate.
4514
 *
4515
 * Results:
4516
 *      The return value is the index *within the chunk* of the
4517
 *      character that covers the position given by "x".
4518
 *
4519
 * Side effects:
4520
 *      None.
4521
 *
4522
 *--------------------------------------------------------------
4523
 */
4524
 
4525
static int
4526
CharMeasureProc(chunkPtr, x)
4527
    TkTextDispChunk *chunkPtr;          /* Chunk containing desired coord. */
4528
    int x;                              /* X-coordinate, in same coordinate
4529
                                         * system as chunkPtr->x. */
4530
{
4531
    CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
4532
    int endX;
4533
 
4534
    return MeasureChars(chunkPtr->stylePtr->sValuePtr->tkfont, ciPtr->chars,
4535
            chunkPtr->numChars-1, chunkPtr->x, x, 0, &endX);
4536
}
4537
 
4538
/*
4539
 *--------------------------------------------------------------
4540
 *
4541
 * CharBboxProc --
4542
 *
4543
 *      This procedure is called to compute the bounding box of
4544
 *      the area occupied by a single character.
4545
 *
4546
 * Results:
4547
 *      There is no return value.  *xPtr and *yPtr are filled in
4548
 *      with the coordinates of the upper left corner of the
4549
 *      character, and *widthPtr and *heightPtr are filled in with
4550
 *      the dimensions of the character in pixels.  Note:  not all
4551
 *      of the returned bbox is necessarily visible on the screen
4552
 *      (the rightmost part might be off-screen to the right,
4553
 *      and the bottommost part might be off-screen to the bottom).
4554
 *
4555
 * Side effects:
4556
 *      None.
4557
 *
4558
 *--------------------------------------------------------------
4559
 */
4560
 
4561
static void
4562
CharBboxProc(chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
4563
        widthPtr, heightPtr)
4564
    TkTextDispChunk *chunkPtr;          /* Chunk containing desired char. */
4565
    int index;                          /* Index of desired character within
4566
                                         * the chunk. */
4567
    int y;                              /* Topmost pixel in area allocated
4568
                                         * for this line. */
4569
    int lineHeight;                     /* Height of line, in pixels. */
4570
    int baseline;                       /* Location of line's baseline, in
4571
                                         * pixels measured down from y. */
4572
    int *xPtr, *yPtr;                   /* Gets filled in with coords of
4573
                                         * character's upper-left pixel.
4574
                                         * X-coord is in same coordinate
4575
                                         * system as chunkPtr->x. */
4576
    int *widthPtr;                      /* Gets filled in with width of
4577
                                         * character, in pixels. */
4578
    int *heightPtr;                     /* Gets filled in with height of
4579
                                         * character, in pixels. */
4580
{
4581
    CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
4582
    int maxX;
4583
 
4584
    maxX = chunkPtr->width + chunkPtr->x;
4585
    MeasureChars(chunkPtr->stylePtr->sValuePtr->tkfont, ciPtr->chars, index,
4586
            chunkPtr->x, 1000000, 0, xPtr);
4587
 
4588
    if (index == ciPtr->numChars) {
4589
        /*
4590
         * This situation only happens if the last character in a line
4591
         * is a space character, in which case it absorbs all of the
4592
         * extra space in the line (see TkTextCharLayoutProc).
4593
         */
4594
 
4595
        *widthPtr = maxX - *xPtr;
4596
    } else if ((ciPtr->chars[index] == '\t')
4597
            && (index == (ciPtr->numChars-1))) {
4598
        /*
4599
         * The desired character is a tab character that terminates a
4600
         * chunk;  give it all the space left in the chunk.
4601
         */
4602
 
4603
        *widthPtr = maxX - *xPtr;
4604
    } else {
4605
        MeasureChars(chunkPtr->stylePtr->sValuePtr->tkfont,
4606
                ciPtr->chars + index, 1, *xPtr, 1000000, 0, widthPtr);
4607
        if (*widthPtr > maxX) {
4608
            *widthPtr = maxX - *xPtr;
4609
        } else {
4610
            *widthPtr -= *xPtr;
4611
        }
4612
    }
4613
    *yPtr = y + baseline - chunkPtr->minAscent;
4614
    *heightPtr = chunkPtr->minAscent + chunkPtr->minDescent;
4615
}
4616
 
4617
/*
4618
 *----------------------------------------------------------------------
4619
 *
4620
 * AdjustForTab --
4621
 *
4622
 *      This procedure is called to move a series of chunks right
4623
 *      in order to align them with a tab stop.
4624
 *
4625
 * Results:
4626
 *      None.
4627
 *
4628
 * Side effects:
4629
 *      The width of chunkPtr gets adjusted so that it absorbs the
4630
 *      extra space due to the tab.  The x locations in all the chunks
4631
 *      after chunkPtr are adjusted rightward to align with the tab
4632
 *      stop given by tabArrayPtr and index.
4633
 *
4634
 *----------------------------------------------------------------------
4635
 */
4636
 
4637
static void
4638
AdjustForTab(textPtr, tabArrayPtr, index, chunkPtr)
4639
    TkText *textPtr;                    /* Information about the text widget as
4640
                                         * a whole. */
4641
    TkTextTabArray *tabArrayPtr;        /* Information about the tab stops
4642
                                         * that apply to this line.  May be
4643
                                         * NULL to indicate default tabbing
4644
                                         * (every 8 chars). */
4645
    int index;                          /* Index of current tab stop. */
4646
    TkTextDispChunk *chunkPtr;          /* Chunk whose last character is
4647
                                         * the tab;  the following chunks
4648
                                         * contain information to be shifted
4649
                                         * right. */
4650
 
4651
{
4652
    int x, desired, delta, width, decimal, i, gotDigit;
4653
    TkTextDispChunk *chunkPtr2, *decimalChunkPtr;
4654
    CharInfo *ciPtr;
4655
    int tabX, prev, spaceWidth;
4656
    char *p;
4657
    TkTextTabAlign alignment;
4658
 
4659
    if (chunkPtr->nextPtr == NULL) {
4660
        /*
4661
         * Nothing after the actual tab;  just return.
4662
         */
4663
 
4664
        return;
4665
    }
4666
 
4667
    /*
4668
     * If no tab information has been given, do the usual thing:
4669
     * round up to the next boundary of 8 average-sized characters.
4670
     */
4671
 
4672
    x = chunkPtr->nextPtr->x;
4673
    if ((tabArrayPtr == NULL) || (tabArrayPtr->numTabs == 0)) {
4674
        /*
4675
         * No tab information has been given, so use the default
4676
         * interpretation of tabs.
4677
         */
4678
 
4679
        desired = NextTabStop(textPtr, textPtr->tkfont, x, 0);
4680
        goto update;
4681
    }
4682
 
4683
    if (index < tabArrayPtr->numTabs) {
4684
        alignment = tabArrayPtr->tabs[index].alignment;
4685
        tabX = tabArrayPtr->tabs[index].location;
4686
    } else {
4687
        /*
4688
         * Ran out of tab stops;  compute a tab position by extrapolating
4689
         * from the last two tab positions.
4690
         */
4691
 
4692
        if (tabArrayPtr->numTabs > 1) {
4693
            prev = tabArrayPtr->tabs[tabArrayPtr->numTabs-2].location;
4694
        } else {
4695
            prev = 0;
4696
        }
4697
        alignment = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].alignment;
4698
        tabX = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location
4699
                + (index + 1 - tabArrayPtr->numTabs)
4700
                * (tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location - prev);
4701
    }
4702
 
4703
    if (alignment == LEFT) {
4704
        desired = tabX;
4705
        goto update;
4706
    }
4707
 
4708
    if ((alignment == CENTER) || (alignment == RIGHT)) {
4709
        /*
4710
         * Compute the width of all the information in the tab group,
4711
         * then use it to pick a desired location.
4712
         */
4713
 
4714
        width = 0;
4715
        for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
4716
                chunkPtr2 = chunkPtr2->nextPtr) {
4717
            width += chunkPtr2->width;
4718
        }
4719
        if (alignment == CENTER) {
4720
            desired = tabX - width/2;
4721
        } else {
4722
            desired = tabX - width;
4723
        }
4724
        goto update;
4725
    }
4726
 
4727
    /*
4728
     * Must be numeric alignment.  Search through the text to be
4729
     * tabbed, looking for the last , or . before the first character
4730
     * that isn't a number, comma, period, or sign.
4731
     */
4732
 
4733
    decimalChunkPtr = NULL;
4734
    decimal = gotDigit = 0;
4735
    for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
4736
            chunkPtr2 = chunkPtr2->nextPtr) {
4737
        if (chunkPtr2->displayProc != CharDisplayProc) {
4738
            continue;
4739
        }
4740
        ciPtr = (CharInfo *) chunkPtr2->clientData;
4741
        for (p = ciPtr->chars, i = 0; i < ciPtr->numChars; p++, i++) {
4742
            if (isdigit(UCHAR(*p))) {
4743
                gotDigit = 1;
4744
            } else if ((*p == '.') || (*p == ',')) {
4745
                decimal = p-ciPtr->chars;
4746
                decimalChunkPtr = chunkPtr2;
4747
            } else if (gotDigit) {
4748
                if (decimalChunkPtr == NULL) {
4749
                    decimal = p-ciPtr->chars;
4750
                    decimalChunkPtr = chunkPtr2;
4751
                }
4752
                goto endOfNumber;
4753
            }
4754
        }
4755
    }
4756
    endOfNumber:
4757
    if (decimalChunkPtr != NULL) {
4758
        int curX;
4759
 
4760
        ciPtr = (CharInfo *) decimalChunkPtr->clientData;
4761
        MeasureChars(decimalChunkPtr->stylePtr->sValuePtr->tkfont,
4762
                ciPtr->chars, decimal, decimalChunkPtr->x, 1000000, 0, &curX);
4763
        desired = tabX - (curX - x);
4764
        goto update;
4765
    } else {
4766
        /*
4767
         * There wasn't a decimal point.  Right justify the text.
4768
         */
4769
 
4770
        width = 0;
4771
        for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
4772
                chunkPtr2 = chunkPtr2->nextPtr) {
4773
            width += chunkPtr2->width;
4774
        }
4775
        desired = tabX - width;
4776
    }
4777
 
4778
    /*
4779
     * Shift all of the chunks to the right so that the left edge is
4780
     * at the desired location, then expand the chunk containing the
4781
     * tab.  Be sure that the tab occupies at least the width of a
4782
     * space character.
4783
     */
4784
 
4785
    update:
4786
    delta = desired - x;
4787
    MeasureChars(textPtr->tkfont, " ", 1, 0, INT_MAX, 0, &spaceWidth);
4788
    if (delta < spaceWidth) {
4789
        delta = spaceWidth;
4790
    }
4791
    for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
4792
            chunkPtr2 = chunkPtr2->nextPtr) {
4793
        chunkPtr2->x += delta;
4794
    }
4795
    chunkPtr->width += delta;
4796
}
4797
 
4798
/*
4799
 *----------------------------------------------------------------------
4800
 *
4801
 * SizeOfTab --
4802
 *
4803
 *      This returns an estimate of the amount of white space that will
4804
 *      be consumed by a tab.
4805
 *
4806
 * Results:
4807
 *      The return value is the minimum number of pixels that will
4808
 *      be occupied by the index'th tab of tabArrayPtr, assuming that
4809
 *      the current position on the line is x and the end of the
4810
 *      line is maxX.  For numeric tabs, this is a conservative
4811
 *      estimate.  The return value is always >= 0.
4812
 *
4813
 * Side effects:
4814
 *      None.
4815
 *
4816
 *----------------------------------------------------------------------
4817
 */
4818
 
4819
static int
4820
SizeOfTab(textPtr, tabArrayPtr, index, x, maxX)
4821
    TkText *textPtr;                    /* Information about the text widget as
4822
                                         * a whole. */
4823
    TkTextTabArray *tabArrayPtr;        /* Information about the tab stops
4824
                                         * that apply to this line.  NULL
4825
                                         * means use default tabbing (every
4826
                                         * 8 chars.) */
4827
    int index;                          /* Index of current tab stop. */
4828
    int x;                              /* Current x-location in line. Only
4829
                                         * used if tabArrayPtr == NULL. */
4830
    int maxX;                           /* X-location of pixel just past the
4831
                                         * right edge of the line. */
4832
{
4833
    int tabX, prev, result, spaceWidth;
4834
    TkTextTabAlign alignment;
4835
 
4836
    if ((tabArrayPtr == NULL) || (tabArrayPtr->numTabs == 0)) {
4837
        tabX = NextTabStop(textPtr, textPtr->tkfont, x, 0);
4838
        return tabX - x;
4839
    }
4840
    if (index < tabArrayPtr->numTabs) {
4841
        tabX = tabArrayPtr->tabs[index].location;
4842
        alignment = tabArrayPtr->tabs[index].alignment;
4843
    } else {
4844
        /*
4845
         * Ran out of tab stops;  compute a tab position by extrapolating
4846
         * from the last two tab positions.
4847
         */
4848
 
4849
        if (tabArrayPtr->numTabs > 1) {
4850
            prev = tabArrayPtr->tabs[tabArrayPtr->numTabs-2].location;
4851
        } else {
4852
            prev = 0;
4853
        }
4854
        tabX = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location
4855
                + (index + 1 - tabArrayPtr->numTabs)
4856
                * (tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location - prev);
4857
        alignment = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].alignment;
4858
    }
4859
    if (alignment == CENTER) {
4860
        /*
4861
         * Be very careful in the arithmetic below, because maxX may
4862
         * be the largest positive number:  watch out for integer
4863
         * overflow.
4864
         */
4865
 
4866
        if ((maxX-tabX) < (tabX - x)) {
4867
            result = (maxX - x) - 2*(maxX - tabX);
4868
        } else {
4869
            result = 0;
4870
        }
4871
        goto done;
4872
    }
4873
    if (alignment == RIGHT) {
4874
        result = 0;
4875
        goto done;
4876
    }
4877
 
4878
    /*
4879
     * Note: this treats NUMERIC alignment the same as LEFT
4880
     * alignment, which is somewhat conservative.  However, it's
4881
     * pretty tricky at this point to figure out exactly where
4882
     * the damn decimal point will be.
4883
     */
4884
 
4885
    if (tabX > x) {
4886
        result = tabX - x;
4887
    } else {
4888
        result = 0;
4889
    }
4890
 
4891
    done:
4892
    MeasureChars(textPtr->tkfont, " ", 1, 0, INT_MAX, 0, &spaceWidth);
4893
    if (result < spaceWidth) {
4894
        result = spaceWidth;
4895
    }
4896
    return result;
4897
}
4898
 
4899
/*
4900
 *---------------------------------------------------------------------------
4901
 *
4902
 * NextTabStop --
4903
 *
4904
 *      Given the current position, determine where the next default
4905
 *      tab stop would be located.  This procedure is called when the
4906
 *      current chunk in the text has no tabs defined and so the default
4907
 *      tab spacing for the font should be used.
4908
 *
4909
 * Results:
4910
 *      The location in pixels of the next tab stop.
4911
 *
4912
 * Side effects:
4913
 *      None.
4914
 *
4915
 *---------------------------------------------------------------------------
4916
 */
4917
 
4918
static int
4919
NextTabStop(textPtr, tkfont, x, tabOrigin)
4920
    TkText *textPtr;
4921
    Tk_Font tkfont;             /* Font in which chunk that contains tab
4922
                                 * stop will be drawn. */
4923
    int x;                      /* X-position in pixels where last
4924
                                 * character was drawn.  The next tab stop
4925
                                 * occurs somewhere after this location. */
4926
    int tabOrigin;              /* The origin for tab stops.  May be
4927
                                 * non-zero if text has been scrolled. */
4928
{
4929
    int tabWidth, rem;
4930
 
4931
#if 1
4932
    tabWidth = Tk_TextWidth(tkfont, "0", 1) * textPtr->tabsize;
4933
#else
4934
    tabWidth = Tk_TextWidth(tkfont, "0", 1) * 8;
4935
#endif
4936
    if (tabWidth == 0) {
4937
        tabWidth = 1;
4938
    }
4939
 
4940
    x += tabWidth;
4941
    rem = (x - tabOrigin) % tabWidth;
4942
    if (rem < 0) {
4943
        rem += tabWidth;
4944
    }
4945
    x -= rem;
4946
    return x;
4947
}
4948
 
4949
/*
4950
 *---------------------------------------------------------------------------
4951
 *
4952
 *  MeasureChars --
4953
 *
4954
 *      Determine the number of characters from the string that will fit
4955
 *      in the given horizontal span.  The measurement is done under the
4956
 *      assumption that Tk_DisplayChars will be used to actually display
4957
 *      the characters.
4958
 *
4959
 *      If tabs are encountered in the string, they will be expanded
4960
 *      to the next tab stop, unless the TK_IGNORE_TABS flag is specified.
4961
 *
4962
 *      If a newline is encountered in the string, the line will be
4963
 *      broken at that point, unless the TK_NEWSLINES_NOT_SPECIAL flag
4964
 *      is specified.
4965
 *
4966
 * Results:
4967
 *      The return value is the number of characters from source
4968
 *      that fit in the span given by startX and maxX.  *nextXPtr
4969
 *      is filled in with the x-coordinate at which the first
4970
 *      character that didn't fit would be drawn, if it were to
4971
 *      be drawn.
4972
 *
4973
 * Side effects:
4974
 *      None.
4975
 *
4976
 *--------------------------------------------------------------
4977
 */
4978
 
4979
static int
4980
MeasureChars(tkfont, source, maxChars, startX, maxX, tabOrigin, nextXPtr)
4981
    Tk_Font tkfont;             /* Font in which to draw characters. */
4982
    CONST char *source;         /* Characters to be displayed.  Need not
4983
                                 * be NULL-terminated. */
4984
    int maxChars;               /* Maximum # of characters to consider from
4985
                                 * source. */
4986
    int startX;                 /* X-position at which first character will
4987
                                 * be drawn. */
4988
    int maxX;                   /* Don't consider any character that would
4989
                                 * cross this x-position. */
4990
    int tabOrigin;              /* X-location that serves as "origin" for
4991
                                 * tab stops. */
4992
    int *nextXPtr;              /* Return x-position of terminating
4993
                                 * character here. */
4994
{
4995
    int curX, width, ch;
4996
    CONST char *special, *end, *start;
4997
 
4998
    ch = 0;                      /* lint. */
4999
    curX = startX;
5000
    special = source;
5001
    end = source + maxChars;
5002
    for (start = source; start < end; ) {
5003
        if (start >= special) {
5004
            /*
5005
             * Find the next special character in the string.
5006
             */
5007
 
5008
            for (special = start; special < end; special++) {
5009
                ch = *special;
5010
                if ((ch == '\t') || (ch == '\n')) {
5011
                    break;
5012
                }
5013
            }
5014
        }
5015
 
5016
        /*
5017
         * Special points at the next special character (or the end of the
5018
         * string).  Process characters between start and special.
5019
         */
5020
 
5021
        if (curX >= maxX) {
5022
            break;
5023
        }
5024
        start += Tk_MeasureChars(tkfont, start, special - start, maxX - curX,
5025
                0, &width);
5026
        curX += width;
5027
        if (start < special) {
5028
            /*
5029
             * No more chars fit in line.
5030
             */
5031
 
5032
            break;
5033
        }
5034
        if (special < end) {
5035
            if (ch == '\t') {
5036
                start++;
5037
            } else {
5038
                break;
5039
            }
5040
        }
5041
    }
5042
 
5043
    *nextXPtr = curX;
5044
    return start - source;
5045
}

powered by: WebSVN 2.1.0

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