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

« back to all changes in this revision

Viewing changes to generic/tkEntry.c

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

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 
 
2
 * tkEntry.c --
 
3
 *
 
4
 *      This module implements entry widgets for the Tk
 
5
 *      toolkit.  An entry displays a string and allows
 
6
 *      the string to be edited.
 
7
 *
 
8
 * Copyright (c) 1990-1994 The Regents of the University of California.
 
9
 * Copyright (c) 1994-1996 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: tkEntry.c,v 1.2 1998/09/14 18:23:09 stanton Exp $
 
15
 */
 
16
 
 
17
#include "tkInt.h"
 
18
#include "default.h"
 
19
 
 
20
/*
 
21
 * A data structure of the following type is kept for each entry
 
22
 * widget managed by this file:
 
23
 */
 
24
 
 
25
typedef struct {
 
26
    Tk_Window tkwin;            /* Window that embodies the entry. NULL
 
27
                                 * means that the window has been destroyed
 
28
                                 * but the data structures haven't yet been
 
29
                                 * cleaned up.*/
 
30
    Display *display;           /* Display containing widget.  Used, among
 
31
                                 * other things, so that resources can be
 
32
                                 * freed even after tkwin has gone away. */
 
33
    Tcl_Interp *interp;         /* Interpreter associated with entry. */
 
34
    Tcl_Command widgetCmd;      /* Token for entry's widget command. */
 
35
 
 
36
    /*
 
37
     * Fields that are set by widget commands other than "configure".
 
38
     */
 
39
     
 
40
    char *string;               /* Pointer to storage for string;
 
41
                                 * NULL-terminated;  malloc-ed. */
 
42
    int insertPos;              /* Index of character before which next
 
43
                                 * typed character will be inserted. */
 
44
 
 
45
    /*
 
46
     * Information about what's selected, if any.
 
47
     */
 
48
 
 
49
    int selectFirst;            /* Index of first selected character (-1 means
 
50
                                 * nothing selected. */
 
51
    int selectLast;             /* Index of last selected character (-1 means
 
52
                                 * nothing selected. */
 
53
    int selectAnchor;           /* Fixed end of selection (i.e. "select to"
 
54
                                 * operation will use this as one end of the
 
55
                                 * selection). */
 
56
 
 
57
    /*
 
58
     * Information for scanning:
 
59
     */
 
60
 
 
61
    int scanMarkX;              /* X-position at which scan started (e.g.
 
62
                                 * button was pressed here). */
 
63
    int scanMarkIndex;          /* Index of character that was at left of
 
64
                                 * window when scan started. */
 
65
 
 
66
    /*
 
67
     * Configuration settings that are updated by Tk_ConfigureWidget.
 
68
     */
 
69
 
 
70
    Tk_3DBorder normalBorder;   /* Used for drawing border around whole
 
71
                                 * window, plus used for background. */
 
72
    int borderWidth;            /* Width of 3-D border around window. */
 
73
    Tk_Cursor cursor;           /* Current cursor for window, or None. */
 
74
    int exportSelection;        /* Non-zero means tie internal entry selection
 
75
                                 * to X selection. */
 
76
    Tk_Font tkfont;             /* Information about text font, or NULL. */
 
77
    XColor *fgColorPtr;         /* Text color in normal mode. */
 
78
    XColor *highlightBgColorPtr;/* Color for drawing traversal highlight
 
79
                                 * area when highlight is off. */
 
80
    XColor *highlightColorPtr;  /* Color for drawing traversal highlight. */
 
81
    int highlightWidth;         /* Width in pixels of highlight to draw
 
82
                                 * around widget when it has the focus.
 
83
                                 * <= 0 means don't draw a highlight. */
 
84
    Tk_3DBorder insertBorder;   /* Used to draw vertical bar for insertion
 
85
                                 * cursor. */
 
86
    int insertBorderWidth;      /* Width of 3-D border around insert cursor. */
 
87
    int insertOffTime;          /* Number of milliseconds cursor should spend
 
88
                                 * in "off" state for each blink. */
 
89
    int insertOnTime;           /* Number of milliseconds cursor should spend
 
90
                                 * in "on" state for each blink. */
 
91
    int insertWidth;            /* Total width of insert cursor. */
 
92
    Tk_Justify justify;         /* Justification to use for text within
 
93
                                 * window. */
 
94
    int relief;                 /* 3-D effect: TK_RELIEF_RAISED, etc. */
 
95
    Tk_3DBorder selBorder;      /* Border and background for selected
 
96
                                 * characters. */
 
97
    int selBorderWidth;         /* Width of border around selection. */
 
98
    XColor *selFgColorPtr;      /* Foreground color for selected text. */
 
99
    char *showChar;             /* Value of -show option.  If non-NULL, first
 
100
                                 * character is used for displaying all
 
101
                                 * characters in entry.  Malloc'ed. */
 
102
    Tk_Uid state;               /* Normal or disabled.  Entry is read-only
 
103
                                 * when disabled. */
 
104
    char *textVarName;          /* Name of variable (malloc'ed) or NULL.
 
105
                                 * If non-NULL, entry's string tracks the
 
106
                                 * contents of this variable and vice versa. */
 
107
    char *takeFocus;            /* Value of -takefocus option;  not used in
 
108
                                 * the C code, but used by keyboard traversal
 
109
                                 * scripts.  Malloc'ed, but may be NULL. */
 
110
    int prefWidth;              /* Desired width of window, measured in
 
111
                                 * average characters. */
 
112
    char *scrollCmd;            /* Command prefix for communicating with
 
113
                                 * scrollbar(s).  Malloc'ed.  NULL means
 
114
                                 * no command to issue. */
 
115
 
 
116
    /*
 
117
     * Fields whose values are derived from the current values of the
 
118
     * configuration settings above.
 
119
     */
 
120
 
 
121
    int numChars;               /* Number of non-NULL characters in
 
122
                                 * string (may be 0). */
 
123
    char *displayString;        /* If non-NULL, points to string with same
 
124
                                 * length as string but whose characters
 
125
                                 * are all equal to showChar.  Malloc'ed. */
 
126
    int inset;                  /* Number of pixels on the left and right
 
127
                                 * sides that are taken up by XPAD, borderWidth
 
128
                                 * (if any), and highlightWidth (if any). */
 
129
    Tk_TextLayout textLayout;   /* Cached text layout information. */
 
130
    int layoutX, layoutY;       /* Origin for layout. */
 
131
    int leftIndex;              /* Index of left-most character visible in
 
132
                                 * window. */
 
133
    int leftX;                  /* X position at which character at leftIndex
 
134
                                 * is drawn (varies depending on justify). */
 
135
    Tcl_TimerToken insertBlinkHandler;
 
136
                                /* Timer handler used to blink cursor on and
 
137
                                 * off. */
 
138
    GC textGC;                  /* For drawing normal text. */
 
139
    GC selTextGC;               /* For drawing selected text. */
 
140
    GC highlightGC;             /* For drawing traversal highlight. */
 
141
    int avgWidth;               /* Width of average character. */
 
142
    int flags;                  /* Miscellaneous flags;  see below for
 
143
                                 * definitions. */
 
144
} Entry;
 
145
 
 
146
/*
 
147
 * Assigned bits of "flags" fields of Entry structures, and what those
 
148
 * bits mean:
 
149
 *
 
150
 * REDRAW_PENDING:              Non-zero means a DoWhenIdle handler has
 
151
 *                              already been queued to redisplay the entry.
 
152
 * BORDER_NEEDED:               Non-zero means 3-D border must be redrawn
 
153
 *                              around window during redisplay.  Normally
 
154
 *                              only text portion needs to be redrawn.
 
155
 * CURSOR_ON:                   Non-zero means insert cursor is displayed at
 
156
 *                              present.  0 means it isn't displayed.
 
157
 * GOT_FOCUS:                   Non-zero means this window has the input
 
158
 *                              focus.
 
159
 * UPDATE_SCROLLBAR:            Non-zero means scrollbar should be updated
 
160
 *                              during next redisplay operation.
 
161
 * GOT_SELECTION:               Non-zero means we've claimed the selection.
 
162
 */
 
163
 
 
164
#define REDRAW_PENDING          1
 
165
#define BORDER_NEEDED           2
 
166
#define CURSOR_ON               4
 
167
#define GOT_FOCUS               8
 
168
#define UPDATE_SCROLLBAR        0x10
 
169
#define GOT_SELECTION           0x20
 
170
 
 
171
/*
 
172
 * The following macro defines how many extra pixels to leave on each
 
173
 * side of the text in the entry.
 
174
 */
 
175
 
 
176
#define XPAD 1
 
177
#define YPAD 1
 
178
 
 
179
/*
 
180
 * Information used for argv parsing.
 
181
 */
 
182
 
 
183
static Tk_ConfigSpec configSpecs[] = {
 
184
    {TK_CONFIG_BORDER, "-background", "background", "Background",
 
185
        DEF_ENTRY_BG_COLOR, Tk_Offset(Entry, normalBorder),
 
186
        TK_CONFIG_COLOR_ONLY},
 
187
    {TK_CONFIG_BORDER, "-background", "background", "Background",
 
188
        DEF_ENTRY_BG_MONO, Tk_Offset(Entry, normalBorder),
 
189
        TK_CONFIG_MONO_ONLY},
 
190
    {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
 
191
        (char *) NULL, 0, 0},
 
192
    {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
 
193
        (char *) NULL, 0, 0},
 
194
    {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
 
195
        DEF_ENTRY_BORDER_WIDTH, Tk_Offset(Entry, borderWidth), 0},
 
196
    {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
 
197
        DEF_ENTRY_CURSOR, Tk_Offset(Entry, cursor), TK_CONFIG_NULL_OK},
 
198
    {TK_CONFIG_BOOLEAN, "-exportselection", "exportSelection",
 
199
        "ExportSelection", DEF_ENTRY_EXPORT_SELECTION,
 
200
        Tk_Offset(Entry, exportSelection), 0},
 
201
    {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
 
202
        (char *) NULL, 0, 0},
 
203
    {TK_CONFIG_FONT, "-font", "font", "Font",
 
204
        DEF_ENTRY_FONT, Tk_Offset(Entry, tkfont), 0},
 
205
    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
 
206
        DEF_ENTRY_FG, Tk_Offset(Entry, fgColorPtr), 0},
 
207
    {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
 
208
        "HighlightBackground", DEF_ENTRY_HIGHLIGHT_BG,
 
209
        Tk_Offset(Entry, highlightBgColorPtr), 0},
 
210
    {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
 
211
        DEF_ENTRY_HIGHLIGHT, Tk_Offset(Entry, highlightColorPtr), 0},
 
212
    {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
 
213
        "HighlightThickness",
 
214
        DEF_ENTRY_HIGHLIGHT_WIDTH, Tk_Offset(Entry, highlightWidth), 0},
 
215
    {TK_CONFIG_BORDER, "-insertbackground", "insertBackground", "Foreground",
 
216
        DEF_ENTRY_INSERT_BG, Tk_Offset(Entry, insertBorder), 0},
 
217
    {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
 
218
        DEF_ENTRY_INSERT_BD_COLOR, Tk_Offset(Entry, insertBorderWidth),
 
219
        TK_CONFIG_COLOR_ONLY},
 
220
    {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
 
221
        DEF_ENTRY_INSERT_BD_MONO, Tk_Offset(Entry, insertBorderWidth),
 
222
        TK_CONFIG_MONO_ONLY},
 
223
    {TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime",
 
224
        DEF_ENTRY_INSERT_OFF_TIME, Tk_Offset(Entry, insertOffTime), 0},
 
225
    {TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime",
 
226
        DEF_ENTRY_INSERT_ON_TIME, Tk_Offset(Entry, insertOnTime), 0},
 
227
    {TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth",
 
228
        DEF_ENTRY_INSERT_WIDTH, Tk_Offset(Entry, insertWidth), 0},
 
229
    {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
 
230
        DEF_ENTRY_JUSTIFY, Tk_Offset(Entry, justify), 0},
 
231
    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
 
232
        DEF_ENTRY_RELIEF, Tk_Offset(Entry, relief), 0},
 
233
    {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
 
234
        DEF_ENTRY_SELECT_COLOR, Tk_Offset(Entry, selBorder),
 
235
        TK_CONFIG_COLOR_ONLY},
 
236
    {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
 
237
        DEF_ENTRY_SELECT_MONO, Tk_Offset(Entry, selBorder),
 
238
        TK_CONFIG_MONO_ONLY},
 
239
    {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
 
240
        DEF_ENTRY_SELECT_BD_COLOR, Tk_Offset(Entry, selBorderWidth),
 
241
        TK_CONFIG_COLOR_ONLY},
 
242
    {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
 
243
        DEF_ENTRY_SELECT_BD_MONO, Tk_Offset(Entry, selBorderWidth),
 
244
        TK_CONFIG_MONO_ONLY},
 
