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

Subversion Repositories or1k

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

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

Line No. Rev Author Line
1 578 markom
/*
2
 * 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.1.1.1 2002-01-16 10:25:51 markom 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_GetGCColor(entryPtr->tkwin, mask, &gcValues, entryPtr->fgColorPtr,
1016
                       NULL);
1017
    if (entryPtr->textGC != None) {
1018
        Tk_FreeGC(entryPtr->display, entryPtr->textGC);
1019
    }
1020
    entryPtr->textGC = gc;
1021
 
1022
    gcValues.foreground = entryPtr->selFgColorPtr->pixel;
1023
    gcValues.font = Tk_FontId(entryPtr->tkfont);
1024
    mask = GCForeground | GCFont;
1025
    gc = Tk_GetGCColor(entryPtr->tkwin, mask, &gcValues,
1026
                       entryPtr->selFgColorPtr, NULL);
1027
    if (entryPtr->selTextGC != None) {
1028
        Tk_FreeGC(entryPtr->display, entryPtr->selTextGC);
1029
    }
1030
    entryPtr->selTextGC = gc;
1031
 
1032
    /*
1033
     * Recompute the window's geometry and arrange for it to be
1034
     * redisplayed.
1035
     */
1036
 
1037
    EntryComputeGeometry(entryPtr);
1038
    entryPtr->flags |= UPDATE_SCROLLBAR;
1039
    EventuallyRedraw(entryPtr);
1040
}
1041
 
1042
/*
1043
 *--------------------------------------------------------------
1044
 *
1045
 * DisplayEntry --
1046
 *
1047
 *      This procedure redraws the contents of an entry window.
1048
 *
1049
 * Results:
1050
 *      None.
1051
 *
1052
 * Side effects:
1053
 *      Information appears on the screen.
1054
 *
1055
 *--------------------------------------------------------------
1056
 */
1057
 
1058
static void
1059
DisplayEntry(clientData)
1060
    ClientData clientData;      /* Information about window. */
1061
{
1062
    register Entry *entryPtr = (Entry *) clientData;
1063
    register Tk_Window tkwin = entryPtr->tkwin;
1064
    int baseY, selStartX, selEndX, cursorX, x, w;
1065
    int xBound;
1066
    Tk_FontMetrics fm;
1067
    Pixmap pixmap;
1068
    int showSelection;
1069
 
1070
    entryPtr->flags &= ~REDRAW_PENDING;
1071
    if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
1072
        return;
1073
    }
1074
 
1075
    Tk_GetFontMetrics(entryPtr->tkfont, &fm);
1076
 
1077
    /*
1078
     * Update the scrollbar if that's needed.
1079
     */
1080
 
1081
    if (entryPtr->flags & UPDATE_SCROLLBAR) {
1082
        entryPtr->flags &= ~UPDATE_SCROLLBAR;
1083
        EntryUpdateScrollbar(entryPtr);
1084
    }
1085
 
1086
    /*
1087
     * In order to avoid screen flashes, this procedure redraws the
1088
     * textual area of the entry into off-screen memory, then copies
1089
     * it back on-screen in a single operation.  This means there's
1090
     * no point in time where the on-screen image has been cleared.
1091
     */
1092
 
1093
    pixmap = Tk_GetPixmap(entryPtr->display, Tk_WindowId(tkwin),
1094
            Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
1095
 
1096
    /*
1097
     * Compute x-coordinate of the pixel just after last visible
1098
     * one, plus vertical position of baseline of text.
1099
     */
1100
 
1101
    xBound = Tk_Width(tkwin) - entryPtr->inset;
1102
    baseY = (Tk_Height(tkwin) + fm.ascent - fm.descent) / 2;
1103
 
1104
    /*
1105
     * On Windows and Mac, we need to hide the selection whenever we
1106
     * don't have the focus.
1107
     */
1108
 
1109
#ifdef ALWAYS_SHOW_SELECTION
1110
    showSelection = 1;
1111
#else
1112
    showSelection = (entryPtr->flags & GOT_FOCUS);
1113
#endif
1114
 
1115
    /*
1116
     * Draw the background in three layers.  From bottom to top the
1117
     * layers are:  normal background, selection background, and
1118
     * insertion cursor background.
1119
     */
1120
 
1121
    Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
1122
            0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
1123
    if (showSelection && (entryPtr->selectLast > entryPtr->leftIndex)) {
1124
        if (entryPtr->selectFirst <= entryPtr->leftIndex) {
1125
            selStartX = entryPtr->leftX;
1126
        } else {
1127
            Tk_CharBbox(entryPtr->textLayout, entryPtr->selectFirst,
1128
                    &x, NULL, NULL, NULL);
1129
            selStartX = x + entryPtr->layoutX;
1130
        }
1131
        if ((selStartX - entryPtr->selBorderWidth) < xBound) {
1132
            Tk_CharBbox(entryPtr->textLayout, entryPtr->selectLast - 1,
1133
                    &x, NULL, &w, NULL);
1134
            selEndX = x + w + entryPtr->layoutX;
1135
            Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->selBorder,
1136
                    selStartX - entryPtr->selBorderWidth,
1137
                    baseY - fm.ascent - entryPtr->selBorderWidth,
1138
                    (selEndX - selStartX) + 2*entryPtr->selBorderWidth,
1139
                    (fm.ascent + fm.descent) + 2*entryPtr->selBorderWidth,
1140
                    entryPtr->selBorderWidth, TK_RELIEF_RAISED);
1141
        }
1142
    }
1143
 
1144
    /*
1145
     * Draw a special background for the insertion cursor, overriding
1146
     * even the selection background.  As a special hack to keep the
1147
     * cursor visible when the insertion cursor color is the same as
1148
     * the color for selected text (e.g., on mono displays), write
1149
     * background in the cursor area (instead of nothing) when the
1150
     * cursor isn't on.  Otherwise the selection would hide the cursor.
1151
     */
1152
 
