/[dtapublic]/projs/trunk/shared_source/c_tk_base_7_5_w_mods/tklistbox.c
ViewVC logotype

Contents of /projs/trunk/shared_source/c_tk_base_7_5_w_mods/tklistbox.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 69 - (show annotations) (download)
Sat Nov 5 10:54:17 2016 UTC (6 years, 3 months ago) by dashley
File MIME type: text/plain
File size: 103290 byte(s)
License and property (keyword) changes.
1 /* $Header$ */
2
3 /*
4 * tkListbox.c --
5 *
6 * This module implements listbox widgets for the Tk
7 * toolkit. A listbox displays a collection of strings,
8 * one per line, and provides scrolling and selection.
9 *
10 * Copyright (c) 1990-1994 The Regents of the University of California.
11 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
12 *
13 * See the file "license.terms" for information on usage and redistribution
14 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15 *
16 * RCS: @(#) $Id: tklistbox.c,v 1.1.1.1 2001/06/13 05:05:02 dtashley Exp $
17 */
18
19 #include "tkPort.h"
20 #include "default.h"
21 #include "tkInt.h"
22
23 typedef struct {
24 Tk_OptionTable listboxOptionTable; /* Table defining configuration options
25 * available for the listbox */
26 Tk_OptionTable itemAttrOptionTable; /* Table definining configuration
27 * options available for listbox
28 * items */
29 } ListboxOptionTables;
30
31 /*
32 * A data structure of the following type is kept for each listbox
33 * widget managed by this file:
34 */
35
36 typedef struct {
37 Tk_Window tkwin; /* Window that embodies the listbox. NULL
38 * means that the window has been destroyed
39 * but the data structures haven't yet been
40 * cleaned up.*/
41 Display *display; /* Display containing widget. Used, among
42 * other things, so that resources can be
43 * freed even after tkwin has gone away. */
44 Tcl_Interp *interp; /* Interpreter associated with listbox. */
45 Tcl_Command widgetCmd; /* Token for listbox's widget command. */
46 Tk_OptionTable optionTable; /* Table that defines configuration options
47 * available for this widget. */
48 Tk_OptionTable itemAttrOptionTable; /* Table that defines configuration
49 * options available for listbox
50 * items */
51 char *listVarName; /* List variable name */
52 Tcl_Obj *listObj; /* Pointer to the list object being used */
53 int nElements; /* Holds the current count of elements */
54 Tcl_HashTable *selection; /* Tracks selection */
55 Tcl_HashTable *itemAttrTable; /* Tracks item attributes */
56
57 /*
58 * Information used when displaying widget:
59 */
60
61 Tk_3DBorder normalBorder; /* Used for drawing border around whole
62 * window, plus used for background. */
63 int borderWidth; /* Width of 3-D border around window. */
64 int relief; /* 3-D effect: TK_RELIEF_RAISED, etc. */
65 int highlightWidth; /* Width in pixels of highlight to draw
66 * around widget when it has the focus.
67 * <= 0 means don't draw a highlight. */
68 XColor *highlightBgColorPtr;
69 /* Color for drawing traversal highlight
70 * area when highlight is off. */
71 XColor *highlightColorPtr; /* Color for drawing traversal highlight. */
72 int inset; /* Total width of all borders, including
73 * traversal highlight and 3-D border.
74 * Indicates how much interior stuff must
75 * be offset from outside edges to leave
76 * room for borders. */
77 Tk_Font tkfont; /* Information about text font, or NULL. */
78 XColor *fgColorPtr; /* Text color in normal mode. */
79 GC textGC; /* For drawing normal text. */
80 Tk_3DBorder selBorder; /* Borders and backgrounds for selected
81 * elements. */
82 int selBorderWidth; /* Width of border around selection. */
83 XColor *selFgColorPtr; /* Foreground color for selected elements. */
84 GC selTextGC; /* For drawing selected text. */
85 int width; /* Desired width of window, in characters. */
86 int height; /* Desired height of window, in lines. */
87 int lineHeight; /* Number of pixels allocated for each line
88 * in display. */
89 int topIndex; /* Index of top-most element visible in
90 * window. */
91 int fullLines; /* Number of lines that fit are completely
92 * visible in window. There may be one
93 * additional line at the bottom that is
94 * partially visible. */
95 int partialLine; /* 0 means that the window holds exactly
96 * fullLines lines. 1 means that there is
97 * one additional line that is partially
98 * visble. */
99 int setGrid; /* Non-zero means pass gridding information
100 * to window manager. */
101
102 /*
103 * Information to support horizontal scrolling:
104 */
105
106 int maxWidth; /* Width (in pixels) of widest string in
107 * listbox. */
108 int xScrollUnit; /* Number of pixels in one "unit" for
109 * horizontal scrolling (window scrolls
110 * horizontally in increments of this size).
111 * This is an average character size. */
112 int xOffset; /* The left edge of each string in the
113 * listbox is offset to the left by this
114 * many pixels (0 means no offset, positive
115 * means there is an offset). */
116
117 /*
118 * Information about what's selected or active, if any.
119 */
120
121 Tk_Uid selectMode; /* Selection style: single, browse, multiple,
122 * or extended. This value isn't used in C
123 * code, but the Tcl bindings use it. */
124 int numSelected; /* Number of elements currently selected. */
125 int selectAnchor; /* Fixed end of selection (i.e. element
126 * at which selection was started.) */
127 int exportSelection; /* Non-zero means tie internal listbox
128 * to X selection. */
129 int active; /* Index of "active" element (the one that
130 * has been selected by keyboard traversal).
131 * -1 means none. */
132
133 /*
134 * Information for scanning:
135 */
136
137 int scanMarkX; /* X-position at which scan started (e.g.
138 * button was pressed here). */
139 int scanMarkY; /* Y-position at which scan started (e.g.
140 * button was pressed here). */
141 int scanMarkXOffset; /* Value of "xOffset" field when scan
142 * started. */
143 int scanMarkYIndex; /* Index of line that was at top of window
144 * when scan started. */
145
146 /*
147 * Miscellaneous information:
148 */
149
150 Tk_Cursor cursor; /* Current cursor for window, or None. */
151 char *takeFocus; /* Value of -takefocus option; not used in
152 * the C code, but used by keyboard traversal
153 * scripts. Malloc'ed, but may be NULL. */
154 char *yScrollCmd; /* Command prefix for communicating with
155 * vertical scrollbar. NULL means no command
156 * to issue. Malloc'ed. */
157 char *xScrollCmd; /* Command prefix for communicating with
158 * horizontal scrollbar. NULL means no command
159 * to issue. Malloc'ed. */
160 int flags; /* Various flag bits: see below for
161 * definitions. */
162 } Listbox;
163
164 /*
165 * ItemAttr structures are used to store item configuration information for
166 * the items in a listbox
167 */
168 typedef struct {
169 Tk_3DBorder border; /* Used for drawing background around text */
170 Tk_3DBorder selBorder; /* Used for selected text */
171 XColor *fgColor; /* Text color in normal mode. */
172 XColor *selFgColor; /* Text color in selected mode. */
173 } ItemAttr;
174
175 /*
176 * Flag bits for listboxes:
177 *
178 * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
179 * has already been queued to redraw
180 * this window.
181 * UPDATE_V_SCROLLBAR: Non-zero means vertical scrollbar needs
182 * to be updated.
183 * UPDATE_H_SCROLLBAR: Non-zero means horizontal scrollbar needs
184 * to be updated.
185 * GOT_FOCUS: Non-zero means this widget currently
186 * has the input focus.
187 * MAXWIDTH_IS_STALE: Stored maxWidth may be out-of-date
188 * LISTBOX_DELETED: This listbox has been effectively destroyed.
189 */
190
191 #define REDRAW_PENDING 1
192 #define UPDATE_V_SCROLLBAR 2
193 #define UPDATE_H_SCROLLBAR 4
194 #define GOT_FOCUS 8
195 #define MAXWIDTH_IS_STALE 16
196 #define LISTBOX_DELETED 32
197
198 /*
199 * The optionSpecs table defines the valid configuration options for the
200 * listbox widget
201 */
202 static Tk_OptionSpec optionSpecs[] = {
203 {TK_OPTION_BORDER, "-background", "background", "Background",
204 DEF_LISTBOX_BG_COLOR, -1, Tk_Offset(Listbox, normalBorder),
205 0, (ClientData) DEF_LISTBOX_BG_MONO, 0},
206 {TK_OPTION_SYNONYM, "-bd", (char *) NULL, (char *) NULL,
207 (char *) NULL, 0, -1, 0, (ClientData) "-borderwidth", 0},
208 {TK_OPTION_SYNONYM, "-bg", (char *) NULL, (char *) NULL,
209 (char *) NULL, 0, -1, 0, (ClientData) "-background", 0},
210 {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
211 DEF_LISTBOX_BORDER_WIDTH, -1, Tk_Offset(Listbox, borderWidth),
212 0, 0, 0},
213 {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor",
214 DEF_LISTBOX_CURSOR, -1, Tk_Offset(Listbox, cursor),
215 TK_OPTION_NULL_OK, 0, 0},
216 {TK_OPTION_BOOLEAN, "-exportselection", "exportSelection",
217 "ExportSelection", DEF_LISTBOX_EXPORT_SELECTION, -1,
218 Tk_Offset(Listbox, exportSelection), 0, 0, 0},
219 {TK_OPTION_SYNONYM, "-fg", "foreground", (char *) NULL,
220 (char *) NULL, 0, -1, 0, (ClientData) "-foreground", 0},
221 {TK_OPTION_FONT, "-font", "font", "Font",
222 DEF_LISTBOX_FONT, -1, Tk_Offset(Listbox, tkfont), 0, 0, 0},
223 {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground",
224 DEF_LISTBOX_FG, -1, Tk_Offset(Listbox, fgColorPtr), 0, 0, 0},
225 {TK_OPTION_INT, "-height", "height", "Height",
226 DEF_LISTBOX_HEIGHT, -1, Tk_Offset(Listbox, height), 0, 0, 0},
227 {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground",
228 "HighlightBackground", DEF_LISTBOX_HIGHLIGHT_BG, -1,
229 Tk_Offset(Listbox, highlightBgColorPtr), 0, 0, 0},
230 {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
231 DEF_LISTBOX_HIGHLIGHT, -1, Tk_Offset(Listbox, highlightColorPtr),
232 0, 0, 0},
233 {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness",
234 "HighlightThickness", DEF_LISTBOX_HIGHLIGHT_WIDTH, -1,
235 Tk_Offset(Listbox, highlightWidth), 0, 0, 0},
236 {TK_OPTION_RELIEF, "-relief", "relief", "Relief",
237 DEF_LISTBOX_RELIEF, -1, Tk_Offset(Listbox, relief), 0, 0, 0},
238 {TK_OPTION_BORDER, "-selectbackground", "selectBackground", "Foreground",
239 DEF_LISTBOX_SELECT_COLOR, -1, Tk_Offset(Listbox, selBorder),
240 0, (ClientData) DEF_LISTBOX_SELECT_MONO, 0},
241 {TK_OPTION_PIXELS, "-selectborderwidth", "selectBorderWidth",
242 "BorderWidth", DEF_LISTBOX_SELECT_BD, -1,
243 Tk_Offset(Listbox, selBorderWidth), 0, 0, 0},
244 {TK_OPTION_COLOR, "-selectforeground", "selectForeground", "Background",
245 DEF_LISTBOX_SELECT_FG_COLOR, -1, Tk_Offset(Listbox, selFgColorPtr),
246 0, (ClientData) DEF_LISTBOX_SELECT_FG_MONO, 0},
247 {TK_OPTION_STRING, "-selectmode", "selectMode", "SelectMode",
248 DEF_LISTBOX_SELECT_MODE, -1, Tk_Offset(Listbox, selectMode),
249 TK_OPTION_NULL_OK, 0, 0},
250 {TK_OPTION_BOOLEAN, "-setgrid", "setGrid", "SetGrid",
251 DEF_LISTBOX_SET_GRID, -1, Tk_Offset(Listbox, setGrid), 0, 0, 0},
252 {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus",
253 DEF_LISTBOX_TAKE_FOCUS, -1, Tk_Offset(Listbox, takeFocus),
254 TK_OPTION_NULL_OK, 0, 0},
255 {TK_OPTION_INT, "-width", "width", "Width",
256 DEF_LISTBOX_WIDTH, -1, Tk_Offset(Listbox, width), 0, 0, 0},
257 {TK_OPTION_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
258 DEF_LISTBOX_SCROLL_COMMAND, -1, Tk_Offset(Listbox, xScrollCmd),
259 TK_OPTION_NULL_OK, 0, 0},
260 {TK_OPTION_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
261 DEF_LISTBOX_SCROLL_COMMAND, -1, Tk_Offset(Listbox, yScrollCmd),
262 TK_OPTION_NULL_OK, 0, 0},
263 {TK_OPTION_STRING, "-listvariable", "listVariable", "Variable",
264 DEF_LISTBOX_LIST_VARIABLE, -1, Tk_Offset(Listbox, listVarName),
265 TK_OPTION_NULL_OK, 0, 0},
266 {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
267 (char *) NULL, 0, -1, 0, 0, 0}
268 };
269
270 /*
271 * The itemAttrOptionSpecs table defines the valid configuration options for
272 * listbox items
273 */
274 static Tk_OptionSpec itemAttrOptionSpecs[] = {
275 {TK_OPTION_BORDER, "-background", "background", "Background",
276 (char *)NULL, -1, Tk_Offset(ItemAttr, border),
277 TK_OPTION_NULL_OK|TK_OPTION_DONT_SET_DEFAULT,
278 (ClientData) DEF_LISTBOX_BG_MONO, 0},
279 {TK_OPTION_SYNONYM, "-bg", (char *) NULL, (char *) NULL,
280 (char *) NULL, 0, -1, 0, (ClientData) "-background", 0},
281 {TK_OPTION_SYNONYM, "-fg", "foreground", (char *) NULL,
282 (char *) NULL, 0, -1, 0, (ClientData) "-foreground", 0},
283 {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground",
284 (char *) NULL, -1, Tk_Offset(ItemAttr, fgColor),
285 TK_OPTION_NULL_OK|TK_OPTION_DONT_SET_DEFAULT, 0, 0},
286 {TK_OPTION_BORDER, "-selectbackground", "selectBackground", "Foreground",
287 (char *) NULL, -1, Tk_Offset(ItemAttr, selBorder),
288 TK_OPTION_NULL_OK|TK_OPTION_DONT_SET_DEFAULT,
289 (ClientData) DEF_LISTBOX_SELECT_MONO, 0},
290 {TK_OPTION_COLOR, "-selectforeground", "selectForeground", "Background",
291 (char *) NULL, -1, Tk_Offset(ItemAttr, selFgColor),
292 TK_OPTION_NULL_OK|TK_OPTION_DONT_SET_DEFAULT,
293 (ClientData) DEF_LISTBOX_SELECT_FG_MONO, 0},
294 {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
295 (char *) NULL, 0, -1, 0, 0, 0}
296 };
297
298 /*
299 * The following tables define the listbox widget commands (and sub-
300 * commands) and map the indexes into the string tables into
301 * enumerated types used to dispatch the listbox widget command.
302 */
303 static char *commandNames[] = {
304 "activate", "bbox", "cget", "configure", "curselection", "delete", "get",
305 "index", "insert", "itemcget", "itemconfigure", "nearest", "scan",
306 "see", "selection", "size", "xview", "yview",
307 (char *) NULL
308 };
309
310 enum command {
311 COMMAND_ACTIVATE, COMMAND_BBOX, COMMAND_CGET, COMMAND_CONFIGURE,
312 COMMAND_CURSELECTION, COMMAND_DELETE, COMMAND_GET, COMMAND_INDEX,
313 COMMAND_INSERT, COMMAND_ITEMCGET, COMMAND_ITEMCONFIGURE,
314 COMMAND_NEAREST, COMMAND_SCAN, COMMAND_SEE, COMMAND_SELECTION,
315 COMMAND_SIZE, COMMAND_XVIEW, COMMAND_YVIEW
316 };
317
318 static char *selCommandNames[] = {
319 "anchor", "clear", "includes", "set", (char *) NULL
320 };
321
322 enum selcommand {
323 SELECTION_ANCHOR, SELECTION_CLEAR, SELECTION_INCLUDES, SELECTION_SET
324 };
325
326 static char *scanCommandNames[] = {
327 "mark", "dragto", (char *) NULL
328 };
329
330 enum scancommand {
331 SCAN_MARK, SCAN_DRAGTO
332 };
333
334 static char *indexNames[] = {
335 "active", "anchor", "end", (char *)NULL
336 };
337
338 enum indices {
339 INDEX_ACTIVE, INDEX_ANCHOR, INDEX_END
340 };
341
342
343 /* Declarations for procedures defined later in this file */
344 static void ChangeListboxOffset _ANSI_ARGS_((Listbox *listPtr,
345 int offset));
346 static void ChangeListboxView _ANSI_ARGS_((Listbox *listPtr,
347 int index));
348 static int ConfigureListbox _ANSI_ARGS_((Tcl_Interp *interp,
349 Listbox *listPtr, int objc, Tcl_Obj *CONST objv[],
350 int flags));
351 static int ConfigureListboxItem _ANSI_ARGS_ ((Tcl_Interp *interp,
352 Listbox *listPtr, ItemAttr *attrs, int objc,
353 Tcl_Obj *CONST objv[]));
354 static int ListboxDeleteSubCmd _ANSI_ARGS_((Listbox *listPtr,
355 int first, int last));
356 static void DestroyListbox _ANSI_ARGS_((char *memPtr));
357 static void DestroyListboxOptionTables _ANSI_ARGS_ (
358 (ClientData clientData, Tcl_Interp *interp));
359 static void DisplayListbox _ANSI_ARGS_((ClientData clientData));
360 static int GetListboxIndex _ANSI_ARGS_((Tcl_Interp *interp,
361 Listbox *listPtr, Tcl_Obj *index, int endIsSize,
362 int *indexPtr));
363 static int ListboxInsertSubCmd _ANSI_ARGS_((Listbox *listPtr,
364 int index, int objc, Tcl_Obj *CONST objv[]));
365 static void ListboxCmdDeletedProc _ANSI_ARGS_((
366 ClientData clientData));
367 static void ListboxComputeGeometry _ANSI_ARGS_((Listbox *listPtr,
368 int fontChanged, int maxIsStale, int updateGrid));
369 static void ListboxEventProc _ANSI_ARGS_((ClientData clientData,
370 XEvent *eventPtr));
371 static int ListboxFetchSelection _ANSI_ARGS_((
372 ClientData clientData, int offset, char *buffer,
373 int maxBytes));
374 static void ListboxLostSelection _ANSI_ARGS_((
375 ClientData clientData));
376 static void EventuallyRedrawRange _ANSI_ARGS_((Listbox *listPtr,
377 int first, int last));
378 static void ListboxScanTo _ANSI_ARGS_((Listbox *listPtr,
379 int x, int y));
380 static int ListboxSelect _ANSI_ARGS_((Listbox *listPtr,
381 int first, int last, int select));
382 static void ListboxUpdateHScrollbar _ANSI_ARGS_(
383 (Listbox *listPtr));
384 static void ListboxUpdateVScrollbar _ANSI_ARGS_(
385 (Listbox *listPtr));
386 static int ListboxWidgetObjCmd _ANSI_ARGS_((ClientData clientData,
387 Tcl_Interp *interp, int objc,
388 Tcl_Obj *CONST objv[]));
389 static int ListboxBboxSubCmd _ANSI_ARGS_ ((Tcl_Interp *interp,
390 Listbox *listPtr, int index));
391 static int ListboxSelectionSubCmd _ANSI_ARGS_ (
392 (Tcl_Interp *interp, Listbox *listPtr, int objc,
393 Tcl_Obj *CONST objv[]));
394 static int ListboxXviewSubCmd _ANSI_ARGS_ ((Tcl_Interp *interp,
395 Listbox *listPtr, int objc,
396 Tcl_Obj *CONST objv[]));
397 static int ListboxYviewSubCmd _ANSI_ARGS_ ((Tcl_Interp *interp,
398 Listbox *listPtr, int objc,
399 Tcl_Obj *CONST objv[]));
400 static ItemAttr * ListboxGetItemAttributes _ANSI_ARGS_ (
401 (Tcl_Interp *interp, Listbox *listPtr, int index));
402 static void ListboxWorldChanged _ANSI_ARGS_((
403 ClientData instanceData));
404 static int NearestListboxElement _ANSI_ARGS_((Listbox *listPtr,
405 int y));
406 static char * ListboxListVarProc _ANSI_ARGS_ ((ClientData clientData,
407 Tcl_Interp *interp, char *name1, char *name2,
408 int flags));
409 static void MigrateHashEntries _ANSI_ARGS_ ((Tcl_HashTable *table,
410 int first, int last, int offset));
411 /*
412 * The structure below defines button class behavior by means of procedures
413 * that can be invoked from generic window code.
414 */
415
416 static TkClassProcs listboxClass = {
417 NULL, /* createProc. */
418 ListboxWorldChanged, /* geometryProc. */
419 NULL /* modalProc. */
420 };
421
422
423 /*
424 *--------------------------------------------------------------
425 *
426 * Tk_ListboxObjCmd --
427 *
428 * This procedure is invoked to process the "listbox" Tcl
429 * command. See the user documentation for details on what
430 * it does.
431 *
432 * Results:
433 * A standard Tcl result.
434 *
435 * Side effects:
436 * See the user documentation.
437 *
438 *--------------------------------------------------------------
439 */
440
441 int
442 Tk_ListboxObjCmd(clientData, interp, objc, objv)
443 ClientData clientData; /* Either NULL or pointer to option table */
444 Tcl_Interp *interp; /* Current interpreter. */
445 int objc; /* Number of arguments. */
446 Tcl_Obj *CONST objv[]; /* Argument objects. */
447 {
448 register Listbox *listPtr;
449 Tk_Window tkwin;
450 ListboxOptionTables *optionTables;
451
452 optionTables = (ListboxOptionTables *)clientData;
453 if (optionTables == NULL) {
454 Tcl_CmdInfo info;
455 char *name;
456
457 /*
458 * We haven't created the option tables for this widget class yet.
459 * Do it now and save the a pointer to them as the ClientData for
460 * the command, so future invocations will have access to it.
461 */
462 optionTables =
463 (ListboxOptionTables *) ckalloc(sizeof(ListboxOptionTables));
464 /* Set up an exit handler to free the optionTables struct */
465 Tcl_SetAssocData(interp, "ListboxOptionTables",
466 DestroyListboxOptionTables, (ClientData) optionTables);
467
468 /* Create the listbox option table and the listbox item option table */
469 optionTables->listboxOptionTable =
470 Tk_CreateOptionTable(interp, optionSpecs);
471 optionTables->itemAttrOptionTable =
472 Tk_CreateOptionTable(interp, itemAttrOptionSpecs);
473
474 /* Store a pointer to the tables as the ClientData for the command */
475 name = Tcl_GetString(objv[0]);
476 Tcl_GetCommandInfo(interp, name, &info);
477 info.objClientData = (ClientData) optionTables;
478 Tcl_SetCommandInfo(interp, name, &info);
479 }
480
481 if (objc < 2) {
482 Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?");
483 return TCL_ERROR;
484 }
485
486 tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp),
487 Tcl_GetString(objv[1]), (char *) NULL);
488 if (tkwin == NULL) {
489 return TCL_ERROR;
490 }
491
492 /*
493 * Initialize the fields of the structure that won't be initialized
494 * by ConfigureListbox, or that ConfigureListbox requires to be
495 * initialized already (e.g. resource pointers).
496 */
497 listPtr = (Listbox *) ckalloc(sizeof(Listbox));
498 listPtr->tkwin = tkwin;
499 listPtr->display = Tk_Display(tkwin);
500 listPtr->interp = interp;
501 listPtr->widgetCmd = Tcl_CreateObjCommand(interp,
502 Tk_PathName(listPtr->tkwin), ListboxWidgetObjCmd,
503 (ClientData) listPtr, ListboxCmdDeletedProc);
504 listPtr->optionTable = optionTables->listboxOptionTable;
505 listPtr->itemAttrOptionTable = optionTables->itemAttrOptionTable;
506 listPtr->listVarName = NULL;
507 listPtr->listObj = NULL;
508 listPtr->selection =
509 (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
510 Tcl_InitHashTable(listPtr->selection, TCL_ONE_WORD_KEYS);
511 listPtr->itemAttrTable =
512 (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
513 Tcl_InitHashTable(listPtr->itemAttrTable, TCL_ONE_WORD_KEYS);
514 listPtr->nElements = 0;
515 listPtr->normalBorder = NULL;
516 listPtr->borderWidth = 0;
517 listPtr->relief = TK_RELIEF_RAISED;
518 listPtr->highlightWidth = 0;
519 listPtr->highlightBgColorPtr = NULL;
520 listPtr->highlightColorPtr = NULL;
521 listPtr->inset = 0;
522 listPtr->tkfont = NULL;
523 listPtr->fgColorPtr = NULL;
524 listPtr->textGC = None;
525 listPtr->selBorder = NULL;
526 listPtr->selBorderWidth = 0;
527 listPtr->selFgColorPtr = None;
528 listPtr->selTextGC = None;
529 listPtr->width = 0;
530 listPtr->height = 0;
531 listPtr->lineHeight = 0;
532 listPtr->topIndex = 0;
533 listPtr->fullLines = 1;
534 listPtr->partialLine = 0;
535 listPtr->setGrid = 0;
536 listPtr->maxWidth = 0;
537 listPtr->xScrollUnit = 1;
538 listPtr->xOffset = 0;
539 listPtr->selectMode = NULL;
540 listPtr->numSelected = 0;
541 listPtr->selectAnchor = 0;
542 listPtr->exportSelection = 1;
543 listPtr->active = 0;
544 listPtr->scanMarkX = 0;
545 listPtr->scanMarkY = 0;
546 listPtr->scanMarkXOffset = 0;
547 listPtr->scanMarkYIndex = 0;
548 listPtr->cursor = None;
549 listPtr->takeFocus = NULL;
550 listPtr->xScrollCmd = NULL;
551 listPtr->yScrollCmd = NULL;
552 listPtr->flags = 0;
553
554 Tk_SetClass(listPtr->tkwin, "Listbox");
555 TkSetClassProcs(listPtr->tkwin, &listboxClass, (ClientData) listPtr);
556 Tk_CreateEventHandler(listPtr->tkwin,
557 ExposureMask|StructureNotifyMask|FocusChangeMask,
558 ListboxEventProc, (ClientData) listPtr);
559 Tk_CreateSelHandler(listPtr->tkwin, XA_PRIMARY, XA_STRING,
560 ListboxFetchSelection, (ClientData) listPtr, XA_STRING);
561 if (Tk_InitOptions(interp, (char *)listPtr,
562 optionTables->listboxOptionTable, tkwin) != TCL_OK) {
563 Tk_DestroyWindow(listPtr->tkwin);
564 return TCL_ERROR;
565 }
566
567 if (ConfigureListbox(interp, listPtr, objc-2, objv+2, 0) != TCL_OK) {
568 Tk_DestroyWindow(listPtr->tkwin);
569 return TCL_ERROR;
570 }
571
572 Tcl_SetResult(interp, Tk_PathName(listPtr->tkwin), TCL_STATIC);
573 return TCL_OK;
574 }
575
576 /*
577 *----------------------------------------------------------------------
578 *
579 * ListboxWidgetObjCmd --
580 *
581 * This Tcl_Obj based procedure is invoked to process the Tcl command
582 * that corresponds to a widget managed by this module. See the user
583 * documentation for details on what it does.
584 *
585 * Results:
586 * A standard Tcl result.
587 *
588 * Side effects:
589 * See the user documentation.
590 *
591 *----------------------------------------------------------------------
592 */
593
594 static int
595 ListboxWidgetObjCmd(clientData, interp, objc, objv)
596 ClientData clientData; /* Information about listbox widget. */
597 Tcl_Interp *interp; /* Current interpreter. */
598 int objc; /* Number of arguments. */
599 Tcl_Obj *CONST objv[]; /* Arguments as Tcl_Obj's. */
600 {
601 register Listbox *listPtr = (Listbox *) clientData;
602 int cmdIndex, index;
603 int result = TCL_OK;
604
605 if (objc < 2) {
606 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
607 return TCL_ERROR;
608 }
609 Tcl_Preserve((ClientData)listPtr);
610
611 /*
612 * Parse the command by looking up the second argument in the list
613 * of valid subcommand names
614 */
615 result = Tcl_GetIndexFromObj(interp, objv[1], commandNames,
616 "option", 0, &cmdIndex);
617 if (result != TCL_OK) {
618 Tcl_Release((ClientData)listPtr);
619 return result;
620 }
621
622 /* The subcommand was valid, so continue processing */
623 switch (cmdIndex) {
624 case COMMAND_ACTIVATE: {
625 if (objc != 3) {
626 Tcl_WrongNumArgs(interp, 2, objv, "index");
627 result = TCL_ERROR;
628 break;
629 }
630 result = GetListboxIndex(interp, listPtr, objv[2], 0, &index);
631 if (result != TCL_OK) {
632 break;
633 }
634 if (index >= listPtr->nElements) {
635 index = listPtr->nElements-1;
636 }
637 if (index < 0) {
638 index = 0;
639 }
640 listPtr->active = index;
641 EventuallyRedrawRange(listPtr, listPtr->active, listPtr->active);
642 result = TCL_OK;
643 break;
644 }
645
646 case COMMAND_BBOX: {
647 if (objc != 3) {
648 Tcl_WrongNumArgs(interp, 2, objv, "index");
649 result = TCL_ERROR;
650 break;
651 }
652 result = GetListboxIndex(interp, listPtr, objv[2], 0, &index);
653 if (result != TCL_OK) {
654 break;
655 }
656
657 result = ListboxBboxSubCmd(interp, listPtr, index);
658 break;
659 }
660
661 case COMMAND_CGET: {
662 Tcl_Obj *objPtr;
663 if (objc != 3) {
664 Tcl_WrongNumArgs(interp, 2, objv, "option");
665 result = TCL_ERROR;
666 break;
667 }
668
669 objPtr = Tk_GetOptionValue(interp, (char *)listPtr,
670 listPtr->optionTable, objv[2], listPtr->tkwin);
671 if (objPtr == NULL) {
672 result = TCL_ERROR;
673 break;
674 }
675 Tcl_SetObjResult(interp, objPtr);
676 result = TCL_OK;
677 break;
678 }
679
680 case COMMAND_CONFIGURE: {
681 Tcl_Obj *objPtr;
682 if (objc <= 3) {
683 objPtr = Tk_GetOptionInfo(interp, (char *) listPtr,
684 listPtr->optionTable,
685 (objc == 3) ? objv[2] : (Tcl_Obj *) NULL,
686 listPtr->tkwin);
687 if (objPtr == NULL) {
688 result = TCL_ERROR;
689 break;
690 } else {
691 Tcl_SetObjResult(interp, objPtr);
692 result = TCL_OK;
693 }
694 } else {
695 result = ConfigureListbox(interp, listPtr, objc-2, objv+2, 0);
696 }
697 break;
698 }
699
700 case COMMAND_CURSELECTION: {
701 char indexStringRep[TCL_INTEGER_SPACE];
702 int i;
703 if (objc != 2) {
704 Tcl_WrongNumArgs(interp, 2, objv, NULL);
705 result = TCL_ERROR;
706 break;
707 }
708 /*
709 * Of course, it would be more efficient to use the Tcl_HashTable
710 * search functions (Tcl_FirstHashEntry, Tcl_NextHashEntry), but
711 * then the result wouldn't be in sorted order. So instead we
712 * loop through the indices in order, adding them to the result
713 * if they are selected
714 */
715 for (i = 0; i < listPtr->nElements; i++) {
716 if (Tcl_FindHashEntry(listPtr->selection, (char *)i) != NULL) {
717 sprintf(indexStringRep, "%d", i);
718 Tcl_AppendElement(interp, indexStringRep);
719 }
720 }
721 result = TCL_OK;
722 break;
723 }
724
725 case COMMAND_DELETE: {
726 int first, last;
727 if ((objc < 3) || (objc > 4)) {
728 Tcl_WrongNumArgs(interp, 2, objv,
729 "firstIndex ?lastIndex?");
730 result = TCL_ERROR;
731 break;
732 }
733
734 result = GetListboxIndex(interp, listPtr, objv[2], 0, &first);
735 if (result != TCL_OK) {
736 break;
737 }
738 if (first < listPtr->nElements) {
739 /*
740 * if a "last index" was given, get it now; otherwise, use the
741 * first index as the last index
742 */
743 if (objc == 4) {
744 result = GetListboxIndex(interp, listPtr,
745 objv[3], 0, &last);
746 if (result != TCL_OK) {
747 break;
748 }
749 } else {
750 last = first;
751 }
752 if (last >= listPtr->nElements) {
753 last = listPtr->nElements - 1;
754 }
755 result = ListboxDeleteSubCmd(listPtr, first, last);
756 } else {
757 result = TCL_OK;
758 }
759 break;
760 }
761
762 case COMMAND_GET: {
763 int first, last;
764 Tcl_Obj **elemPtrs;
765 int listLen;
766 if (objc != 3 && objc != 4) {
767 Tcl_WrongNumArgs(interp, 2, objv, "firstIndex ?lastIndex?");
768 result = TCL_ERROR;
769 break;
770 }
771 result = GetListboxIndex(interp, listPtr, objv[2], 0, &first);
772 if (result != TCL_OK) {
773 break;
774 }
775 last = first;
776 if (objc == 4) {
777 result = GetListboxIndex(interp, listPtr, objv[3], 0, &last);
778 if (result != TCL_OK) {
779 break;
780 }
781 }
782 if (first >= listPtr->nElements) {
783 result = TCL_OK;
784 break;
785 }
786 if (last >= listPtr->nElements) {
787 last = listPtr->nElements - 1;
788 }
789 if (first < 0) {
790 first = 0;
791 }
792 if (first > last) {
793 result = TCL_OK;
794 break;
795 }
796 result = Tcl_ListObjGetElements(interp, listPtr->listObj, &listLen,
797 &elemPtrs);
798 if (result != TCL_OK) {
799 break;
800 }
801 if (objc == 3) {
802 /*
803 * One element request - we return a string
804 */
805 Tcl_SetObjResult(interp, elemPtrs[first]);
806 } else {
807 Tcl_SetListObj(Tcl_GetObjResult(interp), (last - first + 1),
808 &(elemPtrs[first]));
809 }
810 result = TCL_OK;
811 break;
812 }
813
814 case COMMAND_INDEX:{
815 char buf[TCL_INTEGER_SPACE];
816 if (objc != 3) {
817 Tcl_WrongNumArgs(interp, 2, objv, "index");
818 result = TCL_ERROR;
819 break;
820 }
821 result = GetListboxIndex(interp, listPtr, objv[2], 1, &index);
822 if (result != TCL_OK) {
823 break;
824 }
825 sprintf(buf, "%d", index);
826 Tcl_SetResult(interp, buf, TCL_VOLATILE);
827 result = TCL_OK;
828 break;
829 }
830
831 case COMMAND_INSERT: {
832 if (objc < 3) {
833 Tcl_WrongNumArgs(interp, 2, objv,
834 "index ?element element ...?");
835 result = TCL_ERROR;
836 break;
837 }
838
839 result = GetListboxIndex(interp, listPtr, objv[2], 1, &index);
840 if (result != TCL_OK) {
841 break;
842 }
843 result = ListboxInsertSubCmd(listPtr, index, objc-3, objv+3);
844 break;
845 }
846
847 case COMMAND_ITEMCGET: {
848 Tcl_Obj *objPtr;
849 ItemAttr *attrPtr;
850 if (objc != 4) {
851 Tcl_WrongNumArgs(interp, 2, objv, "index option");
852 result = TCL_ERROR;
853 break;
854 }
855
856 result = GetListboxIndex(interp, listPtr, objv[2], 0, &index);
857 if (result != TCL_OK) {
858 break;
859 }
860
861 if (index < 0 || index >= listPtr->nElements) {
862 Tcl_AppendResult(interp, "item number \"",
863 Tcl_GetString(objv[2]), "\" out of range",
864 (char *)NULL);
865 result = TCL_ERROR;
866 break;
867 }
868
869 attrPtr = ListboxGetItemAttributes(interp, listPtr, index);
870
871 objPtr = Tk_GetOptionValue(interp, (char *)attrPtr,
872 listPtr->itemAttrOptionTable, objv[3], listPtr->tkwin);
873 if (objPtr == NULL) {
874 result = TCL_ERROR;
875 break;
876 }
877 Tcl_SetObjResult(interp, objPtr);
878 result = TCL_OK;
879 break;
880 }
881
882 case COMMAND_ITEMCONFIGURE: {
883 Tcl_Obj *objPtr;
884 ItemAttr *attrPtr;
885 if (objc < 3) {
886 Tcl_WrongNumArgs(interp, 2, objv,
887 "index ?option? ?value? ?option value ...?");
888 result = TCL_ERROR;
889 break;
890 }
891
892 result = GetListboxIndex(interp, listPtr, objv[2], 0, &index);
893 if (result != TCL_OK) {
894 break;
895 }
896
897 if (index < 0 || index >= listPtr->nElements) {
898 Tcl_AppendResult(interp, "item number \"",
899 Tcl_GetString(objv[2]), "\" out of range",
900 (char *)NULL);
901 result = TCL_ERROR;
902 break;
903 }
904
905 attrPtr = ListboxGetItemAttributes(interp, listPtr, index);
906 if (objc <= 4) {
907 objPtr = Tk_GetOptionInfo(interp, (char *)attrPtr,
908 listPtr->itemAttrOptionTable,
909 (objc == 4) ? objv[3] : (Tcl_Obj *) NULL,
910 listPtr->tkwin);
911 if (objPtr == NULL) {
912 result = TCL_ERROR;
913 break;
914 } else {
915 Tcl_SetObjResult(interp, objPtr);
916 result = TCL_OK;
917 }
918 } else {
919 result = ConfigureListboxItem(interp, listPtr, attrPtr,
920 objc-3, objv+3);
921 }
922 break;
923 }
924
925 case COMMAND_NEAREST: {
926 char buf[TCL_INTEGER_SPACE];
927 int y;
928 if (objc != 3) {
929 Tcl_WrongNumArgs(interp, 2, objv, "y");
930 result = TCL_ERROR;
931 break;
932 }
933
934 result = Tcl_GetIntFromObj(interp, objv[2], &y);
935 if (result != TCL_OK) {
936 break;
937 }
938 index = NearestListboxElement(listPtr, y);
939 sprintf(buf, "%d", index);
940 Tcl_SetResult(interp, buf, TCL_VOLATILE);
941 result = TCL_OK;
942 break;
943 }
944
945 case COMMAND_SCAN: {
946 int x, y, scanCmdIndex;
947
948 if (objc != 5) {
949 Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x y");
950 result = TCL_ERROR;
951 break;
952 }
953
954 if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK
955 || Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) {
956 result = TCL_ERROR;
957 break;
958 }
959
960 result = Tcl_GetIndexFromObj(interp, objv[2], scanCommandNames,
961 "option", 0, &scanCmdIndex);
962 if (result != TCL_OK) {
963 break;
964 }
965 switch (scanCmdIndex) {
966 case SCAN_MARK: {
967 listPtr->scanMarkX = x;
968 listPtr->scanMarkY = y;
969 listPtr->scanMarkXOffset = listPtr->xOffset;
970 listPtr->scanMarkYIndex = listPtr->topIndex;
971 break;
972 }
973 case SCAN_DRAGTO: {
974 ListboxScanTo(listPtr, x, y);
975 break;
976 }
977 }
978 result = TCL_OK;
979 break;
980 }
981
982 case COMMAND_SEE: {
983 int diff;
984 if (objc != 3) {
985 Tcl_WrongNumArgs(interp, 2, objv, "index");
986 result = TCL_ERROR;
987 break;
988 }
989 result = GetListboxIndex(interp, listPtr, objv[2], 0, &index);
990 if (result != TCL_OK) {
991 break;
992 }
993 if (index >= listPtr->nElements) {
994 index = listPtr->nElements - 1;
995 }
996 if (index < 0) {
997 index = 0;
998 }
999 diff = listPtr->topIndex - index;
1000 if (diff > 0) {
1001 if (diff <= (listPtr->fullLines/3)) {
1002 ChangeListboxView(listPtr, index);
1003 } else {
1004 ChangeListboxView(listPtr,
1005 index - (listPtr->fullLines-1)/2);
1006 }
1007 } else {
1008 diff = index - (listPtr->topIndex + listPtr->fullLines - 1);
1009 if (diff > 0) {
1010 if (diff <= (listPtr->fullLines/3)) {
1011 ChangeListboxView(listPtr, listPtr->topIndex + diff);
1012 } else {
1013 ChangeListboxView(listPtr,
1014 index - (listPtr->fullLines-1)/2);
1015 }
1016 }
1017 }
1018 result = TCL_OK;
1019 break;
1020 }
1021
1022 case COMMAND_SELECTION: {
1023 result = ListboxSelectionSubCmd(interp, listPtr, objc, objv);
1024 break;
1025 }
1026
1027 case COMMAND_SIZE: {
1028 char buf[TCL_INTEGER_SPACE];
1029 if (objc != 2) {
1030 Tcl_WrongNumArgs(interp, 2, objv, NULL);
1031 result = TCL_ERROR;
1032 break;
1033 }
1034 sprintf(buf, "%d", listPtr->nElements);
1035 Tcl_SetResult(interp, buf, TCL_VOLATILE);
1036 result = TCL_OK;
1037 break;
1038 }
1039
1040 case COMMAND_XVIEW: {
1041 result = ListboxXviewSubCmd(interp, listPtr, objc, objv);
1042 break;
1043 }
1044
1045 case COMMAND_YVIEW: {
1046 result = ListboxYviewSubCmd(interp, listPtr, objc, objv);
1047 break;
1048 }
1049 }
1050 Tcl_Release((ClientData)listPtr);
1051 return result;
1052 }
1053
1054 /*
1055 *----------------------------------------------------------------------
1056 *
1057 * ListboxBboxSubCmd --
1058 *
1059 * This procedure is invoked to process a listbox bbox request.
1060 * See the user documentation for more information.
1061 *
1062 * Results:
1063 * A standard Tcl result.
1064 *
1065 * Side effects:
1066 * For valid indices, places the bbox of the requested element in
1067 * the interpreter's result.
1068 *
1069 *----------------------------------------------------------------------
1070 */
1071
1072 static int
1073 ListboxBboxSubCmd(interp, listPtr, index)
1074 Tcl_Interp *interp; /* Pointer to the calling Tcl interpreter */
1075 Listbox *listPtr; /* Information about the listbox */
1076 int index; /* Index of the element to get bbox info on */
1077 {
1078 int lastVisibleIndex;
1079 /* Determine the index of the last visible item in the listbox */
1080 lastVisibleIndex = listPtr->topIndex + listPtr->fullLines
1081 + listPtr->partialLine;
1082 if (listPtr->nElements < lastVisibleIndex) {
1083 lastVisibleIndex = listPtr->nElements;
1084 }
1085
1086 /* Only allow bbox requests for indices that are visible */
1087 if ((listPtr->topIndex <= index) && (index < lastVisibleIndex)) {
1088 char buf[TCL_INTEGER_SPACE * 4];
1089 Tcl_Obj *el;
1090 char *stringRep;
1091 int pixelWidth, stringLen, x, y, result;
1092 Tk_FontMetrics fm;
1093
1094 /* Compute the pixel width of the requested element */
1095 result = Tcl_ListObjIndex(interp, listPtr->listObj, index, &el);
1096 if (result != TCL_OK) {
1097 return result;
1098 }
1099
1100 stringRep = Tcl_GetStringFromObj(el, &stringLen);
1101 Tk_GetFontMetrics(listPtr->tkfont, &fm);
1102 pixelWidth = Tk_TextWidth(listPtr->tkfont, stringRep, stringLen);
1103
1104 x = listPtr->inset + listPtr->selBorderWidth - listPtr->xOffset;
1105 y = ((index - listPtr->topIndex)*listPtr->lineHeight)
1106 + listPtr->inset + listPtr->selBorderWidth;
1107 sprintf(buf, "%d %d %d %d", x, y, pixelWidth, fm.linespace);
1108 Tcl_SetResult(interp, buf, TCL_VOLATILE);
1109 }
1110 return TCL_OK;
1111 }
1112
1113 /*
1114 *----------------------------------------------------------------------
1115 *
1116 * ListboxSelectionSubCmd --
1117 *
1118 * This procedure is invoked to process the selection sub command
1119 * for listbox widgets.
1120 *
1121 * Results:
1122 * Standard Tcl result.
1123 *
1124 * Side effects:
1125 * May set the interpreter's result field.
1126 *
1127 *----------------------------------------------------------------------
1128 */
1129
1130 static int
1131 ListboxSelectionSubCmd(interp, listPtr, objc, objv)
1132 Tcl_Interp *interp; /* Pointer to the calling Tcl interpreter */
1133 Listbox *listPtr; /* Information about the listbox */
1134 int objc; /* Number of arguments in the objv array */
1135 Tcl_Obj *CONST objv[]; /* Array of arguments to the procedure */
1136 {
1137 int selCmdIndex, first, last;
1138 int result = TCL_OK;
1139 if (objc != 4 && objc != 5) {
1140 Tcl_WrongNumArgs(interp, 2, objv, "option index ?index?");
1141 return TCL_ERROR;
1142 }
1143 result = GetListboxIndex(interp, listPtr, objv[3], 0, &first);
1144 if (result != TCL_OK) {
1145 return result;
1146 }
1147 last = first;
1148 if (objc == 5) {
1149 result = GetListboxIndex(interp, listPtr, objv[4], 0, &last);
1150 if (result != TCL_OK) {
1151 return result;
1152 }
1153 }
1154 result = Tcl_GetIndexFromObj(interp, objv[2], selCommandNames,
1155 "option", 0, &selCmdIndex);
1156 if (result != TCL_OK) {
1157 return result;
1158 }
1159 switch (selCmdIndex) {
1160 case SELECTION_ANCHOR: {
1161 if (objc != 4) {
1162 Tcl_WrongNumArgs(interp, 3, objv, "index");
1163 return TCL_ERROR;
1164 }
1165 if (first >= listPtr->nElements) {
1166 first = listPtr->nElements - 1;
1167 }
1168 if (first < 0) {
1169 first = 0;
1170 }
1171 listPtr->selectAnchor = first;
1172 result = TCL_OK;
1173 break;
1174 }
1175 case SELECTION_CLEAR: {
1176 result = ListboxSelect(listPtr, first, last, 0);
1177 break;
1178 }
1179 case SELECTION_INCLUDES: {
1180 if (objc != 4) {
1181 Tcl_WrongNumArgs(interp, 3, objv, "index");
1182 return TCL_ERROR;
1183 }
1184 if (Tcl_FindHashEntry(listPtr->selection, (char *)first)) {
1185 Tcl_SetResult(interp, "1", TCL_STATIC);
1186 } else {
1187 Tcl_SetResult(interp, "0", TCL_STATIC);
1188 }
1189 result = TCL_OK;
1190 break;
1191 }
1192 case SELECTION_SET: {
1193 result = ListboxSelect(listPtr, first, last, 1);
1194 break;
1195 }
1196 }
1197 return result;
1198 }
1199
1200 /*
1201 *----------------------------------------------------------------------
1202 *
1203 * ListboxXviewSubCmd --
1204 *
1205 * Process the listbox "xview" subcommand.
1206 *
1207 * Results:
1208 * Standard Tcl result.
1209 *
1210 * Side effects:
1211 * May change the listbox viewing area; may set the interpreter's result.
1212 *
1213 *----------------------------------------------------------------------
1214 */
1215
1216 static int
1217 ListboxXviewSubCmd(interp, listPtr, objc, objv)
1218 Tcl_Interp *interp; /* Pointer to the calling Tcl interpreter */
1219 Listbox *listPtr; /* Information about the listbox */
1220 int objc; /* Number of arguments in the objv array */
1221 Tcl_Obj *CONST objv[]; /* Array of arguments to the procedure */
1222 {
1223
1224 int index, count, type, windowWidth, windowUnits;
1225 int offset = 0; /* Initialized to stop gcc warnings. */
1226 double fraction, fraction2;
1227
1228 windowWidth = Tk_Width(listPtr->tkwin)
1229 - 2*(listPtr->inset + listPtr->selBorderWidth);
1230 if (objc == 2) {
1231 if (listPtr->maxWidth == 0) {
1232 Tcl_SetResult(interp, "0 1", TCL_STATIC);
1233 } else {
1234 char buf[TCL_DOUBLE_SPACE * 2];
1235
1236 fraction = listPtr->xOffset/((double) listPtr->maxWidth);
1237 fraction2 = (listPtr->xOffset + windowWidth)
1238 /((double) listPtr->maxWidth);
1239 if (fraction2 > 1.0) {
1240 fraction2 = 1.0;
1241 }
1242 sprintf(buf, "%g %g", fraction, fraction2);
1243 Tcl_SetResult(interp, buf, TCL_VOLATILE);
1244 }
1245 } else if (objc == 3) {
1246 if (Tcl_GetIntFromObj(interp, objv[2], &index) != TCL_OK) {
1247 return TCL_ERROR;
1248 }
1249 ChangeListboxOffset(listPtr, index*listPtr->xScrollUnit);
1250 } else {
1251 type = Tk_GetScrollInfoObj(interp, objc, objv, &fraction, &count);
1252 switch (type) {
1253 case TK_SCROLL_ERROR:
1254 return TCL_ERROR;
1255 case TK_SCROLL_MOVETO:
1256 offset = (int) (fraction*listPtr->maxWidth + 0.5);
1257 break;
1258 case TK_SCROLL_PAGES:
1259 windowUnits = windowWidth/listPtr->xScrollUnit;
1260 if (windowUnits > 2) {
1261 offset = listPtr->xOffset
1262 + count*listPtr->xScrollUnit*(windowUnits-2);
1263 } else {
1264 offset = listPtr->xOffset + count*listPtr->xScrollUnit;
1265 }
1266 break;
1267 case TK_SCROLL_UNITS:
1268 offset = listPtr->xOffset + count*listPtr->xScrollUnit;
1269 break;
1270 }
1271 ChangeListboxOffset(listPtr, offset);
1272 }
1273 return TCL_OK;
1274 }
1275
1276 /*
1277 *----------------------------------------------------------------------
1278 *
1279 * ListboxYviewSubCmd --
1280 *
1281 * Process the listbox "yview" subcommand.
1282 *
1283 * Results:
1284 * Standard Tcl result.
1285 *
1286 * Side effects:
1287 * May change the listbox viewing area; may set the interpreter's result.
1288 *
1289 *----------------------------------------------------------------------
1290 */
1291
1292 static int
1293 ListboxYviewSubCmd(interp, listPtr, objc, objv)
1294 Tcl_Interp *interp; /* Pointer to the calling Tcl interpreter */
1295 Listbox *listPtr; /* Information about the listbox */
1296 int objc; /* Number of arguments in the objv array */
1297 Tcl_Obj *CONST objv[]; /* Array of arguments to the procedure */
1298 {
1299 int index, count, type;
1300 double fraction, fraction2;
1301
1302 if (objc == 2) {
1303 if (listPtr->nElements == 0) {
1304 Tcl_SetResult(interp, "0 1", TCL_STATIC);
1305 } else {
1306 char buf[TCL_DOUBLE_SPACE * 2];
1307
1308 fraction = listPtr->topIndex/((double) listPtr->nElements);
1309 fraction2 = (listPtr->topIndex+listPtr->fullLines)
1310 /((double) listPtr->nElements);
1311 if (fraction2 > 1.0) {
1312 fraction2 = 1.0;
1313 }
1314 sprintf(buf, "%g %g", fraction, fraction2);
1315 Tcl_SetResult(interp, buf, TCL_VOLATILE);
1316 }
1317 } else if (objc == 3) {
1318 if (GetListboxIndex(interp, listPtr, objv[2], 0, &index) != TCL_OK) {
1319 return TCL_ERROR;
1320 }
1321 ChangeListboxView(listPtr, index);
1322 } else {
1323 type = Tk_GetScrollInfoObj(interp, objc, objv, &fraction, &count);
1324 switch (type) {
1325 case TK_SCROLL_ERROR:
1326 return TCL_ERROR;
1327 case TK_SCROLL_MOVETO:
1328 index = (int) (listPtr->nElements*fraction + 0.5);
1329 break;
1330 case TK_SCROLL_PAGES:
1331 if (listPtr->fullLines > 2) {
1332 index = listPtr->topIndex
1333 + count*(listPtr->fullLines-2);
1334 } else {
1335 index = listPtr->topIndex + count;
1336 }
1337 break;
1338 case TK_SCROLL_UNITS:
1339 index = listPtr->topIndex + count;
1340 break;
1341 }
1342 ChangeListboxView(listPtr, index);
1343 }
1344 return TCL_OK;
1345 }
1346
1347 /*
1348 *----------------------------------------------------------------------
1349 *
1350 * ListboxGetItemAttributes --
1351 *
1352 * Returns a pointer to the ItemAttr record for a given index,
1353 * creating one if it does not already exist.
1354 *
1355 * Results:
1356 * Pointer to an ItemAttr record.
1357 *
1358 * Side effects:
1359 * Memory may be allocated for the ItemAttr record.
1360 *
1361 *----------------------------------------------------------------------
1362 */
1363
1364 static ItemAttr *
1365 ListboxGetItemAttributes(interp, listPtr, index)
1366 Tcl_Interp *interp; /* Pointer to the calling Tcl interpreter */
1367 Listbox *listPtr; /* Information about the listbox */
1368 int index; /* Index of the item to retrieve attributes
1369 * for */
1370 {
1371 int new;
1372 Tcl_HashEntry *entry;
1373 ItemAttr *attrs;
1374
1375 entry = Tcl_CreateHashEntry(listPtr->itemAttrTable, (char *)index,
1376 &new);
1377 if (new) {
1378 attrs = (ItemAttr *) ckalloc(sizeof(ItemAttr));
1379 attrs->border = NULL;
1380 attrs->selBorder = NULL;
1381 attrs->fgColor = NULL;
1382 attrs->selFgColor = NULL;
1383 Tk_InitOptions(interp, (char *)attrs, listPtr->itemAttrOptionTable,
1384 listPtr->tkwin);
1385 Tcl_SetHashValue(entry, (ClientData) attrs);
1386 }
1387 attrs = (ItemAttr *)Tcl_GetHashValue(entry);
1388 return attrs;
1389 }
1390
1391 /*
1392 *----------------------------------------------------------------------
1393 *
1394 * DestroyListbox --
1395 *
1396 * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
1397 * to clean up the internal structure of a listbox at a safe time
1398 * (when no-one is using it anymore).
1399 *
1400 * Results:
1401 * None.
1402 *
1403 * Side effects:
1404 * Everything associated with the listbox is freed up.
1405 *
1406 *----------------------------------------------------------------------
1407 */
1408
1409 static void
1410 DestroyListbox(memPtr)
1411 char *memPtr; /* Info about listbox widget. */
1412 {
1413 register Listbox *listPtr = (Listbox *) memPtr;
1414 Tcl_HashEntry *entry;
1415 Tcl_HashSearch search;
1416
1417 listPtr->flags |= LISTBOX_DELETED;
1418
1419 Tcl_DeleteCommandFromToken(listPtr->interp, listPtr->widgetCmd);
1420 if (listPtr->setGrid) {
1421 Tk_UnsetGrid(listPtr->tkwin);
1422 }
1423 if (listPtr->flags & REDRAW_PENDING) {
1424 Tcl_CancelIdleCall(DisplayListbox, (ClientData) listPtr);
1425 }
1426
1427 /* If we have an internal list object, free it */
1428 if (listPtr->listObj != NULL) {
1429 Tcl_DecrRefCount(listPtr->listObj);
1430 listPtr->listObj = NULL;
1431 }
1432
1433 if (listPtr->listVarName != NULL) {
1434 Tcl_UntraceVar(listPtr->interp, listPtr->listVarName,
1435 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1436 ListboxListVarProc, (ClientData) listPtr);
1437 }
1438
1439 /* Free the selection hash table */
1440 Tcl_DeleteHashTable(listPtr->selection);
1441 ckfree((char *)listPtr->selection);
1442
1443 /* Free the item attribute hash table */
1444 for (entry = Tcl_FirstHashEntry(listPtr->itemAttrTable, &search);
1445 entry != NULL; entry = Tcl_NextHashEntry(&search)) {
1446 ckfree((char *)Tcl_GetHashValue(entry));
1447 }
1448 Tcl_DeleteHashTable(listPtr->itemAttrTable);
1449 ckfree((char *)listPtr->itemAttrTable);
1450
1451 /*
1452 * Free up all the stuff that requires special handling, then
1453 * let Tk_FreeOptions handle all the standard option-related
1454 * stuff.
1455 */
1456
1457 if (listPtr->textGC != None) {
1458 Tk_FreeGC(listPtr->display, listPtr->textGC);
1459 }
1460 if (listPtr->selTextGC != None) {
1461 Tk_FreeGC(listPtr->display, listPtr->selTextGC);
1462 }
1463 Tk_FreeConfigOptions((char *)listPtr, listPtr->optionTable,
1464 listPtr->tkwin);
1465 listPtr->tkwin = NULL;
1466 ckfree((char *) listPtr);
1467 }
1468
1469 /*
1470 *----------------------------------------------------------------------
1471 *
1472 * DestroyListboxOptionTables --
1473 *
1474 * This procedure is registered as an exit callback when the listbox
1475 * command is first called. It cleans up the OptionTables structure
1476 * allocated by that command.
1477 *
1478 * Results:
1479 * None.
1480 *
1481 * Side effects:
1482 * Frees memory.
1483 *
1484 *----------------------------------------------------------------------
1485 */
1486
1487 static void
1488 DestroyListboxOptionTables(clientData, interp)
1489 ClientData clientData; /* Pointer to the OptionTables struct */
1490 Tcl_Interp *interp; /* Pointer to the calling interp */
1491 {
1492 ckfree((char *)clientData);
1493 return;
1494 }
1495
1496 /*
1497 *----------------------------------------------------------------------
1498 *
1499 * ConfigureListbox --
1500 *
1501 * This procedure is called to process an objv/objc list, plus
1502 * the Tk option database, in order to configure (or reconfigure)
1503 * a listbox widget.
1504 *
1505 * Results:
1506 * The return value is a standard Tcl result. If TCL_ERROR is
1507 * returned, then the interp's result contains an error message.
1508 *
1509 * Side effects:
1510 * Configuration information, such as colors, border width,
1511 * etc. get set for listPtr; old resources get freed,
1512 * if there were any.
1513 *
1514 *----------------------------------------------------------------------
1515 */
1516
1517 static int
1518 ConfigureListbox(interp, listPtr, objc, objv, flags)
1519 Tcl_Interp *interp; /* Used for error reporting. */
1520 register Listbox *listPtr; /* Information about widget; may or may
1521 * not already have values for some fields. */
1522 int objc; /* Number of valid entries in argv. */
1523 Tcl_Obj *CONST objv[]; /* Arguments. */
1524 int flags; /* Flags to pass to Tk_ConfigureWidget. */
1525 {
1526 Tk_SavedOptions savedOptions;
1527 Tcl_Obj *oldListObj = NULL;
1528 int oldExport;
1529
1530 oldExport = listPtr->exportSelection;
1531 if (listPtr->listVarName != NULL) {
1532 Tcl_UntraceVar(interp, listPtr->listVarName,
1533 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1534 ListboxListVarProc, (ClientData) listPtr);
1535 }
1536
1537 if (Tk_SetOptions(interp, (char *)listPtr,
1538 listPtr->optionTable, objc, objv, listPtr->tkwin,
1539 &savedOptions, (int *)NULL) != TCL_OK) {
1540 Tk_RestoreSavedOptions(&savedOptions);
1541 return TCL_ERROR;
1542 }
1543
1544 /*
1545 * A few options need special processing, such as setting the
1546 * background from a 3-D border.
1547 */
1548
1549 Tk_SetBackgroundFromBorder(listPtr->tkwin, listPtr->normalBorder);
1550
1551 if (listPtr->highlightWidth < 0) {
1552 listPtr->highlightWidth = 0;
1553 }
1554 listPtr->inset = listPtr->highlightWidth + listPtr->borderWidth;
1555
1556 /*
1557 * Claim the selection if we've suddenly started exporting it and
1558 * there is a selection to export.
1559 */
1560
1561 if (listPtr->exportSelection && !oldExport
1562 && (listPtr->numSelected != 0)) {
1563 Tk_OwnSelection(listPtr->tkwin, XA_PRIMARY, ListboxLostSelection,
1564 (ClientData) listPtr);
1565 }
1566
1567
1568 /* Verify the current status of the list var.
1569 * PREVIOUS STATE | NEW STATE | ACTION
1570 * ------------------+---------------+----------------------------------
1571 * no listvar | listvar | If listvar does not exist, create
1572 * it and copy the internal list obj's
1573 * content to the new var. If it does
1574 * exist, toss the internal list obj.
1575 *
1576 * listvar | no listvar | Copy old listvar content to the
1577 * internal list obj
1578 *
1579 * listvar | listvar | no special action
1580 *
1581 * no listvar | no listvar | no special action
1582 */
1583 oldListObj = listPtr->listObj;
1584 if (listPtr->listVarName != NULL) {
1585 Tcl_Obj *listVarObj = Tcl_GetVar2Ex(interp, listPtr->listVarName,
1586 (char *)NULL, TCL_GLOBAL_ONLY);
1587 int dummy;
1588 if (listVarObj == NULL) {
1589 if (listPtr->listObj != NULL) {
1590 listVarObj = listPtr->listObj;
1591 } else {
1592 listVarObj = Tcl_NewObj();
1593 }
1594 if (Tcl_SetVar2Ex(interp, listPtr->listVarName, (char *)NULL,
1595 listVarObj, TCL_GLOBAL_ONLY) == NULL) {
1596 Tcl_DecrRefCount(listVarObj);
1597 Tk_RestoreSavedOptions(&savedOptions);
1598 return TCL_ERROR;
1599 }
1600 }
1601 /* Make sure the object is a good list object */
1602 if (Tcl_ListObjLength(listPtr->interp, listVarObj, &dummy) != TCL_OK) {
1603 Tk_RestoreSavedOptions(&savedOptions);
1604 Tcl_AppendResult(listPtr->interp, ": invalid listvar value",
1605 (char *)NULL);
1606 return TCL_ERROR;
1607 }
1608
1609 listPtr->listObj = listVarObj;
1610 Tcl_TraceVar(listPtr->interp, listPtr->listVarName,
1611 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1612 ListboxListVarProc, (ClientData) listPtr);
1613 } else {
1614 if (listPtr->listObj == NULL) {
1615 listPtr->listObj = Tcl_NewObj();
1616 }
1617 }
1618 Tcl_IncrRefCount(listPtr->listObj);
1619 if (oldListObj != NULL) {
1620 Tcl_DecrRefCount(oldListObj);
1621 }
1622
1623 /* Make sure that the list length is correct */
1624 Tcl_ListObjLength(listPtr->interp, listPtr->listObj, &listPtr->nElements);
1625
1626 Tk_FreeSavedOptions(&savedOptions);
1627 ListboxWorldChanged((ClientData) listPtr);
1628 return TCL_OK;
1629 }
1630
1631 /*
1632 *----------------------------------------------------------------------
1633 *
1634 * ConfigureListboxItem --
1635 *
1636 * This procedure is called to process an objv/objc list, plus
1637 * the Tk option database, in order to configure (or reconfigure)
1638 * a listbox item.
1639 *
1640 * Results:
1641 * The return value is a standard Tcl result. If TCL_ERROR is
1642 * returned, then the interp's result contains an error message.
1643 *
1644 * Side effects:
1645 * Configuration information, such as colors, border width,
1646 * etc. get set for a listbox item; old resources get freed,
1647 * if there were any.
1648 *
1649 *----------------------------------------------------------------------
1650 */
1651
1652 static int
1653 ConfigureListboxItem(interp, listPtr, attrs, objc, objv)
1654 Tcl_Interp *interp; /* Used for error reporting. */
1655 register Listbox *listPtr; /* Information about widget; may or may
1656 * not already have values for some fields. */
1657 ItemAttr *attrs; /* Information about the item to configure */
1658 int objc; /* Number of valid entries in argv. */
1659 Tcl_Obj *CONST objv[]; /* Arguments. */
1660 {
1661 Tk_SavedOptions savedOptions;
1662
1663 if (Tk_SetOptions(interp, (char *)attrs,
1664 listPtr->itemAttrOptionTable, objc, objv, listPtr->tkwin,
1665 &savedOptions, (int *)NULL) != TCL_OK) {
1666 Tk_RestoreSavedOptions(&savedOptions);
1667 return TCL_ERROR;
1668 }
1669 Tk_FreeSavedOptions(&savedOptions);
1670 ListboxWorldChanged((ClientData) listPtr);
1671 return TCL_OK;
1672 }
1673
1674 /*
1675 *---------------------------------------------------------------------------
1676 *
1677 * ListboxWorldChanged --
1678 *
1679 * This procedure is called when the world has changed in some
1680 * way and the widget needs to recompute all its graphics contexts
1681 * and determine its new geometry.
1682 *
1683 * Results:
1684 * None.
1685 *
1686 * Side effects:
1687 * Listbox will be relayed out and redisplayed.
1688 *
1689 *---------------------------------------------------------------------------
1690 */
1691
1692 static void
1693 ListboxWorldChanged(instanceData)
1694 ClientData instanceData; /* Information about widget. */
1695 {
1696 XGCValues gcValues;
1697 GC gc;
1698 unsigned long mask;
1699 Listbox *listPtr;
1700
1701 listPtr = (Listbox *) instanceData;
1702
1703 gcValues.foreground = listPtr->fgColorPtr->pixel;
1704 gcValues.font = Tk_FontId(listPtr->tkfont);
1705 gcValues.graphics_exposures = False;
1706 mask = GCForeground | GCFont | GCGraphicsExposures;
1707 gc = Tk_GetGC(listPtr->tkwin, mask, &gcValues);
1708 if (listPtr->textGC != None) {
1709 Tk_FreeGC(listPtr->display, listPtr->textGC);
1710 }
1711 listPtr->textGC = gc;
1712
1713 gcValues.foreground = listPtr->selFgColorPtr->pixel;
1714 gcValues.font = Tk_FontId(listPtr->tkfont);
1715 mask = GCForeground | GCFont;
1716 gc = Tk_GetGC(listPtr->tkwin, mask, &gcValues);
1717 if (listPtr->selTextGC != None) {
1718 Tk_FreeGC(listPtr->display, listPtr->selTextGC);
1719 }
1720 listPtr->selTextGC = gc;
1721
1722 /*
1723 * Register the desired geometry for the window and arrange for
1724 * the window to be redisplayed.
1725 */
1726
1727 ListboxComputeGeometry(listPtr, 1, 1, 1);
1728 listPtr->flags |= UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR;
1729 EventuallyRedrawRange(listPtr, 0, listPtr->nElements-1);
1730 }
1731
1732 /*
1733 *--------------------------------------------------------------
1734 *
1735 * DisplayListbox --
1736 *
1737 * This procedure redraws the contents of a listbox window.
1738 *
1739 * Results:
1740 * None.
1741 *
1742 * Side effects:
1743 * Information appears on the screen.
1744 *
1745 *--------------------------------------------------------------
1746 */
1747
1748 static void
1749 DisplayListbox(clientData)
1750 ClientData clientData; /* Information about window. */
1751 {
1752 register Listbox *listPtr = (Listbox *) clientData;
1753 register Tk_Window tkwin = listPtr->tkwin;
1754 GC gc;
1755 int i, limit, x, y, width, prevSelected;
1756 Tk_FontMetrics fm;
1757 Tcl_Obj *curElement;
1758 Tcl_HashEntry *entry;
1759 char *stringRep;
1760 int stringLen;
1761 ItemAttr *attrs;
1762 Tk_3DBorder selectedBg;
1763 XGCValues gcValues;
1764 unsigned long mask;
1765 int left, right; /* Non-zero values here indicate
1766 * that the left or right edge of
1767 * the listbox is off-screen. */
1768 Pixmap pixmap;
1769
1770 listPtr->flags &= ~REDRAW_PENDING;
1771
1772 if (listPtr->flags & MAXWIDTH_IS_STALE) {
1773 ListboxComputeGeometry(listPtr, 0, 1, 0);
1774 listPtr->flags &= ~MAXWIDTH_IS_STALE;
1775 listPtr->flags |= UPDATE_H_SCROLLBAR;
1776 }
1777
1778 if (listPtr->flags & UPDATE_V_SCROLLBAR) {
1779 ListboxUpdateVScrollbar(listPtr);
1780 }
1781 if (listPtr->flags & UPDATE_H_SCROLLBAR) {
1782 ListboxUpdateHScrollbar(listPtr);
1783 }
1784 listPtr->flags &= ~(REDRAW_PENDING|UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR);
1785 if ((listPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
1786 return;
1787 }
1788
1789 /*
1790 * Redrawing is done in a temporary pixmap that is allocated
1791 * here and freed at the end of the procedure. All drawing is
1792 * done to the pixmap, and the pixmap is copied to the screen
1793 * at the end of the procedure. This provides the smoothest
1794 * possible visual effects (no flashing on the screen).
1795 */
1796
1797 pixmap = Tk_GetPixmap(listPtr->display, Tk_WindowId(tkwin),
1798 Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
1799 Tk_Fill3DRectangle(tkwin, pixmap, listPtr->normalBorder, 0, 0,
1800 Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
1801
1802 /* Display each item in the listbox */
1803 limit = listPtr->topIndex + listPtr->fullLines + listPtr->partialLine - 1;
1804 if (limit >= listPtr->nElements) {
1805 limit = listPtr->nElements-1;
1806 }
1807 left = right = 0;
1808 if (listPtr->xOffset > 0) {
1809 left = listPtr->selBorderWidth+1;
1810 }
1811 if ((listPtr->maxWidth - listPtr->xOffset) > (Tk_Width(listPtr->tkwin)
1812 - 2*(listPtr->inset + listPtr->selBorderWidth))) {
1813 right = listPtr->selBorderWidth+1;
1814 }
1815 prevSelected = 0;
1816
1817 for (i = listPtr->topIndex; i <= limit; i++) {
1818 x = listPtr->inset;
1819 y = ((i - listPtr->topIndex) * listPtr->lineHeight)
1820 + listPtr->inset;
1821 gc = listPtr->textGC;
1822 /*
1823 * Lookup this item in the item attributes table, to see if it has
1824 * special foreground/background colors
1825 */
1826 entry = Tcl_FindHashEntry(listPtr->itemAttrTable, (char *)i);
1827
1828 /* If the item is selected, it is drawn differently */
1829 if (Tcl_FindHashEntry(listPtr->selection, (char *)i) != NULL) {
1830 gc = listPtr->selTextGC;
1831 width = Tk_Width(tkwin) - 2*listPtr->inset;
1832 selectedBg = listPtr->selBorder;
1833
1834 /* If there is attribute information for this item,
1835 * adjust the drawing accordingly */
1836 if (entry != NULL) {
1837 attrs = (ItemAttr *)Tcl_GetHashValue(entry);
1838 /* The default GC has the settings from the widget at large */
1839 gcValues.foreground = listPtr->selFgColorPtr->pixel;
1840 gcValues.font = Tk_FontId(listPtr->tkfont);
1841 gcValues.graphics_exposures = False;
1842 mask = GCForeground | GCFont | GCGraphicsExposures;
1843
1844 if (attrs->selBorder != NULL) {
1845 selectedBg = attrs->selBorder;
1846 }
1847
1848 if (attrs->selFgColor != NULL) {
1849 gcValues.foreground = attrs->selFgColor->pixel;
1850 gc = Tk_GetGC(listPtr->tkwin, mask, &gcValues);
1851 }
1852 }
1853
1854 Tk_Fill3DRectangle(tkwin, pixmap, selectedBg, x, y,
1855 width, listPtr->lineHeight, 0, TK_RELIEF_FLAT);
1856
1857 /*
1858 * Draw beveled edges around the selection, if there are visible
1859 * edges next to this element. Special considerations:
1860 * 1. The left and right bevels may not be visible if horizontal
1861 * scrolling is enabled (the "left" and "right" variables
1862 * are zero to indicate that the corresponding bevel is
1863 * visible).
1864 * 2. Top and bottom bevels are only drawn if this is the
1865 * first or last seleted item.
1866 * 3. If the left or right bevel isn't visible, then the "left"
1867 * and "right" variables, computed above, have non-zero values
1868 * that extend the top and bottom bevels so that the mitered
1869 * corners are off-screen.
1870 */
1871
1872 /* Draw left bevel */
1873 if (left == 0) {
1874 Tk_3DVerticalBevel(tkwin, pixmap, selectedBg,
1875 x, y, listPtr->selBorderWidth, listPtr->lineHeight,
1876 1, TK_RELIEF_RAISED);
1877 }
1878 /* Draw right bevel */
1879 if (right == 0) {
1880 Tk_3DVerticalBevel(tkwin, pixmap, selectedBg,
1881 x + width - listPtr->selBorderWidth, y,
1882 listPtr->selBorderWidth, listPtr->lineHeight,
1883 0, TK_RELIEF_RAISED);
1884 }
1885 /* Draw top bevel */
1886 if (!prevSelected) {
1887 Tk_3DHorizontalBevel(tkwin, pixmap, selectedBg,
1888 x-left, y, width+left+right, listPtr->selBorderWidth,
1889 1, 1, 1, TK_RELIEF_RAISED);
1890 }
1891 /* Draw bottom bevel */
1892 if (i + 1 == listPtr->nElements ||
1893 Tcl_FindHashEntry(listPtr->selection,
1894 (char *)(i + 1)) == NULL ) {
1895 Tk_3DHorizontalBevel(tkwin, pixmap, selectedBg, x-left,
1896 y + listPtr->lineHeight - listPtr->selBorderWidth,
1897 width+left+right, listPtr->selBorderWidth, 0, 0, 0,
1898 TK_RELIEF_RAISED);
1899 }
1900 prevSelected = 1;
1901 } else {
1902 /* If there is an item attributes record for this item,
1903 * draw the background box and set the foreground color
1904 * accordingly */
1905 if (entry != NULL) {
1906 attrs = (ItemAttr *)Tcl_GetHashValue(entry);
1907 gcValues.foreground = listPtr->fgColorPtr->pixel;
1908 gcValues.font = Tk_FontId(listPtr->tkfont);
1909 gcValues.graphics_exposures = False;
1910 mask = GCForeground | GCFont | GCGraphicsExposures;
1911 if (attrs->border != NULL) {
1912 width = Tk_Width(tkwin) - 2*listPtr->inset;
1913 Tk_Fill3DRectangle(tkwin, pixmap, attrs->border, x, y,
1914 width, listPtr->lineHeight, 0, TK_RELIEF_FLAT);
1915 }
1916 if (attrs->fgColor != NULL) {
1917 gcValues.foreground = attrs->fgColor->pixel;
1918 gc = Tk_GetGC(listPtr->tkwin, mask, &gcValues);
1919 }
1920 }
1921 prevSelected = 0;
1922 }
1923
1924 /* Draw the actual text of this item */
1925 Tk_GetFontMetrics(listPtr->tkfont, &fm);
1926 y += fm.ascent + listPtr->selBorderWidth;
1927 x = listPtr->inset + listPtr->selBorderWidth - listPtr->xOffset;
1928 Tcl_ListObjIndex(listPtr->interp, listPtr->listObj, i, &curElement);
1929 stringRep = Tcl_GetStringFromObj(curElement, &stringLen);
1930 Tk_DrawChars(listPtr->display, pixmap, gc, listPtr->tkfont,
1931 stringRep, stringLen, x, y);
1932
1933 /* If this is the active element, underline it. */
1934 if ((i == listPtr->active) && (listPtr->flags & GOT_FOCUS)) {
1935 Tk_UnderlineChars(listPtr->display, pixmap, gc, listPtr->tkfont,
1936 stringRep, x, y, 0, stringLen);
1937 }
1938 }
1939
1940 /*
1941 * Redraw the border for the listbox to make sure that it's on top
1942 * of any of the text of the listbox entries.
1943 */
1944
1945 Tk_Draw3DRectangle(tkwin, pixmap, listPtr->normalBorder,
1946 listPtr->highlightWidth, listPtr->highlightWidth,
1947 Tk_Width(tkwin) - 2*listPtr->highlightWidth,
1948 Tk_Height(tkwin) - 2*listPtr->highlightWidth,
1949 listPtr->borderWidth, listPtr->relief);
1950 if (listPtr->highlightWidth > 0) {
1951 GC fgGC, bgGC;
1952
1953 bgGC = Tk_GCForColor(listPtr->highlightBgColorPtr, pixmap);
1954 if (listPtr->flags & GOT_FOCUS) {
1955 fgGC = Tk_GCForColor(listPtr->highlightColorPtr, pixmap);
1956 TkpDrawHighlightBorder(tkwin, fgGC, bgGC,
1957 listPtr->highlightWidth, pixmap);
1958 } else {
1959 TkpDrawHighlightBorder(tkwin, bgGC, bgGC,
1960 listPtr->highlightWidth, pixmap);
1961 }
1962 }
1963 XCopyArea(listPtr->display, pixmap, Tk_WindowId(tkwin),
1964 listPtr->textGC, 0, 0, (unsigned) Tk_Width(tkwin),
1965 (unsigned) Tk_Height(tkwin), 0, 0);
1966 Tk_FreePixmap(listPtr->display, pixmap);
1967 }
1968
1969 /*
1970 *----------------------------------------------------------------------
1971 *
1972 * ListboxComputeGeometry --
1973 *
1974 * This procedure is invoked to recompute geometry information
1975 * such as the sizes of the elements and the overall dimensions
1976 * desired for the listbox.
1977 *
1978 * Results:
1979 * None.
1980 *
1981 * Side effects:
1982 * Geometry information is updated and a new requested size is
1983 * registered for the widget. Internal border and gridding
1984 * information is also set.
1985 *
1986 *----------------------------------------------------------------------
1987 */
1988
1989 static void
1990 ListboxComputeGeometry(listPtr, fontChanged, maxIsStale, updateGrid)
1991 Listbox *listPtr; /* Listbox whose geometry is to be
1992 * recomputed. */
1993 int fontChanged; /* Non-zero means the font may have changed
1994 * so per-element width information also
1995 * has to be computed. */
1996 int maxIsStale; /* Non-zero means the "maxWidth" field may
1997 * no longer be up-to-date and must
1998 * be recomputed. If fontChanged is 1 then
1999 * this must be 1. */
2000 int updateGrid; /* Non-zero means call Tk_SetGrid or
2001 * Tk_UnsetGrid to update gridding for
2002 * the window. */
2003 {
2004 int width, height, pixelWidth, pixelHeight;
2005 Tk_FontMetrics fm;
2006 Tcl_Obj *element;
2007 int textLength;
2008 char *text;
2009 int i, result;
2010
2011 if (fontChanged || maxIsStale) {
2012 listPtr->xScrollUnit = Tk_TextWidth(listPtr->tkfont, "0", 1);
2013 if (listPtr->xScrollUnit == 0) {
2014 listPtr->xScrollUnit = 1;
2015 }
2016 listPtr->maxWidth = 0;
2017 for (i = 0; i < listPtr->nElements; i++) {
2018 /* Compute the pixel width of the current element */
2019 result = Tcl_ListObjIndex(listPtr->interp, listPtr->listObj, i,
2020 &element);
2021 if (result != TCL_OK) {
2022 continue;
2023 }
2024 text = Tcl_GetStringFromObj(element, &textLength);
2025 Tk_GetFontMetrics(listPtr->tkfont, &fm);
2026 pixelWidth = Tk_TextWidth(listPtr->tkfont, text, textLength);
2027 if (pixelWidth > listPtr->maxWidth) {
2028 listPtr->maxWidth = pixelWidth;
2029 }
2030 }
2031 }
2032
2033 Tk_GetFontMetrics(listPtr->tkfont, &fm);
2034 listPtr->lineHeight = fm.linespace + 1 + 2*listPtr->selBorderWidth;
2035 width = listPtr->width;
2036 if (width <= 0) {
2037 width = (listPtr->maxWidth + listPtr->xScrollUnit - 1)
2038 /listPtr->xScrollUnit;
2039 if (width < 1) {
2040 width = 1;
2041 }
2042 }
2043 pixelWidth = width*listPtr->xScrollUnit + 2*listPtr->inset
2044 + 2*listPtr->selBorderWidth;
2045 height = listPtr->height;
2046 if (listPtr->height <= 0) {
2047 height = listPtr->nElements;
2048 if (height < 1) {
2049 height = 1;
2050 }
2051 }
2052 pixelHeight = height*listPtr->lineHeight + 2*listPtr->inset;
2053 Tk_GeometryRequest(listPtr->tkwin, pixelWidth, pixelHeight);
2054 Tk_SetInternalBorder(listPtr->tkwin, listPtr->inset);
2055 if (updateGrid) {
2056 if (listPtr->setGrid) {
2057 Tk_SetGrid(listPtr->tkwin, width, height, listPtr->xScrollUnit,
2058 listPtr->lineHeight);
2059 } else {
2060 Tk_UnsetGrid(listPtr->tkwin);
2061 }
2062 }
2063 }
2064
2065 /*
2066 *----------------------------------------------------------------------
2067 *
2068 * ListboxInsertSubCmd --
2069 *
2070 * This procedure is invoked to handle the listbox "insert"
2071 * subcommand.
2072 *
2073 * Results:
2074 * Standard Tcl result.
2075 *
2076 * Side effects:
2077 * New elements are added to the listbox pointed to by listPtr;
2078 * a refresh callback is registered for the listbox.
2079 *
2080 *----------------------------------------------------------------------
2081 */
2082
2083 static int
2084 ListboxInsertSubCmd(listPtr, index, objc, objv)
2085 register Listbox *listPtr; /* Listbox that is to get the new
2086 * elements. */
2087 int index; /* Add the new elements before this
2088 * element. */
2089 int objc; /* Number of new elements to add. */
2090 Tcl_Obj *CONST objv[]; /* New elements (one per entry). */
2091 {
2092 int i, oldMaxWidth;
2093 Tcl_Obj *newListObj;
2094 int pixelWidth;
2095 int result;
2096 char *stringRep;
2097 int length;
2098
2099 oldMaxWidth = listPtr->maxWidth;
2100 for (i = 0; i < objc; i++) {
2101 /*
2102 * Check if any of the new elements are wider than the current widest;
2103 * if so, update our notion of "widest."
2104 */
2105 stringRep = Tcl_GetStringFromObj(objv[i], &length);
2106 pixelWidth = Tk_TextWidth(listPtr->tkfont, stringRep, length);
2107 if (pixelWidth > listPtr->maxWidth) {
2108 listPtr->maxWidth = pixelWidth;
2109 }
2110 }
2111
2112 /* Adjust selection and attribute information for every index after
2113 * the first index */
2114 MigrateHashEntries(listPtr->selection, index, listPtr->nElements-1, objc);
2115 MigrateHashEntries(listPtr->itemAttrTable, index, listPtr->nElements-1,
2116 objc);
2117
2118 /* If the object is shared, duplicate it before writing to it */
2119 if (Tcl_IsShared(listPtr->listObj)) {
2120 newListObj = Tcl_DuplicateObj(listPtr->listObj);
2121 } else {
2122 newListObj = listPtr->listObj;
2123 }
2124 result =
2125 Tcl_ListObjReplace(listPtr->interp, newListObj, index, 0, objc, objv);
2126 if (result != TCL_OK) {
2127 return result;
2128 }
2129
2130 Tcl_IncrRefCount(newListObj);
2131 /* Clean up the old reference */
2132 Tcl_DecrRefCount(listPtr->listObj);
2133
2134 /* Set the internal pointer to the new obj */
2135 listPtr->listObj = newListObj;
2136
2137 /* If there is a listvar, make sure it points at the new object */
2138 if (listPtr->listVarName != NULL) {
2139 if (Tcl_SetVar2Ex(listPtr->interp, listPtr->listVarName,
2140 (char *)NULL, newListObj, TCL_GLOBAL_ONLY) == NULL) {
2141 Tcl_DecrRefCount(newListObj);
2142 return TCL_ERROR;
2143 }
2144 }
2145
2146 /* Get the new list length */
2147 Tcl_ListObjLength(listPtr->interp, listPtr->listObj, &listPtr->nElements);
2148
2149 /*
2150 * Update the "special" indices (anchor, topIndex, active) to account
2151 * for the renumbering that just occurred. Then arrange for the new
2152 * information to be displayed.
2153 */
2154
2155 if (index <= listPtr->selectAnchor) {
2156 listPtr->selectAnchor += objc;
2157 }
2158 if (index < listPtr->topIndex) {
2159 listPtr->topIndex += objc;
2160 }
2161 if (index <= listPtr->active) {
2162 listPtr->active += objc;
2163 if ((listPtr->active >= listPtr->nElements) &&
2164 (listPtr->nElements > 0)) {
2165 listPtr->active = listPtr->nElements-1;
2166 }
2167 }
2168 listPtr->flags |= UPDATE_V_SCROLLBAR;
2169 if (listPtr->maxWidth != oldMaxWidth) {
2170 listPtr->flags |= UPDATE_H_SCROLLBAR;
2171 }
2172 ListboxComputeGeometry(listPtr, 0, 0, 0);
2173 EventuallyRedrawRange(listPtr, index, listPtr->nElements-1);
2174 return TCL_OK;
2175 }
2176
2177 /*
2178 *----------------------------------------------------------------------
2179 *
2180 * ListboxDeleteSubCmd --
2181 *
2182 * Process a listbox "delete" subcommand by removing one or more
2183 * elements from a listbox widget.
2184 *
2185 * Results:
2186 * Standard Tcl result.
2187 *
2188 * Side effects:
2189 * The listbox will be modified and (eventually) redisplayed.
2190 *
2191 *----------------------------------------------------------------------
2192 */
2193
2194 static int
2195 ListboxDeleteSubCmd(listPtr, first, last)
2196 register Listbox *listPtr; /* Listbox widget to modify. */
2197 int first; /* Index of first element to delete. */
2198 int last; /* Index of last element to delete. */
2199 {
2200 int count, i, widthChanged;
2201 Tcl_Obj *newListObj;
2202 Tcl_Obj *element;
2203 int length;
2204 char *stringRep;
2205 int result;
2206 int pixelWidth;
2207 Tcl_HashEntry *entry;
2208
2209 /*
2210 * Adjust the range to fit within the existing elements of the
2211 * listbox, and make sure there's something to delete.
2212 */
2213
2214 if (first < 0) {
2215 first = 0;
2216 }
2217 if (last >= listPtr->nElements) {
2218 last = listPtr->nElements-1;
2219 }
2220 count = last + 1 - first;
2221 if (count <= 0) {
2222 return TCL_OK;
2223 }
2224
2225 /*
2226 * Foreach deleted index we must:
2227 * a) remove selection information
2228 * b) check the width of the element; if it is equal to the max, set
2229 * widthChanged to 1, because it may be the only element with that
2230 * width
2231 */
2232 widthChanged = 0;
2233 for (i = first; i <= last; i++) {
2234 /* Remove selection information */
2235 entry = Tcl_FindHashEntry(listPtr->selection, (char *)i);
2236 if (entry != NULL) {
2237 listPtr->numSelected--;
2238 Tcl_DeleteHashEntry(entry);
2239 }
2240
2241 entry = Tcl_FindHashEntry(listPtr->itemAttrTable, (char *)i);
2242 if (entry != NULL) {
2243 Tcl_DeleteHashEntry(entry);
2244 }
2245
2246 /* Check width of the element. We only have to check if widthChanged
2247 * has not already been set to 1, because we only need one maxWidth
2248 * element to disappear for us to have to recompute the width
2249 */
2250 if (widthChanged == 0) {
2251 Tcl_ListObjIndex(listPtr->interp, listPtr->listObj, i, &element);
2252 stringRep = Tcl_GetStringFromObj(element, &length);
2253 pixelWidth = Tk_TextWidth(listPtr->tkfont, stringRep, length);
2254 if (pixelWidth == listPtr->maxWidth) {
2255 widthChanged = 1;
2256 }
2257 }
2258 }
2259
2260 /* Adjust selection and attribute info for indices after lastIndex */
2261 MigrateHashEntries(listPtr->selection, last+1,
2262 listPtr->nElements-1, count*-1);
2263 MigrateHashEntries(listPtr->itemAttrTable, last+1,
2264 listPtr->nElements-1, count*-1);
2265
2266 /* Delete the requested elements */
2267 if (Tcl_IsShared(listPtr->listObj)) {
2268 newListObj = Tcl_DuplicateObj(listPtr->listObj);
2269 } else {
2270 newListObj = listPtr->listObj;
2271 }
2272 result = Tcl_ListObjReplace(listPtr->interp,
2273 newListObj, first, count, 0, NULL);
2274 if (result != TCL_OK) {
2275 return result;
2276 }
2277
2278 Tcl_IncrRefCount(newListObj);
2279 /* Clean up the old reference */
2280 Tcl_DecrRefCount(listPtr->listObj);
2281
2282 /* Set the internal pointer to the new obj */
2283 listPtr->listObj = newListObj;
2284
2285 /* Get the new list length */
2286 Tcl_ListObjLength(listPtr->interp, listPtr->listObj, &listPtr->nElements);
2287
2288 /* If there is a listvar, make sure it points at the new object */
2289 if (listPtr->listVarName != NULL) {
2290 if (Tcl_SetVar2Ex(listPtr->interp, listPtr->listVarName,
2291 (char *)NULL, newListObj, TCL_GLOBAL_ONLY) == NULL) {
2292 Tcl_DecrRefCount(newListObj);
2293 return TCL_ERROR;
2294 }
2295 }
2296
2297 /*
2298 * Update the selection and viewing information to reflect the change
2299 * in the element numbering, and redisplay to slide information up over
2300 * the elements that were deleted.
2301 */
2302
2303 if (first <= listPtr->selectAnchor) {
2304 listPtr->selectAnchor -= count;
2305 if (listPtr->selectAnchor < first) {
2306 listPtr->selectAnchor = first;
2307 }
2308 }
2309 if (first <= listPtr->topIndex) {
2310 listPtr->topIndex -= count;
2311 if (listPtr->topIndex < first) {
2312 listPtr->topIndex = first;
2313 }
2314 }
2315 if (listPtr->topIndex > (listPtr->nElements - listPtr->fullLines)) {
2316 listPtr->topIndex = listPtr->nElements - listPtr->fullLines;
2317 if (listPtr->topIndex < 0) {
2318 listPtr->topIndex = 0;
2319 }
2320 }
2321 if (listPtr->active > last) {
2322 listPtr->active -= count;
2323 } else if (listPtr->active >= first) {
2324 listPtr->active = first;
2325 if ((listPtr->active >= listPtr->nElements) &&
2326 (listPtr->nElements > 0)) {
2327 listPtr->active = listPtr->nElements-1;
2328 }
2329 }
2330 listPtr->flags |= UPDATE_V_SCROLLBAR;
2331 ListboxComputeGeometry(listPtr, 0, widthChanged, 0);
2332 if (widthChanged) {
2333 listPtr->flags |= UPDATE_H_SCROLLBAR;
2334 }
2335 EventuallyRedrawRange(listPtr, first, listPtr->nElements-1);
2336 return TCL_OK;
2337 }
2338
2339 /*
2340 *--------------------------------------------------------------
2341 *
2342 * ListboxEventProc --
2343 *
2344 * This procedure is invoked by the Tk dispatcher for various
2345 * events on listboxes.
2346 *
2347 * Results:
2348 * None.
2349 *
2350 * Side effects:
2351 * When the window gets deleted, internal structures get
2352 * cleaned up. When it gets exposed, it is redisplayed.
2353 *
2354 *--------------------------------------------------------------
2355 */
2356
2357 static void
2358 ListboxEventProc(clientData, eventPtr)
2359 ClientData clientData; /* Information about window. */
2360 XEvent *eventPtr; /* Information about event. */
2361 {
2362 Listbox *listPtr = (Listbox *) clientData;
2363
2364 if (eventPtr->type == Expose) {
2365 EventuallyRedrawRange(listPtr,
2366 NearestListboxElement(listPtr, eventPtr->xexpose.y),
2367 NearestListboxElement(listPtr, eventPtr->xexpose.y
2368 + eventPtr->xexpose.height));
2369 } else if (eventPtr->type == DestroyNotify) {
2370 DestroyListbox((char *) clientData);
2371 } else if (eventPtr->type == ConfigureNotify) {
2372 int vertSpace;
2373
2374 vertSpace = Tk_Height(listPtr->tkwin) - 2*listPtr->inset;
2375 listPtr->fullLines = vertSpace / listPtr->lineHeight;
2376 if ((listPtr->fullLines*listPtr->lineHeight) < vertSpace) {
2377 listPtr->partialLine = 1;
2378 } else {
2379 listPtr->partialLine = 0;
2380 }
2381 listPtr->flags |= UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR;
2382 ChangeListboxView(listPtr, listPtr->topIndex);
2383 ChangeListboxOffset(listPtr, listPtr->xOffset);
2384
2385 /*
2386 * Redraw the whole listbox. It's hard to tell what needs
2387 * to be redrawn (e.g. if the listbox has shrunk then we
2388 * may only need to redraw the borders), so just redraw
2389 * everything for safety.
2390 */
2391
2392 EventuallyRedrawRange(listPtr, 0, listPtr->nElements-1);
2393 } else if (eventPtr->type == FocusIn) {
2394 if (eventPtr->xfocus.detail != NotifyInferior) {
2395 listPtr->flags |= GOT_FOCUS;
2396 EventuallyRedrawRange(listPtr, 0, listPtr->nElements-1);
2397 }
2398 } else if (eventPtr->type == FocusOut) {
2399 if (eventPtr->xfocus.detail != NotifyInferior) {
2400 listPtr->flags &= ~GOT_FOCUS;
2401 EventuallyRedrawRange(listPtr, 0, listPtr->nElements-1);
2402 }
2403 }
2404 }
2405
2406 /*
2407 *----------------------------------------------------------------------
2408 *
2409 * ListboxCmdDeletedProc --
2410 *
2411 * This procedure is invoked when a widget command is deleted. If
2412 * the widget isn't already in the process of being destroyed,
2413 * this command destroys it.
2414 *
2415 * Results:
2416 * None.
2417 *
2418 * Side effects:
2419 * The widget is destroyed.
2420 *
2421 *----------------------------------------------------------------------
2422 */
2423
2424 static void
2425 ListboxCmdDeletedProc(clientData)
2426 ClientData clientData; /* Pointer to widget record for widget. */
2427 {
2428 Listbox *listPtr = (Listbox *) clientData;
2429
2430 /*
2431 * This procedure could be invoked either because the window was
2432 * destroyed and the command was then deleted (in which case tkwin
2433 * is NULL) or because the command was deleted, and then this procedure
2434 * destroys the widget.
2435 */
2436
2437 if (!(listPtr->flags & LISTBOX_DELETED)) {
2438 Tk_DestroyWindow(listPtr->tkwin);
2439 }
2440 }
2441
2442 /*
2443 *--------------------------------------------------------------
2444 *
2445 * GetListboxIndex --
2446 *
2447 * Parse an index into a listbox and return either its value
2448 * or an error.
2449 *
2450 * Results:
2451 * A standard Tcl result. If all went well, then *indexPtr is
2452 * filled in with the index (into listPtr) corresponding to
2453 * string. Otherwise an error message is left in the interp's result.
2454 *
2455 * Side effects:
2456 * None.
2457 *
2458 *--------------------------------------------------------------
2459 */
2460
2461 static int
2462 GetListboxIndex(interp, listPtr, indexObj, endIsSize, indexPtr)
2463 Tcl_Interp *interp; /* For error messages. */
2464 Listbox *listPtr; /* Listbox for which the index is being
2465 * specified. */
2466 Tcl_Obj *indexObj; /* Specifies an element in the listbox. */
2467 int endIsSize; /* If 1, "end" refers to the number of
2468 * entries in the listbox. If 0, "end"
2469 * refers to 1 less than the number of
2470 * entries. */
2471 int *indexPtr; /* Where to store converted index. */
2472 {
2473 int result;
2474 int index;
2475 char *stringRep;
2476
2477 /* First see if the index is one of the named indices */
2478 result = Tcl_GetIndexFromObj(NULL, indexObj, indexNames, "", 0, &index);
2479 if (result == TCL_OK) {
2480 switch (index) {
2481 case INDEX_ACTIVE: {
2482 /* "active" index */
2483 *indexPtr = listPtr->active;
2484 break;
2485 }
2486
2487 case INDEX_ANCHOR: {
2488 /* "anchor" index */
2489 *indexPtr = listPtr->selectAnchor;
2490 break;
2491 }
2492
2493 case INDEX_END: {
2494 /* "end" index */
2495 if (endIsSize) {
2496 *indexPtr = listPtr->nElements;
2497 } else {
2498 *indexPtr = listPtr->nElements - 1;
2499 }
2500 break;
2501 }
2502 }
2503 return TCL_OK;
2504 }
2505
2506 /* The index didn't match any of the named indices; maybe it's an @x,y */
2507 stringRep = Tcl_GetString(indexObj);
2508 if (stringRep[0] == '@') {
2509 /* @x,y index */
2510 int y;
2511 char *start, *end;
2512 start = stringRep + 1;
2513 strtol(start, &end, 0);
2514 if ((start == end) || (*end != ',')) {
2515 Tcl_AppendResult(interp, "bad listbox index \"", stringRep,
2516 "\": must be active, anchor, end, @x,y, or a number",
2517 (char *)NULL);
2518 return TCL_ERROR;
2519 }
2520 start = end+1;
2521 y = strtol(start, &end, 0);
2522 if ((start == end) || (*end != '\0')) {
2523 Tcl_AppendResult(interp, "bad listbox index \"", stringRep,
2524 "\": must be active, anchor, end, @x,y, or a number",
2525 (char *)NULL);
2526 return TCL_ERROR;
2527 }
2528 *indexPtr = NearestListboxElement(listPtr, y);
2529 return TCL_OK;
2530 }
2531
2532 /* Maybe the index is just an integer */
2533 if (Tcl_GetIntFromObj(interp, indexObj, indexPtr) == TCL_OK) {
2534 return TCL_OK;
2535 }
2536
2537 /* Everything failed, nothing matched. Throw up an error message */
2538 Tcl_ResetResult(interp);
2539 Tcl_AppendResult(interp, "bad listbox index \"",
2540 Tcl_GetString(indexObj), "\": must be active, anchor, ",
2541 "end, @x,y, or a number", (char *) NULL);
2542 return TCL_ERROR;
2543 }
2544
2545 /*
2546 *----------------------------------------------------------------------
2547 *
2548 * ChangeListboxView --
2549 *
2550 * Change the view on a listbox widget so that a given element
2551 * is displayed at the top.
2552 *
2553 * Results:
2554 * None.
2555 *
2556 * Side effects:
2557 * What's displayed on the screen is changed. If there is a
2558 * scrollbar associated with this widget, then the scrollbar
2559 * is instructed to change its display too.
2560 *
2561 *----------------------------------------------------------------------
2562 */
2563
2564 static void
2565 ChangeListboxView(listPtr, index)
2566 register Listbox *listPtr; /* Information about widget. */
2567 int index; /* Index of element in listPtr
2568 * that should now appear at the
2569 * top of the listbox. */
2570 {
2571 if (index >= (listPtr->nElements - listPtr->fullLines)) {
2572 index = listPtr->nElements - listPtr->fullLines;
2573 }
2574 if (index < 0) {
2575 index = 0;
2576 }
2577 if (listPtr->topIndex != index) {
2578 listPtr->topIndex = index;
2579 EventuallyRedrawRange(listPtr, 0, listPtr->nElements-1);
2580 listPtr->flags |= UPDATE_V_SCROLLBAR;
2581 }
2582 }
2583
2584 /*
2585 *----------------------------------------------------------------------
2586 *
2587 * ChangListboxOffset --
2588 *
2589 * Change the horizontal offset for a listbox.
2590 *
2591 * Results:
2592 * None.
2593 *
2594 * Side effects:
2595 * The listbox may be redrawn to reflect its new horizontal
2596 * offset.
2597 *
2598 *----------------------------------------------------------------------
2599 */
2600
2601 static void
2602 ChangeListboxOffset(listPtr, offset)
2603 register Listbox *listPtr; /* Information about widget. */
2604 int offset; /* Desired new "xOffset" for
2605 * listbox. */
2606 {
2607 int maxOffset;
2608
2609 /*
2610 * Make sure that the new offset is within the allowable range, and
2611 * round it off to an even multiple of xScrollUnit.
2612 */
2613
2614 maxOffset = listPtr->maxWidth - (Tk_Width(listPtr->tkwin) -
2615 2*listPtr->inset - 2*listPtr->selBorderWidth)
2616 + listPtr->xScrollUnit - 1;
2617 if (offset > maxOffset) {
2618 offset = maxOffset;
2619 }
2620 if (offset < 0) {
2621 offset = 0;
2622 }
2623 offset -= offset % listPtr->xScrollUnit;
2624 if (offset != listPtr->xOffset) {
2625 listPtr->xOffset = offset;
2626 listPtr->flags |= UPDATE_H_SCROLLBAR;
2627 EventuallyRedrawRange(listPtr, 0, listPtr->nElements-1);
2628 }
2629 }
2630
2631 /*
2632 *----------------------------------------------------------------------
2633 *
2634 * ListboxScanTo --
2635 *
2636 * Given a point (presumably of the curent mouse location)
2637 * drag the view in the window to implement the scan operation.
2638 *
2639 * Results:
2640 * None.
2641 *
2642 * Side effects:
2643 * The view in the window may change.
2644 *
2645 *----------------------------------------------------------------------
2646 */
2647
2648 static void
2649 ListboxScanTo(listPtr, x, y)
2650 register Listbox *listPtr; /* Information about widget. */
2651 int x; /* X-coordinate to use for scan
2652 * operation. */
2653 int y; /* Y-coordinate to use for scan
2654 * operation. */
2655 {
2656 int newTopIndex, newOffset, maxIndex, maxOffset;
2657
2658 maxIndex = listPtr->nElements - listPtr->fullLines;
2659 maxOffset = listPtr->maxWidth + (listPtr->xScrollUnit - 1)
2660 - (Tk_Width(listPtr->tkwin) - 2*listPtr->inset
2661 - 2*listPtr->selBorderWidth - listPtr->xScrollUnit);
2662
2663 /*
2664 * Compute new top line for screen by amplifying the difference
2665 * between the current position and the place where the scan
2666 * started (the "mark" position). If we run off the top or bottom
2667 * of the list, then reset the mark point so that the current
2668 * position continues to correspond to the edge of the window.
2669 * This means that the picture will start dragging as soon as the
2670 * mouse reverses direction (without this reset, might have to slide
2671 * mouse a long ways back before the picture starts moving again).
2672 */
2673
2674 newTopIndex = listPtr->scanMarkYIndex
2675 - (10*(y - listPtr->scanMarkY))/listPtr->lineHeight;
2676 if (newTopIndex > maxIndex) {
2677 newTopIndex = listPtr->scanMarkYIndex = maxIndex;
2678 listPtr->scanMarkY = y;
2679 } else if (newTopIndex < 0) {
2680 newTopIndex = listPtr->scanMarkYIndex = 0;
2681 listPtr->scanMarkY = y;
2682 }
2683 ChangeListboxView(listPtr, newTopIndex);
2684
2685 /*
2686 * Compute new left edge for display in a similar fashion by amplifying
2687 * the difference between the current position and the place where the
2688 * scan started.
2689 */
2690
2691 newOffset = listPtr->scanMarkXOffset - (10*(x - listPtr->scanMarkX));
2692 if (newOffset > maxOffset) {
2693 newOffset = listPtr->scanMarkXOffset = maxOffset;
2694 listPtr->scanMarkX = x;
2695 } else if (newOffset < 0) {
2696 newOffset = listPtr->scanMarkXOffset = 0;
2697 listPtr->scanMarkX = x;
2698 }
2699 ChangeListboxOffset(listPtr, newOffset);
2700 }
2701
2702 /*
2703 *----------------------------------------------------------------------
2704 *
2705 * NearestListboxElement --
2706 *
2707 * Given a y-coordinate inside a listbox, compute the index of
2708 * the element under that y-coordinate (or closest to that
2709 * y-coordinate).
2710 *
2711 * Results:
2712 * The return value is an index of an element of listPtr. If
2713 * listPtr has no elements, then 0 is always returned.
2714 *
2715 * Side effects:
2716 * None.
2717 *
2718 *----------------------------------------------------------------------
2719 */
2720
2721 static int
2722 NearestListboxElement(listPtr, y)
2723 register Listbox *listPtr; /* Information about widget. */
2724 int y; /* Y-coordinate in listPtr's window. */
2725 {
2726 int index;
2727
2728 index = (y - listPtr->inset)/listPtr->lineHeight;
2729 if (index >= (listPtr->fullLines + listPtr->partialLine)) {
2730 index = listPtr->fullLines + listPtr->partialLine - 1;
2731 }
2732 if (index < 0) {
2733 index = 0;
2734 }
2735 index += listPtr->topIndex;
2736 if (index >= listPtr->nElements) {
2737 index = listPtr->nElements-1;
2738 }
2739 return index;
2740 }
2741
2742 /*
2743 *----------------------------------------------------------------------
2744 *
2745 * ListboxSelect --
2746 *
2747 * Select or deselect one or more elements in a listbox..
2748 *
2749 * Results:
2750 * Standard Tcl result.
2751 *
2752 * Side effects:
2753 * All of the elements in the range between first and last are
2754 * marked as either selected or deselected, depending on the
2755 * "select" argument. Any items whose state changes are redisplayed.
2756 * The selection is claimed from X when the number of selected
2757 * elements changes from zero to non-zero.
2758 *
2759 *----------------------------------------------------------------------
2760 */
2761
2762 static int
2763 ListboxSelect(listPtr, first, last, select)
2764 register Listbox *listPtr; /* Information about widget. */
2765 int first; /* Index of first element to
2766 * select or deselect. */
2767 int last; /* Index of last element to
2768 * select or deselect. */
2769 int select; /* 1 means select items, 0 means
2770 * deselect them. */
2771 {
2772 int i, firstRedisplay, increment, oldCount;
2773 Tcl_HashEntry *entry;
2774 int new;
2775
2776 if (last < first) {
2777 i = first;
2778 first = last;
2779 last = i;
2780 }
2781 if ((last < 0) || (first >= listPtr->nElements)) {
2782 return TCL_OK;
2783 }
2784 if (first < 0) {
2785 first = 0;
2786 }
2787 if (last >= listPtr->nElements) {
2788 last = listPtr->nElements - 1;
2789 }
2790 oldCount = listPtr->numSelected;
2791 firstRedisplay = -1;
2792 increment = select ? 1 : -1;
2793
2794 /*
2795 * For each index in the range, find it in our selection hash table.
2796 * If it's not there but should be, add it. If it's there but shouldn't
2797 * be, remove it.
2798 */
2799 for (i = first; i <= last; i++) {
2800 entry = Tcl_FindHashEntry(listPtr->selection, (char *)i);
2801 if (entry != NULL) {
2802 if (!select) {
2803 Tcl_DeleteHashEntry(entry);
2804 listPtr->numSelected--;
2805 if (firstRedisplay < 0) {
2806 firstRedisplay = i;
2807 }
2808 }
2809 } else {
2810 if (select) {
2811 entry = Tcl_CreateHashEntry(listPtr->selection,
2812 (char *)i, &new);
2813 Tcl_SetHashValue(entry, (ClientData) NULL);
2814 listPtr->numSelected++;
2815 if (firstRedisplay < 0) {
2816 firstRedisplay = i;
2817 }
2818 }
2819 }
2820 }
2821
2822 if (firstRedisplay >= 0) {
2823 EventuallyRedrawRange(listPtr, first, last);
2824 }
2825 if ((oldCount == 0) && (listPtr->numSelected > 0)
2826 && (listPtr->exportSelection)) {
2827 Tk_OwnSelection(listPtr->tkwin, XA_PRIMARY, ListboxLostSelection,
2828 (ClientData) listPtr);
2829 }
2830 return TCL_OK;
2831 }
2832
2833 /*
2834 *----------------------------------------------------------------------
2835 *
2836 * ListboxFetchSelection --
2837 *
2838 * This procedure is called back by Tk when the selection is
2839 * requested by someone. It returns part or all of the selection
2840 * in a buffer provided by the caller.
2841 *
2842 * Results:
2843 * The return value is the number of non-NULL bytes stored
2844 * at buffer. Buffer is filled (or partially filled) with a
2845 * NULL-terminated string containing part or all of the selection,
2846 * as given by offset and maxBytes. The selection is returned
2847 * as a Tcl list with one list element for each element in the
2848 * listbox.
2849 *
2850 * Side effects:
2851 * None.
2852 *
2853 *----------------------------------------------------------------------
2854 */
2855
2856 static int
2857 ListboxFetchSelection(clientData, offset, buffer, maxBytes)
2858 ClientData clientData; /* Information about listbox widget. */
2859 int offset; /* Offset within selection of first
2860 * byte to be returned. */
2861 char *buffer; /* Location in which to place
2862 * selection. */
2863 int maxBytes; /* Maximum number of bytes to place
2864 * at buffer, not including terminating
2865 * NULL character. */
2866 {
2867 register Listbox *listPtr = (Listbox *) clientData;
2868 Tcl_DString selection;
2869 int length, count, needNewline;
2870 Tcl_Obj *curElement;
2871 char *stringRep;
2872 int stringLen;
2873 Tcl_HashEntry *entry;
2874 int i;
2875
2876 if (!listPtr->exportSelection) {
2877 return -1;
2878 }
2879
2880 /*
2881 * Use a dynamic string to accumulate the contents of the selection.
2882 */
2883
2884 needNewline = 0;
2885 Tcl_DStringInit(&selection);
2886 for (i = 0; i < listPtr->nElements; i++) {
2887 entry = Tcl_FindHashEntry(listPtr->selection, (char *)i);
2888 if (entry != NULL) {
2889 if (needNewline) {
2890 Tcl_DStringAppend(&selection, "\n", 1);
2891 }
2892 Tcl_ListObjIndex(listPtr->interp, listPtr->listObj, i,
2893 &curElement);
2894 stringRep = Tcl_GetStringFromObj(curElement, &stringLen);
2895 Tcl_DStringAppend(&selection, stringRep, stringLen);
2896 needNewline = 1;
2897 }
2898 }
2899
2900 length = Tcl_DStringLength(&selection);
2901 if (length == 0) {
2902 return -1;
2903 }
2904
2905 /*
2906 * Copy the requested portion of the selection to the buffer.
2907 */
2908
2909 count = length - offset;
2910 if (count <= 0) {
2911 count = 0;
2912 } else {
2913 if (count > maxBytes) {
2914 count = maxBytes;
2915 }
2916 memcpy((VOID *) buffer,
2917 (VOID *) (Tcl_DStringValue(&selection) + offset),
2918 (size_t) count);
2919 }
2920 buffer[count] = '\0';
2921 Tcl_DStringFree(&selection);
2922 return count;
2923 }
2924
2925 /*
2926 *----------------------------------------------------------------------
2927 *
2928 * ListboxLostSelection --
2929 *
2930 * This procedure is called back by Tk when the selection is
2931 * grabbed away from a listbox widget.
2932 *
2933 * Results:
2934 * None.
2935 *
2936 * Side effects:
2937 * The existing selection is unhighlighted, and the window is
2938 * marked as not containing a selection.
2939 *
2940 *----------------------------------------------------------------------
2941 */
2942
2943 static void
2944 ListboxLostSelection(clientData)
2945 ClientData clientData; /* Information about listbox widget. */
2946 {
2947 register Listbox *listPtr = (Listbox *) clientData;
2948
2949 if ((listPtr->exportSelection) && (listPtr->nElements > 0)) {
2950 ListboxSelect(listPtr, 0, listPtr->nElements-1, 0);
2951 }
2952 }
2953
2954 /*
2955 *----------------------------------------------------------------------
2956 *
2957 * EventuallyRedrawRange --
2958 *
2959 * Ensure that a given range of elements is eventually redrawn on
2960 * the display (if those elements in fact appear on the display).
2961 *
2962 * Results:
2963 * None.
2964 *
2965 * Side effects:
2966 * Information gets redisplayed.
2967 *
2968 *----------------------------------------------------------------------
2969 */
2970
2971 static void
2972 EventuallyRedrawRange(listPtr, first, last)
2973 register Listbox *listPtr; /* Information about widget. */
2974 int first; /* Index of first element in list
2975 * that needs to be redrawn. */
2976 int last; /* Index of last element in list
2977 * that needs to be redrawn. May
2978 * be less than first;
2979 * these just bracket a range. */
2980 {
2981 /* We don't have to register a redraw callback if one is already pending,
2982 * or if the window doesn't exist, or if the window isn't mapped */
2983 if ((listPtr->flags & REDRAW_PENDING)
2984 || (listPtr->tkwin == NULL)
2985 || !Tk_IsMapped(listPtr->tkwin)) {
2986 return;
2987 }
2988 listPtr->flags |= REDRAW_PENDING;
2989 Tcl_DoWhenIdle(DisplayListbox, (ClientData) listPtr);
2990 }
2991
2992 /*
2993 *----------------------------------------------------------------------
2994 *
2995 * ListboxUpdateVScrollbar --
2996 *
2997 * This procedure is invoked whenever information has changed in
2998 * a listbox in a way that would invalidate a vertical scrollbar
2999 * display. If there is an associated scrollbar, then this command
3000 * updates it by invoking a Tcl command.
3001 *
3002 * Results:
3003 * None.
3004 *
3005 * Side effects:
3006 * A Tcl command is invoked, and an additional command may be
3007 * invoked to process errors in the command.
3008 *
3009 *----------------------------------------------------------------------
3010 */
3011
3012 static void
3013 ListboxUpdateVScrollbar(listPtr)
3014 register Listbox *listPtr; /* Information about widget. */
3015 {
3016 char string[TCL_DOUBLE_SPACE * 2];
3017 double first, last;
3018 int result;
3019 Tcl_Interp *interp;
3020
3021 if (listPtr->yScrollCmd == NULL) {
3022 return;
3023 }
3024 if (listPtr->nElements == 0) {
3025 first = 0.0;
3026 last = 1.0;
3027 } else {
3028 first = listPtr->topIndex/((double) listPtr->nElements);
3029 last = (listPtr->topIndex+listPtr->fullLines)
3030 /((double) listPtr->nElements);
3031 if (last > 1.0) {
3032 last = 1.0;
3033 }
3034 }
3035 sprintf(string, " %g %g", first, last);
3036
3037 /*
3038 * We must hold onto the interpreter from the listPtr because the data
3039 * at listPtr might be freed as a result of the Tcl_VarEval.
3040 */
3041
3042 interp = listPtr->interp;
3043 Tcl_Preserve((ClientData) interp);
3044 result = Tcl_VarEval(interp, listPtr->yScrollCmd, string,
3045 (char *) NULL);
3046 if (result != TCL_OK) {
3047 Tcl_AddErrorInfo(interp,
3048 "\n (vertical scrolling command executed by listbox)");
3049 Tcl_BackgroundError(interp);
3050 }
3051 Tcl_Release((ClientData) interp);
3052 }
3053
3054 /*
3055 *----------------------------------------------------------------------
3056 *
3057 * ListboxUpdateHScrollbar --
3058 *
3059 * This procedure is invoked whenever information has changed in
3060 * a listbox in a way that would invalidate a horizontal scrollbar
3061 * display. If there is an associated horizontal scrollbar, then
3062 * this command updates it by invoking a Tcl command.
3063 *
3064 * Results:
3065 * None.
3066 *
3067 * Side effects:
3068 * A Tcl command is invoked, and an additional command may be
3069 * invoked to process errors in the command.
3070 *
3071 *----------------------------------------------------------------------
3072 */
3073
3074 static void
3075 ListboxUpdateHScrollbar(listPtr)
3076 register Listbox *listPtr; /* Information about widget. */
3077 {
3078 char string[TCL_DOUBLE_SPACE * 2];
3079 int result, windowWidth;
3080 double first, last;
3081 Tcl_Interp *interp;
3082
3083 if (listPtr->xScrollCmd == NULL) {
3084 return;
3085 }
3086 windowWidth = Tk_Width(listPtr->tkwin) - 2*(listPtr->inset
3087 + listPtr->selBorderWidth);
3088 if (listPtr->maxWidth == 0) {
3089 first = 0;
3090 last = 1.0;
3091 } else {
3092 first = listPtr->xOffset/((double) listPtr->maxWidth);
3093 last = (listPtr->xOffset + windowWidth)
3094 /((double) listPtr->maxWidth);
3095 if (last > 1.0) {
3096 last = 1.0;
3097 }
3098 }
3099 sprintf(string, " %g %g", first, last);
3100
3101 /*
3102 * We must hold onto the interpreter because the data referred to at
3103 * listPtr might be freed as a result of the call to Tcl_VarEval.
3104 */
3105
3106 interp = listPtr->interp;
3107 Tcl_Preserve((ClientData) interp);
3108 result = Tcl_VarEval(interp, listPtr->xScrollCmd, string,
3109 (char *) NULL);
3110 if (result != TCL_OK) {
3111 Tcl_AddErrorInfo(interp,
3112 "\n (horizontal scrolling command executed by listbox)");
3113 Tcl_BackgroundError(interp);
3114 }
3115 Tcl_Release((ClientData) interp);
3116 }
3117
3118 /*
3119 *----------------------------------------------------------------------
3120 *
3121 * ListboxListVarProc --
3122 *
3123 * Called whenever the trace on the listbox list var fires.
3124 *
3125 * Results:
3126 * None.
3127 *
3128 * Side effects:
3129 * None.
3130 *
3131 *----------------------------------------------------------------------
3132 */
3133
3134 static char *
3135 ListboxListVarProc(clientData, interp, name1, name2, flags)
3136 ClientData clientData; /* Information about button. */
3137 Tcl_Interp *interp; /* Interpreter containing variable. */
3138 char *name1; /* Not used. */
3139 char *name2; /* Not used. */
3140 int flags; /* Information about what happened. */
3141 {
3142 Listbox *listPtr = (Listbox *)clientData;
3143 Tcl_Obj *oldListObj, *varListObj;
3144 int oldLength;
3145 int i;
3146 Tcl_HashEntry *entry;
3147
3148 /* Bwah hahahaha -- puny mortal, you can't unset a -listvar'd variable! */
3149 if (flags & TCL_TRACE_UNSETS) {
3150 if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
3151 Tcl_SetVar2Ex(interp, listPtr->listVarName,
3152 (char *)NULL, listPtr->listObj, TCL_GLOBAL_ONLY);
3153 Tcl_TraceVar(interp, listPtr->listVarName,
3154 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
3155 ListboxListVarProc, clientData);
3156 return (char *)NULL;
3157 }
3158 } else {
3159 oldListObj = listPtr->listObj;
3160 varListObj = Tcl_GetVar2Ex(listPtr->interp, listPtr->listVarName,
3161 (char *)NULL, TCL_GLOBAL_ONLY);
3162 /*
3163 * Make sure the new value is a good list; if it's not, disallow
3164 * the change -- the fact that it is a listvar means that it must
3165 * always be a valid list -- and return an error message.
3166 */
3167 if (Tcl_ListObjLength(listPtr->interp, varListObj, &i) != TCL_OK) {
3168 Tcl_SetVar2Ex(interp, listPtr->listVarName, (char *)NULL,
3169 oldListObj, TCL_GLOBAL_ONLY);
3170 return("invalid listvar value");
3171 }
3172
3173 listPtr->listObj = varListObj;
3174 /* Incr the obj ref count so it doesn't vanish if the var is unset */
3175 Tcl_IncrRefCount(listPtr->listObj);
3176 /* Clean up the ref to our old list obj */
3177 Tcl_DecrRefCount(oldListObj);
3178 }
3179
3180 /*
3181 * If the list length has decreased, then we should clean up selection and
3182 * attributes information for elements past the end of the new list
3183 */
3184 oldLength = listPtr->nElements;
3185 Tcl_ListObjLength(listPtr->interp, listPtr->listObj, &listPtr->nElements);
3186 if (listPtr->nElements < oldLength) {
3187 for (i = listPtr->nElements; i < oldLength; i++) {
3188 /* Clean up selection */
3189 entry = Tcl_FindHashEntry(listPtr->selection, (char *)i);
3190 if (entry != NULL) {
3191 listPtr->numSelected--;
3192 Tcl_DeleteHashEntry(entry);
3193 }
3194
3195 /* Clean up attributes */
3196 entry = Tcl_FindHashEntry(listPtr->itemAttrTable, (char *)i);
3197 if (entry != NULL) {
3198 Tcl_DeleteHashEntry(entry);
3199 }
3200 }
3201 }
3202
3203 if (oldLength != listPtr->nElements) {
3204 listPtr->flags |= UPDATE_V_SCROLLBAR;
3205 if (listPtr->topIndex > (listPtr->nElements - listPtr->fullLines)) {
3206 listPtr->topIndex = listPtr->nElements - listPtr->fullLines;
3207 if (listPtr->topIndex < 0) {
3208 listPtr->topIndex = 0;
3209 }
3210 }
3211 }
3212
3213 /*
3214 * The computed maxWidth may have changed as a result of this operation.
3215 * However, we don't want to recompute it every time this trace fires
3216 * (imagine the user doing 1000 lappends to the listvar). Therefore, set
3217 * the MAXWIDTH_IS_STALE flag, which will cause the width to be recomputed
3218 * next time the list is redrawn.
3219 */
3220 listPtr->flags |= MAXWIDTH_IS_STALE;
3221
3222 EventuallyRedrawRange(listPtr, 0, listPtr->nElements-1);
3223 return (char*)NULL;
3224 }
3225
3226 /*
3227 *----------------------------------------------------------------------
3228 *
3229 * MigrateHashEntries --
3230 *
3231 * Given a hash table with entries keyed by a single integer value,
3232 * move all entries in a given range by a fixed amount, so that
3233 * if in the original table there was an entry with key n and
3234 * the offset was i, in the new table that entry would have key n + i.
3235 *
3236 * Results:
3237 * None.
3238 *
3239 * Side effects:
3240 * Rekeys some hash table entries.
3241 *
3242 *----------------------------------------------------------------------
3243 */
3244
3245 static void
3246 MigrateHashEntries(table, first, last, offset)
3247 Tcl_HashTable *table;
3248 int first;
3249 int last;
3250 int offset;
3251 {
3252 int i, new;
3253 Tcl_HashEntry *entry;
3254 ClientData clientData;
3255
3256 if (offset == 0) {
3257 return;
3258 }
3259 /* It's more efficient to do one if/else and nest the for loops inside,
3260 * although we could avoid some code duplication if we nested the if/else
3261 * inside the for loops */
3262 if (offset > 0) {
3263 for (i = last; i >= first; i--) {
3264 entry = Tcl_FindHashEntry(table, (char *)i);
3265 if (entry != NULL) {
3266 clientData = Tcl_GetHashValue(entry);
3267 Tcl_DeleteHashEntry(entry);
3268 entry = Tcl_CreateHashEntry(table, (char *)(i + offset), &new);
3269 Tcl_SetHashValue(entry, clientData);
3270 }
3271 }
3272 } else {
3273 for (i = first; i <= last; i++) {
3274 entry = Tcl_FindHashEntry(table, (char *)i);
3275 if (entry != NULL) {
3276 clientData = Tcl_GetHashValue(entry);
3277 Tcl_DeleteHashEntry(entry);
3278 entry = Tcl_CreateHashEntry(table, (char *)(i + offset), &new);
3279 Tcl_SetHashValue(entry, clientData);
3280 }
3281 }
3282 }
3283 return;
3284 }
3285
3286
3287 /* End of tklistbox.c */

Properties

Name Value
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25