245
    {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
 
246
        DEF_ENTRY_SELECT_FG_COLOR, Tk_Offset(Entry, selFgColorPtr),
 
247
        TK_CONFIG_COLOR_ONLY},
 
248
    {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
 
249
        DEF_ENTRY_SELECT_FG_MONO, Tk_Offset(Entry, selFgColorPtr),
 
250
        TK_CONFIG_MONO_ONLY},
 
251
    {TK_CONFIG_STRING, "-show", "show", "Show",
 
252
        DEF_ENTRY_SHOW, Tk_Offset(Entry, showChar), TK_CONFIG_NULL_OK},
 
253
    {TK_CONFIG_UID, "-state", "state", "State",
 
254
        DEF_ENTRY_STATE, Tk_Offset(Entry, state), 0},
 
255
    {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
 
256
        DEF_ENTRY_TAKE_FOCUS, Tk_Offset(Entry, takeFocus), TK_CONFIG_NULL_OK},
 
257
    {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
 
258
        DEF_ENTRY_TEXT_VARIABLE, Tk_Offset(Entry, textVarName),
 
259
        TK_CONFIG_NULL_OK},
 
260
    {TK_CONFIG_INT, "-width", "width", "Width",
 
261
        DEF_ENTRY_WIDTH, Tk_Offset(Entry, prefWidth), 0},
 
262
    {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
 
263
        DEF_ENTRY_SCROLL_COMMAND, Tk_Offset(Entry, scrollCmd),
 
264
        TK_CONFIG_NULL_OK},
 
265
    {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
 
266
        (char *) NULL, 0, 0}
 
267
};
 
268
 
 
269
/*
 
270
 * Flags for GetEntryIndex procedure:
 
271
 */
 
272
 
 
273
#define ZERO_OK                 1
 
274
#define LAST_PLUS_ONE_OK        2
 
275
 
 
276
/*
 
277
 * Forward declarations for procedures defined later in this file:
 
278
 */
 
279
 
 
280
static int              ConfigureEntry _ANSI_ARGS_((Tcl_Interp *interp,
 
281
                            Entry *entryPtr, int argc, char **argv,
 
282
                            int flags));
 
283
static void             DeleteChars _ANSI_ARGS_((Entry *entryPtr, int index,
 
284
                            int count));
 
285
static void             DestroyEntry _ANSI_ARGS_((char *memPtr));
 
286
static void             DisplayEntry _ANSI_ARGS_((ClientData clientData));
 
287
static void             EntryBlinkProc _ANSI_ARGS_((ClientData clientData));
 
288
static void             EntryCmdDeletedProc _ANSI_ARGS_((
 
289
                            ClientData clientData));
 
290
static void             EntryComputeGeometry _ANSI_ARGS_((Entry *entryPtr));
 
291
static void             EntryEventProc _ANSI_ARGS_((ClientData clientData,
 
292
                            XEvent *eventPtr));
 
293
static void             EntryFocusProc _ANSI_ARGS_ ((Entry *entryPtr,
 
294
                            int gotFocus));
 
295
static int              EntryFetchSelection _ANSI_ARGS_((ClientData clientData,
 
296
                            int offset, char *buffer, int maxBytes));
 
297
static void             EntryLostSelection _ANSI_ARGS_((
 
298
                            ClientData clientData));
 
299
static void             EventuallyRedraw _ANSI_ARGS_((Entry *entryPtr));
 
300
static void             EntryScanTo _ANSI_ARGS_((Entry *entryPtr, int y));
 
301
static void             EntrySetValue _ANSI_ARGS_((Entry *entryPtr,
 
302
                            char *value));
 
303
static void             EntrySelectTo _ANSI_ARGS_((
 
304
                            Entry *entryPtr, int index));
 
305
static char *           EntryTextVarProc _ANSI_ARGS_((ClientData clientData,
 
306
                            Tcl_Interp *interp, char *name1, char *name2,
 
307
                            int flags));
 
308
static void             EntryUpdateScrollbar _ANSI_ARGS_((Entry *entryPtr));
 
309
static void             EntryValueChanged _ANSI_ARGS_((Entry *entryPtr));
 
310
static void             EntryVisibleRange _ANSI_ARGS_((Entry *entryPtr,
 
311
                            double *firstPtr, double *lastPtr));
 
312
static int              EntryWidgetCmd _ANSI_ARGS_((ClientData clientData,
 
313
                            Tcl_Interp *interp, int argc, char **argv));
 
314
static void             EntryWorldChanged _ANSI_ARGS_((
 
315
                            ClientData instanceData));
 
316
static int              GetEntryIndex _ANSI_ARGS_((Tcl_Interp *interp,
 
317
                            Entry *entryPtr, char *string, int *indexPtr));
 
318
static void             InsertChars _ANSI_ARGS_((Entry *entryPtr, int index,
 
319
                            char *string));
 
320
 
 
321
/*
 
322
 * The structure below defines entry class behavior by means of procedures
 
323
 * that can be invoked from generic window code.
 
324
 */
 
325
 
 
326
static TkClassProcs entryClass = {
 
327
    NULL,                       /* createProc. */
 
328
    EntryWorldChanged,          /* geometryProc. */
 
329
    NULL                        /* modalProc. */
 
330
};
 
331
 
 
332
 
 
333
/*
 
334
 *--------------------------------------------------------------
 
335
 *
 
336
 * Tk_EntryCmd --
 
337
 *
 
338
 *      This procedure is invoked to process the "entry" Tcl
 
339
 *      command.  See the user documentation for details on what
 
340
 *      it does.
 
341
 *
 
342
 * Results:
 
343
 *      A standard Tcl result.
 
344
 *
 
345
 * Side effects:
 
346
 *      See the user documentation.
 
347
 *
 
348
 *--------------------------------------------------------------
 
349
 */
 
350
 
 
351
int
 
352
Tk_EntryCmd(clientData, interp, argc, argv)
 
353
    ClientData clientData;      /* Main window associated with
 
354
                                 * interpreter. */
 
355
    Tcl_Interp *interp;         /* Current interpreter. */
 
356
    int argc;                   /* Number of arguments. */
 
357
    char **argv;                /* Argument strings. */
 
358
{
 
359
    Tk_Window tkwin = (Tk_Window) clientData;
 
360
    register Entry *entryPtr;
 
361
    Tk_Window new;
 
362
 
 
363
    if (argc < 2) {
 
364
        Tcl_AppendResult(interp, "wrong # args: should be \"",
 
365
                argv[0], " pathName ?options?\"", (char *) NULL);
 
366
        return TCL_ERROR;
 
367
    }
 
368
 
 
369
    new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
 
370
    if (new == NULL) {
 
371
        return TCL_ERROR;
 
372
    }
 
373
 
 
374
    /*
 
375
     * Initialize the fields of the structure that won't be initialized
 
376
     * by ConfigureEntry, or that ConfigureEntry requires to be
 
377
     * initialized already (e.g. resource pointers).
 
378
     */
 
379
 
 
380
    entryPtr = (Entry *) ckalloc(sizeof(Entry));
 
381
    entryPtr->tkwin = new;
 
382
    entryPtr->display = Tk_Display(new);
 
383
    entryPtr->interp = interp;
 
384
    entryPtr->widgetCmd = Tcl_CreateCommand(interp,
 
385
            Tk_PathName(entryPtr->tkwin), EntryWidgetCmd,
 
386
            (ClientData) entryPtr, EntryCmdDeletedProc);
 
387
    entryPtr->string = (char *) ckalloc(1);
 
388
    entryPtr->string[0] = '\0';
 
389
    entryPtr->insertPos = 0;
 
390
    entryPtr->selectFirst = -1;
 
391
    entryPtr->selectLast = -1;
 
392
    entryPtr->selectAnchor = 0;
 
393
    entryPtr->scanMarkX = 0;
 
394
    entryPtr->scanMarkIndex = 0;
 
395
 
 
396
    entryPtr->normalBorder = NULL;
 
397
    entryPtr->borderWidth = 0;
 
398
    entryPtr->cursor = None;
 
399
    entryPtr->exportSelection = 1;
 
400
    entryPtr->tkfont = NULL;
 
401
    entryPtr->fgColorPtr = NULL;
 
402
    entryPtr->highlightBgColorPtr = NULL;
 
403
    entryPtr->highlightColorPtr = NULL;
 
404
    entryPtr->highlightWidth = 0;
 
405
    entryPtr->insertBorder = NULL;
 
406
    entryPtr->insertBorderWidth = 0;
 
407
    entryPtr->insertOffTime = 0;
 
408
    entryPtr->insertOnTime = 0;
 
409
    entryPtr->insertWidth = 0;
 
410
    entryPtr->justify = TK_JUSTIFY_LEFT;
 
411
    entryPtr->relief = TK_RELIEF_FLAT;
 
412
    entryPtr->selBorder = NULL;
 
413
    entryPtr->selBorderWidth = 0;
 
414
    entryPtr->selFgColorPtr = NULL;
 
415
    entryPtr->showChar = NULL;
 
416
    entryPtr->state = tkNormalUid;
 
417
    entryPtr->textVarName = NULL;
 
418
    entryPtr->takeFocus = NULL;
 
419
    entryPtr->prefWidth = 0;
 
420
    entryPtr->scrollCmd = NULL;
 
421
 
 
422
    entryPtr->numChars = 0;
 
423
    entryPtr->displayString = NULL;
 
424
    entryPtr->inset = XPAD;
 
425
    entryPtr->textLayout = NULL;
 
426
    entryPtr->layoutX = 0;
 
427
    entryPtr->layoutY = 0;
 
428
    entryPtr->leftIndex = 0;
 
429
    entryPtr->leftX = 0;
 
430
    entryPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
 
431
    entryPtr->textGC = None;
 
432
    entryPtr->selTextGC = None;
 
433
    entryPtr->highlightGC = None;
 
434
    entryPtr->avgWidth = 1;
 
435
    entryPtr->flags = 0;
 
436
 
 
437
    Tk_SetClass(entryPtr->tkwin, "Entry");
 
438
    TkSetClassProcs(entryPtr->tkwin, &entryClass, (ClientData) entryPtr);
 
439
    Tk_CreateEventHandler(entryPtr->tkwin,
 
440
            ExposureMask|StructureNotifyMask|FocusChangeMask,
 
441
            EntryEventProc, (ClientData) entryPtr);
 
442
    Tk_CreateSelHandler(entryPtr->tkwin, XA_PRIMARY, XA_STRING,
 
443
            EntryFetchSelection, (ClientData) entryPtr, XA_STRING);
 
444
    if (ConfigureEntry(interp, entryPtr, argc-2, argv+2, 0) != TCL_OK) {
 
445
        goto error;
 
446
    }
 
447
 
 
448
    interp->result = Tk_PathName(entryPtr->tkwin);
 
449
    return TCL_OK;
 
450
 
 
451
    error:
 
452
    Tk_DestroyWindow(entryPtr->tkwin);
 
453
    return TCL_ERROR;
 
454
}
 
455
 
 
456
/*
 
457
 *--------------------------------------------------------------
 
458
 *
 
459
 * EntryWidgetCmd --
 
460
 *
 
461
 *      This procedure is invoked to process the Tcl command
 
462
 *      that corresponds to a widget managed by this module.
 
463
 *      See the user documentation for details on what it does.
 
464
 *
 
465
 * Results:
 
466
 *      A standard Tcl result.
 
467
 *
 
468
 * Side effects:
 
469
 *      See the user documentation.
 
470
 *
 
471
 *--------------------------------------------------------------
 
472
 */
 
473
 
 
474
static int
 
475
EntryWidgetCmd(clientData, interp, argc, argv)
 
476
    ClientData clientData;              /* Information about entry widget. */
 
477
    Tcl_Interp *interp;                 /* Current interpreter. */
 
478
    int argc;                           /* Number of arguments. */
 
479
    char **argv;                        /* Argument strings. */
 
480
{
 
481
    register Entry *entryPtr = (Entry *) clientData;
 
482
    int result = TCL_OK;
 
483
    size_t length;
 
484
    int c;
 
485
 
 
486
    if (argc < 2) {
 
487
        Tcl_AppendResult(interp, "wrong # args: should be \"",
 
488
                argv[0], " option ?arg arg ...?\"", (char *) NULL);
 
489
        return TCL_ERROR;
 
490
    }
 
491
    Tcl_Preserve((ClientData) entryPtr);
 
492
    c = argv[1][0];
 
493
    length = strlen(argv[1]);
 
494
    if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) {
 
495
        int index;
 
496
        int x, y, width, height;
 
497
 
 
498
        if (argc != 3) {
 
499
            Tcl_AppendResult(interp, "wrong # args: should be \"",
 
500
                    argv[0], " bbox index\"",
 
501
                    (char *) NULL);
 
502
            goto error;
 
503
        }
 
504
        if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
 
505
            goto error;
 
506
        }
 
507
        if ((index == entryPtr->numChars) && (index > 0)) {
 
508
            index--;
 
509
        }
 