1153
    if ((entryPtr->insertPos >= entryPtr->leftIndex)
1154
            && (entryPtr->state == tkNormalUid)
1155
            && (entryPtr->flags & GOT_FOCUS)) {
1156
        if (entryPtr->insertPos == 0) {
1157
            cursorX = 0;
1158
        } else if (entryPtr->insertPos >= entryPtr->numChars) {
1159
            int idx = entryPtr->numChars >= 1 ? entryPtr->numChars - 1 : 0;
1160
            Tk_CharBbox(entryPtr->textLayout, idx,
1161
                    &x, NULL, &w, NULL);
1162
            cursorX = x + w;
1163
        } else {
1164
            Tk_CharBbox(entryPtr->textLayout, entryPtr->insertPos,
1165
                    &x, NULL, NULL, NULL);
1166
            cursorX = x;
1167
        }
1168
        cursorX += entryPtr->layoutX;
1169
        cursorX -= (entryPtr->insertWidth)/2;
1170
        if (cursorX < xBound) {
1171
            if (entryPtr->flags & CURSOR_ON) {
1172
                Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->insertBorder,
1173
                        cursorX, baseY - fm.ascent,
1174
                        entryPtr->insertWidth, fm.ascent + fm.descent,
1175
                        entryPtr->insertBorderWidth, TK_RELIEF_RAISED);
1176
            } else if (entryPtr->insertBorder == entryPtr->selBorder) {
1177
                Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
1178
                        cursorX, baseY - fm.ascent,
1179
                        entryPtr->insertWidth, fm.ascent + fm.descent,
1180
                        0, TK_RELIEF_FLAT);
1181
            }
1182
        }
1183
    }
1184
 
1185
    /*
1186
     * Draw the text in two pieces:  first the unselected portion, then the
1187
     * selected portion on top of it.
1188
     */
1189
 
1190
    Tk_DrawTextLayout(entryPtr->display, pixmap, entryPtr->textGC,
1191
            entryPtr->textLayout, entryPtr->layoutX, entryPtr->layoutY,
1192
            entryPtr->leftIndex, entryPtr->numChars);
1193
 
1194
    if (showSelection && (entryPtr->selTextGC != entryPtr->textGC) &&
1195
            (entryPtr->selectFirst < entryPtr->selectLast)) {
1196
        int first;
1197
 
1198
        if (entryPtr->selectFirst - entryPtr->leftIndex < 0) {
1199
            first = entryPtr->leftIndex;
1200
        } else {
1201
            first = entryPtr->selectFirst;
1202
        }
1203
        Tk_DrawTextLayout(entryPtr->display, pixmap, entryPtr->selTextGC,
1204
                entryPtr->textLayout, entryPtr->layoutX, entryPtr->layoutY,
1205
                first, entryPtr->selectLast);
1206
    }
1207
 
1208
    /*
1209
     * Draw the border and focus highlight last, so they will overwrite
1210
     * any text that extends past the viewable part of the window.
1211
     */
1212
 
1213
    if (entryPtr->relief != TK_RELIEF_FLAT) {
1214
        Tk_Draw3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
1215
                entryPtr->highlightWidth, entryPtr->highlightWidth,
1216
                Tk_Width(tkwin) - 2*entryPtr->highlightWidth,
1217
                Tk_Height(tkwin) - 2*entryPtr->highlightWidth,
1218
                entryPtr->borderWidth, entryPtr->relief);
1219
    }
1220
    if (entryPtr->highlightWidth != 0) {
1221
        GC gc;
1222
 
1223
        if (entryPtr->flags & GOT_FOCUS) {
1224
            gc = Tk_GCForColor(entryPtr->highlightColorPtr, pixmap);
1225
        } else {
1226
            gc = Tk_GCForColor(entryPtr->highlightBgColorPtr, pixmap);
1227
        }
1228
        Tk_DrawFocusHighlight(tkwin, gc, entryPtr->highlightWidth, pixmap);
1229
    }
1230
 
1231
    /*
1232
     * Everything's been redisplayed;  now copy the pixmap onto the screen
1233
     * and free up the pixmap.
1234
     */
1235
 
1236
    XCopyArea(entryPtr->display, pixmap, Tk_WindowId(tkwin), entryPtr->textGC,
1237
            0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin),
1238
            0, 0);
1239
    Tk_FreePixmap(entryPtr->display, pixmap);
1240
    entryPtr->flags &= ~BORDER_NEEDED;
1241
}
1242
 
1243
/*
1244
 *----------------------------------------------------------------------
1245
 *
1246
 * EntryComputeGeometry --
1247
 *
1248
 *      This procedure is invoked to recompute information about where
1249
 *      in its window an entry's string will be displayed.  It also
1250
 *      computes the requested size for the window.
1251
 *
1252
 * Results:
1253
 *      None.
1254
 *
1255
 * Side effects:
1256
 *      The leftX and tabOrigin fields are recomputed for entryPtr,
1257
 *      and leftIndex may be adjusted.  Tk_GeometryRequest is called
1258
 *      to register the desired dimensions for the window.
1259
 *
1260
 *----------------------------------------------------------------------
1261
 */
1262
 
1263
static void
1264
EntryComputeGeometry(entryPtr)
1265
    Entry *entryPtr;                    /* Widget record for entry. */
