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.
8 * Copyright 1990-1992 Regents of the University of California.
9 * Permission to use, copy, modify, and distribute this
10 * software and its documentation for any purpose and without
11 * fee is hereby granted, provided that the above copyright
12 * notice appear in all copies. The University of California
13 * makes no representations about the suitability of this
14 * software for any purpose. It is provided "as is" without
15 * express or implied warranty.
19 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkListbox.c,v 1.56 92/05/13 09:05:20 ouster Exp $ SPRITE (Berkeley)";
27 * One record of the following type is kept for each element
28 * associated with a listbox widget:
31 typedef struct Element
{
32 int textLength
; /* # non-NULL characters in text. */
33 int lBearing
; /* Distance from first character's
34 * origin to left edge of character. */
35 int pixelWidth
; /* Total width of element in pixels (including
36 * left bearing and right bearing). */
37 struct Element
*nextPtr
; /* Next in list of all elements of this
38 * listbox, or NULL for last element. */
39 char text
[4]; /* Characters of this element, NULL-
40 * terminated. The actual space allocated
41 * here will be as large as needed (> 4,
42 * most likely). Must be the last field
46 #define ElementSize(stringLength) \
47 ((unsigned) (sizeof(Element) - 3 + stringLength))
50 * A data structure of the following type is kept for each listbox
51 * widget managed by this file:
55 Tk_Window tkwin
; /* Window that embodies the listbox. NULL
56 * means that the window has been destroyed
57 * but the data structures haven't yet been
59 Tcl_Interp
*interp
; /* Interpreter associated with listbox. */
60 int numElements
; /* Total number of elements in this listbox. */
61 Element
*elementPtr
; /* First in list of elements (NULL if no
65 * Information used when displaying widget:
68 Tk_3DBorder normalBorder
; /* Used for drawing border around whole
69 * window, plus used for background. */
70 int borderWidth
; /* Width of 3-D border around window. */
71 int relief
; /* 3-D effect: TK_RELIEF_RAISED, etc. */
72 XFontStruct
*fontPtr
; /* Information about text font, or NULL. */
73 XColor
*fgColorPtr
; /* Text color in normal mode. */
74 GC textGC
; /* For drawing normal text. */
75 Tk_3DBorder selBorder
; /* Borders and backgrounds for selected
77 int selBorderWidth
; /* Width of border around selection. */
78 XColor
*selFgColorPtr
; /* Foreground color for selected elements. */
79 GC selTextGC
; /* For drawing selected text. */
80 char *geometry
; /* Desired geometry for window. Malloc'ed. */
81 int lineHeight
; /* Number of pixels allocated for each line
83 int topIndex
; /* Index of top-most element visible in
85 int numLines
; /* Number of lines (elements) that fit
86 * in window at one time. */
89 * Information to support horizontal scrolling:
92 int maxWidth
; /* Width (in pixels) of widest string in
94 int xScrollUnit
; /* Number of pixels in one "unit" for
95 * horizontal scrolling (window scrolls
96 * horizontally in increments of this size).
97 * This is an average character size. */
98 int xOffset
; /* The left edge of each string in the
99 * listbox is offset to the left by this
100 * many pixels (0 means no offset, positive
101 * means there is an offset). */
104 * Information about what's selected, if any.
107 int selectFirst
; /* Index of first selected element (-1 means
108 * nothing selected. */
109 int selectLast
; /* Index of last selected element. */
110 int selectAnchor
; /* Fixed end of selection (i.e. element
111 * at which selection was started.) */
112 int exportSelection
; /* Non-zero means tie internal listbox
116 * Information for scanning:
119 int scanMarkX
; /* X-position at which scan started (e.g.
120 * button was pressed here). */
121 int scanMarkY
; /* Y-position at which scan started (e.g.
122 * button was pressed here). */
123 int scanMarkXOffset
; /* Value of "xOffset" field when scan
125 int scanMarkYIndex
; /* Index of line that was at top of window
126 * when scan started. */
129 * Miscellaneous information:
132 Cursor cursor
; /* Current cursor for window, or None. */
133 char *yScrollCmd
; /* Command prefix for communicating with
134 * vertical scrollbar. NULL means no command
135 * to issue. Malloc'ed. */
136 char *xScrollCmd
; /* Command prefix for communicating with
137 * horizontal scrollbar. NULL means no command
138 * to issue. Malloc'ed. */
139 int flags
; /* Various flag bits: see below for
144 * Flag bits for buttons:
146 * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
147 * has already been queued to redraw
149 * UPDATE_V_SCROLLBAR: Non-zero means vertical scrollbar needs
151 * UPDATE_H_SCROLLBAR: Non-zero means horizontal scrollbar needs
155 #define REDRAW_PENDING 1
156 #define UPDATE_V_SCROLLBAR 2
157 #define UPDATE_H_SCROLLBAR 4
160 * Information used for argv parsing:
163 static Tk_ConfigSpec configSpecs
[] = {
164 {TK_CONFIG_BORDER
, "-background", "background", "Background",
165 DEF_LISTBOX_BG_COLOR
, Tk_Offset(Listbox
, normalBorder
),
166 TK_CONFIG_COLOR_ONLY
},
167 {TK_CONFIG_BORDER
, "-background", "background", "Background",
168 DEF_LISTBOX_BG_MONO
, Tk_Offset(Listbox
, normalBorder
),
169 TK_CONFIG_MONO_ONLY
},
170 {TK_CONFIG_SYNONYM
, "-bd", "borderWidth", (char *) NULL
,
171 (char *) NULL
, 0, 0},
172 {TK_CONFIG_SYNONYM
, "-bg", "background", (char *) NULL
,
173 (char *) NULL
, 0, 0},
174 {TK_CONFIG_PIXELS
, "-borderwidth", "borderWidth", "BorderWidth",
175 DEF_LISTBOX_BORDER_WIDTH
, Tk_Offset(Listbox
, borderWidth
), 0},
176 {TK_CONFIG_ACTIVE_CURSOR
, "-cursor", "cursor", "Cursor",
177 DEF_LISTBOX_CURSOR
, Tk_Offset(Listbox
, cursor
), TK_CONFIG_NULL_OK
},
178 {TK_CONFIG_BOOLEAN
, "-exportselection", "exportSelection",
179 "ExportSelection", DEF_LISTBOX_EXPORT_SELECTION
,
180 Tk_Offset(Listbox
, exportSelection
), 0},
181 {TK_CONFIG_SYNONYM
, "-fg", "foreground", (char *) NULL
,
182 (char *) NULL
, 0, 0},
183 {TK_CONFIG_FONT
, "-font", "font", "Font",
184 DEF_LISTBOX_FONT
, Tk_Offset(Listbox
, fontPtr
), 0},
185 {TK_CONFIG_COLOR
, "-foreground", "foreground", "Foreground",
186 DEF_LISTBOX_FG
, Tk_Offset(Listbox
, fgColorPtr
), 0},
187 {TK_CONFIG_STRING
, "-geometry", "geometry", "Geometry",
188 DEF_LISTBOX_GEOMETRY
, Tk_Offset(Listbox
, geometry
), 0},
189 {TK_CONFIG_RELIEF
, "-relief", "relief", "Relief",
190 DEF_LISTBOX_RELIEF
, Tk_Offset(Listbox
, relief
), 0},
191 {TK_CONFIG_BORDER
, "-selectbackground", "selectBackground", "Foreground",
192 DEF_LISTBOX_SELECT_COLOR
, Tk_Offset(Listbox
, selBorder
),
193 TK_CONFIG_COLOR_ONLY
},
194 {TK_CONFIG_BORDER
, "-selectbackground", "selectBackground", "Foreground",
195 DEF_LISTBOX_SELECT_MONO
, Tk_Offset(Listbox
, selBorder
),
196 TK_CONFIG_MONO_ONLY
},
197 {TK_CONFIG_PIXELS
, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
198 DEF_LISTBOX_SELECT_BD
, Tk_Offset(Listbox
, selBorderWidth
), 0},
199 {TK_CONFIG_COLOR
, "-selectforeground", "selectForeground", "Background",
200 DEF_LISTBOX_SELECT_FG_COLOR
, Tk_Offset(Listbox
, selFgColorPtr
),
201 TK_CONFIG_COLOR_ONLY
},
202 {TK_CONFIG_COLOR
, "-selectforeground", "selectForeground", "Background",
203 DEF_LISTBOX_SELECT_FG_MONO
, Tk_Offset(Listbox
, selFgColorPtr
),
204 TK_CONFIG_MONO_ONLY
},
205 {TK_CONFIG_STRING
, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
206 DEF_LISTBOX_SCROLL_COMMAND
, Tk_Offset(Listbox
, xScrollCmd
), 0},
207 {TK_CONFIG_STRING
, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
208 DEF_LISTBOX_SCROLL_COMMAND
, Tk_Offset(Listbox
, yScrollCmd
), 0},
209 {TK_CONFIG_END
, (char *) NULL
, (char *) NULL
, (char *) NULL
,
214 * Forward declarations for procedures defined later in this file:
217 static void ChangeListboxOffset
_ANSI_ARGS_((Listbox
*listPtr
,
219 static void ChangeListboxView
_ANSI_ARGS_((Listbox
*listPtr
,
221 static int ConfigureListbox
_ANSI_ARGS_((Tcl_Interp
*interp
,
222 Listbox
*listPtr
, int argc
, char **argv
,
224 static void DeleteEls
_ANSI_ARGS_((Listbox
*listPtr
, int first
,
226 static void DestroyListbox
_ANSI_ARGS_((ClientData clientData
));
227 static void DisplayListbox
_ANSI_ARGS_((ClientData clientData
));
228 static int GetListboxIndex
_ANSI_ARGS_((Tcl_Interp
*interp
,
229 Listbox
*listPtr
, char *string
, int *indexPtr
));
230 static void InsertEls
_ANSI_ARGS_((Listbox
*listPtr
, int index
,
231 int argc
, char **argv
));
232 static void ListboxComputeWidths
_ANSI_ARGS_((Listbox
*listPtr
,
234 static void ListboxEventProc
_ANSI_ARGS_((ClientData clientData
,
236 static int ListboxFetchSelection
_ANSI_ARGS_((
237 ClientData clientData
, int offset
, char *buffer
,
239 static void ListboxLostSelection
_ANSI_ARGS_((
240 ClientData clientData
));
241 static void ListboxRedrawRange
_ANSI_ARGS_((Listbox
*listPtr
,
242 int first
, int last
));
243 static void ListboxScanTo
_ANSI_ARGS_((Listbox
*listPtr
,
245 static void ListboxSelectFrom
_ANSI_ARGS_((Listbox
*listPtr
,
247 static void ListboxSelectTo
_ANSI_ARGS_((Listbox
*listPtr
,
249 static void ListboxUpdateHScrollbar
_ANSI_ARGS_((Listbox
*listPtr
));
250 static void ListboxUpdateVScrollbar
_ANSI_ARGS_((Listbox
*listPtr
));
251 static int ListboxWidgetCmd
_ANSI_ARGS_((ClientData clientData
,
252 Tcl_Interp
*interp
, int argc
, char **argv
));
253 static int NearestListboxElement
_ANSI_ARGS_((Listbox
*listPtr
,
257 *--------------------------------------------------------------
261 * This procedure is invoked to process the "listbox" Tcl
262 * command. See the user documentation for details on what
266 * A standard Tcl result.
269 * See the user documentation.
271 *--------------------------------------------------------------
275 Tk_ListboxCmd(clientData
, interp
, argc
, argv
)
276 ClientData clientData
; /* Main window associated with
278 Tcl_Interp
*interp
; /* Current interpreter. */
279 int argc
; /* Number of arguments. */
280 char **argv
; /* Argument strings. */
282 register Listbox
*listPtr
;
284 Tk_Window tkwin
= (Tk_Window
) clientData
;
287 Tcl_AppendResult(interp
, "wrong # args: should be \"",
288 argv
[0], " pathName ?options?\"", (char *) NULL
);
292 new = Tk_CreateWindowFromPath(interp
, tkwin
, argv
[1], (char *) NULL
);
298 * Initialize the fields of the structure that won't be initialized
299 * by ConfigureListbox, or that ConfigureListbox requires to be
300 * initialized already (e.g. resource pointers).
303 listPtr
= (Listbox
*) ckalloc(sizeof(Listbox
));
304 listPtr
->tkwin
= new;
305 listPtr
->interp
= interp
;
306 listPtr
->numElements
= 0;
307 listPtr
->elementPtr
= NULL
;
308 listPtr
->normalBorder
= NULL
;
309 listPtr
->fontPtr
= NULL
;
310 listPtr
->fgColorPtr
= NULL
;
311 listPtr
->textGC
= None
;
312 listPtr
->selBorder
= NULL
;
313 listPtr
->selFgColorPtr
= NULL
;
314 listPtr
->selTextGC
= NULL
;
315 listPtr
->geometry
= NULL
;
316 listPtr
->topIndex
= 0;
317 listPtr
->xOffset
= 0;
318 listPtr
->selectFirst
= -1;
319 listPtr
->selectLast
= -1;
320 listPtr
->exportSelection
= 1;
321 listPtr
->cursor
= None
;
322 listPtr
->yScrollCmd
= NULL
;
323 listPtr
->xScrollCmd
= NULL
;
326 Tk_SetClass(listPtr
->tkwin
, "Listbox");
327 Tk_CreateEventHandler(listPtr
->tkwin
, ExposureMask
|StructureNotifyMask
,
328 ListboxEventProc
, (ClientData
) listPtr
);
329 Tk_CreateSelHandler(listPtr
->tkwin
, XA_STRING
, ListboxFetchSelection
,
330 (ClientData
) listPtr
, XA_STRING
);
331 Tcl_CreateCommand(interp
, Tk_PathName(listPtr
->tkwin
), ListboxWidgetCmd
,
332 (ClientData
) listPtr
, (void (*)()) NULL
);
333 if (ConfigureListbox(interp
, listPtr
, argc
-2, argv
+2, 0) != TCL_OK
) {
337 interp
->result
= Tk_PathName(listPtr
->tkwin
);
341 Tk_DestroyWindow(listPtr
->tkwin
);
346 *--------------------------------------------------------------
348 * ListboxWidgetCmd --
350 * This procedure is invoked to process the Tcl command
351 * that corresponds to a widget managed by this module.
352 * See the user documentation for details on what it does.
355 * A standard Tcl result.
358 * See the user documentation.
360 *--------------------------------------------------------------
364 ListboxWidgetCmd(clientData
, interp
, argc
, argv
)
365 ClientData clientData
; /* Information about listbox widget. */
366 Tcl_Interp
*interp
; /* Current interpreter. */
367 int argc
; /* Number of arguments. */
368 char **argv
; /* Argument strings. */
370 register Listbox
*listPtr
= (Listbox
*) clientData
;
376 Tcl_AppendResult(interp
, "wrong # args: should be \"",
377 argv
[0], " option ?arg arg ...?\"", (char *) NULL
);
380 Tk_Preserve((ClientData
) listPtr
);
382 length
= strlen(argv
[1]);
383 if ((c
== 'c') && (strncmp(argv
[1], "configure", length
) == 0)
386 result
= Tk_ConfigureInfo(interp
, listPtr
->tkwin
, configSpecs
,
387 (char *) listPtr
, (char *) NULL
, 0);
388 } else if (argc
== 3) {
389 result
= Tk_ConfigureInfo(interp
, listPtr
->tkwin
, configSpecs
,
390 (char *) listPtr
, argv
[2], 0);
392 result
= ConfigureListbox(interp
, listPtr
, argc
-2, argv
+2,
393 TK_CONFIG_ARGV_ONLY
);
395 } else if ((c
== 'c') && (strncmp(argv
[1], "curselection", length
) == 0)
401 Tcl_AppendResult(interp
, "wrong # args: should be \"",
402 argv
[0], " curselection\"",
406 if (listPtr
->selectFirst
!= -1) {
407 for (i
= listPtr
->selectFirst
; i
<= listPtr
->selectLast
; i
++) {
408 sprintf(index
, "%d", i
);
409 Tcl_AppendElement(interp
, index
, 0);
412 } else if ((c
== 'd') && (strncmp(argv
[1], "delete", length
) == 0)) {
415 if ((argc
< 3) || (argc
> 4)) {
416 Tcl_AppendResult(interp
, "wrong # args: should be \"",
417 argv
[0], " delete firstIndex ?lastIndex?\"",
421 if (GetListboxIndex(interp
, listPtr
, argv
[2], &first
) != TCL_OK
) {
427 if (GetListboxIndex(interp
, listPtr
, argv
[3], &last
) != TCL_OK
) {
431 DeleteEls(listPtr
, first
, last
);
432 } else if ((c
== 'g') && (strncmp(argv
[1], "get", length
) == 0)) {
434 register Element
*elPtr
;
437 Tcl_AppendResult(interp
, "wrong # args: should be \"",
438 argv
[0], " get index\"", (char *) NULL
);
441 if (GetListboxIndex(interp
, listPtr
, argv
[2], &index
) != TCL_OK
) {
447 if (index
>= listPtr
->numElements
) {
448 index
= listPtr
->numElements
-1;
450 for (elPtr
= listPtr
->elementPtr
; index
> 0;
451 index
--, elPtr
= elPtr
->nextPtr
) {
452 /* Empty loop body. */
455 interp
->result
= elPtr
->text
;
457 } else if ((c
== 'i') && (strncmp(argv
[1], "insert", length
) == 0)) {
461 Tcl_AppendResult(interp
, "wrong # args: should be \"",
462 argv
[0], " insert index ?element? ?element ...?\"",
467 if (GetListboxIndex(interp
, listPtr
, argv
[2], &index
) != TCL_OK
) {
470 InsertEls(listPtr
, index
, argc
-3, argv
+3);
472 } else if ((c
== 'n') && (strncmp(argv
[1], "nearest", length
) == 0)) {
476 Tcl_AppendResult(interp
, "wrong # args: should be \"",
477 argv
[0], " nearest y\"", (char *) NULL
);
480 if (Tcl_GetInt(interp
, argv
[2], &y
) != TCL_OK
) {
483 index
= NearestListboxElement(listPtr
, y
);
484 sprintf(interp
->result
, "%d", index
);
485 } else if ((c
== 's') && (length
>= 2)
486 && (strncmp(argv
[1], "scan", length
) == 0)) {
490 Tcl_AppendResult(interp
, "wrong # args: should be \"",
491 argv
[0], " scan mark|dragto x y\"", (char *) NULL
);
494 if ((Tcl_GetInt(interp
, argv
[3], &x
) != TCL_OK
)
495 || (Tcl_GetInt(interp
, argv
[4], &y
) != TCL_OK
)) {
498 if ((argv
[2][0] == 'm')
499 && (strncmp(argv
[2], "mark", strlen(argv
[2])) == 0)) {
500 listPtr
->scanMarkX
= x
;
501 listPtr
->scanMarkY
= y
;
502 listPtr
->scanMarkXOffset
= listPtr
->xOffset
;
503 listPtr
->scanMarkYIndex
= listPtr
->topIndex
;
504 } else if ((argv
[2][0] == 'd')
505 && (strncmp(argv
[2], "dragto", strlen(argv
[2])) == 0)) {
506 ListboxScanTo(listPtr
, x
, y
);
508 Tcl_AppendResult(interp
, "bad scan option \"", argv
[2],
509 "\": must be mark or dragto", (char *) NULL
);
512 } else if ((c
== 's') && (length
>= 2)
513 && (strncmp(argv
[1], "select", length
) == 0)) {
517 Tcl_AppendResult(interp
, "too few args: should be \"",
518 argv
[0], " select option ?index?\"", (char *) NULL
);
521 length
= strlen(argv
[2]);
523 if ((c
== 'c') && (argv
[2] != NULL
)
524 && (strncmp(argv
[2], "clear", length
) == 0)) {
526 Tcl_AppendResult(interp
, "wrong # args: should be \"",
527 argv
[0], " select clear\"", (char *) NULL
);
530 if (listPtr
->selectFirst
!= -1) {
531 ListboxRedrawRange(listPtr
, listPtr
->selectFirst
,
532 listPtr
->selectLast
);
533 listPtr
->selectFirst
= -1;
538 Tcl_AppendResult(interp
, "wrong # args: should be \"",
539 argv
[0], " select option index\"", (char *) NULL
);
542 if (GetListboxIndex(interp
, listPtr
, argv
[3], &index
) != TCL_OK
) {
545 if ((c
== 'a') && (strncmp(argv
[2], "adjust", length
) == 0)) {
546 if (index
< (listPtr
->selectFirst
+ listPtr
->selectLast
)/2) {
547 listPtr
->selectAnchor
= listPtr
->selectLast
;
549 listPtr
->selectAnchor
= listPtr
->selectFirst
;
551 ListboxSelectTo(listPtr
, index
);
552 } else if ((c
== 'f') && (strncmp(argv
[2], "from", length
) == 0)) {
553 ListboxSelectFrom(listPtr
, index
);
554 } else if ((c
== 't') && (strncmp(argv
[2], "to", length
) == 0)) {
555 ListboxSelectTo(listPtr
, index
);
557 Tcl_AppendResult(interp
, "bad select option \"", argv
[2],
558 "\": must be adjust, clear, from, or to", (char *) NULL
);
561 } else if ((c
== 's') && (length
>= 2)
562 && (strncmp(argv
[1], "size", length
) == 0)) {
563 sprintf(interp
->result
, "%d", listPtr
->numElements
);
564 } else if ((c
== 'x') && (strncmp(argv
[1], "xview", length
) == 0)) {
568 Tcl_AppendResult(interp
, "wrong # args: should be \"",
569 argv
[0], " xview index\"", (char *) NULL
);
572 if (Tcl_GetInt(interp
, argv
[2], &index
) != TCL_OK
) {
575 ChangeListboxOffset(listPtr
, index
*listPtr
->xScrollUnit
);
576 } else if ((c
== 'y') && (strncmp(argv
[1], "yview", length
) == 0)) {
580 Tcl_AppendResult(interp
, "wrong # args: should be \"",
581 argv
[0], " yview index\"", (char *) NULL
);
584 if (GetListboxIndex(interp
, listPtr
, argv
[2], &index
) != TCL_OK
) {
587 ChangeListboxView(listPtr
, index
);
589 Tcl_AppendResult(interp
, "bad option \"", argv
[1],
590 "\": must be configure, curselection, delete, get, ",
591 "insert, nearest, scan, select, size, ",
592 "xview, or yview", (char *) NULL
);
596 Tk_Release((ClientData
) listPtr
);
600 Tk_Release((ClientData
) listPtr
);
605 *----------------------------------------------------------------------
609 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
610 * to clean up the internal structure of a listbox at a safe time
611 * (when no-one is using it anymore).
617 * Everything associated with the listbox is freed up.
619 *----------------------------------------------------------------------
623 DestroyListbox(clientData
)
624 ClientData clientData
; /* Info about listbox widget. */
626 register Listbox
*listPtr
= (Listbox
*) clientData
;
627 register Element
*elPtr
, *nextPtr
;
629 for (elPtr
= listPtr
->elementPtr
; elPtr
!= NULL
; ) {
630 nextPtr
= elPtr
->nextPtr
;
631 ckfree((char *) elPtr
);
634 if (listPtr
->normalBorder
!= NULL
) {
635 Tk_Free3DBorder(listPtr
->normalBorder
);
637 if (listPtr
->fontPtr
!= NULL
) {
638 Tk_FreeFontStruct(listPtr
->fontPtr
);
640 if (listPtr
->fgColorPtr
!= NULL
) {
641 Tk_FreeColor(listPtr
->fgColorPtr
);
643 if (listPtr
->textGC
!= None
) {
644 Tk_FreeGC(listPtr
->textGC
);
646 if (listPtr
->selBorder
!= NULL
) {
647 Tk_Free3DBorder(listPtr
->selBorder
);
649 if (listPtr
->selFgColorPtr
!= NULL
) {
650 Tk_FreeColor(listPtr
->selFgColorPtr
);
652 if (listPtr
->selTextGC
!= None
) {
653 Tk_FreeGC(listPtr
->selTextGC
);
655 if (listPtr
->geometry
!= NULL
) {
656 ckfree(listPtr
->geometry
);
658 if (listPtr
->cursor
!= None
) {
659 Tk_FreeCursor(listPtr
->cursor
);
661 if (listPtr
->yScrollCmd
!= NULL
) {
662 ckfree(listPtr
->yScrollCmd
);
664 if (listPtr
->xScrollCmd
!= NULL
) {
665 ckfree(listPtr
->xScrollCmd
);
667 ckfree((char *) listPtr
);
671 *----------------------------------------------------------------------
673 * ConfigureListbox --
675 * This procedure is called to process an argv/argc list, plus
676 * the Tk option database, in order to configure (or reconfigure)
680 * The return value is a standard Tcl result. If TCL_ERROR is
681 * returned, then interp->result contains an error message.
684 * Configuration information, such as colors, border width,
685 * etc. get set for listPtr; old resources get freed,
688 *----------------------------------------------------------------------
692 ConfigureListbox(interp
, listPtr
, argc
, argv
, flags
)
693 Tcl_Interp
*interp
; /* Used for error reporting. */
694 register Listbox
*listPtr
; /* Information about widget; may or may
695 * not already have values for some fields. */
696 int argc
; /* Number of valid entries in argv. */
697 char **argv
; /* Arguments. */
698 int flags
; /* Flags to pass to Tk_ConfigureWidget. */
702 int width
, height
, fontHeight
, oldExport
;
704 oldExport
= listPtr
->exportSelection
;
705 if (Tk_ConfigureWidget(interp
, listPtr
->tkwin
, configSpecs
,
706 argc
, argv
, (char *) listPtr
, flags
) != TCL_OK
) {
711 * A few options need special processing, such as parsing the
712 * geometry and setting the background from a 3-D border.
715 Tk_SetBackgroundFromBorder(listPtr
->tkwin
, listPtr
->normalBorder
);
717 gcValues
.foreground
= listPtr
->fgColorPtr
->pixel
;
718 gcValues
.font
= listPtr
->fontPtr
->fid
;
719 gcValues
.graphics_exposures
= False
;
720 new = Tk_GetGC(listPtr
->tkwin
, GCForeground
|GCFont
|GCGraphicsExposures
,
722 if (listPtr
->textGC
!= None
) {
723 Tk_FreeGC(listPtr
->textGC
);
725 listPtr
->textGC
= new;
727 gcValues
.foreground
= listPtr
->selFgColorPtr
->pixel
;
728 gcValues
.font
= listPtr
->fontPtr
->fid
;
729 new = Tk_GetGC(listPtr
->tkwin
, GCForeground
|GCFont
, &gcValues
);
730 if (listPtr
->selTextGC
!= None
) {
731 Tk_FreeGC(listPtr
->selTextGC
);
733 listPtr
->selTextGC
= new;
736 * Claim the selection if we've suddenly started exporting it.
739 if (listPtr
->exportSelection
&& (!oldExport
)
740 && (listPtr
->selectFirst
!=-1)) {
741 Tk_OwnSelection(listPtr
->tkwin
, ListboxLostSelection
,
742 (ClientData
) listPtr
);
746 * Register the desired geometry for the window, and arrange for
747 * the window to be redisplayed.
750 if ((sscanf(listPtr
->geometry
, "%dx%d", &width
, &height
) != 2)
751 || (width
<= 0) || (height
<= 0)) {
752 Tcl_AppendResult(interp
, "bad geometry \"",
753 listPtr
->geometry
, "\"", (char *) NULL
);
756 fontHeight
= listPtr
->fontPtr
->ascent
+ listPtr
->fontPtr
->descent
;
757 listPtr
->lineHeight
= fontHeight
+ 1 + 2*listPtr
->selBorderWidth
;
758 listPtr
->numLines
= (Tk_Height(listPtr
->tkwin
) - 2*listPtr
->borderWidth
)
759 / listPtr
->lineHeight
;
760 if (listPtr
->numLines
< 0) {
761 listPtr
->numLines
= 0;
763 ListboxComputeWidths(listPtr
, 1);
764 width
= (width
+1)*listPtr
->xScrollUnit
+ 2*listPtr
->borderWidth
765 + 2*listPtr
->selBorderWidth
;
766 height
= height
*listPtr
->lineHeight
+ 2*listPtr
->borderWidth
;
767 Tk_GeometryRequest(listPtr
->tkwin
, width
, height
);
768 Tk_SetInternalBorder(listPtr
->tkwin
, listPtr
->borderWidth
);
769 listPtr
->flags
|= UPDATE_V_SCROLLBAR
|UPDATE_H_SCROLLBAR
;
770 ListboxRedrawRange(listPtr
, 0, listPtr
->numElements
-1);
775 *--------------------------------------------------------------
779 * This procedure redraws the contents of a listbox window.
785 * Information appears on the screen.
787 *--------------------------------------------------------------
791 DisplayListbox(clientData
)
792 ClientData clientData
; /* Information about window. */
794 register Listbox
*listPtr
= (Listbox
*) clientData
;
795 register Tk_Window tkwin
= listPtr
->tkwin
;
796 register Element
*elPtr
;
798 int i
, limit
, x
, y
, margin
;
801 listPtr
->flags
&= ~REDRAW_PENDING
;
802 if (listPtr
->flags
& UPDATE_V_SCROLLBAR
) {
803 ListboxUpdateVScrollbar(listPtr
);
805 if (listPtr
->flags
& UPDATE_H_SCROLLBAR
) {
806 ListboxUpdateHScrollbar(listPtr
);
808 listPtr
->flags
&= ~(REDRAW_PENDING
|UPDATE_V_SCROLLBAR
|UPDATE_H_SCROLLBAR
);
809 if ((listPtr
->tkwin
== NULL
) || !Tk_IsMapped(tkwin
)) {
814 * Redrawing is done in a temporary pixmap that is allocated
815 * here and freed at the end of the procedure. All drawing is
816 * done to the pixmap, and the pixmap is copied to the screen
817 * at the end of the procedure. This provides the smoothest
818 * possible visual effects (no flashing on the screen).
821 pixmap
= XCreatePixmap(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
822 Tk_Width(tkwin
), Tk_Height(tkwin
),
823 Tk_DefaultDepth(Tk_Screen(tkwin
)));
824 Tk_Fill3DRectangle(Tk_Display(tkwin
), pixmap
, listPtr
->normalBorder
,
825 0, 0, Tk_Width(tkwin
), Tk_Height(tkwin
), listPtr
->borderWidth
,
829 * Iterate through all of the elements of the listbox, displaying each
830 * in turn. Selected elements use a different GC and have a raised
834 limit
= listPtr
->topIndex
+ listPtr
->numLines
- 1;
835 if (limit
>= listPtr
->numElements
) {
836 limit
= listPtr
->numElements
-1;
838 margin
= listPtr
->selBorderWidth
+ listPtr
->xScrollUnit
/2;
839 for (elPtr
= listPtr
->elementPtr
, i
= 0; (elPtr
!= NULL
) && (i
<= limit
);
840 elPtr
= elPtr
->nextPtr
, i
++) {
841 if (i
< listPtr
->topIndex
) {
844 x
= listPtr
->borderWidth
;
845 y
= ((i
- listPtr
->topIndex
) * listPtr
->lineHeight
)
846 + listPtr
->borderWidth
;
847 gc
= listPtr
->textGC
;
848 if ((listPtr
->selectFirst
>= 0) && (i
>= listPtr
->selectFirst
)
849 && (i
<= listPtr
->selectLast
)) {
850 gc
= listPtr
->selTextGC
;
851 Tk_Fill3DRectangle(Tk_Display(tkwin
), pixmap
,
852 listPtr
->selBorder
, x
, y
,
853 Tk_Width(tkwin
) - 2*listPtr
->borderWidth
,
854 listPtr
->lineHeight
, listPtr
->selBorderWidth
,
857 y
+= listPtr
->fontPtr
->ascent
+ listPtr
->selBorderWidth
;
858 x
+= margin
- elPtr
->lBearing
- listPtr
->xOffset
;
859 XDrawString(Tk_Display(tkwin
), pixmap
, gc
, x
, y
,
860 elPtr
->text
, elPtr
->textLength
);
864 * Redraw the border for the listbox to make sure that it's on top
865 * of any of the text of the listbox entries.
868 Tk_Draw3DRectangle(Tk_Display(tkwin
), pixmap
,
869 listPtr
->normalBorder
, 0, 0, Tk_Width(tkwin
),
870 Tk_Height(tkwin
), listPtr
->borderWidth
,
872 XCopyArea(Tk_Display(tkwin
), pixmap
, Tk_WindowId(tkwin
),
873 listPtr
->textGC
, 0, 0, Tk_Width(tkwin
), Tk_Height(tkwin
),
875 XFreePixmap(Tk_Display(tkwin
), pixmap
);
879 *----------------------------------------------------------------------
883 * Add new elements to a listbox widget.
889 * New information gets added to listPtr; it will be redisplayed
890 * soon, but not immediately.
892 *----------------------------------------------------------------------
896 InsertEls(listPtr
, index
, argc
, argv
)
897 register Listbox
*listPtr
; /* Listbox that is to get the new
899 int index
; /* Add the new elements before this
901 int argc
; /* Number of new elements to add. */
902 char **argv
; /* New elements (one per entry). */
904 register Element
*prevPtr
, *newPtr
;
905 int length
, dummy
, i
, oldMaxWidth
;
909 * Find the element before which the new ones will be inserted.
915 if (index
> listPtr
->numElements
) {
916 index
= listPtr
->numElements
;
921 for (prevPtr
= listPtr
->elementPtr
, i
= index
- 1; i
> 0; i
--) {
922 prevPtr
= prevPtr
->nextPtr
;
927 * For each new element, create a record, initialize it, and link
928 * it into the list of elements.
931 oldMaxWidth
= listPtr
->maxWidth
;
932 for (i
= argc
; i
> 0; i
--, argv
++, prevPtr
= newPtr
) {
933 length
= strlen(*argv
);
934 newPtr
= (Element
*) ckalloc(ElementSize(length
));
935 newPtr
->textLength
= length
;
936 strcpy(newPtr
->text
, *argv
);
937 XTextExtents(listPtr
->fontPtr
, newPtr
->text
, newPtr
->textLength
,
938 &dummy
, &dummy
, &dummy
, &bbox
);
939 newPtr
->lBearing
= bbox
.lbearing
;
940 newPtr
->pixelWidth
= bbox
.lbearing
+ bbox
.rbearing
;
941 if (newPtr
->pixelWidth
> listPtr
->maxWidth
) {
942 listPtr
->maxWidth
= newPtr
->pixelWidth
;
944 if (prevPtr
== NULL
) {
945 newPtr
->nextPtr
= listPtr
->elementPtr
;
946 listPtr
->elementPtr
= newPtr
;
948 newPtr
->nextPtr
= prevPtr
->nextPtr
;
949 prevPtr
->nextPtr
= newPtr
;
952 listPtr
->numElements
+= argc
;
955 * Update the selection to account for the renumbering that has just
956 * occurred. Then arrange for the new information to be displayed.
959 if (index
<= listPtr
->selectFirst
) {
960 listPtr
->selectFirst
+= argc
;
962 if (index
<= listPtr
->selectLast
) {
963 listPtr
->selectLast
+= argc
;
965 listPtr
->flags
|= UPDATE_V_SCROLLBAR
;
966 if (listPtr
->maxWidth
!= oldMaxWidth
) {
967 listPtr
->flags
|= UPDATE_H_SCROLLBAR
;
969 ListboxRedrawRange(listPtr
, index
, listPtr
->numElements
-1);
973 *----------------------------------------------------------------------
977 * Remove one or more elements from a listbox widget.
983 * Memory gets freed, the listbox gets modified and (eventually)
986 *----------------------------------------------------------------------
990 DeleteEls(listPtr
, first
, last
)
991 register Listbox
*listPtr
; /* Listbox widget to modify. */
992 int first
; /* Index of first element to delete. */
993 int last
; /* Index of last element to delete. */
995 register Element
*prevPtr
, *elPtr
;
996 int count
, i
, widthChanged
;
999 * Adjust the range to fit within the existing elements of the
1000 * listbox, and make sure there's something to delete.
1006 if (last
>= listPtr
->numElements
) {
1007 last
= listPtr
->numElements
-1;
1009 count
= last
+ 1 - first
;
1015 * Find the element just before the ones to delete.
1021 for (i
= first
-1, prevPtr
= listPtr
->elementPtr
; i
> 0; i
--) {
1022 prevPtr
= prevPtr
->nextPtr
;
1027 * Delete the requested number of elements.
1031 for (i
= count
; i
> 0; i
--) {
1032 if (prevPtr
== NULL
) {
1033 elPtr
= listPtr
->elementPtr
;
1034 listPtr
->elementPtr
= elPtr
->nextPtr
;
1036 elPtr
= prevPtr
->nextPtr
;
1037 prevPtr
->nextPtr
= elPtr
->nextPtr
;
1039 if (elPtr
->pixelWidth
== listPtr
->maxWidth
) {
1042 ckfree((char *) elPtr
);
1044 listPtr
->numElements
-= count
;
1047 * Update the selection and viewing information to reflect the change
1048 * in the element numbering, and redisplay to slide information up over
1049 * the elements that were deleted.
1052 if (first
<= listPtr
->selectFirst
) {
1053 listPtr
->selectFirst
-= count
;
1054 if (listPtr
->selectFirst
< first
) {
1055 listPtr
->selectFirst
= first
;
1058 if (first
<= listPtr
->selectLast
) {
1059 listPtr
->selectLast
-= count
;
1060 if (listPtr
->selectLast
< first
) {
1061 listPtr
->selectLast
= first
-1;
1064 if (listPtr
->selectLast
< listPtr
->selectFirst
) {
1065 listPtr
->selectFirst
= -1;
1067 if (first
<= listPtr
->topIndex
) {
1068 listPtr
->topIndex
-= count
;
1069 if (listPtr
->topIndex
< first
) {
1070 listPtr
->topIndex
= first
;
1073 listPtr
->flags
|= UPDATE_V_SCROLLBAR
;
1075 ListboxComputeWidths(listPtr
, 0);
1076 listPtr
->flags
|= UPDATE_H_SCROLLBAR
;
1078 ListboxRedrawRange(listPtr
, first
, listPtr
->numElements
-1);
1082 *--------------------------------------------------------------
1084 * ListboxEventProc --
1086 * This procedure is invoked by the Tk dispatcher for various
1087 * events on listboxes.
1093 * When the window gets deleted, internal structures get
1094 * cleaned up. When it gets exposed, it is redisplayed.
1096 *--------------------------------------------------------------
1100 ListboxEventProc(clientData
, eventPtr
)
1101 ClientData clientData
; /* Information about window. */
1102 XEvent
*eventPtr
; /* Information about event. */
1104 Listbox
*listPtr
= (Listbox
*) clientData
;
1106 if (eventPtr
->type
== Expose
) {
1107 ListboxRedrawRange(listPtr
,
1108 NearestListboxElement(listPtr
, eventPtr
->xexpose
.y
),
1109 NearestListboxElement(listPtr
, eventPtr
->xexpose
.y
1110 + eventPtr
->xexpose
.height
));
1111 } else if (eventPtr
->type
== DestroyNotify
) {
1112 Tcl_DeleteCommand(listPtr
->interp
, Tk_PathName(listPtr
->tkwin
));
1113 listPtr
->tkwin
= NULL
;
1114 if (listPtr
->flags
& REDRAW_PENDING
) {
1115 Tk_CancelIdleCall(DisplayListbox
, (ClientData
) listPtr
);
1117 Tk_EventuallyFree((ClientData
) listPtr
, DestroyListbox
);
1118 } else if (eventPtr
->type
== ConfigureNotify
) {
1119 Tk_Preserve((ClientData
) listPtr
);
1120 listPtr
->numLines
= (Tk_Height(listPtr
->tkwin
)
1121 - 2*listPtr
->borderWidth
) / listPtr
->lineHeight
;
1122 listPtr
->flags
|= UPDATE_V_SCROLLBAR
|UPDATE_H_SCROLLBAR
;
1123 ListboxRedrawRange(listPtr
, 0, listPtr
->numElements
-1);
1124 Tk_Release((ClientData
) listPtr
);
1129 *--------------------------------------------------------------
1131 * GetListboxIndex --
1133 * Parse an index into a listbox and return either its value
1137 * A standard Tcl result. If all went well, then *indexPtr is
1138 * filled in with the index (into listPtr) corresponding to
1139 * string. Otherwise an error message is left in interp->result.
1144 *--------------------------------------------------------------
1148 GetListboxIndex(interp
, listPtr
, string
, indexPtr
)
1149 Tcl_Interp
*interp
; /* For error messages. */
1150 Listbox
*listPtr
; /* Listbox for which the index is being
1152 char *string
; /* Numerical index into listPtr's element
1153 * list, or "end" to refer to last element. */
1154 int *indexPtr
; /* Where to store converted index. */
1156 if (string
[0] == 'e') {
1157 if (strncmp(string
, "end", strlen(string
)) != 0) {
1159 Tcl_AppendResult(interp
, "bad listbox index \"", string
,
1160 "\"", (char *) NULL
);
1163 *indexPtr
= listPtr
->numElements
;
1164 if (listPtr
->numElements
<= 0) {
1168 if (Tcl_GetInt(interp
, string
, indexPtr
) != TCL_OK
) {
1169 Tcl_ResetResult(interp
);
1177 *----------------------------------------------------------------------
1179 * ChangeListboxView --
1181 * Change the view on a listbox widget.
1187 * What's displayed on the screen is changed. If there is a
1188 * scrollbar associated with this widget, then the scrollbar
1189 * is instructed to change its display too.
1191 *----------------------------------------------------------------------
1195 ChangeListboxView(listPtr
, index
)
1196 register Listbox
*listPtr
; /* Information about widget. */
1197 int index
; /* Index of element in listPtr. */
1199 if (listPtr
->tkwin
== NULL
) {
1203 if (index
>= listPtr
->numElements
) {
1204 index
= listPtr
->numElements
-1;
1209 if (listPtr
->topIndex
!= index
) {
1210 if (!(listPtr
->flags
& REDRAW_PENDING
)) {
1211 Tk_DoWhenIdle(DisplayListbox
, (ClientData
) listPtr
);
1212 listPtr
->flags
|= REDRAW_PENDING
;
1214 listPtr
->topIndex
= index
;
1215 ListboxUpdateVScrollbar(listPtr
);
1220 *----------------------------------------------------------------------
1222 * ChangListboxOffset --
1224 * Change the horizontal offset for a listbox.
1230 * The listbox may be redrawn to reflect its new horizontal
1233 *----------------------------------------------------------------------
1237 ChangeListboxOffset(listPtr
, offset
)
1238 register Listbox
*listPtr
; /* Information about widget. */
1239 int offset
; /* Desired new "xOffset" for
1244 if (listPtr
->tkwin
== NULL
) {
1249 * Make sure that the new offset is within the allowable range, and
1250 * round it off to an even multiple of xScrollUnit.
1253 maxOffset
= listPtr
->maxWidth
+ (listPtr
->xScrollUnit
-1)
1254 - (Tk_Width(listPtr
->tkwin
) - 2*listPtr
->borderWidth
1255 - 2*listPtr
->selBorderWidth
- listPtr
->xScrollUnit
);
1256 if (offset
> maxOffset
) {
1262 offset
-= offset
%listPtr
->xScrollUnit
;
1263 if (offset
!= listPtr
->xOffset
) {
1264 listPtr
->xOffset
= offset
;
1265 listPtr
->flags
|= UPDATE_H_SCROLLBAR
;
1266 ListboxRedrawRange(listPtr
, 0, listPtr
->numElements
);
1271 *----------------------------------------------------------------------
1275 * Given a point (presumably of the curent mouse location)
1276 * drag the view in the window to implement the scan operation.
1282 * The view in the window may change.
1284 *----------------------------------------------------------------------
1288 ListboxScanTo(listPtr
, x
, y
)
1289 register Listbox
*listPtr
; /* Information about widget. */
1290 int x
; /* X-coordinate to use for scan
1292 int y
; /* Y-coordinate to use for scan
1295 int newTopIndex
, newOffset
;
1298 * Compute new top line for screen by amplifying the difference
1299 * between the current position and the place where the scan
1300 * started (the "mark" position). If we run off the top or bottom
1301 * of the list, then reset the mark point so that the current
1302 * position continues to correspond to the edge of the window.
1303 * This means that the picture will start dragging as soon as the
1304 * mouse reverses direction (without this reset, might have to slide
1305 * mouse a long ways back before the picture starts moving again).
1308 newTopIndex
= listPtr
->scanMarkYIndex
1309 - (10*(y
- listPtr
->scanMarkY
))/listPtr
->lineHeight
;
1310 if (newTopIndex
>= listPtr
->numElements
) {
1311 newTopIndex
= listPtr
->scanMarkYIndex
= listPtr
->numElements
-1;
1312 listPtr
->scanMarkY
= y
;
1313 } else if (newTopIndex
< 0) {
1314 newTopIndex
= listPtr
->scanMarkYIndex
= 0;
1315 listPtr
->scanMarkY
= y
;
1317 ChangeListboxView(listPtr
, newTopIndex
);
1320 * Compute new left edge for display in a similar fashion by amplifying
1321 * the difference between the current position and the place where the
1325 newOffset
= listPtr
->scanMarkXOffset
- (10*(x
- listPtr
->scanMarkX
));
1326 if (newOffset
>= listPtr
->maxWidth
) {
1327 newOffset
= listPtr
->scanMarkXOffset
= listPtr
->maxWidth
;
1328 listPtr
->scanMarkX
= x
;
1329 } else if (newOffset
< 0) {
1330 newOffset
= listPtr
->scanMarkXOffset
= 0;
1331 listPtr
->scanMarkX
= x
;
1333 ChangeListboxOffset(listPtr
, newOffset
);
1337 *----------------------------------------------------------------------
1339 * NearestListboxElement --
1341 * Given a y-coordinate inside a listbox, compute the index of
1342 * the element under that y-coordinate (or closest to that
1346 * The return value is an index of an element of listPtr. If
1347 * listPtr has no elements, then 0 is always returned.
1352 *----------------------------------------------------------------------
1356 NearestListboxElement(listPtr
, y
)
1357 register Listbox
*listPtr
; /* Information about widget. */
1358 int y
; /* Y-coordinate in listPtr's window. */
1362 index
= (y
- listPtr
->borderWidth
)/listPtr
->lineHeight
;
1363 if (index
>= listPtr
->numLines
) {
1364 index
= listPtr
->numLines
-1;
1369 index
+= listPtr
->topIndex
;
1370 if (index
>= listPtr
->numElements
) {
1371 index
= listPtr
->numElements
-1;
1377 *----------------------------------------------------------------------
1379 * ListboxSelectFrom --
1381 * Start a new selection in a listbox.
1387 * ListPtr claims the selection, and the selection becomes the
1388 * single element given by index.
1390 *----------------------------------------------------------------------
1394 ListboxSelectFrom(listPtr
, index
)
1395 register Listbox
*listPtr
; /* Information about widget. */
1396 int index
; /* Index of element that is to
1397 * become the new selection. */
1400 * Make sure the index is within the proper range for the listbox.
1406 if (index
>= listPtr
->numElements
) {
1407 index
= listPtr
->numElements
-1;
1410 if (listPtr
->selectFirst
!= -1) {
1411 ListboxRedrawRange(listPtr
, listPtr
->selectFirst
, listPtr
->selectLast
);
1412 } else if (listPtr
->exportSelection
) {
1413 Tk_OwnSelection(listPtr
->tkwin
, ListboxLostSelection
,
1414 (ClientData
) listPtr
);
1417 listPtr
->selectFirst
= listPtr
->selectLast
= index
;
1418 listPtr
->selectAnchor
= index
;
1419 ListboxRedrawRange(listPtr
, index
, index
);
1423 *----------------------------------------------------------------------
1425 * ListboxSelectTo --
1427 * Modify the selection by moving its un-anchored end. This could
1428 * make the selection either larger or smaller.
1434 * The selection changes.
1436 *----------------------------------------------------------------------
1440 ListboxSelectTo(listPtr
, index
)
1441 register Listbox
*listPtr
; /* Information about widget. */
1442 int index
; /* Index of element that is to
1443 * become the "other" end of the
1446 int newFirst
, newLast
;
1449 * Make sure the index is within the proper range for the listbox.
1455 if (index
>= listPtr
->numElements
) {
1456 index
= listPtr
->numElements
-1;
1460 * We should already own the selection, but grab it if we don't.
1463 if (listPtr
->selectFirst
== -1) {
1464 ListboxSelectFrom(listPtr
, index
);
1467 if (listPtr
->selectAnchor
< index
) {
1468 newFirst
= listPtr
->selectAnchor
;
1472 newLast
= listPtr
->selectAnchor
;
1474 if ((listPtr
->selectFirst
== newFirst
)
1475 && (listPtr
->selectLast
== newLast
)) {
1478 if (listPtr
->selectFirst
!= newFirst
) {
1479 if (listPtr
->selectFirst
< newFirst
) {
1480 ListboxRedrawRange(listPtr
, listPtr
->selectFirst
, newFirst
-1);
1482 ListboxRedrawRange(listPtr
, newFirst
, listPtr
->selectFirst
-1);
1484 listPtr
->selectFirst
= newFirst
;
1486 if (listPtr
->selectLast
!= newLast
) {
1487 if (listPtr
->selectLast
< newLast
) {
1488 ListboxRedrawRange(listPtr
, listPtr
->selectLast
+1, newLast
);
1490 ListboxRedrawRange(listPtr
, newLast
+1, listPtr
->selectLast
);
1492 listPtr
->selectLast
= newLast
;
1497 *----------------------------------------------------------------------
1499 * ListboxFetchSelection --
1501 * This procedure is called back by Tk when the selection is
1502 * requested by someone. It returns part or all of the selection
1503 * in a buffer provided by the caller.
1506 * The return value is the number of non-NULL bytes stored
1507 * at buffer. Buffer is filled (or partially filled) with a
1508 * NULL-terminated string containing part or all of the selection,
1509 * as given by offset and maxBytes. The selection is returned
1510 * as a Tcl list with one list element for each element in the
1516 *----------------------------------------------------------------------
1520 ListboxFetchSelection(clientData
, offset
, buffer
, maxBytes
)
1521 ClientData clientData
; /* Information about listbox widget. */
1522 int offset
; /* Offset within selection of first
1523 * byte to be returned. */
1524 char *buffer
; /* Location in which to place
1526 int maxBytes
; /* Maximum number of bytes to place
1527 * at buffer, not including terminating
1528 * NULL character. */
1530 register Listbox
*listPtr
= (Listbox
*) clientData
;
1531 register Element
*elPtr
;
1532 char **argv
, *selection
;
1533 int src
, dst
, length
, count
, argc
;
1535 if ((listPtr
->selectFirst
== -1) || !listPtr
->exportSelection
) {
1540 * Use Tcl_Merge to format the listbox elements into a suitable
1544 argc
= listPtr
->selectLast
- listPtr
->selectFirst
+ 1;
1545 argv
= (char **) ckalloc((unsigned) (argc
*sizeof(char *)));
1546 for (src
= 0, dst
= 0, elPtr
= listPtr
->elementPtr
; ;
1547 src
++, elPtr
= elPtr
->nextPtr
) {
1548 if (src
< listPtr
->selectFirst
) {
1551 if (src
> listPtr
->selectLast
) {
1554 argv
[dst
] = elPtr
->text
;
1557 selection
= Tcl_Merge(argc
, argv
);
1560 * Copy the requested portion of the selection to the buffer.
1563 length
= strlen(selection
);
1564 count
= length
- offset
;
1569 if (count
> maxBytes
) {
1572 memcpy((VOID
*) buffer
, (VOID
*) (selection
+ offset
), count
);
1575 buffer
[count
] = '\0';
1577 ckfree((char *) argv
);
1582 *----------------------------------------------------------------------
1584 * ListboxLostSelection --
1586 * This procedure is called back by Tk when the selection is
1587 * grabbed away from a listbox widget.
1593 * The existing selection is unhighlighted, and the window is
1594 * marked as not containing a selection.
1596 *----------------------------------------------------------------------
1600 ListboxLostSelection(clientData
)
1601 ClientData clientData
; /* Information about listbox widget. */
1603 register Listbox
*listPtr
= (Listbox
*) clientData
;
1605 if ((listPtr
->selectFirst
>= 0) && listPtr
->exportSelection
) {
1606 ListboxRedrawRange(listPtr
, listPtr
->selectFirst
, listPtr
->selectLast
);
1607 listPtr
->selectFirst
= -1;
1612 *----------------------------------------------------------------------
1614 * ListboxRedrawRange --
1616 * Ensure that a given range of elements is eventually redrawn on
1617 * the display (if those elements in fact appear on the display).
1623 * Information gets redisplayed.
1625 *----------------------------------------------------------------------
1630 ListboxRedrawRange(listPtr
, first
, last
)
1631 register Listbox
*listPtr
; /* Information about widget. */
1632 int first
; /* Index of first element in list
1633 * that needs to be redrawn. */
1634 int last
; /* Index of last element in list
1635 * that needs to be redrawn. May
1636 * be less than first;
1637 * these just bracket a range. */
1639 if ((listPtr
->tkwin
== NULL
) || !Tk_IsMapped(listPtr
->tkwin
)
1640 || (listPtr
->flags
& REDRAW_PENDING
)) {
1643 Tk_DoWhenIdle(DisplayListbox
, (ClientData
) listPtr
);
1644 listPtr
->flags
|= REDRAW_PENDING
;
1648 *----------------------------------------------------------------------
1650 * ListboxUpdateVScrollbar --
1652 * This procedure is invoked whenever information has changed in
1653 * a listbox in a way that would invalidate a vertical scrollbar
1654 * display. If there is an associated scrollbar, then this command
1655 * updates it by invoking a Tcl command.
1661 * A Tcl command is invoked, and an additional command may be
1662 * invoked to process errors in the command.
1664 *----------------------------------------------------------------------
1668 ListboxUpdateVScrollbar(listPtr
)
1669 register Listbox
*listPtr
; /* Information about widget. */
1674 if (listPtr
->yScrollCmd
== NULL
) {
1677 last
= listPtr
->topIndex
+ listPtr
->numLines
- 1;
1678 if (last
>= listPtr
->numElements
) {
1679 last
= listPtr
->numElements
-1;
1681 if (last
< listPtr
->topIndex
) {
1682 last
= listPtr
->topIndex
;
1684 sprintf(string
, " %d %d %d %d", listPtr
->numElements
, listPtr
->numLines
,
1685 listPtr
->topIndex
, last
);
1686 result
= Tcl_VarEval(listPtr
->interp
, listPtr
->yScrollCmd
, string
,
1688 if (result
!= TCL_OK
) {
1689 TkBindError(listPtr
->interp
);
1694 *----------------------------------------------------------------------
1696 * ListboxUpdateHScrollbar --
1698 * This procedure is invoked whenever information has changed in
1699 * a listbox in a way that would invalidate a horizontal scrollbar
1700 * display. If there is an associated horizontal scrollbar, then
1701 * this command updates it by invoking a Tcl command.
1707 * A Tcl command is invoked, and an additional command may be
1708 * invoked to process errors in the command.
1710 *----------------------------------------------------------------------
1714 ListboxUpdateHScrollbar(listPtr
)
1715 register Listbox
*listPtr
; /* Information about widget. */
1718 int result
, totalUnits
, windowUnits
, first
, last
;
1720 if (listPtr
->xScrollCmd
== NULL
) {
1723 totalUnits
= 1 + (listPtr
->maxWidth
-1)/listPtr
->xScrollUnit
;
1724 windowUnits
= 1 + (Tk_Width(listPtr
->tkwin
)
1725 - 2*(listPtr
->borderWidth
+ listPtr
->selBorderWidth
)-1)
1726 /listPtr
->xScrollUnit
;
1727 first
= listPtr
->xOffset
/listPtr
->xScrollUnit
;
1728 last
= first
+ windowUnits
- 1;
1732 sprintf(string
, " %d %d %d %d", totalUnits
, windowUnits
, first
, last
);
1733 result
= Tcl_VarEval(listPtr
->interp
, listPtr
->xScrollCmd
, string
,
1735 if (result
!= TCL_OK
) {
1736 TkBindError(listPtr
->interp
);
1741 *----------------------------------------------------------------------
1743 * ListboxComputeWidths --
1745 * This procedure is invoked to completely recompute width
1746 * information used for displaying listboxes and for horizontal
1753 * If "fontChanged" is non-zero then the widths of the individual
1754 * elements are all recomputed. In addition, listPtr->maxWidth is
1757 *----------------------------------------------------------------------
1761 ListboxComputeWidths(listPtr
, fontChanged
)
1762 Listbox
*listPtr
; /* Listbox whose geometry is to be
1764 int fontChanged
; /* Non-zero means the font may have changed
1765 * so per-element width information also
1766 * has to be computed. */
1768 register Element
*elPtr
;
1772 listPtr
->xScrollUnit
= XTextWidth(listPtr
->fontPtr
, "0", 1);
1773 listPtr
->maxWidth
= 0;
1774 for (elPtr
= listPtr
->elementPtr
; elPtr
!= NULL
; elPtr
= elPtr
->nextPtr
) {
1776 XTextExtents(listPtr
->fontPtr
, elPtr
->text
, elPtr
->textLength
,
1777 &dummy
, &dummy
, &dummy
, &bbox
);
1778 elPtr
->lBearing
= bbox
.lbearing
;
1779 elPtr
->pixelWidth
= bbox
.lbearing
+ bbox
.rbearing
;
1781 if (elPtr
->pixelWidth
> listPtr
->maxWidth
) {
1782 listPtr
->maxWidth
= elPtr
->pixelWidth
;