510
        Tk_CharBbox(entryPtr->textLayout, index, &x, &y, &width, &height);
 
511
        sprintf(interp->result, "%d %d %d %d",
 
512
                x + entryPtr->layoutX, y + entryPtr->layoutY, width, height);
 
513
    } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
 
514
            && (length >= 2)) {
 
515
        if (argc != 3) {
 
516
            Tcl_AppendResult(interp, "wrong # args: should be \"",
 
517
                    argv[0], " cget option\"",
 
518
                    (char *) NULL);
 
519
            goto error;
 
520
        }
 
521
        result = Tk_ConfigureValue(interp, entryPtr->tkwin, configSpecs,
 
522
                (char *) entryPtr, argv[2], 0);
 
523
    } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
 
524
            && (length >= 2)) {
 
525
        if (argc == 2) {
 
526
            result = Tk_ConfigureInfo(interp, entryPtr->tkwin, configSpecs,
 
527
                    (char *) entryPtr, (char *) NULL, 0);
 
528
        } else if (argc == 3) {
 
529
            result = Tk_ConfigureInfo(interp, entryPtr->tkwin, configSpecs,
 
530
                    (char *) entryPtr, argv[2], 0);
 
531
        } else {
 
532
            result = ConfigureEntry(interp, entryPtr, argc-2, argv+2,
 
533
                    TK_CONFIG_ARGV_ONLY);
 
534
        }
 
535
    } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
 
536
        int first, last;
 
537
 
 
538
        if ((argc < 3) || (argc > 4)) {
 
539
            Tcl_AppendResult(interp, "wrong # args: should be \"",
 
540
                    argv[0], " delete firstIndex ?lastIndex?\"",
 
541
                    (char *) NULL);
 
542
            goto error;
 
543
        }
 
544
        if (GetEntryIndex(interp, entryPtr, argv[2], &first) != TCL_OK) {
 
545
            goto error;
 
546
        }
 
547
        if (argc == 3) {
 
548
            last = first+1;
 
549
        } else {
 
550
            if (GetEntryIndex(interp, entryPtr, argv[3], &last) != TCL_OK) {
 
551
                goto error;
 
552
            }
 
553
        }
 
554
        if ((last >= first) && (entryPtr->state == tkNormalUid)) {
 
555
            DeleteChars(entryPtr, first, last-first);
 
556
        }
 
557
    } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
 
558
        if (argc != 2) {
 
559
            Tcl_AppendResult(interp, "wrong # args: should be \"",
 
560
                    argv[0], " get\"", (char *) NULL);
 
561
            goto error;
 
562
        }
 
563
        interp->result = entryPtr->string;
 
564
    } else if ((c == 'i') && (strncmp(argv[1], "icursor", length) == 0)
 
565
            && (length >= 2)) {
 
566
        if (argc != 3) {
 
567
            Tcl_AppendResult(interp, "wrong # args: should be \"",
 
568
                    argv[0], " icursor pos\"",
 
569
                    (char *) NULL);
 
570
            goto error;
 
571
        }
 
572
        if (GetEntryIndex(interp, entryPtr, argv[2], &entryPtr->insertPos)
 
573
                != TCL_OK) {
 
574
            goto error;
 
575
        }
 
576
        EventuallyRedraw(entryPtr);
 
577
    } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
 
578
            && (length >= 3)) {
 
579
        int index;
 
580
 
 
581
        if (argc != 3) {
 
582
            Tcl_AppendResult(interp, "wrong # args: should be \"",
 
583
                    argv[0], " index string\"", (char *) NULL);
 
584
            goto error;
 
585
        }
 
586
        if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
 
587
            goto error;
 
588
        }
 
589
        sprintf(interp->result, "%d", index);
 
590
    } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
 
591
            && (length >= 3)) {
 
592
        int index;
 
593
 
 
594
        if (argc != 4) {
 
595
            Tcl_AppendResult(interp, "wrong # args: should be \"",
 
596
                    argv[0], " insert index text\"",
 
597
                    (char *) NULL);
 
598
            goto error;
 
599
        }
 
600
        if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
 
601
            goto error;
 
602
        }
 
603
        if (entryPtr->state == tkNormalUid) {
 
604
            InsertChars(entryPtr, index, argv[3]);
 
605
        }
 
606
    } else if ((c == 's') && (length >= 2)
 
607
            && (strncmp(argv[1], "scan", length) == 0)) {
 
608
        int x;
 
609
 
 
610
        if (argc != 4) {
 
611
            Tcl_AppendResult(interp, "wrong # args: should be \"",
 
612
                    argv[0], " scan mark|dragto x\"", (char *) NULL);
 
613
            goto error;
 
614
        }
 
615
        if (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) {
 
616
            goto error;
 
617
        }
 
618
        if ((argv[2][0] == 'm')
 
619
                && (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
 
620
            entryPtr->scanMarkX = x;
 
621
            entryPtr->scanMarkIndex = entryPtr->leftIndex;
 
622
        } else if ((argv[2][0] == 'd')
 
623
                && (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
 
624
            EntryScanTo(entryPtr, x);
 
625
        } else {
 
626
            Tcl_AppendResult(interp, "bad scan option \"", argv[2],
 
627
                    "\": must be mark or dragto", (char *) NULL);
 
628
            goto error;
 
629
        }
 
630
    } else if ((c == 's') && (length >= 2)
 
631
            && (strncmp(argv[1], "selection", length) == 0)) {
 
632
        int index, index2;
 
633
 
 
634
        if (argc < 3) {
 
635
            Tcl_AppendResult(interp, "wrong # args: should be \"",
 
636
                    argv[0], " select option ?index?\"", (char *) NULL);
 
637
            goto error;
 
638
        }
 
639
        length = strlen(argv[2]);
 
640
        c = argv[2][0];
 
641
        if ((c == 'c') && (strncmp(argv[2], "clear", length) == 0)) {
 
642
            if (argc != 3) {
 
643
                Tcl_AppendResult(interp, "wrong # args: should be \"",
 
644
                        argv[0], " selection clear\"", (char *) NULL);
 
645
                goto error;
 
646
            }
 
647
            if (entryPtr->selectFirst != -1) {
 
648
                entryPtr->selectFirst = entryPtr->selectLast = -1;
 
649
                EventuallyRedraw(entryPtr);
 
650
            }
 
651
            goto done;
 
652
        } else if ((c == 'p') && (strncmp(argv[2], "present", length) == 0)) {
 
653
            if (argc != 3) {
 
654
                Tcl_AppendResult(interp, "wrong # args: should be \"",
 
655
                        argv[0], " selection present\"", (char *) NULL);
 
656
                goto error;
 
657
            }
 
658
            if (entryPtr->selectFirst == -1) {
 
659
                interp->result = "0";
 
660
            } else {
 
661
                interp->result = "1";
 
662
            }
 
663
            goto done;
 
664
        }
 
665
        if (argc >= 4) {
 
666
            if (GetEntryIndex(interp, entryPtr, argv[3], &index) != TCL_OK) {
 
667
                goto error;
 
668
            }
 
669
        }
 
670
        if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
 
671
            if (argc != 4) {
 
672
                Tcl_AppendResult(interp, "wrong # args: should be \"",
 
673
                        argv[0], " selection adjust index\"",
 
674
                        (char *) NULL);
 
675
                goto error;
 
676
            }
 
677
            if (entryPtr->selectFirst >= 0) {
 
678
                int half1, half2;
 
679
 
 
680
                half1 = (entryPtr->selectFirst + entryPtr->selectLast)/2;
 
681
                half2 = (entryPtr->selectFirst + entryPtr->selectLast + 1)/2;
 
682
                if (index < half1) {
 
683
                    entryPtr->selectAnchor = entryPtr->selectLast;
 
684
                } else if (index > half2) {
 
685
                    entryPtr->selectAnchor = entryPtr->selectFirst;
 
686
                } else {
 
687
                    /*
 
688
                     * We're at about the halfway point in the selection;
 
689
                     * just keep the existing anchor.
 
690
                     */
 
691
                }
 
692
            }
 
693
            EntrySelectTo(entryPtr, index);
 
694
        } else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
 
695
            if (argc != 4) {
 
696
                Tcl_AppendResult(interp, "wrong # args: should be \"",
 
697
                        argv[0], " selection from index\"",
 
698
                        (char *) NULL);
 
699
                goto error;
 
700
            }
 
701
            entryPtr->selectAnchor = index;
 
702
        } else if ((c == 'r') && (strncmp(argv[2], "range", length) == 0)) {
 
703
            if (argc != 5) {
 
704
                Tcl_AppendResult(interp, "wrong # args: should be \"",
 
705
                        argv[0], " selection range start end\"",
 
706
                        (char *) NULL);
 
707
                goto error;
 
708
            }
 
709
            if (GetEntryIndex(interp, entryPtr, argv[4], &index2) != TCL_OK) {
 
710
                goto error;
 
711
            }
 
712
            if (index >= index2) {
 
713
                entryPtr->selectFirst = entryPtr->selectLast = -1;
 
714
            } else {
 
715
                entryPtr->selectFirst = index;
 
716
                entryPtr->selectLast = index2;
 
717
            }
 
718
            if (!(entryPtr->flags & GOT_SELECTION)
 
719
                    && (entryPtr->exportSelection)) {
 
720
                Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, 
 
721
                        EntryLostSelection, (ClientData) entryPtr);
 
722
                entryPtr->flags |= GOT_SELECTION;
 
723
            }
 
724
            EventuallyRedraw(entryPtr);
 
725
        } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
 
726
            if (argc != 4) {
 
727
                Tcl_AppendResult(interp, "wrong # args: should be \"",
 
728
                        argv[0], " selection to index\"",
 
729
                        (char *) NULL);
 
730
                goto error;
 
731
            }
 
732
            EntrySelectTo(entryPtr, index);
 
733
        } else {
 
734
            Tcl_AppendResult(interp, "bad selection option \"", argv[2],
 
735
                    "\": must be adjust, clear, from, present, range, or to",
 
736
                    (char *) NULL);
 
737
            goto error;
 
738
        }
 
739
    } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
 
740
        int index, type, count, charsPerPage;
 
741
        double fraction, first, last;
 
742
 
 
743
        if (argc == 2) {
 
744
            EntryVisibleRange(entryPtr, &first, &last);
 
745
            sprintf(interp->result, "%g %g", first, last);
 
746
            goto done;
 
747
        } else if (argc == 3) {
 
748
            if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
 
749
                goto error;
 
750
            }
 
751
        } else {
 
752
            type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
 
753
            index = entryPtr->leftIndex;
 
754
            switch (type) {
 
755
                case TK_SCROLL_ERROR:
 
756
                    goto error;
 
757
                case TK_SCROLL_MOVETO:
 
758
                    index = (int) ((fraction * entryPtr->numChars) + 0.5);
 
759
                    break;
 
760
                case TK_SCROLL_PAGES:
 
761
                    charsPerPage = ((Tk_Width(entryPtr->tkwin)
 
762
                            - 2*entryPtr->inset) / entryPtr->avgWidth) - 2;
 
763
                    if (charsPerPage < 1) {
 
764
                        charsPerPage = 1;
 
765
                    }
 
766
                    index += charsPerPage*count;
 
767
                    break;
 
768
                case TK_SCROLL_UNITS:
 
769
                    index += count;
 
770
                    break;
 
771
            }
 
772
        }
 
773
        if (index >= entryPtr->numChars) {
 
774
            index = entryPtr->numChars-1;
 
775
        }
 
776
        if (index < 0) {
 
777
            index = 0;
 
778
        }
 
779
        entryPtr->leftIndex = index;
 
780
        entryPtr->flags |= UPDATE_SCROLLBAR;
 
781
        EntryComputeGeometry(entryPtr);
 
782
        EventuallyRedraw(entryPtr);
 
783
    } else {
 
784
        Tcl_AppendResult(interp, "bad option \"", argv[1],
 
785
                "\": must be bbox, cget, configure, delete, get, ",
 
786
                "icursor, index, insert, scan, selection, or xview",
 
787
                (char *) NULL);
 
788
        goto error;
 
789
    }
 
790
    done:
 
791
    Tcl_Release((ClientData) entryPtr);
 
792
    return result;
 
793
 
 
794
    error:
 
795
    Tcl_Release((ClientData) entryPtr);
 
796
    return TCL_ERROR;
 
797
}
 
798
 
 
799
/*
 
800
 *----------------------------------------------------------------------
 
801
 *
 
802
 * DestroyEntry --
 
803
 *
 
804
 *      This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
 
805
 *      to clean up the internal structure of an entry at a safe time
 
806
 *      (when no-one is using it anymore).
 
807
 *
 
808
 * Results:
 
809
 *      None.
 
810
 *
 
811
 * Side effects:
 
812
 *      Everything associated with the entry is freed up.
 
813
 *
 
814
 *----------------------------------------------------------------------
 
815
 */
 
816
 
 
817
static void
 
818
DestroyEntry(memPtr)
 