1266
{
1267
    int totalLength, overflow, maxOffScreen, rightX;
1268
    int height, width, i;
1269
    Tk_FontMetrics fm;
1270
    char *p, *displayString;
1271
 
1272
    /*
1273
     * If we're displaying a special character instead of the value of
1274
     * the entry, recompute the displayString.
1275
     */
1276
 
1277
    if (entryPtr->displayString != NULL) {
1278
        ckfree(entryPtr->displayString);
1279
        entryPtr->displayString = NULL;
1280
    }
1281
    if (entryPtr->showChar != NULL) {
1282
        entryPtr->displayString = (char *) ckalloc((unsigned)
1283
                (entryPtr->numChars + 1));
1284
        for (p = entryPtr->displayString, i = entryPtr->numChars; i > 0;
1285
                i--, p++) {
1286
            *p = entryPtr->showChar[0];
1287
        }
1288
        *p = 0;
1289
        displayString = entryPtr->displayString;
1290
    } else {
1291
        displayString = entryPtr->string;
1292
    }
1293
    Tk_FreeTextLayout(entryPtr->textLayout);
1294
    entryPtr->textLayout = Tk_ComputeTextLayout(entryPtr->tkfont,
1295
            displayString, entryPtr->numChars, 0, entryPtr->justify,
1296
            TK_IGNORE_NEWLINES, &totalLength, &height);
1297
 
1298
    entryPtr->layoutY = (Tk_Height(entryPtr->tkwin) - height) / 2;
1299
 
1300
    /*
1301
     * Recompute where the leftmost character on the display will
1302
     * be drawn (entryPtr->leftX) and adjust leftIndex if necessary
1303
     * so that we don't let characters hang off the edge of the
1304
     * window unless the entire window is full.
1305
     */
1306
 
1307
    overflow = totalLength - (Tk_Width(entryPtr->tkwin) - 2*entryPtr->inset);
1308
    if (overflow <= 0) {
1309
        entryPtr->leftIndex = 0;
1310
        if (entryPtr->justify == TK_JUSTIFY_LEFT) {
1311
            entryPtr->leftX = entryPtr->inset;
1312
        } else if (entryPtr->justify == TK_JUSTIFY_RIGHT) {
1313
            entryPtr->leftX = Tk_Width(entryPtr->tkwin) - entryPtr->inset
1314
                    - totalLength;
1315
        } else {
1316
            entryPtr->leftX = (Tk_Width(entryPtr->tkwin) - totalLength)/2;
1317
        }
1318
        entryPtr->layoutX = entryPtr->leftX;
1319
    } else {
1320
        /*
1321
         * The whole string can't fit in the window.  Compute the
1322
         * maximum number of characters that may be off-screen to
1323
         * the left without leaving empty space on the right of the
1324
         * window, then don't let leftIndex be any greater than that.
1325
         */
1326
 
1327
        maxOffScreen = Tk_PointToChar(entryPtr->textLayout, overflow, 0);
1328
        Tk_CharBbox(entryPtr->textLayout, maxOffScreen,
1329
                &rightX, NULL, NULL, NULL);
1330
        if (rightX < overflow) {
1331
            maxOffScreen += 1;
1332
        }
1333
        if (entryPtr->leftIndex > maxOffScreen) {
1334
            entryPtr->leftIndex = maxOffScreen;
1335
        }
1336
        Tk_CharBbox(entryPtr->textLayout, entryPtr->leftIndex,
1337
                &rightX, NULL, NULL, NULL);
1338
        entryPtr->leftX = entryPtr->inset;
1339
        entryPtr->layoutX = entryPtr->leftX - rightX;
1340
    }
1341
 
1342
    Tk_GetFontMetrics(entryPtr->tkfont, &fm);
1343
    height = fm.linespace + 2*entryPtr->inset + 2*(YPAD-XPAD);
1344
    if (entryPtr->prefWidth > 0) {
1345
        width = entryPtr->prefWidth*entryPtr->avgWidth + 2*entryPtr->inset;
1346
    } else {
1347
        if (totalLength == 0) {
1348
            width = entryPtr->avgWidth + 2*entryPtr->inset;
1349
        } else {
1350
            width = totalLength + 2*entryPtr->inset;
1351
        }
1352
    }
1353
    Tk_GeometryRequest(entryPtr->tkwin, width, height);
1354
}
1355
 
1356
/*
1357
 *----------------------------------------------------------------------
1358
 *
1359
 * InsertChars --
1360
 *
1361
 *      Add new characters to an entry widget.
1362
 *
1363
 * Results:
1364
 *      None.
1365
 *
1366
 * Side effects:
1367
 *      New information gets added to entryPtr;  it will be redisplayed
1368
 *      soon, but not necessarily immediately.
1369
 *
1370
 *----------------------------------------------------------------------
1371
 */
1372
 
1373
static void
1374
InsertChars(entryPtr, index, string)
1375
    register Entry *entryPtr;   /* Entry that is to get the new
1376
                                 * elements. */
1377
    int index;                  /* Add the new elements before this
1378
                                 * element. */
1379
    char *string;               /* New characters to add (NULL-terminated
1380
                                 * string). */
1381
{
1382
    int length;
1383
    char *new;
1384
 
1385
    length = strlen(string);
1386
    if (length == 0) {
1387
        return;
1388
    }
1389
    new = (char *) ckalloc((unsigned) (entryPtr->numChars + length + 1));
1390
    strncpy(new, entryPtr->string, (size_t) index);
1391
    strcpy(new+index, string);
1392
    strcpy(new+index+length, entryPtr->string+index);
1393
    ckfree(entryPtr->string);
1394
    entryPtr->string = new;
1395
    entryPtr->numChars += length;
1396
 
1397
    /*
1398
     * Inserting characters invalidates all indexes into the string.
1399
     * Touch up the indexes so that they still refer to the same
1400
     * characters (at new positions).  When updating the selection
1401
     * end-points, don't include the new text in the selection unless
1402
     * it was completely surrounded by the selection.
1403
     */
1404
 
1405
    if (entryPtr->selectFirst >= index) {
1406
        entryPtr->selectFirst += length;
1407
    }
1408
    if (entryPtr->selectLast > index) {
1409
        entryPtr->selectLast += length;
1410
    }
1411
    if ((entryPtr->selectAnchor > index) || (entryPtr->selectFirst >= index)) {
1412
        entryPtr->selectAnchor += length;
1413
    }
1414
    if (entryPtr->leftIndex > index) {
1415
        entryPtr->leftIndex += length;
1416
    }
1417
    if (entryPtr->insertPos >= index) {
1418
        entryPtr->insertPos += length;
1419
    }
1420
    EntryValueChanged(entryPtr);
1421
}
1422
 
1423
/*
1424
 *----------------------------------------------------------------------
1425
 *
1426
 * DeleteChars --
1427
 *
1428
 *      Remove one or more characters from an entry widget.
1429
 *
1430
 * Results:
1431
 *      None.
1432
 *
1433
 * Side effects:
1434
 *      Memory gets freed, the entry gets modified and (eventually)
1435
 *      redisplayed.
1436
 *
1437
 *----------------------------------------------------------------------
1438
 */
1439
 
1440
static void
1441
DeleteChars(entryPtr, index, count)
1442
    register Entry *entryPtr;   /* Entry widget to modify. */
1443
    int index;                  /* Index of first character to delete. */
1444
    int count;                  /* How many characters to delete. */
