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 *--------------------------------------------------------------
276 ClientData clientData
, /* Main window associated with
278 Tcl_Interp
*interp
, /* Current interpreter. */
279 int argc
, /* Number of arguments. */
280 char **argv
/* Argument strings. */
283 register Listbox
*listPtr
;
285 Tk_Window tkwin
= (Tk_Window
) clientData
;
288 Tcl_AppendResult(interp
, "wrong # args: should be \"",
289 argv
[0], " pathName ?options?\"", (char *) NULL
);
293 new = Tk_CreateWindowFromPath(interp
, tkwin
, argv
[1], (char *) NULL
);
299 * Initialize the fields of the structure that won't be initialized
300 * by ConfigureListbox, or that ConfigureListbox requires to be
301 * initialized already (e.g. resource pointers).
304 listPtr
= (Listbox
*) ckalloc(sizeof(Listbox
));
305 listPtr
->tkwin
= new;
306 listPtr
->interp
= interp
;
307 listPtr
->numElements
= 0;
308 listPtr
->elementPtr
= NULL
;
309 listPtr
->normalBorder
= NULL
;
310 listPtr
->fontPtr
= NULL
;
311 listPtr
->fgColorPtr
= NULL
;
312 listPtr
->textGC
= None
;
313 listPtr
->selBorder
= NULL
;
314 listPtr
->selFgColorPtr
= NULL
;
315 listPtr
->selTextGC
= NULL
;
316 listPtr
->geometry
= NULL
;
317 listPtr
->topIndex
= 0;
318 listPtr
->xOffset
= 0;
319 listPtr
->selectFirst
= -1;
320 listPtr
->selectLast
= -1;
321 listPtr
->exportSelection
= 1;
322 listPtr
->cursor
= None
;
323 listPtr
->yScrollCmd
= NULL
;
324 listPtr
->xScrollCmd
= NULL
;
327 Tk_SetClass(listPtr
->tkwin
, "Listbox");
328 Tk_CreateEventHandler(listPtr
->tkwin
, ExposureMask
|StructureNotifyMask
,
329 ListboxEventProc
, (ClientData
) listPtr
);
330 Tk_CreateSelHandler(listPtr
->tkwin
, XA_STRING
, ListboxFetchSelection
,
331 (ClientData
) listPtr
, XA_STRING
);
332 Tcl_CreateCommand(interp
, Tk_PathName(listPtr
->tkwin
), ListboxWidgetCmd
,
333 (ClientData
) listPtr
, (void (*)(int *)) NULL
);
334 if (ConfigureListbox(interp
, listPtr
, argc
-2, argv
+2, 0) != TCL_OK
) {
338 interp
->result
= Tk_PathName(listPtr
->tkwin
);
342 Tk_DestroyWindow(listPtr
->tkwin
);
347 *--------------------------------------------------------------
349 * ListboxWidgetCmd --
351 * This procedure is invoked to process the Tcl command
352 * that corresponds to a widget managed by this module.
353 * See the user documentation for details on what it does.
356 * A standard Tcl result.
359 * See the user documentation.
361 *--------------------------------------------------------------
366 ClientData clientData
, /* Information about listbox widget. */
367 Tcl_Interp
*interp
, /* Current interpreter. */
368 int argc
, /* Number of arguments. */
369 char **argv
/* Argument strings. */
372 register Listbox
*listPtr
= (Listbox
*) clientData
;
378 Tcl_AppendResult(interp
, "wrong # args: should be \"",
379 argv
[0], " option ?arg arg ...?\"", (char *) NULL
);
382 Tk_Preserve((ClientData
) listPtr
);
384 length
= strlen(argv
[1]);
385 if ((c
== 'c') && (strncmp(argv
[1], "configure", length
) == 0)
388 result
= Tk_ConfigureInfo(interp
, listPtr
->tkwin
, configSpecs
,
389 (char *) listPtr
, (char *) NULL
, 0);
390 } else if (argc
== 3) {
391 result
= Tk_ConfigureInfo(interp
, listPtr
->tkwin
, configSpecs
,
392 (char *) listPtr
, argv
[2], 0);
394 result
= ConfigureListbox(interp
, listPtr
, argc
-2, argv
+2,
395 TK_CONFIG_ARGV_ONLY
);
397 } else if ((c
== 'c') && (strncmp(argv
[1], "curselection", length
) == 0)
403 Tcl_AppendResult(interp
, "wrong # args: should be \"",
404 argv
[0], " curselection\"",
408 if (listPtr
->selectFirst
!= -1) {
409 for (i
= listPtr
->selectFirst
; i
<= listPtr
->selectLast
; i
++) {
410 sprintf(index
, "%d", i
);
411 Tcl_AppendElement(interp
, index
, 0);
414 } else if ((c
== 'd') && (strncmp(argv
[1], "delete", length
) == 0)) {
417 if ((argc
< 3) || (argc
> 4)) {
418 Tcl_AppendResult(interp
, "wrong # args: should be \"",
419 argv
[0], " delete firstIndex ?lastIndex?\"",
423 if (GetListboxIndex(interp
, listPtr
, argv
[2], &first
) != TCL_OK
) {
429 if (GetListboxIndex(interp
, listPtr
, argv
[3], &last
) != TCL_OK
) {
433 DeleteEls(listPtr
, first
, last
);
434 } else if ((c
== 'g') && (strncmp(argv
[1], "get", length
) == 0)) {
436 register Element
*elPtr
;
439 Tcl_AppendResult(interp
, "wrong # args: should be \"",
440 argv
[0], " get index\"", (char *) NULL
);
443 if (GetListboxIndex(interp
, listPtr
, argv
[2], &index
) != TCL_OK
) {
449 if (index
>= listPtr
->numElements
) {
450 index
= listPtr
->numElements
-1;
452 for (elPtr
= listPtr
->elementPtr
; index
> 0;
453 index
--, elPtr
= elPtr
->nextPtr
) {
454 /* Empty loop body. */
457 interp
->result
= elPtr
->text
;
459 } else if ((c
== 'i') && (strncmp(argv
[1], "insert", length
) == 0)) {
463 Tcl_AppendResult(interp
, "wrong # args: should be \"",
464 argv
[0], " insert index ?element? ?element ...?\"",
469 if (GetListboxIndex(interp
, listPtr
, argv
[2], &index
) != TCL_OK
) {
472 InsertEls(listPtr
, index
, argc
-3, argv
+3);
474 } else if ((c
== 'n') && (strncmp(argv
[1], "nearest", length
) == 0)) {
478 Tcl_AppendResult(interp
, "wrong # args: should be \"",
479 argv
[0], " nearest y\"", (char *) NULL
);
482 if (Tcl_GetInt(interp
, argv
[2], &y
) != TCL_OK
) {
485 index
= NearestListboxElement(listPtr
, y
);
486 sprintf(interp
->result
, "%d", index
);
487 } else if ((c
== 's') && (length
>= 2)
488 && (strncmp(argv
[1], "scan", length
) == 0)) {
492 Tcl_AppendResult(interp
, "wrong # args: should be \"",
493 argv
[0], " scan mark|dragto x y\"", (char *) NULL
);
496 if ((Tcl_GetInt(interp
, argv
[3], &x
) != TCL_OK
)
497 || (Tcl_GetInt(interp
, argv
[4], &y
) != TCL_OK
)) {
500 if ((argv
[2][0] == 'm')
501 && (strncmp(argv
[2], "mark", strlen(argv
[2])) == 0)) {
502 listPtr
->scanMarkX
= x
;
503 listPtr
->scanMarkY
= y
;
504 listPtr
->scanMarkXOffset
= listPtr
->xOffset
;
505 listPtr
->scanMarkYIndex
= listPtr
->topIndex
;
506 } else if ((argv
[2][0] == 'd')
507 && (strncmp(argv
[2], "dragto", strlen(argv
[2])) == 0)) {
508 ListboxScanTo(listPtr
, x
, y
);
510 Tcl_AppendResult(interp
, "bad scan option \"", argv
[2],
511 "\": must be mark or dragto", (char *) NULL
);
514 } else if ((c
== 's') && (length
>= 2)
515 && (strncmp(argv
[1], "select", length
) == 0)) {
519 Tcl_AppendResult(interp
, "too few args: should be \"",
520 argv
[0], " select option ?index?\"", (char *) NULL
);
523 length
= strlen(argv
[2]);
525 if ((c
== 'c') && (argv
[2] != NULL
)
526 && (strncmp(argv
[2], "clear", length
) == 0)) {
528 Tcl_AppendResult(interp
, "wrong # args: should be \"",
529 argv
[0], " select clear\"", (char *) NULL
);
532 if (listPtr
->selectFirst
!= -1) {
533 ListboxRedrawRange(listPtr
, listPtr
->selectFirst
,
534 listPtr
->selectLast
);
535 listPtr
->selectFirst
= -1;
540 Tcl_AppendResult(interp
, "wrong # args: should be \"",
541 argv
[0], " select option index\"", (char *) NULL
);
544 if (GetListboxIndex(interp
, listPtr
, argv
[3], &index
) != TCL_OK
) {
547 if ((c
== 'a') && (strncmp(argv
[2], "adjust", length
) == 0)) {
548 if (index
< (listPtr
->selectFirst
+ listPtr
->selectLast
)/2) {
549 listPtr
->selectAnchor
= listPtr
->selectLast
;
551 listPtr
->selectAnchor
= listPtr
->selectFirst
;
553 ListboxSelectTo(listPtr
, index
);
554 } else if ((c
== 'f') && (strncmp(argv
[2], "from", length
) == 0)) {
555 ListboxSelectFrom(listPtr
, index
);
556 } else if ((c
== 't') && (strncmp(argv
[2], "to", length
) == 0)) {
557 ListboxSelectTo(listPtr
, index
);
559 Tcl_AppendResult(interp
, "bad select option \"", argv
[2],
560 "\": must be adjust, clear, from, or to", (char *) NULL
);
563 } else if ((c
== 's') && (length
>= 2)
564 && (strncmp(argv
[1], "size", length
) == 0)) {
565 sprintf(interp
->result
, "%d", listPtr
->numElements
);
566 } else if ((c
== 'x') && (strncmp(argv
[1], "xview", length
) == 0)) {
570 Tcl_AppendResult(interp
, "wrong # args: should be \"",
571 argv
[0], " xview index\"", (char *) NULL
);
574 if (Tcl_GetInt(interp
, argv
[2], &index
) != TCL_OK
) {
577 ChangeListboxOffset(listPtr
, index
*listPtr
->xScrollUnit
);
578 } else if ((c
== 'y') && (strncmp(argv
[1], "yview", length
) == 0)) {
582 Tcl_AppendResult(interp
, "wrong # args: should be \"",
583 argv
[0], " yview index\"", (char *) NULL
);
586 if (GetListboxIndex(interp
, listPtr
, argv
[2], &index
) != TCL_OK
) {
589 ChangeListboxView(listPtr
, index
);
591 Tcl_AppendResult(interp
, "bad option \"", argv
[1],
592 "\": must be configure, curselection, delete, get, ",
593 "insert, nearest, scan, select, size, ",
594 "xview, or yview", (char *) NULL
);
598 Tk_Release((ClientData
) listPtr
);
602 Tk_Release((ClientData
) listPtr
);
607 *----------------------------------------------------------------------
611 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
612 * to clean up the internal structure of a listbox at a safe time
613 * (when no-one is using it anymore).
619 * Everything associated with the listbox is freed up.
621 *----------------------------------------------------------------------
626 ClientData clientData
/* Info about listbox widget. */
629 register Listbox
*listPtr
= (Listbox
*) clientData
;
630 register Element
*elPtr
, *nextPtr
;
632 for (elPtr
= listPtr
->elementPtr
; elPtr
!= NULL
; ) {
633 nextPtr
= elPtr
->nextPtr
;
634 ckfree((char *) elPtr
);
637 if (listPtr
->normalBorder
!= NULL
) {
638 Tk_Free3DBorder(listPtr
->normalBorder
);
640 if (listPtr
->fontPtr
!= NULL
) {
641 Tk_FreeFontStruct(listPtr
->fontPtr
);
643 if (listPtr
->fgColorPtr
!= NULL
) {
644 Tk_FreeColor(listPtr
->fgColorPtr
);
646 if (listPtr
->textGC
!= None
) {
647 Tk_FreeGC(listPtr
->textGC
);
649 if (listPtr
->selBorder
!= NULL
) {
650 Tk_Free3DBorder(listPtr
->selBorder
);
652 if (listPtr
->selFgColorPtr
!= NULL
) {
653 Tk_FreeColor(listPtr
->selFgColorPtr
);
655 if (listPtr
->selTextGC
!= None
) {
656 Tk_FreeGC(listPtr
->selTextGC
);
658 if (listPtr
->geometry
!= NULL
) {
659 ckfree(listPtr
->geometry
);
661 if (listPtr
->cursor
!= None
) {
662 Tk_FreeCursor(listPtr
->cursor
);
664 if (listPtr
->yScrollCmd
!= NULL
) {
665 ckfree(listPtr
->yScrollCmd
);
667 if (listPtr
->xScrollCmd
!= NULL
) {
668 ckfree(listPtr
->xScrollCmd
);
670 ckfree((char *) listPtr
);
674 *----------------------------------------------------------------------
676 * ConfigureListbox --
678 * This procedure is called to process an argv/argc list, plus
679 * the Tk option database, in order to configure (or reconfigure)
683 * The return value is a standard Tcl result. If TCL_ERROR is
684 * returned, then interp->result contains an error message.
687 * Configuration information, such as colors, border width,
688 * etc. get set for listPtr; old resources get freed,
691 *----------------------------------------------------------------------
696 Tcl_Interp
*interp
, /* Used for error reporting. */
697 register Listbox
*listPtr
, /* Information about widget; may or may
698 * not already have values for some fields. */
699 int argc
, /* Number of valid entries in argv. */
700 char **argv
, /* Arguments. */
701 int flags
/* Flags to pass to Tk_ConfigureWidget. */
706 int width
, height
, fontHeight
, oldExport
;
708 oldExport
= listPtr
->exportSelection
;
709 if (Tk_ConfigureWidget(interp
, listPtr
->tkwin
, configSpecs
,
710 argc
, argv
, (char *) listPtr
, flags
) != TCL_OK
) {
715 * A few options need special processing, such as parsing the
716 * geometry and setting the background from a 3-D border.
719 Tk_SetBackgroundFromBorder(listPtr
->tkwin
, listPtr
->normalBorder
);
721 gcValues
.foreground
= listPtr
->fgColorPtr
->pixel
;
722 gcValues
.font
= listPtr
->fontPtr
->fid
;
723 gcValues
.graphics_exposures
= False
;
724 new = Tk_GetGC(listPtr
->tkwin
, GCForeground
|GCFont
|GCGraphicsExposures
,
726 if (listPtr
->textGC
!= None
) {
727 Tk_FreeGC(listPtr
->textGC
);
729 listPtr
->textGC
= new;
731 gcValues
.foreground
= listPtr
->selFgColorPtr
->pixel
;
732 gcValues
.font
= listPtr
->fontPtr
->fid
;
733 new = Tk_GetGC(listPtr
->tkwin
, GCForeground
|GCFont
, &gcValues
);
734 if (listPtr
->selTextGC
!= None
) {
735 Tk_FreeGC(listPtr
->selTextGC
);
737 listPtr
->selTextGC
= new;
740 * Claim the selection if we've suddenly started exporting it.
743 if (listPtr
->exportSelection
&& (!oldExport
)
744 && (listPtr
->selectFirst
!=-1)) {
745 Tk_OwnSelection(listPtr
->tkwin
, ListboxLostSelection
,
746 (ClientData
) listPtr
);
750 * Register the desired geometry for the window, and arrange for
751 * the window to be redisplayed.
754 if ((sscanf(listPtr
->geometry
, "%dx%d", &width
, &height
) != 2)
755 || (width
<= 0) || (height
<= 0)) {
756 Tcl_AppendResult(interp
, "bad geometry \"",
757 listPtr
->geometry
, "\"", (char *) NULL
);
760 fontHeight
= listPtr
->fontPtr
->ascent
+ listPtr
->fontPtr
->descent
;
761 listPtr
->lineHeight
= fontHeight
+ 1 + 2*listPtr
->selBorderWidth
;
762 listPtr
->numLines
= (Tk_Height(listPtr
->tkwin
) - 2*listPtr
->borderWidth
)
763 / listPtr
->lineHeight
;
764 if (listPtr
->numLines
< 0) {
765 listPtr
->numLines
= 0;
767 ListboxComputeWidths(listPtr
, 1);
768 width
= (width
+1)*listPtr
->xScrollUnit
+ 2*listPtr
->borderWidth
769 + 2*listPtr
->selBorderWidth
;
770 height
= height
*listPtr
->lineHeight
+ 2*listPtr
->borderWidth
;
771 Tk_GeometryRequest(listPtr
->tkwin
, width
, height
);
772 Tk_SetInternalBorder(listPtr
->tkwin
, listPtr
->borderWidth
);
773 listPtr
->flags
|= UPDATE_V_SCROLLBAR
|UPDATE_H_SCROLLBAR
;
774 ListboxRedrawRange(listPtr
, 0, listPtr
->numElements
-1);
779 *--------------------------------------------------------------
783 * This procedure redraws the contents of a listbox window.
789 * Information appears on the screen.
791 *--------------------------------------------------------------
796 ClientData clientData
/* Information about window. */
799 register Listbox
*listPtr
= (Listbox
*) clientData
;
800 register Tk_Window tkwin
= listPtr
->tkwin
;
801 register Element
*elPtr
;
803 int i
, limit
, x
, y
, margin
;
806 listPtr
->flags
&= ~REDRAW_PENDING
;
807 if (listPtr
->flags
& UPDATE_V_SCROLLBAR
) {
808 ListboxUpdateVScrollbar(listPtr
);
810 if (listPtr
->flags
& UPDATE_H_SCROLLBAR
) {
811 ListboxUpdateHScrollbar(listPtr
);
813 listPtr
->flags
&= ~(REDRAW_PENDING
|UPDATE_V_SCROLLBAR
|UPDATE_H_SCROLLBAR
);
814 if ((listPtr
->tkwin
== NULL
) || !Tk_IsMapped(tkwin
)) {
819 * Redrawing is done in a temporary pixmap that is allocated
820 * here and freed at the end of the procedure. All drawing is
821 * done to the pixmap, and the pixmap is copied to the screen
822 * at the end of the procedure. This provides the smoothest
823 * possible visual effects (no flashing on the screen).
826 pixmap
= XCreatePixmap(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
827 Tk_Width(tkwin
), Tk_Height(tkwin
),
828 Tk_DefaultDepth(Tk_Screen(tkwin
)));
829 Tk_Fill3DRectangle(Tk_Display(tkwin
), pixmap
, listPtr
->normalBorder
,
830 0, 0, Tk_Width(tkwin
), Tk_Height(tkwin
), listPtr
->borderWidth
,
834 * Iterate through all of the elements of the listbox, displaying each
835 * in turn. Selected elements use a different GC and have a raised
839 limit
= listPtr
->topIndex
+ listPtr
->numLines
- 1;
840 if (limit
>= listPtr
->numElements
) {
841 limit
= listPtr
->numElements
-1;
843 margin
= listPtr
->selBorderWidth
+ listPtr
->xScrollUnit
/2;
844 for (elPtr
= listPtr
->elementPtr
, i
= 0; (elPtr
!= NULL
) && (i
<= limit
);
845 elPtr
= elPtr
->nextPtr
, i
++) {
846 if (i
< listPtr
->topIndex
) {
849 x
= listPtr
->borderWidth
;
850 y
= ((i
- listPtr
->topIndex
) * listPtr
->lineHeight
)
851 + listPtr
->borderWidth
;
852 gc
= listPtr
->textGC
;
853 if ((listPtr
->selectFirst
>= 0) && (i
>= listPtr
->selectFirst
)
854 && (i
<= listPtr
->selectLast
)) {
855 gc
= listPtr
->selTextGC
;
856 Tk_Fill3DRectangle(Tk_Display(tkwin
), pixmap
,
857 listPtr
->selBorder
, x
, y
,
858 Tk_Width(tkwin
) - 2*listPtr
->borderWidth
,
859 listPtr
->lineHeight
, listPtr
->selBorderWidth
,
862 y
+= listPtr
->fontPtr
->ascent
+ listPtr
->selBorderWidth
;
863 x
+= margin
- elPtr
->lBearing
- listPtr
->xOffset
;
864 XDrawString(Tk_Display(tkwin
), pixmap
, gc
, x
, y
,
865 elPtr
->text
, elPtr
->textLength
);
869 * Redraw the border for the listbox to make sure that it's on top
870 * of any of the text of the listbox entries.
873 Tk_Draw3DRectangle(Tk_Display(tkwin
), pixmap
,
874 listPtr
->normalBorder
, 0, 0, Tk_Width(tkwin
),
875 Tk_Height(tkwin
), listPtr
->borderWidth
,
877 XCopyArea(Tk_Display(tkwin
), pixmap
, Tk_WindowId(tkwin
),
878 listPtr
->textGC
, 0, 0, Tk_Width(tkwin
), Tk_Height(tkwin
),
880 XFreePixmap(Tk_Display(tkwin
), pixmap
);
884 *----------------------------------------------------------------------
888 * Add new elements to a listbox widget.
894 * New information gets added to listPtr; it will be redisplayed
895 * soon, but not immediately.
897 *----------------------------------------------------------------------
902 register Listbox
*listPtr
, /* Listbox that is to get the new
904 int index
, /* Add the new elements before this
906 int argc
, /* Number of new elements to add. */
907 char **argv
/* New elements (one per entry). */
910 register Element
*prevPtr
, *newPtr
;
911 int length
, dummy
, i
, oldMaxWidth
;
915 * Find the element before which the new ones will be inserted.
921 if (index
> listPtr
->numElements
) {
922 index
= listPtr
->numElements
;
927 for (prevPtr
= listPtr
->elementPtr
, i
= index
- 1; i
> 0; i
--) {
928 prevPtr
= prevPtr
->nextPtr
;
933 * For each new element, create a record, initialize it, and link
934 * it into the list of elements.
937 oldMaxWidth
= listPtr
->maxWidth
;
938 for (i
= argc
; i
> 0; i
--, argv
++, prevPtr
= newPtr
) {
939 length
= strlen(*argv
);
940 newPtr
= (Element
*) ckalloc(ElementSize(length
));
941 newPtr
->textLength
= length
;
942 strcpy(newPtr
->text
, *argv
);
943 XTextExtents(listPtr
->fontPtr
, newPtr
->text
, newPtr
->textLength
,
944 &dummy
, &dummy
, &dummy
, &bbox
);
945 newPtr
->lBearing
= bbox
.lbearing
;
946 newPtr
->pixelWidth
= bbox
.lbearing
+ bbox
.rbearing
;
947 if (newPtr
->pixelWidth
> listPtr
->maxWidth
) {
948 listPtr
->maxWidth
= newPtr
->pixelWidth
;
950 if (prevPtr
== NULL
) {
951 newPtr
->nextPtr
= listPtr
->elementPtr
;
952 listPtr
->elementPtr
= newPtr
;
954 newPtr
->nextPtr
= prevPtr
->nextPtr
;
955 prevPtr
->nextPtr
= newPtr
;
958 listPtr
->numElements
+= argc
;
961 * Update the selection to account for the renumbering that has just
962 * occurred. Then arrange for the new information to be displayed.
965 if (index
<= listPtr
->selectFirst
) {
966 listPtr
->selectFirst
+= argc
;
968 if (index
<= listPtr
->selectLast
) {
969 listPtr
->selectLast
+= argc
;
971 listPtr
->flags
|= UPDATE_V_SCROLLBAR
;
972 if (listPtr
->maxWidth
!= oldMaxWidth
) {
973 listPtr
->flags
|= UPDATE_H_SCROLLBAR
;
975 ListboxRedrawRange(listPtr
, index
, listPtr
->numElements
-1);
979 *----------------------------------------------------------------------
983 * Remove one or more elements from a listbox widget.
989 * Memory gets freed, the listbox gets modified and (eventually)
992 *----------------------------------------------------------------------
997 register Listbox
*listPtr
, /* Listbox widget to modify. */
998 int first
, /* Index of first element to delete. */
999 int last
/* Index of last element to delete. */
1002 register Element
*prevPtr
, *elPtr
;
1003 int count
, i
, widthChanged
;
1006 * Adjust the range to fit within the existing elements of the
1007 * listbox, and make sure there's something to delete.
1013 if (last
>= listPtr
->numElements
) {
1014 last
= listPtr
->numElements
-1;
1016 count
= last
+ 1 - first
;
1022 * Find the element just before the ones to delete.
1028 for (i
= first
-1, prevPtr
= listPtr
->elementPtr
; i
> 0; i
--) {
1029 prevPtr
= prevPtr
->nextPtr
;
1034 * Delete the requested number of elements.
1038 for (i
= count
; i
> 0; i
--) {
1039 if (prevPtr
== NULL
) {
1040 elPtr
= listPtr
->elementPtr
;
1041 listPtr
->elementPtr
= elPtr
->nextPtr
;
1043 elPtr
= prevPtr
->nextPtr
;
1044 prevPtr
->nextPtr
= elPtr
->nextPtr
;
1046 if (elPtr
->pixelWidth
== listPtr
->maxWidth
) {
1049 ckfree((char *) elPtr
);
1051 listPtr
->numElements
-= count
;
1054 * Update the selection and viewing information to reflect the change
1055 * in the element numbering, and redisplay to slide information up over
1056 * the elements that were deleted.
1059 if (first
<= listPtr
->selectFirst
) {
1060 listPtr
->selectFirst
-= count
;
1061 if (listPtr
->selectFirst
< first
) {
1062 listPtr
->selectFirst
= first
;
1065 if (first
<= listPtr
->selectLast
) {
1066 listPtr
->selectLast
-= count
;
1067 if (listPtr
->selectLast
< first
) {
1068 listPtr
->selectLast
= first
-1;
1071 if (listPtr
->selectLast
< listPtr
->selectFirst
) {
1072 listPtr
->selectFirst
= -1;
1074 if (first
<= listPtr
->topIndex
) {
1075 listPtr
->topIndex
-= count
;
1076 if (listPtr
->topIndex
< first
) {
1077 listPtr
->topIndex
= first
;
1080 listPtr
->flags
|= UPDATE_V_SCROLLBAR
;
1082 ListboxComputeWidths(listPtr
, 0);
1083 listPtr
->flags
|= UPDATE_H_SCROLLBAR
;
1085 ListboxRedrawRange(listPtr
, first
, listPtr
->numElements
-1);
1089 *--------------------------------------------------------------
1091 * ListboxEventProc --
1093 * This procedure is invoked by the Tk dispatcher for various
1094 * events on listboxes.
1100 * When the window gets deleted, internal structures get
1101 * cleaned up. When it gets exposed, it is redisplayed.
1103 *--------------------------------------------------------------
1108 ClientData clientData
, /* Information about window. */
1109 XEvent
*eventPtr
/* Information about event. */
1112 Listbox
*listPtr
= (Listbox
*) clientData
;
1114 if (eventPtr
->type
== Expose
) {
1115 ListboxRedrawRange(listPtr
,
1116 NearestListboxElement(listPtr
, eventPtr
->xexpose
.y
),
1117 NearestListboxElement(listPtr
, eventPtr
->xexpose
.y
1118 + eventPtr
->xexpose
.height
));
1119 } else if (eventPtr
->type
== DestroyNotify
) {
1120 Tcl_DeleteCommand(listPtr
->interp
, Tk_PathName(listPtr
->tkwin
));
1121 listPtr
->tkwin
= NULL
;
1122 if (listPtr
->flags
& REDRAW_PENDING
) {
1123 Tk_CancelIdleCall(DisplayListbox
, (ClientData
) listPtr
);
1125 Tk_EventuallyFree((ClientData
) listPtr
, DestroyListbox
);
1126 } else if (eventPtr
->type
== ConfigureNotify
) {
1127 Tk_Preserve((ClientData
) listPtr
);
1128 listPtr
->numLines
= (Tk_Height(listPtr
->tkwin
)
1129 - 2*listPtr
->borderWidth
) / listPtr
->lineHeight
;
1130 listPtr
->flags
|= UPDATE_V_SCROLLBAR
|UPDATE_H_SCROLLBAR
;
1131 ListboxRedrawRange(listPtr
, 0, listPtr
->numElements
-1);
1132 Tk_Release((ClientData
) listPtr
);
1137 *--------------------------------------------------------------
1139 * GetListboxIndex --
1141 * Parse an index into a listbox and return either its value
1145 * A standard Tcl result. If all went well, then *indexPtr is
1146 * filled in with the index (into listPtr) corresponding to
1147 * string. Otherwise an error message is left in interp->result.
1152 *--------------------------------------------------------------
1157 Tcl_Interp
*interp
, /* For error messages. */
1158 Listbox
*listPtr
, /* Listbox for which the index is being
1160 char *string
, /* Numerical index into listPtr's element
1161 * list, or "end" to refer to last element. */
1162 int *indexPtr
/* Where to store converted index. */
1165 if (string
[0] == 'e') {
1166 if (strncmp(string
, "end", strlen(string
)) != 0) {
1168 Tcl_AppendResult(interp
, "bad listbox index \"", string
,
1169 "\"", (char *) NULL
);
1172 *indexPtr
= listPtr
->numElements
;
1173 if (listPtr
->numElements
<= 0) {
1177 if (Tcl_GetInt(interp
, string
, indexPtr
) != TCL_OK
) {
1178 Tcl_ResetResult(interp
);
1186 *----------------------------------------------------------------------
1188 * ChangeListboxView --
1190 * Change the view on a listbox widget.
1196 * What's displayed on the screen is changed. If there is a
1197 * scrollbar associated with this widget, then the scrollbar
1198 * is instructed to change its display too.
1200 *----------------------------------------------------------------------
1205 register Listbox
*listPtr
, /* Information about widget. */
1206 int index
/* Index of element in listPtr. */
1209 if (listPtr
->tkwin
== NULL
) {
1213 if (index
>= listPtr
->numElements
) {
1214 index
= listPtr
->numElements
-1;
1219 if (listPtr
->topIndex
!= index
) {
1220 if (!(listPtr
->flags
& REDRAW_PENDING
)) {
1221 Tk_DoWhenIdle(DisplayListbox
, (ClientData
) listPtr
);
1222 listPtr
->flags
|= REDRAW_PENDING
;
1224 listPtr
->topIndex
= index
;
1225 ListboxUpdateVScrollbar(listPtr
);
1230 *----------------------------------------------------------------------
1232 * ChangListboxOffset --
1234 * Change the horizontal offset for a listbox.
1240 * The listbox may be redrawn to reflect its new horizontal
1243 *----------------------------------------------------------------------
1247 ChangeListboxOffset (
1248 register Listbox
*listPtr
, /* Information about widget. */
1249 int offset
/* Desired new "xOffset" for
1255 if (listPtr
->tkwin
== NULL
) {
1260 * Make sure that the new offset is within the allowable range, and
1261 * round it off to an even multiple of xScrollUnit.
1264 maxOffset
= listPtr
->maxWidth
+ (listPtr
->xScrollUnit
-1)
1265 - (Tk_Width(listPtr
->tkwin
) - 2*listPtr
->borderWidth
1266 - 2*listPtr
->selBorderWidth
- listPtr
->xScrollUnit
);
1267 if (offset
> maxOffset
) {
1273 offset
-= offset
%listPtr
->xScrollUnit
;
1274 if (offset
!= listPtr
->xOffset
) {
1275 listPtr
->xOffset
= offset
;
1276 listPtr
->flags
|= UPDATE_H_SCROLLBAR
;
1277 ListboxRedrawRange(listPtr
, 0, listPtr
->numElements
);
1282 *----------------------------------------------------------------------
1286 * Given a point (presumably of the curent mouse location)
1287 * drag the view in the window to implement the scan operation.
1293 * The view in the window may change.
1295 *----------------------------------------------------------------------
1300 register Listbox
*listPtr
, /* Information about widget. */
1301 int x
, /* X-coordinate to use for scan
1303 int y
/* Y-coordinate to use for scan
1307 int newTopIndex
, newOffset
;
1310 * Compute new top line for screen by amplifying the difference
1311 * between the current position and the place where the scan
1312 * started (the "mark" position). If we run off the top or bottom
1313 * of the list, then reset the mark point so that the current
1314 * position continues to correspond to the edge of the window.
1315 * This means that the picture will start dragging as soon as the
1316 * mouse reverses direction (without this reset, might have to slide
1317 * mouse a long ways back before the picture starts moving again).
1320 newTopIndex
= listPtr
->scanMarkYIndex
1321 - (10*(y
- listPtr
->scanMarkY
))/listPtr
->lineHeight
;
1322 if (newTopIndex
>= listPtr
->numElements
) {
1323 newTopIndex
= listPtr
->scanMarkYIndex
= listPtr
->numElements
-1;
1324 listPtr
->scanMarkY
= y
;
1325 } else if (newTopIndex
< 0) {
1326 newTopIndex
= listPtr
->scanMarkYIndex
= 0;
1327 listPtr
->scanMarkY
= y
;
1329 ChangeListboxView(listPtr
, newTopIndex
);
1332 * Compute new left edge for display in a similar fashion by amplifying
1333 * the difference between the current position and the place where the
1337 newOffset
= listPtr
->scanMarkXOffset
- (10*(x
- listPtr
->scanMarkX
));
1338 if (newOffset
>= listPtr
->maxWidth
) {
1339 newOffset
= listPtr
->scanMarkXOffset
= listPtr
->maxWidth
;
1340 listPtr
->scanMarkX
= x
;
1341 } else if (newOffset
< 0) {
1342 newOffset
= listPtr
->scanMarkXOffset
= 0;
1343 listPtr
->scanMarkX
= x
;
1345 ChangeListboxOffset(listPtr
, newOffset
);
1349 *----------------------------------------------------------------------
1351 * NearestListboxElement --
1353 * Given a y-coordinate inside a listbox, compute the index of
1354 * the element under that y-coordinate (or closest to that
1358 * The return value is an index of an element of listPtr. If
1359 * listPtr has no elements, then 0 is always returned.
1364 *----------------------------------------------------------------------
1368 NearestListboxElement (
1369 register Listbox
*listPtr
, /* Information about widget. */
1370 int y
/* Y-coordinate in listPtr's window. */
1375 index
= (y
- listPtr
->borderWidth
)/listPtr
->lineHeight
;
1376 if (index
>= listPtr
->numLines
) {
1377 index
= listPtr
->numLines
-1;
1382 index
+= listPtr
->topIndex
;
1383 if (index
>= listPtr
->numElements
) {
1384 index
= listPtr
->numElements
-1;
1390 *----------------------------------------------------------------------
1392 * ListboxSelectFrom --
1394 * Start a new selection in a listbox.
1400 * ListPtr claims the selection, and the selection becomes the
1401 * single element given by index.
1403 *----------------------------------------------------------------------
1408 register Listbox
*listPtr
, /* Information about widget. */
1409 int index
/* Index of element that is to
1410 * become the new selection. */
1414 * Make sure the index is within the proper range for the listbox.
1420 if (index
>= listPtr
->numElements
) {
1421 index
= listPtr
->numElements
-1;
1424 if (listPtr
->selectFirst
!= -1) {
1425 ListboxRedrawRange(listPtr
, listPtr
->selectFirst
, listPtr
->selectLast
);
1426 } else if (listPtr
->exportSelection
) {
1427 Tk_OwnSelection(listPtr
->tkwin
, ListboxLostSelection
,
1428 (ClientData
) listPtr
);
1431 listPtr
->selectFirst
= listPtr
->selectLast
= index
;
1432 listPtr
->selectAnchor
= index
;
1433 ListboxRedrawRange(listPtr
, index
, index
);
1437 *----------------------------------------------------------------------
1439 * ListboxSelectTo --
1441 * Modify the selection by moving its un-anchored end. This could
1442 * make the selection either larger or smaller.
1448 * The selection changes.
1450 *----------------------------------------------------------------------
1455 register Listbox
*listPtr
, /* Information about widget. */
1456 int index
/* Index of element that is to
1457 * become the "other" end of the
1461 int newFirst
, newLast
;
1464 * Make sure the index is within the proper range for the listbox.
1470 if (index
>= listPtr
->numElements
) {
1471 index
= listPtr
->numElements
-1;
1475 * We should already own the selection, but grab it if we don't.
1478 if (listPtr
->selectFirst
== -1) {
1479 ListboxSelectFrom(listPtr
, index
);
1482 if (listPtr
->selectAnchor
< index
) {
1483 newFirst
= listPtr
->selectAnchor
;
1487 newLast
= listPtr
->selectAnchor
;
1489 if ((listPtr
->selectFirst
== newFirst
)
1490 && (listPtr
->selectLast
== newLast
)) {
1493 if (listPtr
->selectFirst
!= newFirst
) {
1494 if (listPtr
->selectFirst
< newFirst
) {
1495 ListboxRedrawRange(listPtr
, listPtr
->selectFirst
, newFirst
-1);
1497 ListboxRedrawRange(listPtr
, newFirst
, listPtr
->selectFirst
-1);
1499 listPtr
->selectFirst
= newFirst
;
1501 if (listPtr
->selectLast
!= newLast
) {
1502 if (listPtr
->selectLast
< newLast
) {
1503 ListboxRedrawRange(listPtr
, listPtr
->selectLast
+1, newLast
);
1505 ListboxRedrawRange(listPtr
, newLast
+1, listPtr
->selectLast
);
1507 listPtr
->selectLast
= newLast
;
1512 *----------------------------------------------------------------------
1514 * ListboxFetchSelection --
1516 * This procedure is called back by Tk when the selection is
1517 * requested by someone. It returns part or all of the selection
1518 * in a buffer provided by the caller.
1521 * The return value is the number of non-NULL bytes stored
1522 * at buffer. Buffer is filled (or partially filled) with a
1523 * NULL-terminated string containing part or all of the selection,
1524 * as given by offset and maxBytes. The selection is returned
1525 * as a Tcl list with one list element for each element in the
1531 *----------------------------------------------------------------------
1535 ListboxFetchSelection (
1536 ClientData clientData
, /* Information about listbox widget. */
1537 int offset
, /* Offset within selection of first
1538 * byte to be returned. */
1539 char *buffer
, /* Location in which to place
1541 int maxBytes
/* Maximum number of bytes to place
1542 * at buffer, not including terminating
1543 * NULL character. */
1546 register Listbox
*listPtr
= (Listbox
*) clientData
;
1547 register Element
*elPtr
;
1548 char **argv
, *selection
;
1549 int src
, dst
, length
, count
, argc
;
1551 if ((listPtr
->selectFirst
== -1) || !listPtr
->exportSelection
) {
1556 * Use Tcl_Merge to format the listbox elements into a suitable
1560 argc
= listPtr
->selectLast
- listPtr
->selectFirst
+ 1;
1561 argv
= (char **) ckalloc((unsigned) (argc
*sizeof(char *)));
1562 for (src
= 0, dst
= 0, elPtr
= listPtr
->elementPtr
; ;
1563 src
++, elPtr
= elPtr
->nextPtr
) {
1564 if (src
< listPtr
->selectFirst
) {
1567 if (src
> listPtr
->selectLast
) {
1570 argv
[dst
] = elPtr
->text
;
1573 selection
= Tcl_Merge(argc
, argv
);
1576 * Copy the requested portion of the selection to the buffer.
1579 length
= strlen(selection
);
1580 count
= length
- offset
;
1585 if (count
> maxBytes
) {
1588 memcpy((VOID
*) buffer
, (VOID
*) (selection
+ offset
), count
);
1591 buffer
[count
] = '\0';
1593 ckfree((char *) argv
);
1598 *----------------------------------------------------------------------
1600 * ListboxLostSelection --
1602 * This procedure is called back by Tk when the selection is
1603 * grabbed away from a listbox widget.
1609 * The existing selection is unhighlighted, and the window is
1610 * marked as not containing a selection.
1612 *----------------------------------------------------------------------
1616 ListboxLostSelection (
1617 ClientData clientData
/* Information about listbox widget. */
1620 register Listbox
*listPtr
= (Listbox
*) clientData
;
1622 if ((listPtr
->selectFirst
>= 0) && listPtr
->exportSelection
) {
1623 ListboxRedrawRange(listPtr
, listPtr
->selectFirst
, listPtr
->selectLast
);
1624 listPtr
->selectFirst
= -1;
1629 *----------------------------------------------------------------------
1631 * ListboxRedrawRange --
1633 * Ensure that a given range of elements is eventually redrawn on
1634 * the display (if those elements in fact appear on the display).
1640 * Information gets redisplayed.
1642 *----------------------------------------------------------------------
1647 ListboxRedrawRange (
1648 register Listbox
*listPtr
, /* Information about widget. */
1649 int first
, /* Index of first element in list
1650 * that needs to be redrawn. */
1651 int last
/* Index of last element in list
1652 * that needs to be redrawn. May
1653 * be less than first;
1654 * these just bracket a range. */
1657 if ((listPtr
->tkwin
== NULL
) || !Tk_IsMapped(listPtr
->tkwin
)
1658 || (listPtr
->flags
& REDRAW_PENDING
)) {
1661 Tk_DoWhenIdle(DisplayListbox
, (ClientData
) listPtr
);
1662 listPtr
->flags
|= REDRAW_PENDING
;
1666 *----------------------------------------------------------------------
1668 * ListboxUpdateVScrollbar --
1670 * This procedure is invoked whenever information has changed in
1671 * a listbox in a way that would invalidate a vertical scrollbar
1672 * display. If there is an associated scrollbar, then this command
1673 * updates it by invoking a Tcl command.
1679 * A Tcl command is invoked, and an additional command may be
1680 * invoked to process errors in the command.
1682 *----------------------------------------------------------------------
1686 ListboxUpdateVScrollbar (
1687 register Listbox
*listPtr
/* Information about widget. */
1693 if (listPtr
->yScrollCmd
== NULL
) {
1696 last
= listPtr
->topIndex
+ listPtr
->numLines
- 1;
1697 if (last
>= listPtr
->numElements
) {
1698 last
= listPtr
->numElements
-1;
1700 if (last
< listPtr
->topIndex
) {
1701 last
= listPtr
->topIndex
;
1703 sprintf(string
, " %d %d %d %d", listPtr
->numElements
, listPtr
->numLines
,
1704 listPtr
->topIndex
, last
);
1705 result
= Tcl_VarEval(listPtr
->interp
, listPtr
->yScrollCmd
, string
,
1707 if (result
!= TCL_OK
) {
1708 TkBindError(listPtr
->interp
);
1713 *----------------------------------------------------------------------
1715 * ListboxUpdateHScrollbar --
1717 * This procedure is invoked whenever information has changed in
1718 * a listbox in a way that would invalidate a horizontal scrollbar
1719 * display. If there is an associated horizontal scrollbar, then
1720 * this command updates it by invoking a Tcl command.
1726 * A Tcl command is invoked, and an additional command may be
1727 * invoked to process errors in the command.
1729 *----------------------------------------------------------------------
1733 ListboxUpdateHScrollbar (
1734 register Listbox
*listPtr
/* Information about widget. */
1738 int result
, totalUnits
, windowUnits
, first
, last
;
1740 if (listPtr
->xScrollCmd
== NULL
) {
1743 totalUnits
= 1 + (listPtr
->maxWidth
-1)/listPtr
->xScrollUnit
;
1744 windowUnits
= 1 + (Tk_Width(listPtr
->tkwin
)
1745 - 2*(listPtr
->borderWidth
+ listPtr
->selBorderWidth
)-1)
1746 /listPtr
->xScrollUnit
;
1747 first
= listPtr
->xOffset
/listPtr
->xScrollUnit
;
1748 last
= first
+ windowUnits
- 1;
1752 sprintf(string
, " %d %d %d %d", totalUnits
, windowUnits
, first
, last
);
1753 result
= Tcl_VarEval(listPtr
->interp
, listPtr
->xScrollCmd
, string
,
1755 if (result
!= TCL_OK
) {
1756 TkBindError(listPtr
->interp
);
1761 *----------------------------------------------------------------------
1763 * ListboxComputeWidths --
1765 * This procedure is invoked to completely recompute width
1766 * information used for displaying listboxes and for horizontal
1773 * If "fontChanged" is non-zero then the widths of the individual
1774 * elements are all recomputed. In addition, listPtr->maxWidth is
1777 *----------------------------------------------------------------------
1781 ListboxComputeWidths (
1782 Listbox
*listPtr
, /* Listbox whose geometry is to be
1784 int fontChanged
/* Non-zero means the font may have changed
1785 * so per-element width information also
1786 * has to be computed. */
1789 register Element
*elPtr
;
1793 listPtr
->xScrollUnit
= XTextWidth(listPtr
->fontPtr
, "0", 1);
1794 listPtr
->maxWidth
= 0;
1795 for (elPtr
= listPtr
->elementPtr
; elPtr
!= NULL
; elPtr
= elPtr
->nextPtr
) {
1797 XTextExtents(listPtr
->fontPtr
, elPtr
->text
, elPtr
->textLength
,
1798 &dummy
, &dummy
, &dummy
, &bbox
);
1799 elPtr
->lBearing
= bbox
.lbearing
;
1800 elPtr
->pixelWidth
= bbox
.lbearing
+ bbox
.rbearing
;
1802 if (elPtr
->pixelWidth
> listPtr
->maxWidth
) {
1803 listPtr
->maxWidth
= elPtr
->pixelWidth
;