819
    char *memPtr;               /* Info about entry widget. */
 
820
{
 
821
    register Entry *entryPtr = (Entry *) memPtr;
 
822
 
 
823
    /*
 
824
     * Free up all the stuff that requires special handling, then
 
825
     * let Tk_FreeOptions handle all the standard option-related
 
826
     * stuff.
 
827
     */
 
828
 
 
829
    ckfree(entryPtr->string);
 
830
    if (entryPtr->textVarName != NULL) {
 
831
        Tcl_UntraceVar(entryPtr->interp, entryPtr->textVarName,
 
832
                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
 
833
                EntryTextVarProc, (ClientData) entryPtr);
 
834
    }
 
835
    if (entryPtr->textGC != None) {
 
836
        Tk_FreeGC(entryPtr->display, entryPtr->textGC);
 
837
    }
 
838
    if (entryPtr->selTextGC != None) {
 
839
        Tk_FreeGC(entryPtr->display, entryPtr->selTextGC);
 
840
    }
 
841
    Tcl_DeleteTimerHandler(entryPtr->insertBlinkHandler);
 
842
    if (entryPtr->displayString != NULL) {
 
843
        ckfree(entryPtr->displayString);
 
844
    }
 
845
    Tk_FreeTextLayout(entryPtr->textLayout);
 
846
    Tk_FreeOptions(configSpecs, (char *) entryPtr, entryPtr->display, 0);
 
847
    ckfree((char *) entryPtr);
 
848
}
 
849
 
 
850
/*
 
851
 *----------------------------------------------------------------------
 
852
 *
 
853
 * ConfigureEntry --
 
854
 *
 
855
 *      This procedure is called to process an argv/argc list, plus
 
856
 *      the Tk option database, in order to configure (or reconfigure)
 
857
 *      an entry widget.
 
858
 *
 
859
 * Results:
 
860
 *      The return value is a standard Tcl result.  If TCL_ERROR is
 
861
 *      returned, then interp->result contains an error message.
 
862
 *
 
863
 * Side effects:
 
864
 *      Configuration information, such as colors, border width,
 
865
 *      etc. get set for entryPtr;  old resources get freed,
 
866
 *      if there were any.
 
867
 *
 
868
 *----------------------------------------------------------------------
 
869
 */
 
870
 
 
871
static int
 
872
ConfigureEntry(interp, entryPtr, argc, argv, flags)
 
873
    Tcl_Interp *interp;         /* Used for error reporting. */
 
874
    register Entry *entryPtr;   /* Information about widget;  may or may
 
875
                                 * not already have values for some fields. */
 
876
    int argc;                   /* Number of valid entries in argv. */
 
877
    char **argv;                /* Arguments. */
 
878
    int flags;                  /* Flags to pass to Tk_ConfigureWidget. */
 
879
{
 
880
    int oldExport;
 
881
 
 
882
    /*
 
883
     * Eliminate any existing trace on a variable monitored by the entry.
 
884
     */
 
885
 
 
886
    if (entryPtr->textVarName != NULL) {
 
887
        Tcl_UntraceVar(interp, entryPtr->textVarName, 
 
888
                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
 
889
                EntryTextVarProc, (ClientData) entryPtr);
 
890
    }
 
891
 
 
892
    oldExport = entryPtr->exportSelection;
 
893
    if (Tk_ConfigureWidget(interp, entryPtr->tkwin, configSpecs,
 
894
            argc, argv, (char *) entryPtr, flags) != TCL_OK) {
 
895
        return TCL_ERROR;
 
896
    }
 
897
 
 
898
    /*
 
899
     * If the entry is tied to the value of a variable, then set up
 
900
     * a trace on the variable's value, create the variable if it doesn't
 
901
     * exist, and set the entry's value from the variable's value.
 
902
     */
 
903
 
 
904
    if (entryPtr->textVarName != NULL) {
 
905
        char *value;
 
906
 
 
907
        value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
 
908
        if (value == NULL) {
 
909
            EntryValueChanged(entryPtr);
 
910
        } else {
 
911
            EntrySetValue(entryPtr, value);
 
912
        }
 
913
        Tcl_TraceVar(interp, entryPtr->textVarName,
 
914
                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
 
915
                EntryTextVarProc, (ClientData) entryPtr);
 
916
    }
 
917
 
 
918
    /*
 
919
     * A few other options also need special processing, such as parsing
 
920
     * the geometry and setting the background from a 3-D border.
 
921
     */
 
922
 
 
923
    if ((entryPtr->state != tkNormalUid)
 
924
            && (entryPtr->state != tkDisabledUid)) {
 
925
        Tcl_AppendResult(interp, "bad state value \"", entryPtr->state,
 
926
                "\": must be normal or disabled", (char *) NULL);
 
927
        entryPtr->state = tkNormalUid;
 
928
        return TCL_ERROR;
 
929
    }
 
930
 
 
931
    Tk_SetBackgroundFromBorder(entryPtr->tkwin, entryPtr->normalBorder);
 
932
 
 
933
    if (entryPtr->insertWidth <= 0) {
 
934
        entryPtr->insertWidth = 2;
 
935
    }
 
936
    if (entryPtr->insertBorderWidth > entryPtr->insertWidth/2) {
 
937
        entryPtr->insertBorderWidth = entryPtr->insertWidth/2;
 
938
    }
 
939
 
 
940
    /*
 
941
     * Restart the cursor timing sequence in case the on-time or off-time
 
942
     * just changed.
 
943
     */
 
944
 
 
945
    if (entryPtr->flags & GOT_FOCUS) {
 
946
        EntryFocusProc(entryPtr, 1);
 
947
    }
 
948
 
 
949
    /*
 
950
     * Claim the selection if we've suddenly started exporting it.
 
951
     */
 
952
 
 
953
    if (entryPtr->exportSelection && (!oldExport)
 
954
            && (entryPtr->selectFirst != -1)
 
955
            && !(entryPtr->flags & GOT_SELECTION)) {
 
956
        Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, EntryLostSelection,
 
957
                (ClientData) entryPtr);
 
958
        entryPtr->flags |= GOT_SELECTION;
 
959
    }
 
960
 
 
961
    /*
 
962
     * Recompute the window's geometry and arrange for it to be
 
963
     * redisplayed.
 
964
     */
 
965
 
 
966
    Tk_SetInternalBorder(entryPtr->tkwin,
 
967
            entryPtr->borderWidth + entryPtr->highlightWidth);
 
968
    if (entryPtr->highlightWidth <= 0) {
 
969
        entryPtr->highlightWidth = 0;
 
970
    }
 
971
    entryPtr->inset = entryPtr->highlightWidth + entryPtr->borderWidth + XPAD;
 
972
 
 
973
    EntryWorldChanged((ClientData) entryPtr);
 
974
    return TCL_OK;
 
975
}
 
976
 
 
977
/*
 
978
 *---------------------------------------------------------------------------
 
979
 *
 
980
 * EntryWorldChanged --
 
981
 *
 
982
 *      This procedure is called when the world has changed in some
 
983
 *      way and the widget needs to recompute all its graphics contexts
 
984
 *      and determine its new geometry.
 
985
 *
 
986
 * Results:
 
987
 *      None.
 
988
 *
 
989
 * Side effects:
 
990
 *      Entry will be relayed out and redisplayed.
 
991
 *
 
992
 *---------------------------------------------------------------------------
 
993
 */
 
994
 
 
995
static void
 
996
EntryWorldChanged(instanceData)
 
997
    ClientData instanceData;    /* Information about widget. */
 
998
{
 
999
    XGCValues gcValues;
 
1000
    GC gc;
 
1001
    unsigned long mask;
 
1002
    Entry *entryPtr;
 
1003
 
 
1004
    entryPtr = (Entry *) instanceData;
 
1005
 
 
1006
    entryPtr->avgWidth = Tk_TextWidth(entryPtr->tkfont, "0", 1);
 
1007
    if (entryPtr->avgWidth == 0) {
 
1008
        entryPtr->avgWidth = 1;
 
1009
    }
 
1010
 
 
1011
    gcValues.foreground = entryPtr->fgColorPtr->pixel;
 
1012
    gcValues.font = Tk_FontId(entryPtr->tkfont);
 
1013
    gcValues.graphics_exposures = False;
 
1014
    mask = GCForeground | GCFont | GCGraphicsExposures;
 
1015
    gc = Tk_GetGC(entryPtr->tkwin, mask, &gcValues);
 
1016
    if (entryPtr->textGC != None) {
 
1017
        Tk_FreeGC(entryPtr->display, entryPtr->textGC);
 
1018
    }
 
1019
    entryPtr->textGC = gc;
 
1020
 
 
1021
    gcValues.foreground = entryPtr->selFgColorPtr->pixel;
 
1022
    gcValues.font = Tk_FontId(entryPtr->tkfont);
 
1023
    mask = GCForeground | GCFont;
 
1024
    gc = Tk_GetGC(entryPtr->tkwin, mask, &gcValues);
 
1025
    if (entryPtr->selTextGC != None) {
 
1026
        Tk_FreeGC(entryPtr->display, entryPtr->selTextGC);
 
1027
    }
 
1028
    entryPtr->selTextGC = gc;
 
1029
 
 
1030
    /*
 
1031
     * Recompute the window's geometry and arrange for it to be
 
1032
     * redisplayed.
 
1033
     */
 
1034
 
 
1035
    EntryComputeGeometry(entryPtr);
 
1036
    entryPtr->flags |= UPDATE_SCROLLBAR;
 
1037
    EventuallyRedraw(entryPtr);
 
1038
}
 
1039
 
 
1040
/*
 
1041
 *--------------------------------------------------------------
 
1042
 *
 
1043
 * DisplayEntry --
 
1044
 *
 
1045
 *      This procedure redraws the contents of an entry window.
 
1046
 *
 
1047
 * Results:
 
1048
 *      None.
 
1049
 *
 
1050
 * Side effects:
 
1051
 *      Information appears on the screen.
 
1052
 *
 
1053
 *--------------------------------------------------------------
 
1054
 */
 
1055
 
 
1056
static void
 
1057
DisplayEntry(clientData)
 
1058
    ClientData clientData;      /* Information about window. */
 
1059
{
 
1060
    register Entry *entryPtr = (Entry *) clientData;
 
1061
    register Tk_Window tkwin = entryPtr->tkwin;
 
1062
    int baseY, selStartX, selEndX, cursorX, x, w;
 
1063
    int xBound;
 
1064
    Tk_FontMetrics fm;
 
1065
    Pixmap pixmap;
 
1066
    int showSelection;
 
1067
 
 
1068
    entryPtr->flags &= ~REDRAW_PENDING;
 
1069
    if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
 
1070
        return;
 
1071
    }
 
1072
 
 
1073
    Tk_GetFontMetrics(entryPtr->tkfont, &fm);
 
1074
 
 
1075
    /*
 
1076
     * Update the scrollbar if that's needed.
 
1077
     */
 
1078
 
 
1079
    if (entryPtr->flags & UPDATE_SCROLLBAR) {
 
1080
        entryPtr->flags &= ~UPDATE_SCROLLBAR;
 
1081
        EntryUpdateScrollbar(entryPtr);
 
1082
    }
 
1083
 
 
1084
    /*
 
1085
     * In order to avoid screen flashes, this procedure redraws the
 
1086
     * textual area of the entry into off-screen memory, then copies
 
1087
     * it back on-screen in a single operation.  This means there's
 
1088
     * no point in time where the on-screen image has been cleared.
 
1089
     */
 
1090
 
 
1091
    pixmap = Tk_GetPixmap(entryPtr->display, Tk_WindowId(tkwin),
 
1092
            Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
 
1093
 
 
1094
    /*
 
1095
     * Compute x-coordinate of the pixel just after last visible
 
1096
     * one, plus vertical position of baseline of text.
 
1097
     */
 
1098
 
 
1099
    xBound = Tk_Width(tkwin) - entryPtr->inset;
 
1100
    baseY = (Tk_Height(tkwin) + fm.ascent - fm.descent) / 2;
 
1101
 
 
1102
    /*
 
1103
     * On Windows and Mac, we need to hide the selection whenever we
 
1104
     * don't have the focus.
 
1105
     */
 
1106
 
 
1107
#ifdef ALWAYS_SHOW_SELECTION
 
1108
    showSelection = 1;
 
1109
#else
 
1110
    showSelection = (entryPtr->flags & GOT_FOCUS);
 
1111
#endif
 
1112
 
 
1113
    /*
 
1114
     * Draw the background in three layers.  From bottom to top the
 
1115
     * layers are:  normal background, selection background, and
 
1116
     * insertion cursor background.
 
1117
     */
 
1118
 
 
1119
    Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
 
1120
            0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
 
1121
    if (showSelection && (entryPtr->selectLast > entryPtr->leftIndex)) {
 
1122
        if (entryPtr->selectFirst <= entryPtr->leftIndex) {
 
1123
            selStartX = entryPtr->leftX;
 
1124
        } else {
 
1125
            Tk_CharBbox(entryPtr->textLayout, entryPtr->selectFirst,
 
1126
                    &x, NULL, NULL, NULL);
 
1127
            selStartX = x + entryPtr->layoutX;
 
1128
        }
 
1129
        if ((selStartX - entryPtr->selBorderWidth) < xBound) {
 
1130
            Tk_CharBbox(entryPtr->textLayout, entryPtr->selectLast - 1,
 
1131
                    &x, NULL, &w, NULL);
 
1132
            selEndX = x + w + entryPtr->layoutX;
 
1133
            Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->selBorder,
 
1134
                    selStartX - entryPtr->selBorderWidth,
 
1135
                    baseY - fm.ascent - entryPtr->selBorderWidth,
 
1136
                    (selEndX - selStartX) + 2*entryPtr->selBorderWidth,
 
1137
                    (fm.ascent + fm.descent) + 2*entryPtr->selBorderWidth,
 
1138
                    entryPtr->selBorderWidth, TK_RELIEF_RAISED);
 
1139
        } 
 
1140
    }
 