1445
{
1446
    char *new;
1447
 
1448
    if ((index + count) > entryPtr->numChars) {
1449
        count = entryPtr->numChars - index;
1450
    }
1451
    if (count <= 0) {
1452
        return;
1453
    }
1454
 
1455
    new = (char *) ckalloc((unsigned) (entryPtr->numChars + 1 - count));
1456
    strncpy(new, entryPtr->string, (size_t) index);
1457
    strcpy(new+index, entryPtr->string+index+count);
1458
    ckfree(entryPtr->string);
1459
    entryPtr->string = new;
1460
    entryPtr->numChars -= count;
1461
 
1462
    /*
1463
     * Deleting characters results in the remaining characters being
1464
     * renumbered.  Update the various indexes into the string to reflect
1465
     * this change.
1466
     */
1467
 
1468
    if (entryPtr->selectFirst >= index) {
1469
        if (entryPtr->selectFirst >= (index+count)) {
1470
            entryPtr->selectFirst -= count;
1471
        } else {
1472
            entryPtr->selectFirst = index;
1473
        }
1474
    }
1475
    if (entryPtr->selectLast >= index) {
1476
        if (entryPtr->selectLast >= (index+count)) {
1477
            entryPtr->selectLast -= count;
1478
        } else {
1479
            entryPtr->selectLast = index;
1480
        }
1481
    }
1482
    if (entryPtr->selectLast <= entryPtr->selectFirst) {
1483
        entryPtr->selectFirst = entryPtr->selectLast = -1;
1484
    }
1485
    if (entryPtr->selectAnchor >= index) {
1486
        if (entryPtr->selectAnchor >= (index+count)) {
1487
            entryPtr->selectAnchor -= count;
1488
        } else {
1489
            entryPtr->selectAnchor = index;
1490
        }
1491
    }
1492
    if (entryPtr->leftIndex > index) {
1493
        if (entryPtr->leftIndex >= (index+count)) {
1494
            entryPtr->leftIndex -= count;
1495
        } else {
1496
            entryPtr->leftIndex = index;
1497
        }
1498
    }
1499
    if (entryPtr->insertPos >= index) {
1500
        if (entryPtr->insertPos >= (index+count)) {
1501
            entryPtr->insertPos -= count;
1502
        } else {
1503
            entryPtr->insertPos = index;
1504
        }
1505
    }
1506
    EntryValueChanged(entryPtr);
1507
}
1508
 
1509
/*
1510
 *----------------------------------------------------------------------
1511
 *
1512
 * EntryValueChanged --
1513
 *
1514
 *      This procedure is invoked when characters are inserted into
1515
 *      an entry or deleted from it.  It updates the entry's associated
1516
 *      variable, if there is one, and does other bookkeeping such
1517
 *      as arranging for redisplay.
1518
 *
1519
 * Results:
1520
 *      None.
1521
 *
1522
 * Side effects:
1523
 *      None.
1524
 *
1525
 *----------------------------------------------------------------------
1526
 */
1527
 
1528
static void
1529
EntryValueChanged(entryPtr)
1530
    Entry *entryPtr;            /* Entry whose value just changed. */
1531
{
1532
    char *newValue;
1533
 
1534
    if (entryPtr->textVarName == NULL) {
1535
        newValue = NULL;
1536
    } else {
1537
        newValue = Tcl_SetVar(entryPtr->interp, entryPtr->textVarName,
1538
                entryPtr->string, TCL_GLOBAL_ONLY);
1539
    }
1540
 
1541
    if ((newValue != NULL) && (strcmp(newValue, entryPtr->string) != 0)) {
1542
        /*
1543
         * The value of the variable is different than what we asked for.
1544
         * This means that a trace on the variable modified it.  In this
1545
         * case our trace procedure wasn't invoked since the modification
1546
         * came while a trace was already active on the variable.  So,
1547
         * update our value to reflect the variable's latest value.
1548
         */
1549
 
1550
        EntrySetValue(entryPtr, newValue);
1551
    } else {
1552
        /*
1553
         * Arrange for redisplay.
1554
         */
1555
 
1556
        entryPtr->flags |= UPDATE_SCROLLBAR;
1557
        EntryComputeGeometry(entryPtr);
1558
        EventuallyRedraw(entryPtr);
1559
    }
1560
}
1561
 
1562
/*
1563
 *----------------------------------------------------------------------
1564
 *
1565
 * EntrySetValue --
1566
 *
1567
 *      Replace the contents of a text entry with a given value.  This
1568
 *      procedure is invoked when updating the entry from the entry's
1569
 *      associated variable.
1570
 *
1571
 * Results:
1572
 *      None.
1573
 *
1574
 * Side effects:
1575
 *      The string displayed in the entry will change.  The selection,
1576
 *      insertion point, and view may have to be adjusted to keep them
1577
 *      within the bounds of the new string.  Note: this procedure does
1578
 *      *not* update the entry's associated variable, since that could
1579
 *      result in an infinite loop.
1580
 *
1581
 *----------------------------------------------------------------------
1582
 */
1583
 
1584
static void
1585
EntrySetValue(entryPtr, value)
1586
    register Entry *entryPtr;           /* Entry whose value is to be
1587
                                         * changed. */
1588
    char *value;                        /* New text to display in entry. */
1589
{
1590
    ckfree(entryPtr->string);
1591
    entryPtr->numChars = strlen(value);
1592
    entryPtr->string = (char *) ckalloc((unsigned) (entryPtr->numChars + 1));
1593
    strcpy(entryPtr->string, value);
1594
    if (entryPtr->selectFirst != -1) {
1595
        if (entryPtr->selectFirst >= entryPtr->numChars) {
1596
            entryPtr->selectFirst = entryPtr->selectLast = -1;
1597
        } else if (entryPtr->selectLast > entryPtr->numChars) {
1598
            entryPtr->selectLast = entryPtr->numChars;
1599
        }
1600
    }
1601
    if (entryPtr->leftIndex >= entryPtr->numChars) {
1602
        entryPtr->leftIndex = entryPtr->numChars-1;
1603
        if (entryPtr->leftIndex < 0) {
1604
            entryPtr->leftIndex = 0;
1605
        }
1606
    }
1607
    if (entryPtr->insertPos > entryPtr->numChars) {
1608
        entryPtr->insertPos = entryPtr->numChars;
1609
    }
1610
 
1611
    entryPtr->flags |= UPDATE_SCROLLBAR;
1612
    EntryComputeGeometry(entryPtr);
1613
    EventuallyRedraw(entryPtr);
1614
}
1615
 
1616
/*
1617
 *--------------------------------------------------------------
1618
 *
1619
 * EntryEventProc --
1620
 *
1621
 *      This procedure is invoked by the Tk dispatcher for various
1622
 *      events on entryes.
1623
 *
1624
 * Results:
1625
 *      None.
1626
 *
1627
 * Side effects:
1628
 *      When the window gets deleted, internal structures get
1629
 *      cleaned up.  When it gets exposed, it is redisplayed.
1630
 *
1631
 *--------------------------------------------------------------
1632
 */
