4 * This module implements listbox widgets for the
5 * toolkit. A listbox displays a collection of strings,
6 * one per line, and provides scrolling and selection.
8 * Copyright (c) 1990-1994 The Regents of the University of California.
9 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
10 * Copyright (c) 1995 Christian Werner
12 * See the file "license.terms" for information on usage and redistribution
13 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
21 * One record of the following type is kept for each element
22 * associated with a listbox widget:
25 typedef struct Element {
26 int textLength; /* # non-NULL characters in text. */
27 int textWidth; /* Total width of element in screen
29 int selected; /* 1 means this item is selected, 0 means
31 struct Element *nextPtr; /* Next in list of all elements of this
32 * listbox, or NULL for last element. */
33 char text[4]; /* Characters of this element, NULL-
34 * terminated. The actual space allocated
35 * here will be as large as needed (> 4,
36 * most likely). Must be the last field
40 #define ElementSize(stringLength) \
41 (sizeof(Element) - 3 + stringLength)
44 * A data structure of the following type is kept for each listbox
45 * widget managed by this file:
49 CkWindow *winPtr; /* Window that embodies the listbox. NULL
50 * means that the window has been destroyed
51 * but the data structures haven't yet been
53 Tcl_Interp *interp; /* Interpreter associated with listbox. */
54 Tcl_Command widgetCmd; /* Token for listbox's widget command. */
55 int numElements; /* Total number of elements in this listbox. */
56 Element *firstPtr; /* First in list of elements (NULL if no
58 Element *lastPtr; /* Last in list of elements (NULL if no
62 * Information used when displaying widget:
65 int normalBg; /* Normal background color. */
66 int normalFg; /* Normal foreground color. */
67 int normalAttr; /* Normal video attributes. */
68 int selBg; /* Select background color. */
69 int selFg; /* Select foreground color. */
70 int selAttr; /* Select video attributes. */
71 int activeBg; /* Active background color. */
72 int activeFg; /* Active foreground color. */
73 int activeAttr; /* Video attribute for active item. */
74 int width; /* Desired width of window, in characters. */
75 int height; /* Desired height of window, in lines. */
76 int topIndex; /* Index of top-most element visible in
78 int fullLines; /* Number of lines that fit are completely
79 * visible in window. There may be one
80 * additional line at the bottom that is
81 * partially visible. */
84 * Information to support horizontal scrolling:
87 int maxWidth; /* Width of widest string in listbox. */
88 int xOffset; /* The left edge of each string in the
89 * listbox is offset to the left by this
90 * many chars (0 means no offset, positive
91 * means there is an offset). */
94 * Information about what's selected or active, if any.
97 Ck_Uid selectMode; /* Selection style: single, browse, multiple,
98 * or extended. This value isn't used in C
99 * code, but the Tcl bindings use it. */
100 int numSelected; /* Number of elements currently selected. */
101 int selectAnchor; /* Fixed end of selection (i.e. element
102 * at which selection was started.) */
103 int active; /* Index of "active" element (the one that
104 * has been selected by keyboard traversal).
108 * Miscellaneous information:
111 char *takeFocus; /* Value of -takefocus option; not used in
112 * the C code, but used by keyboard traversal
113 * scripts. Malloc'ed, but may be NULL. */
114 char *yScrollCmd; /* Command prefix for communicating with
115 * vertical scrollbar. NULL means no command
116 * to issue. Malloc'ed. */
117 char *xScrollCmd; /* Command prefix for communicating with
118 * horizontal scrollbar. NULL means no command
119 * to issue. Malloc'ed. */
120 int flags; /* Various flag bits: see below for
125 * Flag bits for listboxes:
127 * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
128 * has already been queued to redraw
130 * UPDATE_V_SCROLLBAR: Non-zero means vertical scrollbar needs
132 * UPDATE_H_SCROLLBAR: Non-zero means horizontal scrollbar needs
134 * GOT_FOCUS: Non-zero means this widget currently
135 * has the input focus.
138 #define REDRAW_PENDING 1
139 #define UPDATE_V_SCROLLBAR 2
140 #define UPDATE_H_SCROLLBAR 4
144 * Information used for argv parsing:
147 static Ck_ConfigSpec configSpecs[] = {
148 {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes",
149 "ActiveAttributes", DEF_LISTBOX_ACTIVE_ATTR_COLOR,
150 Ck_Offset(Listbox, activeAttr), CK_CONFIG_COLOR_ONLY},
151 {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes",
152 "ActiveAttributes", DEF_LISTBOX_ACTIVE_ATTR_MONO,
153 Ck_Offset(Listbox, activeAttr), CK_CONFIG_MONO_ONLY},
154 {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
155 DEF_LISTBOX_ACTIVE_BG_COLOR, Ck_Offset(Listbox, activeBg),
156 CK_CONFIG_COLOR_ONLY},
157 {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
158 DEF_LISTBOX_ACTIVE_BG_MONO, Ck_Offset(Listbox, activeBg),
159 CK_CONFIG_MONO_ONLY},
160 {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
161 DEF_LISTBOX_ACTIVE_FG_COLOR, Ck_Offset(Listbox, activeFg),
162 CK_CONFIG_COLOR_ONLY},
163 {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
164 DEF_LISTBOX_ACTIVE_FG_MONO, Ck_Offset(Listbox, activeFg),
165 CK_CONFIG_MONO_ONLY},
166 {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
167 DEF_LISTBOX_ATTR, Ck_Offset(Listbox, normalAttr), 0},
168 {CK_CONFIG_COLOR, "-background", "background", "Background",
169 DEF_LISTBOX_BG_COLOR, Ck_Offset(Listbox, normalBg),
170 CK_CONFIG_COLOR_ONLY},
171 {CK_CONFIG_COLOR, "-background", "background", "Background",
172 DEF_LISTBOX_BG_MONO, Ck_Offset(Listbox, normalBg),
173 CK_CONFIG_MONO_ONLY},
174 {CK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
175 (char *) NULL, 0, 0},
176 {CK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
177 (char *) NULL, 0, 0},
178 {CK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
179 (char *) NULL, 0, 0},
180 {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
181 DEF_LISTBOX_FG, Ck_Offset(Listbox, normalFg), 0},
182 {CK_CONFIG_INT, "-height", "height", "Height",
183 DEF_LISTBOX_HEIGHT, Ck_Offset(Listbox, height), 0},
184 {CK_CONFIG_ATTR, "-selectattributes", "selectAttributes",
185 "SelectAttributes", DEF_LISTBOX_SELECT_ATTR_COLOR,
186 Ck_Offset(Listbox, selAttr), CK_CONFIG_COLOR_ONLY},
187 {CK_CONFIG_ATTR, "-selectattributes", "selectAttributes",
188 "SelectAttributes", DEF_LISTBOX_SELECT_ATTR_MONO,
189 Ck_Offset(Listbox, selAttr), CK_CONFIG_MONO_ONLY},
190 {CK_CONFIG_COLOR, "-selectbackground", "selectBackground", "Foreground",
191 DEF_LISTBOX_SELECT_BG_COLOR, Ck_Offset(Listbox, selBg),
192 CK_CONFIG_COLOR_ONLY},
193 {CK_CONFIG_COLOR, "-selectbackground", "selectBackground", "Foreground",
194 DEF_LISTBOX_SELECT_BG_MONO, Ck_Offset(Listbox, selBg),
195 CK_CONFIG_MONO_ONLY},
196 {CK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
197 DEF_LISTBOX_SELECT_FG_COLOR, Ck_Offset(Listbox, selFg),
198 CK_CONFIG_COLOR_ONLY},
199 {CK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
200 DEF_LISTBOX_SELECT_FG_MONO, Ck_Offset(Listbox, selFg),
201 CK_CONFIG_MONO_ONLY},
202 {CK_CONFIG_UID, "-selectmode", "selectMode", "SelectMode",
203 DEF_LISTBOX_SELECT_MODE, Ck_Offset(Listbox, selectMode), 0},
204 {CK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
205 DEF_LISTBOX_TAKE_FOCUS, Ck_Offset(Listbox, takeFocus),
207 {CK_CONFIG_INT, "-width", "width", "Width",
208 DEF_LISTBOX_WIDTH, Ck_Offset(Listbox, width), 0},
209 {CK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
210 DEF_LISTBOX_SCROLL_COMMAND, Ck_Offset(Listbox, xScrollCmd),
212 {CK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
213 DEF_LISTBOX_SCROLL_COMMAND, Ck_Offset(Listbox, yScrollCmd),
215 {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
220 * Forward declarations for procedures defined later in this file:
223 static void ChangeListboxOffset _ANSI_ARGS_((Listbox *listPtr,
225 static void ChangeListboxView _ANSI_ARGS_((Listbox *listPtr,
227 static int ConfigureListbox _ANSI_ARGS_((Tcl_Interp *interp,
228 Listbox *listPtr, int argc, char **argv,
230 static void DeleteEls _ANSI_ARGS_((Listbox *listPtr, int first,
232 static void DestroyListbox _ANSI_ARGS_((ClientData clientData));
233 static void DisplayListbox _ANSI_ARGS_((ClientData clientData));
234 static int GetListboxIndex _ANSI_ARGS_((Tcl_Interp *interp,
235 Listbox *listPtr, char *string, int numElsOK,
237 static void InsertEls _ANSI_ARGS_((Listbox *listPtr, int index,
238 int argc, char **argv));
239 static void ListboxCmdDeletedProc _ANSI_ARGS_((
240 ClientData clientData));
241 static void ListboxComputeGeometry _ANSI_ARGS_((Listbox *listPtr));
242 static void ListboxEventProc _ANSI_ARGS_((ClientData clientData,
244 static void ListboxRedrawRange _ANSI_ARGS_((Listbox *listPtr,
245 int first, int last));
246 static void ListboxSelect _ANSI_ARGS_((Listbox *listPtr,
247 int first, int last, int select));
248 static void ListboxUpdateHScrollbar _ANSI_ARGS_((Listbox *listPtr));
249 static void ListboxUpdateVScrollbar _ANSI_ARGS_((Listbox *listPtr));
250 static int ListboxWidgetCmd _ANSI_ARGS_((ClientData clientData,
251 Tcl_Interp *interp, int argc, char **argv));
252 static int NearestListboxElement _ANSI_ARGS_((Listbox *listPtr,
256 *--------------------------------------------------------------
260 * This procedure is invoked to process the "listbox" Tcl
261 * command. See the user documentation for details on what
265 * A standard Tcl result.
268 * See the user documentation.
270 *--------------------------------------------------------------
274 Ck_ListboxCmd(clientData, interp, argc, argv)
275 ClientData clientData; /* Main window associated with
277 Tcl_Interp *interp; /* Current interpreter. */
278 int argc; /* Number of arguments. */
279 char **argv; /* Argument strings. */
281 register Listbox *listPtr;
283 CkWindow *mainPtr = (CkWindow *) clientData;
286 Tcl_AppendResult(interp, "wrong # args: should be \"",
287 argv[0], " pathName ?options?\"", (char *) NULL);
291 new = Ck_CreateWindowFromPath(interp, mainPtr, argv[1], 0);
297 * Initialize the fields of the structure that won't be initialized
298 * by ConfigureListbox, or that ConfigureListbox requires to be
299 * initialized already (e.g. resource pointers).
302 listPtr = (Listbox *) ckalloc(sizeof(Listbox));
303 listPtr->winPtr = new;
304 listPtr->interp = interp;
305 listPtr->widgetCmd = Tcl_CreateCommand(interp, listPtr->winPtr->pathName,
306 ListboxWidgetCmd, (ClientData) listPtr, ListboxCmdDeletedProc);
307 listPtr->numElements = 0;
308 listPtr->firstPtr = NULL;
309 listPtr->lastPtr = NULL;
310 listPtr->normalBg = 0;
311 listPtr->normalFg = 0;
312 listPtr->normalAttr = 0;
315 listPtr->selAttr = 0;
316 listPtr->activeBg = 0;
317 listPtr->activeFg = 0;
318 listPtr->activeAttr = 0;
321 listPtr->topIndex = 0;
322 listPtr->fullLines = 1;
323 listPtr->maxWidth = 0;
324 listPtr->xOffset = 0;
325 listPtr->selectMode = NULL;
326 listPtr->numSelected = 0;
327 listPtr->selectAnchor = 0;
329 listPtr->takeFocus = NULL;
330 listPtr->xScrollCmd = NULL;
331 listPtr->yScrollCmd = NULL;
334 Ck_SetClass(listPtr->winPtr, "Listbox");
335 Ck_CreateEventHandler(listPtr->winPtr,
336 CK_EV_EXPOSE | CK_EV_MAP | CK_EV_DESTROY |
337 CK_EV_FOCUSIN | CK_EV_FOCUSOUT,
338 ListboxEventProc, (ClientData) listPtr);
339 if (ConfigureListbox(interp, listPtr, argc-2, argv+2, 0) != TCL_OK) {
343 interp->result = listPtr->winPtr->pathName;
347 Ck_DestroyWindow(listPtr->winPtr);
352 *--------------------------------------------------------------
354 * ListboxWidgetCmd --
356 * This procedure is invoked to process the Tcl command
357 * that corresponds to a widget managed by this module.
358 * See the user documentation for details on what it does.
361 * A standard Tcl result.
364 * See the user documentation.
366 *--------------------------------------------------------------
370 ListboxWidgetCmd(clientData, interp, argc, argv)
371 ClientData clientData; /* Information about listbox widget. */
372 Tcl_Interp *interp; /* Current interpreter. */
373 int argc; /* Number of arguments. */
374 char **argv; /* Argument strings. */
376 register Listbox *listPtr = (Listbox *) clientData;
382 Tcl_AppendResult(interp, "wrong # args: should be \"",
383 argv[0], " option ?arg arg ...?\"", (char *) NULL);
386 Ck_Preserve((ClientData) listPtr);
388 length = strlen(argv[1]);
389 if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)) {
393 Tcl_AppendResult(interp, "wrong # args: should be \"",
394 argv[0], " activate index\"",
398 ListboxRedrawRange(listPtr, listPtr->active, listPtr->active);
399 if (GetListboxIndex(interp, listPtr, argv[2], 0, &index)
403 listPtr->active = index;
404 ListboxRedrawRange(listPtr, listPtr->active, listPtr->active);
405 } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
408 Tcl_AppendResult(interp, "wrong # args: should be \"",
409 argv[0], " cget option\"",
413 result = Ck_ConfigureValue(interp, listPtr->winPtr, configSpecs,
414 (char *) listPtr, argv[2], 0);
415 } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
418 result = Ck_ConfigureInfo(interp, listPtr->winPtr, configSpecs,
419 (char *) listPtr, (char *) NULL, 0);
420 } else if (argc == 3) {
421 result = Ck_ConfigureInfo(interp, listPtr->winPtr, configSpecs,
422 (char *) listPtr, argv[2], 0);
424 result = ConfigureListbox(interp, listPtr, argc-2, argv+2,
425 CK_CONFIG_ARGV_ONLY);
427 } else if ((c == 'c') && (strncmp(argv[1], "curselection", length) == 0)
434 Tcl_AppendResult(interp, "wrong # args: should be \"",
435 argv[0], " curselection\"",
440 for (i = 0, elPtr = listPtr->firstPtr; elPtr != NULL;
441 i++, elPtr = elPtr->nextPtr) {
442 if (elPtr->selected) {
443 sprintf(index, "%d", i);
444 Tcl_AppendElement(interp, index);
448 if (count != listPtr->numSelected) {
449 panic("ListboxWidgetCmd: selection count incorrect");
451 } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
454 if ((argc < 3) || (argc > 4)) {
455 Tcl_AppendResult(interp, "wrong # args: should be \"",
456 argv[0], " delete firstIndex ?lastIndex?\"",
460 if (GetListboxIndex(interp, listPtr, argv[2], 0, &first) != TCL_OK) {
466 if (GetListboxIndex(interp, listPtr, argv[3], 0, &last) != TCL_OK) {
470 DeleteEls(listPtr, first, last);
471 } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
475 if ((argc != 3) && (argc != 4)) {
476 Tcl_AppendResult(interp, "wrong # args: should be \"",
477 argv[0], " get first ?last?\"", (char *) NULL);
480 if (GetListboxIndex(interp, listPtr, argv[2], 0, &first) != TCL_OK) {
483 if ((argc == 4) && (GetListboxIndex(interp, listPtr, argv[3],
484 0, &last) != TCL_OK)) {
487 for (elPtr = listPtr->firstPtr, i = 0; i < first;
488 i++, elPtr = elPtr->nextPtr) {
489 /* Empty loop body. */
493 interp->result = elPtr->text;
495 for ( ; i <= last; i++, elPtr = elPtr->nextPtr) {
496 Tcl_AppendElement(interp, elPtr->text);
500 } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
505 Tcl_AppendResult(interp, "wrong # args: should be \"",
506 argv[0], " index index\"",
510 if (GetListboxIndex(interp, listPtr, argv[2], 1, &index)
514 sprintf(interp->result, "%d", index);
515 } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
520 Tcl_AppendResult(interp, "wrong # args: should be \"",
521 argv[0], " insert index ?element element ...?\"",
525 if (GetListboxIndex(interp, listPtr, argv[2], 1, &index)
529 InsertEls(listPtr, index, argc-3, argv+3);
530 } else if ((c == 'n') && (strncmp(argv[1], "nearest", length) == 0)) {
534 Tcl_AppendResult(interp, "wrong # args: should be \"",
535 argv[0], " nearest y\"", (char *) NULL);
538 if (Tcl_GetInt(interp, argv[2], &y) != TCL_OK) {
541 index = NearestListboxElement(listPtr, y);
542 sprintf(interp->result, "%d", index);
543 } else if ((c == 's') && (strncmp(argv[1], "see", length) == 0)
547 Tcl_AppendResult(interp, "wrong # args: should be \"",
548 argv[0], " see index\"",
552 if (GetListboxIndex(interp, listPtr, argv[2], 0, &index) != TCL_OK) {
555 diff = listPtr->topIndex-index;
557 if (diff <= (listPtr->fullLines/3)) {
558 ChangeListboxView(listPtr, index);
560 ChangeListboxView(listPtr, index - (listPtr->fullLines-1)/2);
563 diff = index - (listPtr->topIndex + listPtr->fullLines - 1);
565 if (diff <= (listPtr->fullLines/3)) {
566 ChangeListboxView(listPtr, listPtr->topIndex + diff);
568 ChangeListboxView(listPtr,
569 index - (listPtr->fullLines-1)/2);
573 } else if ((c == 's') && (length >= 3)
574 && (strncmp(argv[1], "selection", length) == 0)) {
577 if ((argc != 4) && (argc != 5)) {
578 Tcl_AppendResult(interp, "wrong # args: should be \"",
579 argv[0], " selection option index ?index?\"",
583 if (GetListboxIndex(interp, listPtr, argv[3], 0, &first) != TCL_OK) {
587 if (GetListboxIndex(interp, listPtr, argv[4], 0, &last) != TCL_OK) {
593 length = strlen(argv[2]);
595 if ((c == 'a') && (strncmp(argv[2], "anchor", length) == 0)) {
597 Tcl_AppendResult(interp, "wrong # args: should be \"",
598 argv[0], " selection anchor index\"", (char *) NULL);
601 listPtr->selectAnchor = first;
602 } else if ((c == 'c') && (strncmp(argv[2], "clear", length) == 0)) {
603 ListboxSelect(listPtr, first, last, 0);
604 } else if ((c == 'i') && (strncmp(argv[2], "includes", length) == 0)) {
609 Tcl_AppendResult(interp, "wrong # args: should be \"",
610 argv[0], " selection includes index\"", (char *) NULL);
613 for (elPtr = listPtr->firstPtr, i = 0; i < first;
614 i++, elPtr = elPtr->nextPtr) {
615 /* Empty loop body. */
617 if ((elPtr != NULL) && (elPtr->selected)) {
618 interp->result = "1";
620 interp->result = "0";
622 } else if ((c == 's') && (strncmp(argv[2], "set", length) == 0)) {
623 ListboxSelect(listPtr, first, last, 1);
625 Tcl_AppendResult(interp, "bad selection option \"", argv[2],
626 "\": must be anchor, clear, includes, or set",
630 } else if ((c == 's') && (length >= 2)
631 && (strncmp(argv[1], "size", length) == 0)) {
633 Tcl_AppendResult(interp, "wrong # args: should be \"",
634 argv[0], " size\"", (char *) NULL);
637 sprintf(interp->result, "%d", listPtr->numElements);
638 } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
639 int index, count, type, windowWidth;
640 int offset = 0; /* Initialized to stop gcc warnings. */
641 double fraction, fraction2;
643 windowWidth = listPtr->winPtr->width;
645 if (listPtr->maxWidth == 0) {
646 interp->result = "0 1";
648 fraction = listPtr->xOffset/((double) listPtr->maxWidth);
649 fraction2 = (listPtr->xOffset + windowWidth)
650 /((double) listPtr->maxWidth);
651 if (fraction2 > 1.0) {
654 sprintf(interp->result, "%g %g", fraction, fraction2);
656 } else if (argc == 3) {
657 if (Tcl_GetInt(interp, argv[2], &index) != TCL_OK) {
660 ChangeListboxOffset(listPtr, index);
662 type = Ck_GetScrollInfo(interp, argc, argv, &fraction, &count);
664 case CK_SCROLL_ERROR:
666 case CK_SCROLL_MOVETO:
667 offset = (int) fraction*listPtr->maxWidth;
669 case CK_SCROLL_PAGES:
670 offset = listPtr->xOffset + count * windowWidth;
672 case CK_SCROLL_UNITS:
673 offset = listPtr->xOffset + count;
676 ChangeListboxOffset(listPtr, offset);
678 } else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)) {
679 int index, count, type;
680 double fraction, fraction2;
683 if (listPtr->numElements == 0) {
684 interp->result = "0 1";
686 fraction = listPtr->topIndex/((double) listPtr->numElements);
687 fraction2 = (listPtr->topIndex+listPtr->fullLines)
688 /((double) listPtr->numElements);
689 if (fraction2 > 1.0) {
692 sprintf(interp->result, "%g %g", fraction, fraction2);
694 } else if (argc == 3) {
695 if (GetListboxIndex(interp, listPtr, argv[2], 0, &index)
699 ChangeListboxView(listPtr, index);
701 type = Ck_GetScrollInfo(interp, argc, argv, &fraction, &count);
703 case CK_SCROLL_ERROR:
705 case CK_SCROLL_MOVETO:
706 index = (int) (listPtr->numElements * fraction);
708 case CK_SCROLL_PAGES:
709 if (listPtr->fullLines > 2) {
710 index = listPtr->topIndex
711 + count * (listPtr->fullLines - 2);
713 index = listPtr->topIndex + count;
716 case CK_SCROLL_UNITS:
717 index = listPtr->topIndex + count;
720 ChangeListboxView(listPtr, index);
723 Tcl_AppendResult(interp, "bad option \"", argv[1],
724 "\": must be activate, cget, configure, ",
725 "curselection, delete, get, index, insert, nearest, ",
726 "see, selection, size, ",
727 "xview, or yview", (char *) NULL);
730 Ck_Release((ClientData) listPtr);
734 Ck_Release((ClientData) listPtr);
739 *----------------------------------------------------------------------
743 * This procedure is invoked by Ck_EventuallyFree or Ck_Release
744 * to clean up the internal structure of a listbox at a safe time
745 * (when no-one is using it anymore).
751 * Everything associated with the listbox is freed up.
753 *----------------------------------------------------------------------
757 DestroyListbox(clientData)
758 ClientData clientData; /* Info about listbox widget. */
760 register Listbox *listPtr = (Listbox *) clientData;
761 register Element *elPtr, *nextPtr;
764 * Free up all of the list elements.
767 for (elPtr = listPtr->firstPtr; elPtr != NULL; ) {
768 nextPtr = elPtr->nextPtr;
769 ckfree((char *) elPtr);
773 Ck_FreeOptions(configSpecs, (char *) listPtr, 0);
774 ckfree((char *) listPtr);
778 *----------------------------------------------------------------------
780 * ListboxCmdDeletedProc --
782 * This procedure is invoked when a widget command is deleted. If
783 * the widget isn't already in the process of being destroyed,
784 * this command destroys it.
790 * The widget is destroyed.
792 *----------------------------------------------------------------------
796 ListboxCmdDeletedProc(clientData)
797 ClientData clientData; /* Pointer to widget record for widget. */
799 Listbox *listPtr = (Listbox *) clientData;
800 CkWindow *winPtr = listPtr->winPtr;
803 * This procedure could be invoked either because the window was
804 * destroyed and the command was then deleted (in which case winPtr
805 * is NULL) or because the command was deleted, and then this procedure
806 * destroys the widget.
809 if (winPtr != NULL) {
810 listPtr->winPtr = NULL;
811 Ck_DestroyWindow(winPtr);
816 *----------------------------------------------------------------------
818 * ConfigureListbox --
820 * This procedure is called to process an argv/argc list, plus
821 * the option database, in order to configure (or reconfigure)
825 * The return value is a standard Tcl result. If TCL_ERROR is
826 * returned, then interp->result contains an error message.
829 * Configuration information, such as colors, border width,
830 * etc. get set for listPtr; old resources get freed,
833 *----------------------------------------------------------------------
837 ConfigureListbox(interp, listPtr, argc, argv, flags)
838 Tcl_Interp *interp; /* Used for error reporting. */
839 register Listbox *listPtr; /* Information about widget; may or may
840 * not already have values for some fields. */
841 int argc; /* Number of valid entries in argv. */
842 char **argv; /* Arguments. */
843 int flags; /* Flags to pass to Ck_ConfigureWidget. */
845 if (Ck_ConfigureWidget(interp, listPtr->winPtr, configSpecs,
846 argc, argv, (char *) listPtr, flags) != TCL_OK) {
851 * Register the desired geometry for the window and arrange for
852 * the window to be redisplayed.
855 ListboxComputeGeometry(listPtr);
856 listPtr->flags |= UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR;
857 ListboxRedrawRange(listPtr, 0, listPtr->numElements-1);
862 *--------------------------------------------------------------
866 * This procedure redraws the contents of a listbox window.
872 * Information appears on the screen.
874 *--------------------------------------------------------------
878 DisplayListbox(clientData)
879 ClientData clientData; /* Information about window. */
881 Listbox *listPtr = (Listbox *) clientData;
882 CkWindow *winPtr = listPtr->winPtr;
884 int i, limit, y, width, cursorY;
886 listPtr->flags &= ~REDRAW_PENDING;
887 if (listPtr->flags & UPDATE_V_SCROLLBAR) {
888 ListboxUpdateVScrollbar(listPtr);
890 if (listPtr->flags & UPDATE_H_SCROLLBAR) {
891 ListboxUpdateHScrollbar(listPtr);
893 listPtr->flags &= ~(REDRAW_PENDING|UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR);
894 if ((listPtr->winPtr == NULL) || !(winPtr->flags & CK_MAPPED)) {
898 Ck_SetWindowAttr(winPtr, listPtr->normalFg, listPtr->normalBg,
899 listPtr->normalAttr);
900 Ck_ClearToBot(winPtr, 0, 0);
903 * Iterate through all of the elements of the listbox, displaying each
904 * in turn. Selected elements use a different fg/bg/attr.
907 limit = listPtr->topIndex + listPtr->fullLines;
908 if (limit > listPtr->numElements) {
909 limit = listPtr->numElements;
911 width = listPtr->xOffset + winPtr->width;
912 for (elPtr = listPtr->firstPtr, i = 0, y = cursorY = 0;
913 (elPtr != NULL) && (i < limit);
914 elPtr = elPtr->nextPtr, i++) {
915 if (i < listPtr->topIndex) {
918 if (i == listPtr->active && (listPtr->flags & GOT_FOCUS)) {
920 Ck_SetWindowAttr(winPtr, listPtr->activeFg, listPtr->activeBg,
921 listPtr->activeAttr |
922 (elPtr->selected ? listPtr->selAttr : 0));
923 } else if (elPtr->selected) {
924 Ck_SetWindowAttr(winPtr, listPtr->selFg, listPtr->selBg,
927 Ck_SetWindowAttr(winPtr, listPtr->normalFg, listPtr->normalBg,
928 listPtr->normalAttr);
931 if (listPtr->xOffset < elPtr->textWidth) {
932 char *p = Tcl_UtfAtIndex(elPtr->text, listPtr->xOffset);
934 CkDisplayChars(winPtr->mainPtr, winPtr->window, p,
936 CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS | CK_FILL_UNTIL_EOL);
939 CkDisplayChars(winPtr->mainPtr,
940 winPtr->window, &elPtr->text[listPtr->xOffset],
941 elPtr->textLength - listPtr->xOffset, 0, y, 0,
942 CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS | CK_FILL_UNTIL_EOL);
946 wmove(winPtr->window, cursorY, 0);
947 Ck_EventuallyRefresh(winPtr);
951 *----------------------------------------------------------------------
953 * ListboxComputeGeometry --
955 * This procedure is invoked to recompute geometry information
956 * such as the sizes of the elements and the overall dimensions
957 * desired for the listbox.
963 * Geometry information is updated and a new requested size is
964 * registered for the widget. Internal border and gridding
965 * information is also set.
967 *----------------------------------------------------------------------
971 ListboxComputeGeometry(listPtr)
972 Listbox *listPtr; /* Listbox whose geometry is to be
977 width = listPtr->width;
979 width = listPtr->maxWidth;
984 height = listPtr->height;
985 if (listPtr->height <= 0) {
986 height = listPtr->numElements;
991 Ck_GeometryRequest(listPtr->winPtr, width, height);
995 *----------------------------------------------------------------------
999 * Add new elements to a listbox widget.
1005 * New information gets added to listPtr; it will be redisplayed
1006 * soon, but not immediately.
1008 *----------------------------------------------------------------------
1012 InsertEls(listPtr, index, argc, argv)
1013 register Listbox *listPtr; /* Listbox that is to get the new
1015 int index; /* Add the new elements before this
1017 int argc; /* Number of new elements to add. */
1018 char **argv; /* New elements (one per entry). */
1020 register Element *prevPtr, *newPtr;
1021 int length, i, oldMaxWidth;
1024 * Find the element before which the new ones will be inserted.
1030 if (index > listPtr->numElements) {
1031 index = listPtr->numElements;
1035 } else if (index == listPtr->numElements) {
1036 prevPtr = listPtr->lastPtr;
1038 for (prevPtr = listPtr->firstPtr, i = index - 1; i > 0; i--) {
1039 prevPtr = prevPtr->nextPtr;
1044 * For each new element, create a record, initialize it, and link
1045 * it into the list of elements.
1048 oldMaxWidth = listPtr->maxWidth;
1049 for (i = argc ; i > 0; i--, argv++, prevPtr = newPtr) {
1050 length = strlen(*argv);
1051 newPtr = (Element *) ckalloc(ElementSize(length));
1052 newPtr->textLength = length;
1053 strcpy(newPtr->text, *argv);
1055 newPtr->textWidth = Tcl_NumUtfChars(*argv, length);
1057 newPtr->textWidth = newPtr->textLength;
1059 if (newPtr->textWidth > listPtr->maxWidth) {
1060 listPtr->maxWidth = newPtr->textWidth;
1062 newPtr->selected = 0;
1063 if (prevPtr == NULL) {
1064 newPtr->nextPtr = listPtr->firstPtr;
1065 listPtr->firstPtr = newPtr;
1067 newPtr->nextPtr = prevPtr->nextPtr;
1068 prevPtr->nextPtr = newPtr;
1071 if ((prevPtr != NULL) && (prevPtr->nextPtr == NULL)) {
1072 listPtr->lastPtr = prevPtr;
1074 listPtr->numElements += argc;
1077 * Update the selection and other indexes to account for the
1078 * renumbering that has just occurred. Then arrange for the new
1079 * information to be displayed.
1082 if (index <= listPtr->selectAnchor) {
1083 listPtr->selectAnchor += argc;
1085 if (index < listPtr->topIndex) {
1086 listPtr->topIndex += argc;
1088 if (index <= listPtr->active) {
1089 listPtr->active += argc;
1090 if ((listPtr->active >= listPtr->numElements)
1091 && (listPtr->numElements > 0)) {
1092 listPtr->active = listPtr->numElements-1;
1095 listPtr->flags |= UPDATE_V_SCROLLBAR;
1096 if (listPtr->maxWidth != oldMaxWidth) {
1097 listPtr->flags |= UPDATE_H_SCROLLBAR;
1099 ListboxComputeGeometry(listPtr);
1100 ListboxRedrawRange(listPtr, index, listPtr->numElements-1);
1104 *----------------------------------------------------------------------
1108 * Remove one or more elements from a listbox widget.
1114 * Memory gets freed, the listbox gets modified and (eventually)
1117 *----------------------------------------------------------------------
1121 DeleteEls(listPtr, first, last)
1122 register Listbox *listPtr; /* Listbox widget to modify. */
1123 int first; /* Index of first element to delete. */
1124 int last; /* Index of last element to delete. */
1126 register Element *prevPtr, *elPtr;
1127 int count, i, widthChanged;
1130 * Adjust the range to fit within the existing elements of the
1131 * listbox, and make sure there's something to delete.
1137 if (last >= listPtr->numElements) {
1138 last = listPtr->numElements-1;
1140 count = last + 1 - first;
1146 * Find the element just before the ones to delete.
1152 for (i = first-1, prevPtr = listPtr->firstPtr; i > 0; i--) {
1153 prevPtr = prevPtr->nextPtr;
1158 * Delete the requested number of elements.
1162 for (i = count; i > 0; i--) {
1163 if (prevPtr == NULL) {
1164 elPtr = listPtr->firstPtr;
1165 listPtr->firstPtr = elPtr->nextPtr;
1166 if (listPtr->firstPtr == NULL) {
1167 listPtr->lastPtr = NULL;
1170 elPtr = prevPtr->nextPtr;
1171 prevPtr->nextPtr = elPtr->nextPtr;
1172 if (prevPtr->nextPtr == NULL) {
1173 listPtr->lastPtr = prevPtr;
1176 if (elPtr->textWidth == listPtr->maxWidth) {
1179 if (elPtr->selected) {
1180 listPtr->numSelected -= 1;
1182 ckfree((char *) elPtr);
1184 listPtr->numElements -= count;
1187 * Update the selection and viewing information to reflect the change
1188 * in the element numbering, and redisplay to slide information up over
1189 * the elements that were deleted.
1192 if (first <= listPtr->selectAnchor) {
1193 listPtr->selectAnchor -= count;
1194 if (listPtr->selectAnchor < first) {
1195 listPtr->selectAnchor = first;
1198 if (first <= listPtr->topIndex) {
1199 listPtr->topIndex -= count;
1200 if (listPtr->topIndex < first) {
1201 listPtr->topIndex = first;
1204 if (listPtr->topIndex > (listPtr->numElements - listPtr->fullLines)) {
1205 listPtr->topIndex = listPtr->numElements - listPtr->fullLines;
1206 if (listPtr->topIndex < 0) {
1207 listPtr->topIndex = 0;
1210 if (listPtr->active > last) {
1211 listPtr->active -= count;
1212 } else if (listPtr->active >= first) {
1213 listPtr->active = first;
1214 if ((listPtr->active >= listPtr->numElements)
1215 && (listPtr->numElements > 0)) {
1216 listPtr->active = listPtr->numElements-1;
1219 listPtr->flags |= UPDATE_V_SCROLLBAR;
1220 ListboxComputeGeometry(listPtr);
1224 for (elPtr = listPtr->firstPtr; elPtr != NULL; elPtr = elPtr->nextPtr)
1225 if (elPtr->textWidth > maxWidth)
1226 maxWidth = elPtr->textWidth;
1227 if (maxWidth != listPtr->maxWidth) {
1228 listPtr->maxWidth = maxWidth;
1229 listPtr->flags |= UPDATE_H_SCROLLBAR;
1230 if (listPtr->xOffset + listPtr->width >= listPtr->maxWidth)
1231 listPtr->xOffset = listPtr->maxWidth - listPtr->width;
1232 if (listPtr->xOffset < 0)
1233 listPtr->xOffset = 0;
1236 ListboxRedrawRange(listPtr, first, listPtr->numElements-1);
1240 *--------------------------------------------------------------
1242 * ListboxEventProc --
1244 * This procedure is invoked by the dispatcher for various
1245 * events on listboxes.
1251 * When the window gets deleted, internal structures get
1252 * cleaned up. When it gets exposed, it is redisplayed.
1254 *--------------------------------------------------------------
1258 ListboxEventProc(clientData, eventPtr)
1259 ClientData clientData; /* Information about window. */
1260 CkEvent *eventPtr; /* Information about event. */
1262 Listbox *listPtr = (Listbox *) clientData;
1264 if (eventPtr->type == CK_EV_DESTROY) {
1265 if (listPtr->winPtr != NULL) {
1266 listPtr->winPtr = NULL;
1267 Tcl_DeleteCommand(listPtr->interp,
1268 Tcl_GetCommandName(listPtr->interp, listPtr->widgetCmd));
1270 if (listPtr->flags & REDRAW_PENDING) {
1271 Tk_CancelIdleCall(DisplayListbox, (ClientData) listPtr);
1273 Ck_EventuallyFree((ClientData) listPtr,
1274 (Ck_FreeProc *) DestroyListbox);
1275 } else if (eventPtr->type == CK_EV_EXPOSE) {
1276 listPtr->fullLines = listPtr->winPtr->height;
1277 listPtr->flags |= UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR;
1278 ChangeListboxView(listPtr, listPtr->topIndex);
1279 ChangeListboxOffset(listPtr, listPtr->xOffset);
1282 * Redraw the whole listbox. It's hard to tell what needs
1283 * to be redrawn (e.g. if the listbox has shrunk then we
1284 * may only need to redraw the borders), so just redraw
1285 * everything for safety.
1288 ListboxRedrawRange(listPtr, 0, listPtr->numElements-1);
1289 } else if (eventPtr->type == CK_EV_FOCUSIN) {
1290 listPtr->flags |= GOT_FOCUS;
1291 ListboxRedrawRange(listPtr, listPtr->active, listPtr->active);
1292 } else if (eventPtr->type == CK_EV_FOCUSOUT) {
1293 listPtr->flags &= ~GOT_FOCUS;
1294 ListboxRedrawRange(listPtr, listPtr->active, listPtr->active);
1299 *--------------------------------------------------------------
1301 * GetListboxIndex --
1303 * Parse an index into a listbox and return either its value
1307 * A standard Tcl result. If all went well, then *indexPtr is
1308 * filled in with the index (into listPtr) corresponding to
1309 * string. Otherwise an error message is left in interp->result.
1314 *--------------------------------------------------------------
1318 GetListboxIndex(interp, listPtr, string, numElsOK, indexPtr)
1319 Tcl_Interp *interp; /* For error messages. */
1320 Listbox *listPtr; /* Listbox for which the index is being
1322 char *string; /* Specifies an element in the listbox. */
1323 int numElsOK; /* 0 means the return value must be less
1324 * less than the number of entries in
1325 * the listbox; 1 means it may also be
1326 * equal to the number of entries. */
1327 int *indexPtr; /* Where to store converted index. */
1332 length = strlen(string);
1334 if ((c == 'a') && (strncmp(string, "active", length) == 0)
1336 *indexPtr = listPtr->active;
1337 } else if ((c == 'a') && (strncmp(string, "anchor", length) == 0)
1339 *indexPtr = listPtr->selectAnchor;
1340 } else if ((c == 'e') && (strncmp(string, "end", length) == 0)) {
1341 *indexPtr = listPtr->numElements;
1342 } else if (c == '@') {
1347 x = strtol(p, &end, 0);
1348 if ((end == p) || (*end != ',')) {
1352 y = strtol(p, &end, 0);
1353 if ((end == p) || (*end != 0)) {
1356 *indexPtr = NearestListboxElement(listPtr, y);
1358 if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
1359 Tcl_ResetResult(interp);
1364 if (*indexPtr > listPtr->numElements) {
1365 *indexPtr = listPtr->numElements;
1367 } else if (*indexPtr >= listPtr->numElements) {
1368 *indexPtr = listPtr->numElements-1;
1370 if (*indexPtr < 0) {
1376 Tcl_AppendResult(interp, "bad listbox index \"", string,
1377 "\": must be active, anchor, end, @x,y, or a number",
1383 *----------------------------------------------------------------------
1385 * ChangeListboxView --
1387 * Change the view on a listbox widget so that a given element
1388 * is displayed at the top.
1394 * What's displayed on the screen is changed. If there is a
1395 * scrollbar associated with this widget, then the scrollbar
1396 * is instructed to change its display too.
1398 *----------------------------------------------------------------------
1402 ChangeListboxView(listPtr, index)
1403 register Listbox *listPtr; /* Information about widget. */
1404 int index; /* Index of element in listPtr
1405 * that should now appear at the
1406 * top of the listbox. */
1408 if (index >= (listPtr->numElements - listPtr->fullLines)) {
1409 index = listPtr->numElements - listPtr->fullLines;
1414 if (listPtr->topIndex != index) {
1415 listPtr->topIndex = index;
1416 if (!(listPtr->flags & REDRAW_PENDING)) {
1417 Tk_DoWhenIdle(DisplayListbox, (ClientData) listPtr);
1418 listPtr->flags |= REDRAW_PENDING;
1420 listPtr->flags |= UPDATE_V_SCROLLBAR;
1425 *----------------------------------------------------------------------
1427 * ChangListboxOffset --
1429 * Change the horizontal offset for a listbox.
1435 * The listbox may be redrawn to reflect its new horizontal
1438 *----------------------------------------------------------------------
1442 ChangeListboxOffset(listPtr, offset)
1443 register Listbox *listPtr; /* Information about widget. */
1444 int offset; /* Desired new "xOffset" for
1450 * Make sure that the new offset is within the allowable range, and
1451 * round it off to an even multiple of xScrollUnit.
1454 maxOffset = listPtr->maxWidth - listPtr->winPtr->width;
1455 if (offset > maxOffset) {
1461 listPtr->xOffset = offset;
1462 listPtr->flags |= UPDATE_H_SCROLLBAR;
1463 ListboxRedrawRange(listPtr, 0, listPtr->numElements);
1467 *----------------------------------------------------------------------
1469 * NearestListboxElement --
1471 * Given a y-coordinate inside a listbox, compute the index of
1472 * the element under that y-coordinate (or closest to that
1476 * The return value is an index of an element of listPtr. If
1477 * listPtr has no elements, then 0 is always returned.
1482 *----------------------------------------------------------------------
1486 NearestListboxElement(listPtr, y)
1487 register Listbox *listPtr; /* Information about widget. */
1488 int y; /* Y-coordinate in listPtr's window. */
1493 if (index >= listPtr->fullLines) {
1494 index = listPtr->fullLines - 1;
1499 index += listPtr->topIndex;
1500 if (index >= listPtr->numElements) {
1501 index = listPtr->numElements-1;
1507 *----------------------------------------------------------------------
1511 * Select or deselect one or more elements in a listbox..
1517 * All of the elements in the range between first and last are
1518 * marked as either selected or deselected, depending on the
1519 * "select" argument. Any items whose state changes are redisplayed.
1520 * The selection is claimed from X when the number of selected
1521 * elements changes from zero to non-zero.
1523 *----------------------------------------------------------------------
1527 ListboxSelect(listPtr, first, last, select)
1528 register Listbox *listPtr; /* Information about widget. */
1529 int first; /* Index of first element to
1530 * select or deselect. */
1531 int last; /* Index of last element to
1532 * select or deselect. */
1533 int select; /* 1 means select items, 0 means
1536 int i, firstRedisplay, lastRedisplay, increment, oldCount;
1544 if (first >= listPtr->numElements) {
1547 oldCount = listPtr->numSelected;
1548 firstRedisplay = -1;
1549 increment = select ? 1 : -1;
1550 for (i = 0, elPtr = listPtr->firstPtr; i < first;
1551 i++, elPtr = elPtr->nextPtr) {
1552 /* Empty loop body. */
1554 for ( ; i <= last; i++, elPtr = elPtr->nextPtr) {
1555 if (elPtr->selected == select) {
1558 listPtr->numSelected += increment;
1559 elPtr->selected = select;
1560 if (firstRedisplay < 0) {
1565 if (firstRedisplay >= 0) {
1566 ListboxRedrawRange(listPtr, first, last);
1571 *----------------------------------------------------------------------
1573 * ListboxRedrawRange --
1575 * Ensure that a given range of elements is eventually redrawn on
1576 * the display (if those elements in fact appear on the display).
1582 * Information gets redisplayed.
1584 *----------------------------------------------------------------------
1588 ListboxRedrawRange(listPtr, first, last)
1589 register Listbox *listPtr; /* Information about widget. */
1590 int first; /* Index of first element in list
1591 * that needs to be redrawn. */
1592 int last; /* Index of last element in list
1593 * that needs to be redrawn. May
1594 * be less than first;
1595 * these just bracket a range. */
1597 if ((listPtr->winPtr == NULL) || !(listPtr->winPtr->flags & CK_MAPPED)
1598 || (listPtr->flags & REDRAW_PENDING)) {
1601 Tk_DoWhenIdle(DisplayListbox, (ClientData) listPtr);
1602 listPtr->flags |= REDRAW_PENDING;
1606 *----------------------------------------------------------------------
1608 * ListboxUpdateVScrollbar --
1610 * This procedure is invoked whenever information has changed in
1611 * a listbox in a way that would invalidate a vertical scrollbar
1612 * display. If there is an associated scrollbar, then this command
1613 * updates it by invoking a Tcl command.
1619 * A Tcl command is invoked, and an additional command may be
1620 * invoked to process errors in the command.
1622 *----------------------------------------------------------------------
1626 ListboxUpdateVScrollbar(listPtr)
1627 register Listbox *listPtr; /* Information about widget. */
1633 if (listPtr->yScrollCmd == NULL) {
1636 if (listPtr->numElements == 0) {
1640 first = listPtr->topIndex/((double) listPtr->numElements);
1641 last = (listPtr->topIndex+listPtr->fullLines)
1642 /((double) listPtr->numElements);
1647 sprintf(string, " %g %g", first, last);
1648 result = Tcl_VarEval(listPtr->interp, listPtr->yScrollCmd, string,
1650 if (result != TCL_OK) {
1651 Tcl_AddErrorInfo(listPtr->interp,
1652 "\n (vertical scrolling command executed by listbox)");
1653 Tk_BackgroundError(listPtr->interp);
1658 *----------------------------------------------------------------------
1660 * ListboxUpdateHScrollbar --
1662 * This procedure is invoked whenever information has changed in
1663 * a listbox in a way that would invalidate a horizontal scrollbar
1664 * display. If there is an associated horizontal scrollbar, then
1665 * this command updates it by invoking a Tcl command.
1671 * A Tcl command is invoked, and an additional command may be
1672 * invoked to process errors in the command.
1674 *----------------------------------------------------------------------
1678 ListboxUpdateHScrollbar(listPtr)
1679 register Listbox *listPtr; /* Information about widget. */
1682 int result, windowWidth;
1685 if (listPtr->xScrollCmd == NULL) {
1688 windowWidth = listPtr->winPtr->width;
1689 if (listPtr->maxWidth == 0) {
1693 first = listPtr->xOffset/((double) listPtr->maxWidth);
1694 last = (listPtr->xOffset + windowWidth)
1695 /((double) listPtr->maxWidth);
1700 sprintf(string, " %g %g", first, last);
1701 result = Tcl_VarEval(listPtr->interp, listPtr->xScrollCmd, string,
1703 if (result != TCL_OK) {
1704 Tcl_AddErrorInfo(listPtr->interp,
1705 "\n (horizontal scrolling command executed by listbox)");
1706 Tk_BackgroundError(listPtr->interp);