1141
 
 
1142
    /*
 
1143
     * Draw a special background for the insertion cursor, overriding
 
1144
     * even the selection background.  As a special hack to keep the
 
1145
     * cursor visible when the insertion cursor color is the same as
 
1146
     * the color for selected text (e.g., on mono displays), write
 
1147
     * background in the cursor area (instead of nothing) when the
 
1148
     * cursor isn't on.  Otherwise the selection would hide the cursor.
 
1149
     */
 
1150
 
 
1151
    if ((entryPtr->insertPos >= entryPtr->leftIndex)
 
1152
            && (entryPtr->state == tkNormalUid)
 
1153
            && (entryPtr->flags & GOT_FOCUS)) {
 
1154
        if (entryPtr->insertPos == 0) {
 
1155
            cursorX = 0;
 
1156
        } else if (entryPtr->insertPos >= entryPtr->numChars) {
 
1157
            Tk_CharBbox(entryPtr->textLayout, entryPtr->numChars - 1,
 
1158
                    &x, NULL, &w, NULL);
 
1159
            cursorX = x + w;
 
1160
        } else {
 
1161
            Tk_CharBbox(entryPtr->textLayout, entryPtr->insertPos,
 
1162
                    &x, NULL, NULL, NULL);
 
1163
            cursorX = x;
 
1164
        }
 
1165
        cursorX += entryPtr->layoutX;
 
1166
        cursorX -= (entryPtr->insertWidth)/2;
 
1167
        if (cursorX < xBound) {
 
1168
            if (entryPtr->flags & CURSOR_ON) {
 
1169
                Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->insertBorder,
 
1170
                        cursorX, baseY - fm.ascent,
 
1171
                        entryPtr->insertWidth, fm.ascent + fm.descent, 
 
1172
                        entryPtr->insertBorderWidth, TK_RELIEF_RAISED);
 
1173
            } else if (entryPtr->insertBorder == entryPtr->selBorder) {
 
1174
                Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
 
1175
                        cursorX, baseY - fm.ascent,
 
1176
                        entryPtr->insertWidth, fm.ascent + fm.descent,
 
1177
                        0, TK_RELIEF_FLAT);
 
1178
            }
 
1179
        }
 
1180
    }
 
1181
 
 
1182
    /*
 
1183
     * Draw the text in two pieces:  first the unselected portion, then the
 
1184
     * selected portion on top of it.
 
1185
     */
 
1186
 
 
1187
    Tk_DrawTextLayout(entryPtr->display, pixmap, entryPtr->textGC,
 
1188
            entryPtr->textLayout, entryPtr->layoutX, entryPtr->layoutY,
 
1189
            entryPtr->leftIndex, entryPtr->numChars);
 
1190
 
 
1191
    if (showSelection && (entryPtr->selTextGC != entryPtr->textGC) &&
 
1192
            (entryPtr->selectFirst < entryPtr->selectLast)) {
 
1193
        int first;
 
1194
 
 
1195
        if (entryPtr->selectFirst - entryPtr->leftIndex < 0) {
 
1196
            first = entryPtr->leftIndex;
 
1197
        } else {
 
1198
            first = entryPtr->selectFirst;
 
1199
        }
 
1200
        Tk_DrawTextLayout(entryPtr->display, pixmap, entryPtr->selTextGC,
 
1201
                entryPtr->textLayout, entryPtr->layoutX, entryPtr->layoutY,
 
1202
                first, entryPtr->selectLast);
 
1203
    }
 
1204
 
 
1205
    /*
 
1206
     * Draw the border and focus highlight last, so they will overwrite
 
1207
     * any text that extends past the viewable part of the window.
 
1208
     */
 
1209
 
 
1210
    if (entryPtr->relief != TK_RELIEF_FLAT) {
 
1211
        Tk_Draw3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
 
1212
                entryPtr->highlightWidth, entryPtr->highlightWidth,
 
1213
                Tk_Width(tkwin) - 2*entryPtr->highlightWidth,
 
1214
                Tk_Height(tkwin) - 2*entryPtr->highlightWidth,
 
1215
                entryPtr->borderWidth, entryPtr->relief);
 
1216
    }
 
1217
    if (entryPtr->highlightWidth != 0) {
 
1218
        GC gc;
 
1219
 
 
1220
        if (entryPtr->flags & GOT_FOCUS) {
 
1221
            gc = Tk_GCForColor(entryPtr->highlightColorPtr, pixmap);
 
1222
        } else {
 
1223
            gc = Tk_GCForColor(entryPtr->highlightBgColorPtr, pixmap);
 
1224
        }
 
1225
        Tk_DrawFocusHighlight(tkwin, gc, entryPtr->highlightWidth, pixmap);
 
1226
    }
 
1227
 
 
1228
    /*
 
1229
     * Everything's been redisplayed;  now copy the pixmap onto the screen
 
1230
     * and free up the pixmap.
 
1231
     */
 
1232
 
 
1233
    XCopyArea(entryPtr->display, pixmap, Tk_WindowId(tkwin), entryPtr->textGC,
 
1234
            0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin),
 
1235
            0, 0);
 
1236
    Tk_FreePixmap(entryPtr->display, pixmap);
 
1237
    entryPtr->flags &= ~BORDER_NEEDED;
 
1238
}
 
1239
 
 
1240
/*
 
1241
 *----------------------------------------------------------------------
 
1242
 *
 
1243
 * EntryComputeGeometry --
 
1244
 *
 
1245
 *      This procedure is invoked to recompute information about where
 
1246
 *      in its window an entry's string will be displayed.  It also
 
1247
 *      computes the requested size for the window.
 
1248
 *
 
1249
 * Results:
 
1250
 *      None.
 
1251
 *
 
1252
 * Side effects:
 
1253
 *      The leftX and tabOrigin fields are recomputed for entryPtr,
 
1254
 *      and leftIndex may be adjusted.  Tk_GeometryRequest is called
 
1255
 *      to register the desired dimensions for the window.
 
1256
 *
 
1257
 *----------------------------------------------------------------------
 
1258
 */
 
1259
 
 
1260
static void
 
1261
EntryComputeGeometry(entryPtr)
 
1262
    Entry *entryPtr;                    /* Widget record for entry. */
 
1263
{
 
1264
    int totalLength, overflow, maxOffScreen, rightX;
 
1265
    int height, width, i;
 
1266
    Tk_FontMetrics fm;
 
1267
    char *p, *displayString;
 
1268
 
 
1269
    /*
 
1270
     * If we're displaying a special character instead of the value of
 
1271
     * the entry, recompute the displayString.
 
1272
     */
 
1273
 
 
1274
    if (entryPtr->displayString != NULL) {
 
1275
        ckfree(entryPtr->displayString);
 
1276
        entryPtr->displayString = NULL;
 
1277
    }
 
1278
    if (entryPtr->showChar != NULL) {
 
1279
        entryPtr->displayString = (char *) ckalloc((unsigned)
 
1280
                (entryPtr->numChars + 1));
 
1281
        for (p = entryPtr->displayString, i = entryPtr->numChars; i > 0;
 
1282
                i--, p++) {
 
1283
            *p = entryPtr->showChar[0];
 
1284
        }
 
1285
        *p = 0;
 
1286
        displayString = entryPtr->displayString;
 
1287
    } else {
 
1288
        displayString = entryPtr->string;
 
1289
    }
 
1290
    Tk_FreeTextLayout(entryPtr->textLayout);
 
1291
    entryPtr->textLayout = Tk_ComputeTextLayout(entryPtr->tkfont,
 
1292
            displayString, entryPtr->numChars, 0, entryPtr->justify,
 
1293
            TK_IGNORE_NEWLINES, &totalLength, &height);
 
1294
 
 
1295
    entryPtr->layoutY = (Tk_Height(entryPtr->tkwin) - height) / 2;
 
1296
 
 
1297
    /*
 
1298
     * Recompute where the leftmost character on the display will
 
1299
     * be drawn (entryPtr->leftX) and adjust leftIndex if necessary
 
1300
     * so that we don't let characters hang off the edge of the
 
1301
     * window unless the entire window is full.
 
1302
     */
 
1303
 
 
1304
    overflow = totalLength - (Tk_Width(entryPtr->tkwin) - 2*entryPtr->inset);
 
1305
    if (overflow <= 0) {
 
1306
        entryPtr->leftIndex = 0;
 
1307
        if (entryPtr->justify == TK_JUSTIFY_LEFT) {
 
1308
            entryPtr->leftX = entryPtr->inset;
 
1309
        } else if (entryPtr->justify == TK_JUSTIFY_RIGHT) {
 
1310
            entryPtr->leftX = Tk_Width(entryPtr->tkwin) - entryPtr->inset
 
1311
                    - totalLength;
 
1312
        } else {
 
1313
            entryPtr->leftX = (Tk_Width(entryPtr->tkwin) - totalLength)/2;
 
1314
        }
 
1315
        entryPtr->layoutX = entryPtr->leftX;
 
1316
    } else {
 
1317
        /*
 
1318
         * The whole string can't fit in the window.  Compute the
 
1319
         * maximum number of characters that may be off-screen to
 
1320
         * the left without leaving empty space on the right of the
 
1321
         * window, then don't let leftIndex be any greater than that.
 
1322
         */
 
1323
 
 
1324
        maxOffScreen = Tk_PointToChar(entryPtr->textLayout, overflow, 0);
 
1325
        Tk_CharBbox(entryPtr->textLayout, maxOffScreen,
 
1326
                &rightX, NULL, NULL, NULL);
 
1327
        if (rightX < overflow) {
 
1328
            maxOffScreen += 1;
 
1329
        }
 
1330
        if (entryPtr->leftIndex > maxOffScreen) {
 
1331
            entryPtr->leftIndex = maxOffScreen;
 
1332
        }
 
1333
        Tk_CharBbox(entryPtr->textLayout, entryPtr->leftIndex,
 
1334
                &rightX, NULL, NULL, NULL);
 
1335
        entryPtr->leftX = entryPtr->inset;
 
1336
        entryPtr->layoutX = entryPtr->leftX - rightX;
 
1337
    }
 
1338
 
 
1339
    Tk_GetFontMetrics(entryPtr->tkfont, &fm);
 
1340
    height = fm.linespace + 2*entryPtr->inset + 2*(YPAD-XPAD);
 
1341
    if (entryPtr->prefWidth > 0) {
 
1342
        width = entryPtr->prefWidth*entryPtr->avgWidth + 2*entryPtr->inset;
 
1343
    } else {
 
1344
        if (totalLength == 0) {
 
1345
            width = entryPtr->avgWidth + 2*entryPtr->inset;
 
1346
        } else {
 
1347
            width = totalLength + 2*entryPtr->inset;
 
1348
        }
 
1349
    }
 
1350
    Tk_GeometryRequest(entryPtr->tkwin, width, height);
 
1351
}
 
1352
 
 
1353
/*
 
1354
 *----------------------------------------------------------------------
 
1355
 *
 
1356
 * InsertChars --
 
1357
 *
 
1358
 *      Add new characters to an entry widget.
 
1359
 *
 
1360
 * Results:
 
1361
 *      None.
 
1362
 *
 
1363
 * Side effects:
 
1364
 *      New information gets added to entryPtr;  it will be redisplayed
 
1365
 *      soon, but not necessarily immediately.
 
1366
 *
 
1367
 *----------------------------------------------------------------------
 
1368
 */
 
1369
 
 
1370
static void
 
1371
InsertChars(entryPtr, index, string)
 
1372
    register Entry *entryPtr;   /* Entry that is to get the new
 
1373
                                 * elements. */
 
1374
    int index;                  /* Add the new elements before this
 
1375
                                 * element. */
 
1376
    char *string;               /* New characters to add (NULL-terminated
 
1377
                                 * string). */
 