1633
 
1634
static void
1635
EntryEventProc(clientData, eventPtr)
1636
    ClientData clientData;      /* Information about window. */
1637
    XEvent *eventPtr;           /* Information about event. */
1638
{
1639
    Entry *entryPtr = (Entry *) clientData;
1640
    if (eventPtr->type == Expose) {
1641
        EventuallyRedraw(entryPtr);
1642
        entryPtr->flags |= BORDER_NEEDED;
1643
    } else if (eventPtr->type == DestroyNotify) {
1644
        if (entryPtr->tkwin != NULL) {
1645
            entryPtr->tkwin = NULL;
1646
            Tcl_DeleteCommandFromToken(entryPtr->interp, entryPtr->widgetCmd);
1647
        }
1648
        if (entryPtr->flags & REDRAW_PENDING) {
1649
            Tcl_CancelIdleCall(DisplayEntry, (ClientData) entryPtr);
1650
        }
1651
        Tcl_EventuallyFree((ClientData) entryPtr, DestroyEntry);
1652
    } else if (eventPtr->type == ConfigureNotify) {
1653
        Tcl_Preserve((ClientData) entryPtr);
1654
        entryPtr->flags |= UPDATE_SCROLLBAR;
1655
        EntryComputeGeometry(entryPtr);
1656
        EventuallyRedraw(entryPtr);
1657
        Tcl_Release((ClientData) entryPtr);
1658
    } else if (eventPtr->type == FocusIn) {
1659
        if (eventPtr->xfocus.detail != NotifyInferior) {
1660
            EntryFocusProc(entryPtr, 1);
1661
        }
1662
    } else if (eventPtr->type == FocusOut) {
1663
        if (eventPtr->xfocus.detail != NotifyInferior) {
1664
            EntryFocusProc(entryPtr, 0);
1665
        }
1666
    }
1667
}
1668
 
1669
/*
1670
 *----------------------------------------------------------------------
1671
 *
1672
 * EntryCmdDeletedProc --
1673
 *
1674
 *      This procedure is invoked when a widget command is deleted.  If
1675
 *      the widget isn't already in the process of being destroyed,
1676
 *      this command destroys it.
1677
 *
1678
 * Results:
1679
 *      None.
1680
 *
1681
 * Side effects:
1682
 *      The widget is destroyed.
1683
 *
1684
 *----------------------------------------------------------------------
1685
 */
1686
 
1687
static void
1688
EntryCmdDeletedProc(clientData)
1689
    ClientData clientData;      /* Pointer to widget record for widget. */
1690
{
1691
    Entry *entryPtr = (Entry *) clientData;
1692
    Tk_Window tkwin = entryPtr->tkwin;
1693
 
1694
    /*
1695
     * This procedure could be invoked either because the window was
1696
     * destroyed and the command was then deleted (in which case tkwin
1697
     * is NULL) or because the command was deleted, and then this procedure
1698
     * destroys the widget.
1699
     */
1700
 
1701
    if (tkwin != NULL) {
1702
        entryPtr->tkwin = NULL;
1703
        Tk_DestroyWindow(tkwin);
1704
    }
1705
}
1706
 
1707
/*
1708
 *--------------------------------------------------------------
1709
 *
1710
 * GetEntryIndex --
1711
 *
1712
 *      Parse an index into an entry and return either its value
1713
 *      or an error.
1714
 *
1715
 * Results:
1716
 *      A standard Tcl result.  If all went well, then *indexPtr is
1717
 *      filled in with the index (into entryPtr) corresponding to
1718
 *      string.  The index value is guaranteed to lie between 0 and
1719
 *      the number of characters in the string, inclusive.  If an
1720
 *      error occurs then an error message is left in interp->result.
1721
 *
1722
 * Side effects:
1723
 *      None.
1724
 *
1725
 *--------------------------------------------------------------
1726
 */
1727
 
1728
static int
1729
GetEntryIndex(interp, entryPtr, string, indexPtr)
1730
    Tcl_Interp *interp;         /* For error messages. */
1731
    Entry *entryPtr;            /* Entry for which the index is being
1732
                                 * specified. */
1733
    char *string;               /* Specifies character in entryPtr. */
1734
    int *indexPtr;              /* Where to store converted index. */
1735
{
1736
    size_t length;
1737
 
1738
    length = strlen(string);
1739
 
1740
    if (string[0] == 'a') {
1741
        if (strncmp(string, "anchor", length) == 0) {
1742
            *indexPtr = entryPtr->selectAnchor;
1743
        } else {
1744
            badIndex:
1745
 
1746
            /*
1747
             * Some of the paths here leave messages in interp->result,
1748
             * so we have to clear it out before storing our own message.
1749
             */
1750
 
1751
            Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
1752
            Tcl_AppendResult(interp, "bad entry index \"", string,
1753
                    "\"", (char *) NULL);
1754
            return TCL_ERROR;
1755
        }
1756
    } else if (string[0] == 'e') {
1757
        if (strncmp(string, "end", length) == 0) {
1758
            *indexPtr = entryPtr->numChars;
1759
        } else {
1760
            goto badIndex;
1761
        }
1762
    } else if (string[0] == 'i') {
1763
        if (strncmp(string, "insert", length) == 0) {
1764
            *indexPtr = entryPtr->insertPos;
1765
        } else {
1766
            goto badIndex;
1767
        }
1768
    } else if (string[0] == 's') {
1769
        if (entryPtr->selectFirst == -1) {
1770
            interp->result = "selection isn't in entry";
1771
            return TCL_ERROR;
1772
        }
1773
        if (length < 5) {
1774
            goto badIndex;
1775
        }
1776
        if (strncmp(string, "sel.first", length) == 0) {
1777
            *indexPtr = entryPtr->selectFirst;
1778
        } else if (strncmp(string, "sel.last", length) == 0) {
1779
            *indexPtr = entryPtr->selectLast;
1780
        } else {
1781
            goto badIndex;
1782
        }
1783
    } else if (string[0] == '@') {
1784
        int x, roundUp;
1785
 
1786
        if (Tcl_GetInt(interp, string+1, &x) != TCL_OK) {
1787
            goto badIndex;
1788
        }
1789
        if (x < entryPtr->inset) {
1790
            x = entryPtr->inset;
1791
        }
1792
        roundUp = 0;
1793
        if (x >= (Tk_Width(entryPtr->tkwin) - entryPtr->inset)) {
1794
            x = Tk_Width(entryPtr->tkwin) - entryPtr->inset - 1;
1795
            roundUp = 1;
1796
        }
1797
        *indexPtr = Tk_PointToChar(entryPtr->textLayout,
1798
                x - entryPtr->layoutX, 0);
1799
 
1800
        /*
1801
         * Special trick:  if the x-position was off-screen to the right,
1802
         * round the index up to refer to the character just after the
1803
         * last visible one on the screen.  This is needed to enable the
1804
         * last character to be selected, for example.
1805
         */
1806
 
1807
        if (roundUp && (*indexPtr < entryPtr->numChars)) {
1808
            *indexPtr += 1;
1809
        }
1810
    } else {
1811
        if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
1812
            goto badIndex;
1813
        }
1814
        if (*indexPtr < 0){
1815
            *indexPtr = 0;
1816
        } else if (*indexPtr > entryPtr->numChars) {
1817
            *indexPtr = entryPtr->numChars;
1818
        }
1819
    }
