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