1378
{
 
1379
    int length;
 
1380
    char *new;
 
1381
 
 
1382
    length = strlen(string);
 
1383
    if (length == 0) {
 
1384
        return;
 
1385
    }
 
1386
    new = (char *) ckalloc((unsigned) (entryPtr->numChars + length + 1));
 
1387
    strncpy(new, entryPtr->string, (size_t) index);
 
1388
    strcpy(new+index, string);
 
1389
    strcpy(new+index+length, entryPtr->string+index);
 
1390
    ckfree(entryPtr->string);
 
1391
    entryPtr->string = new;
 
1392
    entryPtr->numChars += length;
 
1393
 
 
1394
    /*
 
1395
     * Inserting characters invalidates all indexes into the string.
 
1396
     * Touch up the indexes so that they still refer to the same
 
1397
     * characters (at new positions).  When updating the selection
 
1398
     * end-points, don't include the new text in the selection unless
 
1399
     * it was completely surrounded by the selection.
 
1400
     */
 
1401
 
 
1402
    if (entryPtr->selectFirst >= index) {
 
1403
        entryPtr->selectFirst += length;
 
1404
    }
 
1405
    if (entryPtr->selectLast > index) {
 
1406
        entryPtr->selectLast += length;
 
1407
    }
 
1408
    if ((entryPtr->selectAnchor > index) || (entryPtr->selectFirst >= index)) {
 
1409
        entryPtr->selectAnchor += length;
 
1410
    }
 
1411
    if (entryPtr->leftIndex > index) {
 
1412
        entryPtr->leftIndex += length;
 
1413
    }
 
1414
    if (entryPtr->insertPos >= index) {
 
1415
        entryPtr->insertPos += length;
 
1416
    }
 
1417
    EntryValueChanged(entryPtr);
 
1418
}
 
1419
 
 
1420
/*
 
1421
 *----------------------------------------------------------------------
 
1422
 *
 
1423
 * DeleteChars --
 
1424
 *
 
1425
 *      Remove one or more characters from an entry widget.
 
1426
 *
 
1427
 * Results:
 
1428
 *      None.
 
1429
 *
 
1430
 * Side effects:
 
1431
 *      Memory gets freed, the entry gets modified and (eventually)
 
1432
 *      redisplayed.
 
1433
 *
 
1434
 *----------------------------------------------------------------------
 
1435
 */
 
1436
 
 
1437
static void
 
1438
DeleteChars(entryPtr, index, count)
 
1439
    register Entry *entryPtr;   /* Entry widget to modify. */
 
1440
    int index;                  /* Index of first character to delete. */
 
1441
    int count;                  /* How many characters to delete. */
 
1442
{
 
1443
    char *new;
 
1444
 
 
1445
    if ((index + count) > entryPtr->numChars) {
 
1446
        count = entryPtr->numChars - index;
 
1447
    }
 
1448
    if (count <= 0) {
 
1449
        return;
 
1450
    }
 
1451
 
 
1452
    new = (char *) ckalloc((unsigned) (entryPtr->numChars + 1 - count));
 
1453
    strncpy(new, entryPtr->string, (size_t) index);
 
1454
    strcpy(new+index, entryPtr->string+index+count);
 
1455
    ckfree(entryPtr->string);
 
1456
    entryPtr->string = new;
 
1457
    entryPtr->numChars -= count;
 
1458
 
 
1459
    /*
 
1460
     * Deleting characters results in the remaining characters being
 
1461
     * renumbered.  Update the various indexes into the string to reflect
 
1462
     * this change.
 
1463
     */
 
1464
 
 
1465
    if (entryPtr->selectFirst >= index) {
 
1466
        if (entryPtr->selectFirst >= (index+count)) {
 
1467
            entryPtr->selectFirst -= count;
 
1468
        } else {
 
1469
            entryPtr->selectFirst = index;
 
1470
        }
 
1471
    }
 
1472
    if (entryPtr->selectLast >= index) {
 
1473
        if (entryPtr->selectLast >= (index+count)) {
 
1474
            entryPtr->selectLast -= count;
 
1475
        } else {
 
1476
            entryPtr->selectLast = index;
 
1477
        }
 
1478
    }
 
1479
    if (entryPtr->selectLast <= entryPtr->selectFirst) {
 
1480
        entryPtr->selectFirst = entryPtr->selectLast = -1;
 
1481
    }
 
1482
    if (entryPtr->selectAnchor >= index) {
 
1483
        if (entryPtr->selectAnchor >= (index+count)) {
 
1484
            entryPtr->selectAnchor -= count;
 
1485
        } else {
 
1486
            entryPtr->selectAnchor = index;
 
1487
        }
 
1488
    }
 
1489
    if (entryPtr->leftIndex > index) {
 
1490
        if (entryPtr->leftIndex >= (index+count)) {
 
1491
            entryPtr->leftIndex -= count;
 
1492
        } else {
 
1493
            entryPtr->leftIndex = index;
 
1494
        }
 
1495
    }
 
1496
    if (entryPtr->insertPos >= index) {
 
1497
        if (entryPtr->insertPos >= (index+count)) {
 
1498
            entryPtr->insertPos -= count;
 
1499
        } else {
 
1500
            entryPtr->insertPos = index;
 
1501
        }
 
1502
    }
 
1503
    EntryValueChanged(entryPtr);
 
1504
}
 
1505
 
 
1506
/*
 
1507
 *----------------------------------------------------------------------
 
1508
 *
 
1509
 * EntryValueChanged --
 
1510
 *
 
1511
 *      This procedure is invoked when characters are inserted into
 
1512
 *      an entry or deleted from it.  It updates the entry's associated
 
1513
 *      variable, if there is one, and does other bookkeeping such
 
1514
 *      as arranging for redisplay.
 
1515
 *
 
1516
 * Results:
 
1517
 *      None.
 
1518
 *
 
1519
 * Side effects:
 
1520
 *      None.
 
1521
 *
 
1522
 *----------------------------------------------------------------------
 
1523
 */
 
1524
 
 
1525
static void
 
1526
EntryValueChanged(entryPtr)
 
1527
    Entry *entryPtr;            /* Entry whose value just changed. */
 
1528
{
 
1529
    char *newValue;
 
1530
 
 
1531
    if (entryPtr->textVarName == NULL) {
 
1532
        newValue = NULL;
 
1533
    } else {
 
1534
        newValue = Tcl_SetVar(entryPtr->interp, entryPtr->textVarName,
 
1535
                entryPtr->string, TCL_GLOBAL_ONLY);
 
1536
    }
 
1537
 
 
1538
    if ((newValue != NULL) && (strcmp(newValue, entryPtr->string) != 0)) {
 
1539
        /*
 
1540
         * The value of the variable is different than what we asked for.
 
1541
         * This means that a trace on the variable modified it.  In this
 
1542
         * case our trace procedure wasn't invoked since the modification
 
1543
         * came while a trace was already active on the variable.  So,
 
1544
         * update our value to reflect the variable's latest value.
 
1545
         */
 
1546
 
 
1547
        EntrySetValue(entryPtr, newValue);
 
1548
    } else {
 
1549
        /*
 
1550
         * Arrange for redisplay.
 
1551
         */
 
1552
 
 
1553
        entryPtr->flags |= UPDATE_SCROLLBAR;
 
1554
        EntryComputeGeometry(entryPtr);
 
1555
        EventuallyRedraw(entryPtr);
 
1556
    }
 
1557
}
 
1558
 
 
1559
/*
 
1560
 *----------------------------------------------------------------------
 
1561
 *
 
1562
 * EntrySetValue --
 
1563
 *
 
1564
 *      Replace the contents of a text entry with a given value.  This
 
1565
 *      procedure is invoked when updating the entry from the entry's
 
1566
 *      associated variable.
 
1567
 *
 
1568
 * Results:
 
1569
 *      None.
 
1570
 *
 
1571
 * Side effects:
 
1572
 *      The string displayed in the entry will change.  The selection,
 
1573
 *      insertion point, and view may have to be adjusted to keep them
 
1574
 *      within the bounds of the new string.  Note: this procedure does
 
1575
 *      *not* update the entry's associated variable, since that could
 
1576
 *      result in an infinite loop.
 
1577
 *
 
1578
 *----------------------------------------------------------------------
 
1579
 */
 
1580
 
 
1581
static void
 
1582
EntrySetValue(entryPtr, value)
 
1583
    register Entry *entryPtr;           /* Entry whose value is to be
 
1584
                                         * changed. */
 
1585
    char *value;                        /* New text to display in entry. */
 
1586
{
 
1587
    ckfree(entryPtr->string);
 
1588
    entryPtr->numChars = strlen(value);
 
1589
    entryPtr->string = (char *) ckalloc((unsigned) (entryPtr->numChars + 1));
 
1590
    strcpy(entryPtr->string, value);
 
1591
    if (entryPtr->selectFirst != -1) {
 
1592
        if (entryPtr->selectFirst >= entryPtr->numChars) {
 
1593
            entryPtr->selectFirst = entryPtr->selectLast = -1;
 
1594
        } else if (entryPtr->selectLast > entryPtr->numChars) {
 
1595
            entryPtr->selectLast = entryPtr->numChars;
 
1596
        }
 
1597
    }
 
1598
    if (entryPtr->leftIndex >= entryPtr->numChars) {
 
1599
        entryPtr->leftIndex = entryPtr->numChars-1;
 
1600
        if (entryPtr->leftIndex < 0) {
 
1601
            entryPtr->leftIndex = 0;
 
1602
        }
 
1603
    }
 
1604
    if (entryPtr->insertPos > entryPtr->numChars) {
 
1605
        entryPtr->insertPos = entryPtr->numChars;
 
1606
    }
 
1607
 
 
1608
    entryPtr->flags |= UPDATE_SCROLLBAR;
 
1609
    EntryComputeGeometry(entryPtr);
 
1610
    EventuallyRedraw(entryPtr);
 
1611
}
 
1612
 
 
1613
/*
 
1614
 *--------------------------------------------------------------
 
1615
 *
 
1616
 * EntryEventProc --
 
1617
 *
 
1618
 *      This procedure is invoked by the Tk dispatcher for various
 
1619
 *      events on entryes.
 
1620
 *
 
1621
 * Results:
 
1622
 *      None.
 
1623
 *
 
1624
 * Side effects:
 
1625
 *      When the window gets deleted, internal structures get
 
1626
 *      cleaned up.  When it gets exposed, it is redisplayed.
 
1627
 *
 
1628
 *--------------------------------------------------------------
 
1629
 */
 
1630
 
 
1631
static void
 
1632
EntryEventProc(clientData, eventPtr)
 
1633
    ClientData clientData;      /* Information about window. */
 
1634
    XEvent *eventPtr;           /* Information about event. */
 
1635
{
 
1636
    Entry *entryPtr = (Entry *) clientData;
 
1637
    if (eventPtr->type == Expose) {
 
1638
        EventuallyRedraw(entryPtr);
 
1639
        entryPtr->flags |= BORDER_NEEDED;
 
1640
    } else if (eventPtr->type == DestroyNotify) {
 
1641
        if (entryPtr->tkwin != NULL) {
 
1642
            entryPtr->tkwin = NULL;
 
1643
            Tcl_DeleteCommandFromToken(entryPtr->interp, entryPtr->widgetCmd);
 
1644
        }
 
1645
        if (entryPtr->flags & REDRAW_PENDING) {
 
1646
            Tcl_CancelIdleCall(DisplayEntry, (ClientData) entryPtr);
 
1647
        }
 
1648
        Tcl_EventuallyFree((ClientData) entryPtr, DestroyEntry);
 
1649
    } else if (eventPtr->type == ConfigureNotify) {
 
1650
        Tcl_Preserve((ClientData) entryPtr);
 
1651
        entryPtr->flags |= UPDATE_SCROLLBAR;
 
1652
        EntryComputeGeometry(entryPtr);
 
1653
        EventuallyRedraw(entryPtr);
 
1654
        Tcl_Release((ClientData) entryPtr);
 
1655
    } else if (eventPtr->type == FocusIn) {
 
1656
        if (eventPtr->xfocus.detail != NotifyInferior) {
 
1657
            EntryFocusProc(entryPtr, 1);
 
1658
        }
 
1659
    } else if (eventPtr->type == FocusOut) {
 
1660
        if (eventPtr->xfocus.detail != NotifyInferior) {
 
1661
            EntryFocusProc(entryPtr, 0);
 
1662
        }
 
1663
    }
 
1664
}
 
1665
 
 
1666
/*
 
1667
 *----------------------------------------------------------------------
 
1668
 *
 
1669
 * EntryCmdDeletedProc --
 
1670
 *
 
1671
 *      This procedure is invoked when a widget command is deleted.  If
 
1672
 *      the widget isn't already in the process of being destroyed,
 
1673
 *      this command destroys it.
 
1674
 *
 
1675
 * Results:
 
1676
 *      None.
 
1677
 *
 
1678
 * Side effects:
 
1679
 *      The widget is destroyed.
 
1680
 *
 
1681
 *----------------------------------------------------------------------
 
1682
 */
 