1820
    if(*indexPtr > entryPtr->numChars)
1821
        *indexPtr = entryPtr->numChars;
1822
    return TCL_OK;
1823
}
1824
 
1825
/*
1826
 *----------------------------------------------------------------------
1827
 *
1828
 * EntryScanTo --
1829
 *
1830
 *      Given a y-coordinate (presumably of the curent mouse location)
1831
 *      drag the view in the window to implement the scan operation.
1832
 *
1833
 * Results:
1834
 *      None.
1835
 *
1836
 * Side effects:
1837
 *      The view in the window may change.
1838
 *
1839
 *----------------------------------------------------------------------
1840
 */
1841
 
1842
static void
1843
EntryScanTo(entryPtr, x)
1844
    register Entry *entryPtr;           /* Information about widget. */
1845
    int x;                              /* X-coordinate to use for scan
1846
                                         * operation. */
1847
{
1848
    int newLeftIndex;
1849
 
1850
    /*
1851
     * Compute new leftIndex for entry by amplifying the difference
1852
     * between the current position and the place where the scan
1853
     * started (the "mark" position).  If we run off the left or right
1854
     * side of the entry, then reset the mark point so that the current
1855
     * position continues to correspond to the edge of the window.
1856
     * This means that the picture will start dragging as soon as the
1857
     * mouse reverses direction (without this reset, might have to slide
1858
     * mouse a long ways back before the picture starts moving again).
1859
     */
1860
 
1861
    newLeftIndex = entryPtr->scanMarkIndex
1862
            - (10*(x - entryPtr->scanMarkX))/entryPtr->avgWidth;
1863
    if (newLeftIndex >= entryPtr->numChars) {
1864
        newLeftIndex = entryPtr->scanMarkIndex = entryPtr->numChars-1;
1865
        entryPtr->scanMarkX = x;
1866
    }
1867
    if (newLeftIndex < 0) {
1868
        newLeftIndex = entryPtr->scanMarkIndex = 0;
1869
        entryPtr->scanMarkX = x;
1870
    }
1871
    if (newLeftIndex != entryPtr->leftIndex) {
1872
        entryPtr->leftIndex = newLeftIndex;
1873
        entryPtr->flags |= UPDATE_SCROLLBAR;
1874
        EntryComputeGeometry(entryPtr);
1875
        EventuallyRedraw(entryPtr);
1876
    }
1877
}
1878
 
1879
/*
1880
 *----------------------------------------------------------------------
1881
 *
1882
 * EntrySelectTo --
1883
 *
1884
 *      Modify the selection by moving its un-anchored end.  This could
1885
 *      make the selection either larger or smaller.
1886
 *
1887
 * Results:
1888
 *      None.
1889
 *
1890
 * Side effects:
1891
 *      The selection changes.
1892
 *
1893
 *----------------------------------------------------------------------
1894
 */
1895
 
1896
static void
1897
EntrySelectTo(entryPtr, index)
1898
    register Entry *entryPtr;           /* Information about widget. */
1899
    int index;                          /* Index of element that is to
1900
                                         * become the "other" end of the
1901
                                         * selection. */
1902
{
1903
    int newFirst, newLast;
1904
 
1905
    /*
1906
     * Grab the selection if we don't own it already.
1907
     */
1908
 
1909
    if (!(entryPtr->flags & GOT_SELECTION) && (entryPtr->exportSelection)) {
1910
        Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, EntryLostSelection,
1911
                (ClientData) entryPtr);
1912
        entryPtr->flags |= GOT_SELECTION;
1913
    }
1914
 
1915
    /*
1916
     * Pick new starting and ending points for the selection.
1917
     */
1918
 
1919
    if (entryPtr->selectAnchor > entryPtr->numChars) {
1920
        entryPtr->selectAnchor = entryPtr->numChars;
1921
    }
1922
    if (entryPtr->selectAnchor <= index) {
1923
        newFirst = entryPtr->selectAnchor;
1924
        newLast = index;
1925
    } else {
1926
        newFirst = index;
1927
        newLast = entryPtr->selectAnchor;
1928
        if (newLast < 0) {
1929
            newFirst = newLast = -1;
1930
        }
1931
    }
1932
    if ((entryPtr->selectFirst == newFirst)
1933
            && (entryPtr->selectLast == newLast)) {
1934
        return;
1935
    }
1936
    entryPtr->selectFirst = newFirst;
1937
    entryPtr->selectLast = newLast;
1938
    EventuallyRedraw(entryPtr);
1939
}
1940
 
1941
/*
1942
 *----------------------------------------------------------------------
1943
 *
1944
 * EntryFetchSelection --
1945
 *
1946
 *      This procedure is called back by Tk when the selection is
1947
 *      requested by someone.  It returns part or all of the selection
1948
 *      in a buffer provided by the caller.
1949
 *
1950
 * Results:
1951
 *      The return value is the number of non-NULL bytes stored
1952
 *      at buffer.  Buffer is filled (or partially filled) with a
1953
 *      NULL-terminated string containing part or all of the selection,
1954
 *      as given by offset and maxBytes.
1955
 *
1956
 * Side effects:
1957
 *      None.
1958
 *
1959
 *----------------------------------------------------------------------
1960
 */
1961
 
1962
static int
1963
EntryFetchSelection(clientData, offset, buffer, maxBytes)
1964
    ClientData clientData;              /* Information about entry widget. */
