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