1683
 
 
1684
static void
 
1685
EntryCmdDeletedProc(clientData)
 
1686
    ClientData clientData;      /* Pointer to widget record for widget. */
 
1687
{
 
1688
    Entry *entryPtr = (Entry *) clientData;
 
1689
    Tk_Window tkwin = entryPtr->tkwin;
 
1690
 
 
1691
    /*
 
1692
     * This procedure could be invoked either because the window was
 
1693
     * destroyed and the command was then deleted (in which case tkwin
 
1694
     * is NULL) or because the command was deleted, and then this procedure
 
1695
     * destroys the widget.
 
1696
     */
 
1697
 
 
1698
    if (tkwin != NULL) {
 
1699
        entryPtr->tkwin = NULL;
 
1700
        Tk_DestroyWindow(tkwin);
 
1701
    }
 
1702
}
 
1703
 
 
1704
/*
 
1705
 *--------------------------------------------------------------
 
1706
 *
 
1707
 * GetEntryIndex --
 
1708
 *
 
1709
 *      Parse an index into an entry and return either its value
 
1710
 *      or an error.
 
1711
 *
 
1712
 * Results:
 
1713
 *      A standard Tcl result.  If all went well, then *indexPtr is
 
1714
 *      filled in with the index (into entryPtr) corresponding to
 
1715
 *      string.  The index value is guaranteed to lie between 0 and
 
1716
 *      the number of characters in the string, inclusive.  If an
 
1717
 *      error occurs then an error message is left in interp->result.
 
1718
 *
 
1719
 * Side effects:
 
1720
 *      None.
 
1721
 *
 
1722
 *--------------------------------------------------------------
 
1723
 */
 
1724
 
 
1725
static int
 
1726
GetEntryIndex(interp, entryPtr, string, indexPtr)
 
1727
    Tcl_Interp *interp;         /* For error messages. */
 
1728
    Entry *entryPtr;            /* Entry for which the index is being
 
1729
                                 * specified. */
 
1730
    char *string;               /* Specifies character in entryPtr. */
 
1731
    int *indexPtr;              /* Where to store converted index. */
 
1732
{
 
1733
    size_t length;
 
1734
 
 
1735
    length = strlen(string);
 
1736
 
 
1737
    if (string[0] == 'a') {
 
1738
        if (strncmp(string, "anchor", length) == 0) {
 
1739
            *indexPtr = entryPtr->selectAnchor;
 
1740
        } else {
 
1741
            badIndex:
 
1742
 
 
1743
            /*
 
1744
             * Some of the paths here leave messages in interp->result,
 
1745
             * so we have to clear it out before storing our own message.
 
1746
             */
 
1747
 
 
1748
            Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
 
1749
            Tcl_AppendResult(interp, "bad entry index \"", string,
 
1750
                    "\"", (char *) NULL);
 
1751
            return TCL_ERROR;
 
1752
        }
 
1753
    } else if (string[0] == 'e') {
 
1754
        if (strncmp(string, "end", length) == 0) {
 
1755
            *indexPtr = entryPtr->numChars;
 
1756
        } else {
 
1757
            goto badIndex;
 
1758
        }
 
1759
    } else if (string[0] == 'i') {
 
1760
        if (strncmp(string, "insert", length) == 0) {
 
1761
            *indexPtr = entryPtr->insertPos;
 
1762
        } else {
 
1763
            goto badIndex;
 
1764
        }
 
1765
    } else if (string[0] == 's') {
 
1766
        if (entryPtr->selectFirst == -1) {
 
1767
            interp->result = "selection isn't in entry";
 
1768
            return TCL_ERROR;
 
1769
        }
 
1770
        if (length < 5) {
 
1771
            goto badIndex;
 
1772
        }
 
1773
        if (strncmp(string, "sel.first", length) == 0) {
 
1774
            *indexPtr = entryPtr->selectFirst;
 
1775
        } else if (strncmp(string, "sel.last", length) == 0) {
 
1776
            *indexPtr = entryPtr->selectLast;
 
1777
        } else {
 
1778
            goto badIndex;
 
1779
        }
 
1780
    } else if (string[0] == '@') {
 
1781
        int x, roundUp;
 
1782
 
 
1783
        if (Tcl_GetInt(interp, string+1, &x) != TCL_OK) {
 
1784
            goto badIndex;
 
1785
        }
 
1786
        if (x < entryPtr->inset) {
 
1787
            x = entryPtr->inset;
 
1788
        }
 
1789
        roundUp = 0;
 
1790
        if (x >= (Tk_Width(entryPtr->tkwin) - entryPtr->inset)) {
 
1791
            x = Tk_Width(entryPtr->tkwin) - entryPtr->inset - 1;
 
1792
            roundUp = 1;
 
1793
        }
 
1794
        *indexPtr = Tk_PointToChar(entryPtr->textLayout,
 
1795
                x - entryPtr->layoutX, 0);
 
1796
 
 
1797
        /*
 
1798
         * Special trick:  if the x-position was off-screen to the right,
 
1799
         * round the index up to refer to the character just after the
 
1800
         * last visible one on the screen.  This is needed to enable the
 
1801
         * last character to be selected, for example.
 
1802
         */
 
1803
 
 
1804
        if (roundUp && (*indexPtr < entryPtr->numChars)) {
 
1805
            *indexPtr += 1;
 
1806
        }
 
1807
    } else {
 
1808
        if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
 
1809
            goto badIndex;
 
1810
        }
 
1811
        if (*indexPtr < 0){
 
1812
            *indexPtr = 0;
 
1813
        } else if (*indexPtr > entryPtr->numChars) {
 
1814
            *indexPtr = entryPtr->numChars;
 
1815
        }
 
1816
    }
 
1817
    return TCL_OK;
 
1818
}
 
1819
 
 
1820
/*
 
1821
 *----------------------------------------------------------------------
 
1822
 *
 
1823
 * EntryScanTo --
 
1824
 *
 
1825
 *      Given a y-coordinate (presumably of the curent mouse location)
 
1826
 *      drag the view in the window to implement the scan operation.
 
1827
 *
 
1828
 * Results:
 
1829
 *      None.
 
1830
 *
 
1831
 * Side effects:
 
1832
 *      The view in the window may change.
 
1833
 *
 
1834
 *----------------------------------------------------------------------
 
1835
 */
 
1836
 
 
1837
static void
 
1838
EntryScanTo(entryPtr, x)
 
1839
    register Entry *entryPtr;           /* Information about widget. */
 
1840
    int x;                              /* X-coordinate to use for scan
 
1841
                                         * operation. */
 
1842
{
 
1843
    int newLeftIndex;
 
1844
 
 
1845
    /*
 
1846
     * Compute new leftIndex for entry by amplifying the difference
 
1847
     * between the current position and the place where the scan
 
1848
     * started (the "mark" position).  If we run off the left or right
 
1849
     * side of the entry, then reset the mark point so that the current
 
1850
     * position continues to correspond to the edge of the window.
 
1851
     * This means that the picture will start dragging as soon as the
 
1852
     * mouse reverses direction (without this reset, might have to slide
 
1853
     * mouse a long ways back before the picture starts moving again).
 
1854
     */
 
1855
 
 
1856
    newLeftIndex = entryPtr->scanMarkIndex
 
1857
            - (10*(x - entryPtr->scanMarkX))/entryPtr->avgWidth;
 
1858
    if (newLeftIndex >= entryPtr->numChars) {
 
1859
        newLeftIndex = entryPtr->scanMarkIndex = entryPtr->numChars-1;
 
1860
        entryPtr->scanMarkX = x;
 
1861
    }
 
1862
    if (newLeftIndex < 0) {
 
1863
        newLeftIndex = entryPtr->scanMarkIndex = 0;
 
1864
        entryPtr->scanMarkX = x;
 
1865
    } 
 
1866
    if (newLeftIndex != entryPtr->leftIndex) {
 
1867
        entryPtr->leftIndex = newLeftIndex;
 
1868
        entryPtr->flags |= UPDATE_SCROLLBAR;
 
1869
        EntryComputeGeometry(entryPtr);
 
1870
        EventuallyRedraw(entryPtr);
 
1871
    }
 
1872
}
 
1873
 
 
1874
/*
 
1875
 *----------------------------------------------------------------------
 
1876
 *
 
1877
 * EntrySelectTo --
 
1878
 *
 
1879
 *      Modify the selection by moving its un-anchored end.  This could
 
1880
 *      make the selection either larger or smaller.
 
1881
 *
 
1882
 * Results:
 
1883
 *      None.
 
1884
 *
 
1885
 * Side effects:
 
1886
 *      The selection changes.
 
1887
 *
 
1888
 *----------------------------------------------------------------------
 
1889
 */
 
1890
 
 
1891
static void
 
1892
EntrySelectTo(entryPtr, index)
 
1893
    register Entry *entryPtr;           /* Information about widget. */
 
1894
    int index;                          /* Index of element that is to
 
1895
                                         * become the "other" end of the
 
1896
                                         * selection. */
 
1897
{
 
1898
    int newFirst, newLast;
 
1899
 
 
1900
    /*
 
1901
     * Grab the selection if we don't own it already.
 
1902
     */
 
1903
 
 
1904
    if (!(entryPtr->flags & GOT_SELECTION) && (entryPtr->exportSelection)) {
 
1905
        Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, EntryLostSelection,
 
1906
                (ClientData) entryPtr);
 
1907
        entryPtr->flags |= GOT_SELECTION;
 
1908
    }
 
1909
 
 
1910
    /*
 
1911
     * Pick new starting and ending points for the selection.
 
1912
     */
 
1913
 
 
1914
    if (entryPtr->selectAnchor > entryPtr->numChars) {
 
1915
        entryPtr->selectAnchor = entryPtr->numChars;
 
1916
    }
 
1917
    if (entryPtr->selectAnchor <= index) {
 
1918
        newFirst = entryPtr->selectAnchor;
 
1919
        newLast = index;
 
1920
    } else {
 
1921
        newFirst = index;
 
1922
        newLast = entryPtr->selectAnchor;
 
1923
        if (newLast < 0) {
 
1924
            newFirst = newLast = -1;
 
1925
        }
 
1926
    }
 
1927
    if ((entryPtr->selectFirst == newFirst)
 
1928
            && (entryPtr->selectLast == newLast)) {
 
1929
        return;
 
1930
    }
 
1931
    entryPtr->selectFirst = newFirst;
 
1932
    entryPtr->selectLast = newLast;
 
1933
    EventuallyRedraw(entryPtr);
 
1934
}
 
1935
 
 
1936
/*
 
1937
 *----------------------------------------------------------------------
 
1938
 *
 
1939
 * EntryFetchSelection --
 
1940
 *
 
1941
 *      This procedure is called back by Tk when the selection is
 
1942
 *      requested by someone.  It returns part or all of the selection
 
1943
 *      in a buffer provided by the caller.
 
1944
 *
 
1945
 * Results:
 
1946
 *      The return value is the number of non-NULL bytes stored
 
1947
 *      at buffer.  Buffer is filled (or partially filled) with a
 
1948
 *      NULL-terminated string containing part or all of the selection,
 
1949
 *      as given by offset and maxBytes.
 
1950
 *
 
1951
 * Side effects:
 
1952
 *      None.
 
1953
 *
 
1954
 *----------------------------------------------------------------------
 
1955
 */
 
1956
 
 
1957
static int
 
1958
EntryFetchSelection(clientData, offset, buffer, maxBytes)
 
1959
    ClientData clientData;              /* Information about entry widget. */
 
1960
    int offset;                         /* Offset within selection of first
 
1961
                                         * character to be returned. */
 
1962
    char *buffer;                       /* Location in which to place
 
1963
                                         * selection. */
 
1964
    int maxBytes;                       /* Maximum number of bytes to place
 
1965
                                         * at buffer, not including terminating
 
1966
                                         * NULL character. */
 
1967
{
 
1968
    Entry *entryPtr = (Entry *) clientData;
 
1969
    int count;
 
1970
    char *displayString;
 
1971
 
 
1972
    if ((entryPtr->selectFirst < 0) || !(entryPtr->exportSelection)) {
 
1973
        return -1;
 
1974
    }
 
1975
    count = entryPtr->selectLast - entryPtr->selectFirst - offset;
 
1976
    if (count > maxBytes) {
 
1977
        count = maxBytes;
 
1978
    }
 
1979
    if (count <= 0) {
 
1980
        return 0;
 
1981
    }
 
1982
    if (entryPtr->displayString == NULL) {
 
1983
        displayString = entryPtr->string;
 
1984
    } else {
 
1985
        displayString = entryPtr->displayString;
 
1986
    }
 
1987
    strncpy(buffer, displayString + entryPtr->selectFirst + offset,
 
1988
            (size_t) count);
 
1989
    buffer[count] = '\0';
 
1990
    return count;
 
1991
}
 