1965
    int offset;                         /* Offset within selection of first
1966
                                         * character to be returned. */
1967
    char *buffer;                       /* Location in which to place
1968
                                         * selection. */
1969
    int maxBytes;                       /* Maximum number of bytes to place
1970
                                         * at buffer, not including terminating
1971
                                         * NULL character. */
1972
{
1973
    Entry *entryPtr = (Entry *) clientData;
1974
    int count;
1975
    char *displayString;
1976
 
1977
    if ((entryPtr->selectFirst < 0) || !(entryPtr->exportSelection)) {
1978
        return -1;
1979
    }
1980
    count = entryPtr->selectLast - entryPtr->selectFirst - offset;
1981
    if (count > maxBytes) {
1982
        count = maxBytes;
1983
    }
1984
    if (count <= 0) {
1985
        return 0;
1986
    }
1987
    if (entryPtr->displayString == NULL) {
1988
        displayString = entryPtr->string;
1989
    } else {
1990
        displayString = entryPtr->displayString;
1991
    }
1992
    strncpy(buffer, displayString + entryPtr->selectFirst + offset,
1993
            (size_t) count);
1994
    buffer[count] = '\0';
1995
    return count;
1996
}
1997
 
1998
/*
1999
 *----------------------------------------------------------------------
2000
 *
2001
 * EntryLostSelection --
2002
 *
2003
 *      This procedure is called back by Tk when the selection is
2004
 *      grabbed away from an entry widget.
2005
 *
2006
 * Results:
2007
 *      None.
2008
 *
2009
 * Side effects:
2010
 *      The existing selection is unhighlighted, and the window is
2011
 *      marked as not containing a selection.
2012
 *
2013
 *----------------------------------------------------------------------
2014
 */
2015
 
2016
static void
2017
EntryLostSelection(clientData)
2018
    ClientData clientData;              /* Information about entry widget. */
2019
{
2020
    Entry *entryPtr = (Entry *) clientData;
2021
 
2022
    entryPtr->flags &= ~GOT_SELECTION;
2023
 
2024
    /*
2025
     * On Windows and Mac systems, we want to remember the selection
2026
     * for the next time the focus enters the window.  On Unix, we need
2027
     * to clear the selection since it is always visible.
2028
     */
2029
 
2030
#ifdef ALWAYS_SHOW_SELECTION
2031
    if ((entryPtr->selectFirst != -1) && entryPtr->exportSelection) {
2032
        entryPtr->selectFirst = -1;
2033
        entryPtr->selectLast = -1;
2034
        EventuallyRedraw(entryPtr);
2035
    }
2036
#endif
2037
}
2038
 
2039
/*
2040
 *----------------------------------------------------------------------
2041
 *
2042
 * EventuallyRedraw --
2043
 *
2044
 *      Ensure that an entry is eventually redrawn on the display.
2045
 *
2046
 * Results:
2047
 *      None.
2048
 *
2049
 * Side effects:
2050
 *      Information gets redisplayed.  Right now we don't do selective
2051
 *      redisplays:  the whole window will be redrawn.  This doesn't
2052
 *      seem to hurt performance noticeably, but if it does then this
2053
 *      could be changed.
2054
 *
2055
 *----------------------------------------------------------------------
2056
 */
2057
 
2058
static void
2059
EventuallyRedraw(entryPtr)
2060
    register Entry *entryPtr;           /* Information about widget. */
2061
{
2062
    if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(entryPtr->tkwin)) {
2063
        return;
2064
    }
2065
 
2066
    /*
2067
     * Right now we don't do selective redisplays:  the whole window
2068
     * will be redrawn.  This doesn't seem to hurt performance noticeably,
2069
     * but if it does then this could be changed.
2070
     */
2071
 
2072
    if (!(entryPtr->flags & REDRAW_PENDING)) {
2073
        entryPtr->flags |= REDRAW_PENDING;
2074
        Tcl_DoWhenIdle(DisplayEntry, (ClientData) entryPtr);
2075
    }
2076
}
2077
 
2078
/*
2079
 *----------------------------------------------------------------------
2080
 *
2081
 * EntryVisibleRange --
2082
 *
2083
 *      Return information about the range of the entry that is
2084
 *      currently visible.
2085
 *
2086
 * Results:
2087
 *      *firstPtr and *lastPtr are modified to hold fractions between
2088
 *      0 and 1 identifying the range of characters visible in the
2089
 *      entry.
2090
 *
2091
 * Side effects:
2092
 *      None.
2093
 *
2094
 *----------------------------------------------------------------------
2095
 */
2096
 
2097
static void
2098
EntryVisibleRange(entryPtr, firstPtr, lastPtr)
2099
    Entry *entryPtr;                    /* Information about widget. */
2100
    double *firstPtr;                   /* Return position of first visible
2101
                                         * character in widget. */
2102
    double *lastPtr;                    /* Return position of char just after
2103
                                         * last visible one. */
2104
{
2105
    int charsInWindow;
2106
 
2107
    if (entryPtr->numChars == 0) {
2108
        *firstPtr = 0.0;
2109
        *lastPtr = 1.0;
2110
    } else {
2111
        charsInWindow = Tk_PointToChar(entryPtr->textLayout,
2112
                Tk_Width(entryPtr->tkwin) - entryPtr->inset
2113
                        - entryPtr->layoutX - 1, 0) + 1;
2114
        if (charsInWindow > entryPtr->numChars) {
2115
            /*
2116
             * If all chars were visible, then charsInWindow will be
2117
             * the index just after the last char that was visible.
2118
             */
2119
 
2120
            charsInWindow = entryPtr->numChars;
2121
        }
2122
        charsInWindow -= entryPtr->leftIndex;
2123
        if (charsInWindow == 0) {
2124
            charsInWindow = 1;
2125
        }
2126
        *firstPtr = ((double) entryPtr->leftIndex)/entryPtr->numChars;
2127
        *lastPtr = ((double) (entryPtr->leftIndex + charsInWindow))
2128
                /entryPtr->numChars;
2129
    }
2130
}
2131
 
2132
/*
2133
 *----------------------------------------------------------------------
2134
 *
2135
 * EntryUpdateScrollbar --
2136
 *
2137
 *      This procedure is invoked whenever information has changed in
2138
 *      an entry in a way that would invalidate a scrollbar display.
2139
 *      If there is an associated scrollbar, then this procedure updates
2140
 *      it by invoking a Tcl command.
2141
 *
2142
 * Results:
2143
 *      None.
2144
 *
2145
 * Side effects:
2146
 *      A Tcl command is invoked, and an additional command may be
2147
 *      invoked to process errors in the command.
2148
 *
2149
 *----------------------------------------------------------------------
2150
 */