1992
 
 
1993
/*
 
1994
 *----------------------------------------------------------------------
 
1995
 *
 
1996
 * EntryLostSelection --
 
1997
 *
 
1998
 *      This procedure is called back by Tk when the selection is
 
1999
 *      grabbed away from an entry widget.
 
2000
 *
 
2001
 * Results:
 
2002
 *      None.
 
2003
 *
 
2004
 * Side effects:
 
2005
 *      The existing selection is unhighlighted, and the window is
 
2006
 *      marked as not containing a selection.
 
2007
 *
 
2008
 *----------------------------------------------------------------------
 
2009
 */
 
2010
 
 
2011
static void
 
2012
EntryLostSelection(clientData)
 
2013
    ClientData clientData;              /* Information about entry widget. */
 
2014
{
 
2015
    Entry *entryPtr = (Entry *) clientData;
 
2016
 
 
2017
    entryPtr->flags &= ~GOT_SELECTION;
 
2018
 
 
2019
    /*
 
2020
     * On Windows and Mac systems, we want to remember the selection
 
2021
     * for the next time the focus enters the window.  On Unix, we need
 
2022
     * to clear the selection since it is always visible.
 
2023
     */
 
2024
 
 
2025
#ifdef ALWAYS_SHOW_SELECTION
 
2026
    if ((entryPtr->selectFirst != -1) && entryPtr->exportSelection) {
 
2027
        entryPtr->selectFirst = -1;
 
2028
        entryPtr->selectLast = -1;
 
2029
        EventuallyRedraw(entryPtr);
 
2030
    }
 
2031
#endif
 
2032
}
 
2033
 
 
2034
/*
 
2035
 *----------------------------------------------------------------------
 
2036
 *
 
2037
 * EventuallyRedraw --
 
2038
 *
 
2039
 *      Ensure that an entry is eventually redrawn on the display.
 
2040
 *
 
2041
 * Results:
 
2042
 *      None.
 
2043
 *
 
2044
 * Side effects:
 
2045
 *      Information gets redisplayed.  Right now we don't do selective
 
2046
 *      redisplays:  the whole window will be redrawn.  This doesn't
 
2047
 *      seem to hurt performance noticeably, but if it does then this
 
2048
 *      could be changed.
 
2049
 *
 
2050
 *----------------------------------------------------------------------
 
2051
 */
 
2052
 
 
2053
static void
 
2054
EventuallyRedraw(entryPtr)
 
2055
    register Entry *entryPtr;           /* Information about widget. */
 
2056
{
 
2057
    if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(entryPtr->tkwin)) {
 
2058
        return;
 
2059
    }
 
2060
 
 
2061
    /*
 
2062
     * Right now we don't do selective redisplays:  the whole window
 
2063
     * will be redrawn.  This doesn't seem to hurt performance noticeably,
 
2064
     * but if it does then this could be changed.
 
2065
     */
 
2066
 
 
2067
    if (!(entryPtr->flags & REDRAW_PENDING)) {
 
2068
        entryPtr->flags |= REDRAW_PENDING;
 
2069
        Tcl_DoWhenIdle(DisplayEntry, (ClientData) entryPtr);
 
2070
    }
 
2071
}
 
2072
 
 
2073
/*
 
2074
 *----------------------------------------------------------------------
 
2075
 *
 
2076
 * EntryVisibleRange --
 
2077
 *
 
2078
 *      Return information about the range of the entry that is
 
2079
 *      currently visible.
 
2080
 *
 
2081
 * Results:
 
2082
 *      *firstPtr and *lastPtr are modified to hold fractions between
 
2083
 *      0 and 1 identifying the range of characters visible in the
 
2084
 *      entry.
 
2085
 *
 
2086
 * Side effects:
 
2087
 *      None.
 
2088
 *
 
2089
 *----------------------------------------------------------------------
 
2090
 */
 
2091
 
 
2092
static void
 
2093
EntryVisibleRange(entryPtr, firstPtr, lastPtr)
 
2094
    Entry *entryPtr;                    /* Information about widget. */
 
2095
    double *firstPtr;                   /* Return position of first visible
 
2096
                                         * character in widget. */
 
2097
    double *lastPtr;                    /* Return position of char just after
 
2098
                                         * last visible one. */
 
2099
{
 
2100
    int charsInWindow;
 
2101
 
 
2102
    if (entryPtr->numChars == 0) {
 
2103
        *firstPtr = 0.0;
 
2104
        *lastPtr = 1.0;
 
2105
    } else {
 
2106
        charsInWindow = Tk_PointToChar(entryPtr->textLayout,
 
2107
                Tk_Width(entryPtr->tkwin) - entryPtr->inset
 
2108
                        - entryPtr->layoutX - 1, 0) + 1;
 
2109
        if (charsInWindow > entryPtr->numChars) {
 
2110
            /*
 
2111
             * If all chars were visible, then charsInWindow will be
 
2112
             * the index just after the last char that was visible.
 
2113
             */
 
2114
             
 
2115
            charsInWindow = entryPtr->numChars;
 
2116
        }
 
2117
        charsInWindow -= entryPtr->leftIndex;
 
2118
        if (charsInWindow == 0) {
 
2119
            charsInWindow = 1;
 
2120
        }
 
2121
        *firstPtr = ((double) entryPtr->leftIndex)/entryPtr->numChars;
 
2122
        *lastPtr = ((double) (entryPtr->leftIndex + charsInWindow))
 
2123
                /entryPtr->numChars;
 
2124
    }
 
2125
}
 
2126
 
 
2127
/*
 
2128
 *----------------------------------------------------------------------
 
2129
 *
 
2130
 * EntryUpdateScrollbar --
 
2131
 *
 
2132
 *      This procedure is invoked whenever information has changed in
 
2133
 *      an entry in a way that would invalidate a scrollbar display.
 
2134
 *      If there is an associated scrollbar, then this procedure updates
 
2135
 *      it by invoking a Tcl command.
 
2136
 *
 
2137
 * Results:
 
2138
 *      None.
 
2139
 *
 
2140
 * Side effects:
 
2141
 *      A Tcl command is invoked, and an additional command may be
 
2142
 *      invoked to process errors in the command.
 
2143
 *
 
2144
 *----------------------------------------------------------------------
 
2145
 */
 
2146
 
 
2147
static void
 
2148
EntryUpdateScrollbar(entryPtr)
 
2149
    Entry *entryPtr;                    /* Information about widget. */
 
2150
{
 
2151
    char args[100];
 
2152
    int code;
 
2153
    double first, last;
 
2154
    Tcl_Interp *interp;
 
2155
 
 
2156
    if (entryPtr->scrollCmd == NULL) {
 
2157
        return;
 
2158
    }
 
2159
 
 
2160
    interp = entryPtr->interp;
 
2161
    Tcl_Preserve((ClientData) interp);
 
2162
    EntryVisibleRange(entryPtr, &first, &last);
 
2163
    sprintf(args, " %g %g", first, last);
 
2164
    code = Tcl_VarEval(interp, entryPtr->scrollCmd, args, (char *) NULL);
 
2165
    if (code != TCL_OK) {
 
2166
        Tcl_AddErrorInfo(interp,
 
2167
                "\n    (horizontal scrolling command executed by entry)");
 
2168
        Tcl_BackgroundError(interp);
 
2169
    }
 
2170
    Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
 
2171
    Tcl_Release((ClientData) interp);
 
2172
}
 
2173
 
 
2174
/*
 
2175
 *----------------------------------------------------------------------
 
2176
 *
 
2177
 * EntryBlinkProc --
 
2178
 *
 
2179
 *      This procedure is called as a timer handler to blink the
 
2180
 *      insertion cursor off and on.
 
2181
 *
 
2182
 * Results:
 
2183
 *      None.
 
2184
 *
 
2185
 * Side effects:
 
2186
 *      The cursor gets turned on or off, redisplay gets invoked,
 
2187
 *      and this procedure reschedules itself.
 
2188
 *
 
2189
 *----------------------------------------------------------------------
 
2190
 */
 
2191
 
 
2192
static void
 
2193
EntryBlinkProc(clientData)
 
2194
    ClientData clientData;      /* Pointer to record describing entry. */
 
2195
{
 
2196
    register Entry *entryPtr = (Entry *) clientData;
 
2197
 
 
2198
    if (!(entryPtr->flags & GOT_FOCUS) || (entryPtr->insertOffTime == 0)) {
 
2199
        return;
 
2200
    }
 
2201
    if (entryPtr->flags & CURSOR_ON) {
 
2202
        entryPtr->flags &= ~CURSOR_ON;
 
2203
        entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
 
2204
                entryPtr->insertOffTime, EntryBlinkProc, (ClientData) entryPtr);
 
2205
    } else {
 
2206
        entryPtr->flags |= CURSOR_ON;
 
2207
        entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
 
2208
                entryPtr->insertOnTime, EntryBlinkProc, (ClientData) entryPtr);
 
2209
    }
 
2210
    EventuallyRedraw(entryPtr);
 
2211
}
 
2212
 
 
2213
/*
 
2214
 *----------------------------------------------------------------------
 
2215
 *
 
2216
 * EntryFocusProc --
 
2217
 *
 
2218
 *      This procedure is called whenever the entry gets or loses the
 
2219
 *      input focus.  It's also called whenever the window is reconfigured
 
2220
 *      while it has the focus.
 
2221
 *
 
2222
 * Results:
 
2223
 *      None.
 
2224
 *
 
2225
 * Side effects:
 
2226
 *      The cursor gets turned on or off.
 
2227
 *
 
2228
 *----------------------------------------------------------------------
 
2229
 */
 
2230
 
 
2231
static void
 
2232
EntryFocusProc(entryPtr, gotFocus)
 
2233
    register Entry *entryPtr;   /* Entry that got or lost focus. */
 
2234
    int gotFocus;               /* 1 means window is getting focus, 0 means
 
2235
                                 * it's losing it. */
 
2236
{
 
2237
    Tcl_DeleteTimerHandler(entryPtr->insertBlinkHandler);
 
2238
    if (gotFocus) {
 
2239
        entryPtr->flags |= GOT_FOCUS | CURSOR_ON;
 
2240
        if (entryPtr->insertOffTime != 0) {
 
2241
            entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
 
2242
                    entryPtr->insertOnTime, EntryBlinkProc,
 
2243
                    (ClientData) entryPtr);
 
2244
        }
 
2245
    } else {
 
2246
        entryPtr->flags &= ~(GOT_FOCUS | CURSOR_ON);
 
2247
        entryPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
 
2248
    }
 
2249
    EventuallyRedraw(entryPtr);
 
2250
}
 
2251
 
 
2252
/*
 
2253
 *--------------------------------------------------------------
 
2254
 *
 
2255
 * EntryTextVarProc --
 
2256
 *
 
2257
 *      This procedure is invoked when someone changes the variable
 
2258
 *      whose contents are to be displayed in an entry.
 
2259
 *
 
2260
 * Results:
 
2261
 *      NULL is always returned.
 
2262
 *
 
2263
 * Side effects:
 
2264
 *      The text displayed in the entry will change to match the
 
2265
 *      variable.
 
2266
 *
 
2267
 *--------------------------------------------------------------
 
2268
 */
 
2269
 
 
2270
        /* ARGSUSED */
 
2271
static char *
 
2272
EntryTextVarProc(clientData, interp, name1, name2, flags)
 
2273
    ClientData clientData;      /* Information about button. */
 
2274
    Tcl_Interp *interp;         /* Interpreter containing variable. */
 
2275
    char *name1;                /* Not used. */
 
2276
    char *name2;                /* Not used. */
 
2277
    int flags;                  /* Information about what happened. */
 
2278
{
 
2279
    register Entry *entryPtr = (Entry *) clientData;
 
2280
    char *value;
 
2281
 
 
2282
    /*
 
2283
     * If the variable is unset, then immediately recreate it unless
 
2284
     * the whole interpreter is going away.
 
2285
     */
 
2286
 
 
2287
    if (flags & TCL_TRACE_UNSETS) {
 
2288
        if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
 
2289
            Tcl_SetVar(interp, entryPtr->textVarName, entryPtr->string,
 
2290
                    TCL_GLOBAL_ONLY);
 
2291
            Tcl_TraceVar(interp, entryPtr->textVarName,
 
2292
                    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
 
2293
                    EntryTextVarProc, clientData);
 
2294
        }
 
2295
        return (char *) NULL;
 
2296
    }
 
2297
 
 
2298
    /*
 
2299
     * Update the entry's text with the value of the variable, unless
 
2300
     * the entry already has that value (this happens when the variable
 
2301
     * changes value because we changed it because someone typed in
 
2302
     * the entry).
 
2303
     */
 
2304
 
 
2305
    value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
 
2306
    if (value == NULL) {
 
2307
        value = "";
 
2308
    }
 
2309
    if (strcmp(value, entryPtr->string) != 0) {
 
2310
        EntrySetValue(entryPtr, value);
 
2311
    }
 
2312
    return (char *) NULL;
 
2313
}