2151
 
2152
static void
2153
EntryUpdateScrollbar(entryPtr)
2154
    Entry *entryPtr;                    /* Information about widget. */
2155
{
2156
    char args[100];
2157
    int code;
2158
    double first, last;
2159
    Tcl_Interp *interp;
2160
 
2161
    if (entryPtr->scrollCmd == NULL) {
2162
        return;
2163
    }
2164
 
2165
    interp = entryPtr->interp;
2166
    Tcl_Preserve((ClientData) interp);
2167
    EntryVisibleRange(entryPtr, &first, &last);
2168
    sprintf(args, " %g %g", first, last);
2169
    code = Tcl_VarEval(interp, entryPtr->scrollCmd, args, (char *) NULL);
2170
    if (code != TCL_OK) {
2171
        Tcl_AddErrorInfo(interp,
2172
                "\n    (horizontal scrolling command executed by entry)");
2173
        Tcl_BackgroundError(interp);
2174
    }
2175
    Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
2176
    Tcl_Release((ClientData) interp);
2177
}
2178
 
2179
/*
2180
 *----------------------------------------------------------------------
2181
 *
2182
 * EntryBlinkProc --
2183
 *
2184
 *      This procedure is called as a timer handler to blink the
2185
 *      insertion cursor off and on.
2186
 *
2187
 * Results:
2188
 *      None.
2189
 *
2190
 * Side effects:
2191
 *      The cursor gets turned on or off, redisplay gets invoked,
2192
 *      and this procedure reschedules itself.
2193
 *
2194
 *----------------------------------------------------------------------
2195
 */
2196
 
2197
static void
2198
EntryBlinkProc(clientData)
2199
    ClientData clientData;      /* Pointer to record describing entry. */
2200
{
2201
    register Entry *entryPtr = (Entry *) clientData;
2202
 
2203
    if (!(entryPtr->flags & GOT_FOCUS) || (entryPtr->insertOffTime == 0)) {
2204
        return;
2205
    }
2206
    if (entryPtr->flags & CURSOR_ON) {
2207
        entryPtr->flags &= ~CURSOR_ON;
2208
        entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
2209
                entryPtr->insertOffTime, EntryBlinkProc, (ClientData) entryPtr);
2210
    } else {
2211
        entryPtr->flags |= CURSOR_ON;
2212
        entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
2213
                entryPtr->insertOnTime, EntryBlinkProc, (ClientData) entryPtr);
2214
    }
2215
    EventuallyRedraw(entryPtr);
2216
}
2217
 
2218
/*
2219
 *----------------------------------------------------------------------
2220
 *
2221
 * EntryFocusProc --
2222
 *
2223
 *      This procedure is called whenever the entry gets or loses the
2224
 *      input focus.  It's also called whenever the window is reconfigured
2225
 *      while it has the focus.
2226
 *
2227
 * Results:
2228
 *      None.
2229
 *
2230
 * Side effects:
2231
 *      The cursor gets turned on or off.
2232
 *
2233
 *----------------------------------------------------------------------
2234
 */
2235
 
2236
static void
2237
EntryFocusProc(entryPtr, gotFocus)
2238
    register Entry *entryPtr;   /* Entry that got or lost focus. */
2239
    int gotFocus;               /* 1 means window is getting focus, 0 means
2240
                                 * it's losing it. */
2241
{
2242
    Tcl_DeleteTimerHandler(entryPtr->insertBlinkHandler);
2243
    if (gotFocus) {
2244
        entryPtr->flags |= GOT_FOCUS | CURSOR_ON;
2245
        if (entryPtr->insertOffTime != 0) {
2246
            entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
2247
                    entryPtr->insertOnTime, EntryBlinkProc,
2248
                    (ClientData) entryPtr);
2249
        }
2250
    } else {
2251
        entryPtr->flags &= ~(GOT_FOCUS | CURSOR_ON);
2252
        entryPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
2253
    }
2254
    EventuallyRedraw(entryPtr);
2255
}
2256
 
2257
/*
2258
 *--------------------------------------------------------------
2259
 *
2260
 * EntryTextVarProc --
2261
 *
2262
 *      This procedure is invoked when someone changes the variable
2263
 *      whose contents are to be displayed in an entry.
2264
 *
2265
 * Results:
2266
 *      NULL is always returned.
2267
 *
2268
 * Side effects:
2269
 *      The text displayed in the entry will change to match the
2270
 *      variable.
2271
 *
2272
 *--------------------------------------------------------------
2273
 */
2274
 
2275
        /* ARGSUSED */
2276
static char *
2277
EntryTextVarProc(clientData, interp, name1, name2, flags)
2278
    ClientData clientData;      /* Information about button. */
2279
    Tcl_Interp *interp;         /* Interpreter containing variable. */
2280
    char *name1;                /* Not used. */
2281
    char *name2;                /* Not used. */
2282
    int flags;                  /* Information about what happened. */
2283
{
2284
    register Entry *entryPtr = (Entry *) clientData;
2285
    char *value;
2286
 
2287
    /*
2288
     * If the variable is unset, then immediately recreate it unless
2289
     * the whole interpreter is going away.
2290
     */
2291
 
2292
    if (flags & TCL_TRACE_UNSETS) {
2293
        if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
2294
            Tcl_SetVar(interp, entryPtr->textVarName, entryPtr->string,
2295
                    TCL_GLOBAL_ONLY);
2296
            Tcl_TraceVar(interp, entryPtr->textVarName,
2297
                    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
2298
                    EntryTextVarProc, clientData);
2299
        }
2300
        return (char *) NULL;
2301
    }
2302
 
2303
    /*
2304
     * Update the entry's text with the value of the variable, unless
2305
     * the entry already has that value (this happens when the variable
2306
     * changes value because we changed it because someone typed in
2307
     * the entry).
2308
     */
2309
 
2310
    value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
2311
    if (value == NULL) {
2312
        value = "";
2313
    }
2314
    if (strcmp(value, entryPtr->string) != 0) {
2315
        EntrySetValue(entryPtr, value);
2316
    }
2317
    return (char *) NULL;
2318
}

powered by: WebSVN 2.1.0

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