]> cvs.zerfleddert.de Git - micropolis/blob - src/tk/tklist.c
Fixes for compilation with gcc 15
[micropolis] / src / tk / tklist.c
1 /*
2 * tkListbox.c --
3 *
4 * This module implements listbox widgets for the Tk
5 * toolkit. A listbox displays a collection of strings,
6 * one per line, and provides scrolling and selection.
7 *
8 * Copyright 1990-1992 Regents of the University of California.
9 * Permission to use, copy, modify, and distribute this
10 * software and its documentation for any purpose and without
11 * fee is hereby granted, provided that the above copyright
12 * notice appear in all copies. The University of California
13 * makes no representations about the suitability of this
14 * software for any purpose. It is provided "as is" without
15 * express or implied warranty.
16 */
17
18 #ifndef lint
19 static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkListbox.c,v 1.56 92/05/13 09:05:20 ouster Exp $ SPRITE (Berkeley)";
20 #endif
21
22 #include "tkconfig.h"
23 #include "default.h"
24 #include "tkint.h"
25
26 /*
27 * One record of the following type is kept for each element
28 * associated with a listbox widget:
29 */
30
31 typedef struct Element {
32 int textLength; /* # non-NULL characters in text. */
33 int lBearing; /* Distance from first character's
34 * origin to left edge of character. */
35 int pixelWidth; /* Total width of element in pixels (including
36 * left bearing and right bearing). */
37 struct Element *nextPtr; /* Next in list of all elements of this
38 * listbox, or NULL for last element. */
39 char text[4]; /* Characters of this element, NULL-
40 * terminated. The actual space allocated
41 * here will be as large as needed (> 4,
42 * most likely). Must be the last field
43 * of the record. */
44 } Element;
45
46 #define ElementSize(stringLength) \
47 ((unsigned) (sizeof(Element) - 3 + stringLength))
48
49 /*
50 * A data structure of the following type is kept for each listbox
51 * widget managed by this file:
52 */
53
54 typedef struct {
55 Tk_Window tkwin; /* Window that embodies the listbox. NULL
56 * means that the window has been destroyed
57 * but the data structures haven't yet been
58 * cleaned up.*/
59 Tcl_Interp *interp; /* Interpreter associated with listbox. */
60 int numElements; /* Total number of elements in this listbox. */
61 Element *elementPtr; /* First in list of elements (NULL if no
62 * elements. */
63
64 /*
65 * Information used when displaying widget:
66 */
67
68 Tk_3DBorder normalBorder; /* Used for drawing border around whole
69 * window, plus used for background. */
70 int borderWidth; /* Width of 3-D border around window. */
71 int relief; /* 3-D effect: TK_RELIEF_RAISED, etc. */
72 XFontStruct *fontPtr; /* Information about text font, or NULL. */
73 XColor *fgColorPtr; /* Text color in normal mode. */
74 GC textGC; /* For drawing normal text. */
75 Tk_3DBorder selBorder; /* Borders and backgrounds for selected
76 * elements. */
77 int selBorderWidth; /* Width of border around selection. */
78 XColor *selFgColorPtr; /* Foreground color for selected elements. */
79 GC selTextGC; /* For drawing selected text. */
80 char *geometry; /* Desired geometry for window. Malloc'ed. */
81 int lineHeight; /* Number of pixels allocated for each line
82 * in display. */
83 int topIndex; /* Index of top-most element visible in
84 * window. */
85 int numLines; /* Number of lines (elements) that fit
86 * in window at one time. */
87
88 /*
89 * Information to support horizontal scrolling:
90 */
91
92 int maxWidth; /* Width (in pixels) of widest string in
93 * listbox. */
94 int xScrollUnit; /* Number of pixels in one "unit" for
95 * horizontal scrolling (window scrolls
96 * horizontally in increments of this size).
97 * This is an average character size. */
98 int xOffset; /* The left edge of each string in the
99 * listbox is offset to the left by this
100 * many pixels (0 means no offset, positive
101 * means there is an offset). */
102
103 /*
104 * Information about what's selected, if any.
105 */
106
107 int selectFirst; /* Index of first selected element (-1 means
108 * nothing selected. */
109 int selectLast; /* Index of last selected element. */
110 int selectAnchor; /* Fixed end of selection (i.e. element
111 * at which selection was started.) */
112 int exportSelection; /* Non-zero means tie internal listbox
113 * to X selection. */
114
115 /*
116 * Information for scanning:
117 */
118
119 int scanMarkX; /* X-position at which scan started (e.g.
120 * button was pressed here). */
121 int scanMarkY; /* Y-position at which scan started (e.g.
122 * button was pressed here). */
123 int scanMarkXOffset; /* Value of "xOffset" field when scan
124 * started. */
125 int scanMarkYIndex; /* Index of line that was at top of window
126 * when scan started. */
127
128 /*
129 * Miscellaneous information:
130 */
131
132 Cursor cursor; /* Current cursor for window, or None. */
133 char *yScrollCmd; /* Command prefix for communicating with
134 * vertical scrollbar. NULL means no command
135 * to issue. Malloc'ed. */
136 char *xScrollCmd; /* Command prefix for communicating with
137 * horizontal scrollbar. NULL means no command
138 * to issue. Malloc'ed. */
139 int flags; /* Various flag bits: see below for
140 * definitions. */
141 } Listbox;
142
143 /*
144 * Flag bits for buttons:
145 *
146 * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
147 * has already been queued to redraw
148 * this window.
149 * UPDATE_V_SCROLLBAR: Non-zero means vertical scrollbar needs
150 * to be updated.
151 * UPDATE_H_SCROLLBAR: Non-zero means horizontal scrollbar needs
152 * to be updated.
153 */
154
155 #define REDRAW_PENDING 1
156 #define UPDATE_V_SCROLLBAR 2
157 #define UPDATE_H_SCROLLBAR 4
158
159 /*
160 * Information used for argv parsing:
161 */
162
163 static Tk_ConfigSpec configSpecs[] = {
164 {TK_CONFIG_BORDER, "-background", "background", "Background",
165 DEF_LISTBOX_BG_COLOR, Tk_Offset(Listbox, normalBorder),
166 TK_CONFIG_COLOR_ONLY},
167 {TK_CONFIG_BORDER, "-background", "background", "Background",
168 DEF_LISTBOX_BG_MONO, Tk_Offset(Listbox, normalBorder),
169 TK_CONFIG_MONO_ONLY},
170 {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
171 (char *) NULL, 0, 0},
172 {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
173 (char *) NULL, 0, 0},
174 {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
175 DEF_LISTBOX_BORDER_WIDTH, Tk_Offset(Listbox, borderWidth), 0},
176 {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
177 DEF_LISTBOX_CURSOR, Tk_Offset(Listbox, cursor), TK_CONFIG_NULL_OK},
178 {TK_CONFIG_BOOLEAN, "-exportselection", "exportSelection",
179 "ExportSelection", DEF_LISTBOX_EXPORT_SELECTION,
180 Tk_Offset(Listbox, exportSelection), 0},
181 {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
182 (char *) NULL, 0, 0},
183 {TK_CONFIG_FONT, "-font", "font", "Font",
184 DEF_LISTBOX_FONT, Tk_Offset(Listbox, fontPtr), 0},
185 {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
186 DEF_LISTBOX_FG, Tk_Offset(Listbox, fgColorPtr), 0},
187 {TK_CONFIG_STRING, "-geometry", "geometry", "Geometry",
188 DEF_LISTBOX_GEOMETRY, Tk_Offset(Listbox, geometry), 0},
189 {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
190 DEF_LISTBOX_RELIEF, Tk_Offset(Listbox, relief), 0},
191 {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
192 DEF_LISTBOX_SELECT_COLOR, Tk_Offset(Listbox, selBorder),
193 TK_CONFIG_COLOR_ONLY},
194 {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
195 DEF_LISTBOX_SELECT_MONO, Tk_Offset(Listbox, selBorder),
196 TK_CONFIG_MONO_ONLY},
197 {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
198 DEF_LISTBOX_SELECT_BD, Tk_Offset(Listbox, selBorderWidth), 0},
199 {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
200 DEF_LISTBOX_SELECT_FG_COLOR, Tk_Offset(Listbox, selFgColorPtr),
201 TK_CONFIG_COLOR_ONLY},
202 {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
203 DEF_LISTBOX_SELECT_FG_MONO, Tk_Offset(Listbox, selFgColorPtr),
204 TK_CONFIG_MONO_ONLY},
205 {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
206 DEF_LISTBOX_SCROLL_COMMAND, Tk_Offset(Listbox, xScrollCmd), 0},
207 {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
208 DEF_LISTBOX_SCROLL_COMMAND, Tk_Offset(Listbox, yScrollCmd), 0},
209 {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
210 (char *) NULL, 0, 0}
211 };
212
213 /*
214 * Forward declarations for procedures defined later in this file:
215 */
216
217 static void ChangeListboxOffset _ANSI_ARGS_((Listbox *listPtr,
218 int offset));
219 static void ChangeListboxView _ANSI_ARGS_((Listbox *listPtr,
220 int index));
221 static int ConfigureListbox _ANSI_ARGS_((Tcl_Interp *interp,
222 Listbox *listPtr, int argc, char **argv,
223 int flags));
224 static void DeleteEls _ANSI_ARGS_((Listbox *listPtr, int first,
225 int last));
226 static void DestroyListbox _ANSI_ARGS_((ClientData clientData));
227 static void DisplayListbox _ANSI_ARGS_((ClientData clientData));
228 static int GetListboxIndex _ANSI_ARGS_((Tcl_Interp *interp,
229 Listbox *listPtr, char *string, int *indexPtr));
230 static void InsertEls _ANSI_ARGS_((Listbox *listPtr, int index,
231 int argc, char **argv));
232 static void ListboxComputeWidths _ANSI_ARGS_((Listbox *listPtr,
233 int fontChanged));
234 static void ListboxEventProc _ANSI_ARGS_((ClientData clientData,
235 XEvent *eventPtr));
236 static int ListboxFetchSelection _ANSI_ARGS_((
237 ClientData clientData, int offset, char *buffer,
238 int maxBytes));
239 static void ListboxLostSelection _ANSI_ARGS_((
240 ClientData clientData));
241 static void ListboxRedrawRange _ANSI_ARGS_((Listbox *listPtr,
242 int first, int last));
243 static void ListboxScanTo _ANSI_ARGS_((Listbox *listPtr,
244 int x, int y));
245 static void ListboxSelectFrom _ANSI_ARGS_((Listbox *listPtr,
246 int index));
247 static void ListboxSelectTo _ANSI_ARGS_((Listbox *listPtr,
248 int index));
249 static void ListboxUpdateHScrollbar _ANSI_ARGS_((Listbox *listPtr));
250 static void ListboxUpdateVScrollbar _ANSI_ARGS_((Listbox *listPtr));
251 static int ListboxWidgetCmd _ANSI_ARGS_((ClientData clientData,
252 Tcl_Interp *interp, int argc, char **argv));
253 static int NearestListboxElement _ANSI_ARGS_((Listbox *listPtr,
254 int y));
255 \f
256 /*
257 *--------------------------------------------------------------
258 *
259 * Tk_ListboxCmd --
260 *
261 * This procedure is invoked to process the "listbox" Tcl
262 * command. See the user documentation for details on what
263 * it does.
264 *
265 * Results:
266 * A standard Tcl result.
267 *
268 * Side effects:
269 * See the user documentation.
270 *
271 *--------------------------------------------------------------
272 */
273
274 int
275 Tk_ListboxCmd (
276 ClientData clientData, /* Main window associated with
277 * interpreter. */
278 Tcl_Interp *interp, /* Current interpreter. */
279 int argc, /* Number of arguments. */
280 char **argv /* Argument strings. */
281 )
282 {
283 register Listbox *listPtr;
284 Tk_Window new;
285 Tk_Window tkwin = (Tk_Window) clientData;
286
287 if (argc < 2) {
288 Tcl_AppendResult(interp, "wrong # args: should be \"",
289 argv[0], " pathName ?options?\"", (char *) NULL);
290 return TCL_ERROR;
291 }
292
293 new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
294 if (new == NULL) {
295 return TCL_ERROR;
296 }
297
298 /*
299 * Initialize the fields of the structure that won't be initialized
300 * by ConfigureListbox, or that ConfigureListbox requires to be
301 * initialized already (e.g. resource pointers).
302 */
303
304 listPtr = (Listbox *) ckalloc(sizeof(Listbox));
305 listPtr->tkwin = new;
306 listPtr->interp = interp;
307 listPtr->numElements = 0;
308 listPtr->elementPtr = NULL;
309 listPtr->normalBorder = NULL;
310 listPtr->fontPtr = NULL;
311 listPtr->fgColorPtr = NULL;
312 listPtr->textGC = None;
313 listPtr->selBorder = NULL;
314 listPtr->selFgColorPtr = NULL;
315 listPtr->selTextGC = NULL;
316 listPtr->geometry = NULL;
317 listPtr->topIndex = 0;
318 listPtr->xOffset = 0;
319 listPtr->selectFirst = -1;
320 listPtr->selectLast = -1;
321 listPtr->exportSelection = 1;
322 listPtr->cursor = None;
323 listPtr->yScrollCmd = NULL;
324 listPtr->xScrollCmd = NULL;
325 listPtr->flags = 0;
326
327 Tk_SetClass(listPtr->tkwin, "Listbox");
328 Tk_CreateEventHandler(listPtr->tkwin, ExposureMask|StructureNotifyMask,
329 ListboxEventProc, (ClientData) listPtr);
330 Tk_CreateSelHandler(listPtr->tkwin, XA_STRING, ListboxFetchSelection,
331 (ClientData) listPtr, XA_STRING);
332 Tcl_CreateCommand(interp, Tk_PathName(listPtr->tkwin), ListboxWidgetCmd,
333 (ClientData) listPtr, (void (*)(int *)) NULL);
334 if (ConfigureListbox(interp, listPtr, argc-2, argv+2, 0) != TCL_OK) {
335 goto error;
336 }
337
338 interp->result = Tk_PathName(listPtr->tkwin);
339 return TCL_OK;
340
341 error:
342 Tk_DestroyWindow(listPtr->tkwin);
343 return TCL_ERROR;
344 }
345 \f
346 /*
347 *--------------------------------------------------------------
348 *
349 * ListboxWidgetCmd --
350 *
351 * This procedure is invoked to process the Tcl command
352 * that corresponds to a widget managed by this module.
353 * See the user documentation for details on what it does.
354 *
355 * Results:
356 * A standard Tcl result.
357 *
358 * Side effects:
359 * See the user documentation.
360 *
361 *--------------------------------------------------------------
362 */
363
364 static int
365 ListboxWidgetCmd (
366 ClientData clientData, /* Information about listbox widget. */
367 Tcl_Interp *interp, /* Current interpreter. */
368 int argc, /* Number of arguments. */
369 char **argv /* Argument strings. */
370 )
371 {
372 register Listbox *listPtr = (Listbox *) clientData;
373 int result = TCL_OK;
374 int length;
375 char c;
376
377 if (argc < 2) {
378 Tcl_AppendResult(interp, "wrong # args: should be \"",
379 argv[0], " option ?arg arg ...?\"", (char *) NULL);
380 return TCL_ERROR;
381 }
382 Tk_Preserve((ClientData) listPtr);
383 c = argv[1][0];
384 length = strlen(argv[1]);
385 if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
386 && (length >= 2)) {
387 if (argc == 2) {
388 result = Tk_ConfigureInfo(interp, listPtr->tkwin, configSpecs,
389 (char *) listPtr, (char *) NULL, 0);
390 } else if (argc == 3) {
391 result = Tk_ConfigureInfo(interp, listPtr->tkwin, configSpecs,
392 (char *) listPtr, argv[2], 0);
393 } else {
394 result = ConfigureListbox(interp, listPtr, argc-2, argv+2,
395 TK_CONFIG_ARGV_ONLY);
396 }
397 } else if ((c == 'c') && (strncmp(argv[1], "curselection", length) == 0)
398 && (length >= 2)) {
399 int i;
400 char index[20];
401
402 if (argc != 2) {
403 Tcl_AppendResult(interp, "wrong # args: should be \"",
404 argv[0], " curselection\"",
405 (char *) NULL);
406 goto error;
407 }
408 if (listPtr->selectFirst != -1) {
409 for (i = listPtr->selectFirst; i <= listPtr->selectLast; i++) {
410 sprintf(index, "%d", i);
411 Tcl_AppendElement(interp, index, 0);
412 }
413 }
414 } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
415 int first, last;
416
417 if ((argc < 3) || (argc > 4)) {
418 Tcl_AppendResult(interp, "wrong # args: should be \"",
419 argv[0], " delete firstIndex ?lastIndex?\"",
420 (char *) NULL);
421 goto error;
422 }
423 if (GetListboxIndex(interp, listPtr, argv[2], &first) != TCL_OK) {
424 goto error;
425 }
426 if (argc == 3) {
427 last = first;
428 } else {
429 if (GetListboxIndex(interp, listPtr, argv[3], &last) != TCL_OK) {
430 goto error;
431 }
432 }
433 DeleteEls(listPtr, first, last);
434 } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
435 int index;
436 register Element *elPtr;
437
438 if (argc != 3) {
439 Tcl_AppendResult(interp, "wrong # args: should be \"",
440 argv[0], " get index\"", (char *) NULL);
441 goto error;
442 }
443 if (GetListboxIndex(interp, listPtr, argv[2], &index) != TCL_OK) {
444 goto error;
445 }
446 if (index < 0) {
447 index = 0;
448 }
449 if (index >= listPtr->numElements) {
450 index = listPtr->numElements-1;
451 }
452 for (elPtr = listPtr->elementPtr; index > 0;
453 index--, elPtr = elPtr->nextPtr) {
454 /* Empty loop body. */
455 }
456 if (elPtr != NULL) {
457 interp->result = elPtr->text;
458 }
459 } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)) {
460 int index;
461
462 if (argc < 3) {
463 Tcl_AppendResult(interp, "wrong # args: should be \"",
464 argv[0], " insert index ?element? ?element ...?\"",
465 (char *) NULL);
466 goto error;
467 }
468 if (argc > 3) {
469 if (GetListboxIndex(interp, listPtr, argv[2], &index) != TCL_OK) {
470 goto error;
471 }
472 InsertEls(listPtr, index, argc-3, argv+3);
473 }
474 } else if ((c == 'n') && (strncmp(argv[1], "nearest", length) == 0)) {
475 int index, y;
476
477 if (argc != 3) {
478 Tcl_AppendResult(interp, "wrong # args: should be \"",
479 argv[0], " nearest y\"", (char *) NULL);
480 goto error;
481 }
482 if (Tcl_GetInt(interp, argv[2], &y) != TCL_OK) {
483 goto error;
484 }
485 index = NearestListboxElement(listPtr, y);
486 sprintf(interp->result, "%d", index);
487 } else if ((c == 's') && (length >= 2)
488 && (strncmp(argv[1], "scan", length) == 0)) {
489 int x, y;
490
491 if (argc != 5) {
492 Tcl_AppendResult(interp, "wrong # args: should be \"",
493 argv[0], " scan mark|dragto x y\"", (char *) NULL);
494 goto error;
495 }
496 if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK)
497 || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)) {
498 goto error;
499 }
500 if ((argv[2][0] == 'm')
501 && (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
502 listPtr->scanMarkX = x;
503 listPtr->scanMarkY = y;
504 listPtr->scanMarkXOffset = listPtr->xOffset;
505 listPtr->scanMarkYIndex = listPtr->topIndex;
506 } else if ((argv[2][0] == 'd')
507 && (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
508 ListboxScanTo(listPtr, x, y);
509 } else {
510 Tcl_AppendResult(interp, "bad scan option \"", argv[2],
511 "\": must be mark or dragto", (char *) NULL);
512 goto error;
513 }
514 } else if ((c == 's') && (length >= 2)
515 && (strncmp(argv[1], "select", length) == 0)) {
516 int index;
517
518 if (argc < 3) {
519 Tcl_AppendResult(interp, "too few args: should be \"",
520 argv[0], " select option ?index?\"", (char *) NULL);
521 goto error;
522 }
523 length = strlen(argv[2]);
524 c = argv[2][0];
525 if ((c == 'c') && (argv[2] != NULL)
526 && (strncmp(argv[2], "clear", length) == 0)) {
527 if (argc != 3) {
528 Tcl_AppendResult(interp, "wrong # args: should be \"",
529 argv[0], " select clear\"", (char *) NULL);
530 goto error;
531 }
532 if (listPtr->selectFirst != -1) {
533 ListboxRedrawRange(listPtr, listPtr->selectFirst,
534 listPtr->selectLast);
535 listPtr->selectFirst = -1;
536 }
537 goto done;
538 }
539 if (argc != 4) {
540 Tcl_AppendResult(interp, "wrong # args: should be \"",
541 argv[0], " select option index\"", (char *) NULL);
542 goto error;
543 }
544 if (GetListboxIndex(interp, listPtr, argv[3], &index) != TCL_OK) {
545 goto error;
546 }
547 if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
548 if (index < (listPtr->selectFirst + listPtr->selectLast)/2) {
549 listPtr->selectAnchor = listPtr->selectLast;
550 } else {
551 listPtr->selectAnchor = listPtr->selectFirst;
552 }
553 ListboxSelectTo(listPtr, index);
554 } else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
555 ListboxSelectFrom(listPtr, index);
556 } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
557 ListboxSelectTo(listPtr, index);
558 } else {
559 Tcl_AppendResult(interp, "bad select option \"", argv[2],
560 "\": must be adjust, clear, from, or to", (char *) NULL);
561 goto error;
562 }
563 } else if ((c == 's') && (length >= 2)
564 && (strncmp(argv[1], "size", length) == 0)) {
565 sprintf(interp->result, "%d", listPtr->numElements);
566 } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
567 int index;
568
569 if (argc != 3) {
570 Tcl_AppendResult(interp, "wrong # args: should be \"",
571 argv[0], " xview index\"", (char *) NULL);
572 goto error;
573 }
574 if (Tcl_GetInt(interp, argv[2], &index) != TCL_OK) {
575 goto error;
576 }
577 ChangeListboxOffset(listPtr, index*listPtr->xScrollUnit);
578 } else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)) {
579 int index;
580
581 if (argc != 3) {
582 Tcl_AppendResult(interp, "wrong # args: should be \"",
583 argv[0], " yview index\"", (char *) NULL);
584 goto error;
585 }
586 if (GetListboxIndex(interp, listPtr, argv[2], &index) != TCL_OK) {
587 goto error;
588 }
589 ChangeListboxView(listPtr, index);
590 } else {
591 Tcl_AppendResult(interp, "bad option \"", argv[1],
592 "\": must be configure, curselection, delete, get, ",
593 "insert, nearest, scan, select, size, ",
594 "xview, or yview", (char *) NULL);
595 goto error;
596 }
597 done:
598 Tk_Release((ClientData) listPtr);
599 return result;
600
601 error:
602 Tk_Release((ClientData) listPtr);
603 return TCL_ERROR;
604 }
605 \f
606 /*
607 *----------------------------------------------------------------------
608 *
609 * DestroyListbox --
610 *
611 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
612 * to clean up the internal structure of a listbox at a safe time
613 * (when no-one is using it anymore).
614 *
615 * Results:
616 * None.
617 *
618 * Side effects:
619 * Everything associated with the listbox is freed up.
620 *
621 *----------------------------------------------------------------------
622 */
623
624 static void
625 DestroyListbox (
626 ClientData clientData /* Info about listbox widget. */
627 )
628 {
629 register Listbox *listPtr = (Listbox *) clientData;
630 register Element *elPtr, *nextPtr;
631
632 for (elPtr = listPtr->elementPtr; elPtr != NULL; ) {
633 nextPtr = elPtr->nextPtr;
634 ckfree((char *) elPtr);
635 elPtr = nextPtr;
636 }
637 if (listPtr->normalBorder != NULL) {
638 Tk_Free3DBorder(listPtr->normalBorder);
639 }
640 if (listPtr->fontPtr != NULL) {
641 Tk_FreeFontStruct(listPtr->fontPtr);
642 }
643 if (listPtr->fgColorPtr != NULL) {
644 Tk_FreeColor(listPtr->fgColorPtr);
645 }
646 if (listPtr->textGC != None) {
647 Tk_FreeGC(listPtr->textGC);
648 }
649 if (listPtr->selBorder != NULL) {
650 Tk_Free3DBorder(listPtr->selBorder);
651 }
652 if (listPtr->selFgColorPtr != NULL) {
653 Tk_FreeColor(listPtr->selFgColorPtr);
654 }
655 if (listPtr->selTextGC != None) {
656 Tk_FreeGC(listPtr->selTextGC);
657 }
658 if (listPtr->geometry != NULL) {
659 ckfree(listPtr->geometry);
660 }
661 if (listPtr->cursor != None) {
662 Tk_FreeCursor(listPtr->cursor);
663 }
664 if (listPtr->yScrollCmd != NULL) {
665 ckfree(listPtr->yScrollCmd);
666 }
667 if (listPtr->xScrollCmd != NULL) {
668 ckfree(listPtr->xScrollCmd);
669 }
670 ckfree((char *) listPtr);
671 }
672 \f
673 /*
674 *----------------------------------------------------------------------
675 *
676 * ConfigureListbox --
677 *
678 * This procedure is called to process an argv/argc list, plus
679 * the Tk option database, in order to configure (or reconfigure)
680 * a listbox widget.
681 *
682 * Results:
683 * The return value is a standard Tcl result. If TCL_ERROR is
684 * returned, then interp->result contains an error message.
685 *
686 * Side effects:
687 * Configuration information, such as colors, border width,
688 * etc. get set for listPtr; old resources get freed,
689 * if there were any.
690 *
691 *----------------------------------------------------------------------
692 */
693
694 static int
695 ConfigureListbox (
696 Tcl_Interp *interp, /* Used for error reporting. */
697 register Listbox *listPtr, /* Information about widget; may or may
698 * not already have values for some fields. */
699 int argc, /* Number of valid entries in argv. */
700 char **argv, /* Arguments. */
701 int flags /* Flags to pass to Tk_ConfigureWidget. */
702 )
703 {
704 XGCValues gcValues;
705 GC new;
706 int width, height, fontHeight, oldExport;
707
708 oldExport = listPtr->exportSelection;
709 if (Tk_ConfigureWidget(interp, listPtr->tkwin, configSpecs,
710 argc, argv, (char *) listPtr, flags) != TCL_OK) {
711 return TCL_ERROR;
712 }
713
714 /*
715 * A few options need special processing, such as parsing the
716 * geometry and setting the background from a 3-D border.
717 */
718
719 Tk_SetBackgroundFromBorder(listPtr->tkwin, listPtr->normalBorder);
720
721 gcValues.foreground = listPtr->fgColorPtr->pixel;
722 gcValues.font = listPtr->fontPtr->fid;
723 gcValues.graphics_exposures = False;
724 new = Tk_GetGC(listPtr->tkwin, GCForeground|GCFont|GCGraphicsExposures,
725 &gcValues);
726 if (listPtr->textGC != None) {
727 Tk_FreeGC(listPtr->textGC);
728 }
729 listPtr->textGC = new;
730
731 gcValues.foreground = listPtr->selFgColorPtr->pixel;
732 gcValues.font = listPtr->fontPtr->fid;
733 new = Tk_GetGC(listPtr->tkwin, GCForeground|GCFont, &gcValues);
734 if (listPtr->selTextGC != None) {
735 Tk_FreeGC(listPtr->selTextGC);
736 }
737 listPtr->selTextGC = new;
738
739 /*
740 * Claim the selection if we've suddenly started exporting it.
741 */
742
743 if (listPtr->exportSelection && (!oldExport)
744 && (listPtr->selectFirst !=-1)) {
745 Tk_OwnSelection(listPtr->tkwin, ListboxLostSelection,
746 (ClientData) listPtr);
747 }
748
749 /*
750 * Register the desired geometry for the window, and arrange for
751 * the window to be redisplayed.
752 */
753
754 if ((sscanf(listPtr->geometry, "%dx%d", &width, &height) != 2)
755 || (width <= 0) || (height <= 0)) {
756 Tcl_AppendResult(interp, "bad geometry \"",
757 listPtr->geometry, "\"", (char *) NULL);
758 return TCL_ERROR;
759 }
760 fontHeight = listPtr->fontPtr->ascent + listPtr->fontPtr->descent;
761 listPtr->lineHeight = fontHeight + 1 + 2*listPtr->selBorderWidth;
762 listPtr->numLines = (Tk_Height(listPtr->tkwin) - 2*listPtr->borderWidth)
763 / listPtr->lineHeight;
764 if (listPtr->numLines < 0) {
765 listPtr->numLines = 0;
766 }
767 ListboxComputeWidths(listPtr, 1);
768 width = (width+1)*listPtr->xScrollUnit + 2*listPtr->borderWidth
769 + 2*listPtr->selBorderWidth;
770 height = height*listPtr->lineHeight + 2*listPtr->borderWidth;
771 Tk_GeometryRequest(listPtr->tkwin, width, height);
772 Tk_SetInternalBorder(listPtr->tkwin, listPtr->borderWidth);
773 listPtr->flags |= UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR;
774 ListboxRedrawRange(listPtr, 0, listPtr->numElements-1);
775 return TCL_OK;
776 }
777 \f
778 /*
779 *--------------------------------------------------------------
780 *
781 * DisplayListbox --
782 *
783 * This procedure redraws the contents of a listbox window.
784 *
785 * Results:
786 * None.
787 *
788 * Side effects:
789 * Information appears on the screen.
790 *
791 *--------------------------------------------------------------
792 */
793
794 static void
795 DisplayListbox (
796 ClientData clientData /* Information about window. */
797 )
798 {
799 register Listbox *listPtr = (Listbox *) clientData;
800 register Tk_Window tkwin = listPtr->tkwin;
801 register Element *elPtr;
802 GC gc;
803 int i, limit, x, y, margin;
804 Pixmap pixmap;
805
806 listPtr->flags &= ~REDRAW_PENDING;
807 if (listPtr->flags & UPDATE_V_SCROLLBAR) {
808 ListboxUpdateVScrollbar(listPtr);
809 }
810 if (listPtr->flags & UPDATE_H_SCROLLBAR) {
811 ListboxUpdateHScrollbar(listPtr);
812 }
813 listPtr->flags &= ~(REDRAW_PENDING|UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR);
814 if ((listPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
815 return;
816 }
817
818 /*
819 * Redrawing is done in a temporary pixmap that is allocated
820 * here and freed at the end of the procedure. All drawing is
821 * done to the pixmap, and the pixmap is copied to the screen
822 * at the end of the procedure. This provides the smoothest
823 * possible visual effects (no flashing on the screen).
824 */
825
826 pixmap = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
827 Tk_Width(tkwin), Tk_Height(tkwin),
828 Tk_DefaultDepth(Tk_Screen(tkwin)));
829 Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, listPtr->normalBorder,
830 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), listPtr->borderWidth,
831 listPtr->relief);
832
833 /*
834 * Iterate through all of the elements of the listbox, displaying each
835 * in turn. Selected elements use a different GC and have a raised
836 * background.
837 */
838
839 limit = listPtr->topIndex + listPtr->numLines - 1;
840 if (limit >= listPtr->numElements) {
841 limit = listPtr->numElements-1;
842 }
843 margin = listPtr->selBorderWidth + listPtr->xScrollUnit/2;
844 for (elPtr = listPtr->elementPtr, i = 0; (elPtr != NULL) && (i <= limit);
845 elPtr = elPtr->nextPtr, i++) {
846 if (i < listPtr->topIndex) {
847 continue;
848 }
849 x = listPtr->borderWidth;
850 y = ((i - listPtr->topIndex) * listPtr->lineHeight)
851 + listPtr->borderWidth;
852 gc = listPtr->textGC;
853 if ((listPtr->selectFirst >= 0) && (i >= listPtr->selectFirst)
854 && (i <= listPtr->selectLast)) {
855 gc = listPtr->selTextGC;
856 Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap,
857 listPtr->selBorder, x, y,
858 Tk_Width(tkwin) - 2*listPtr->borderWidth,
859 listPtr->lineHeight, listPtr->selBorderWidth,
860 TK_RELIEF_RAISED);
861 }
862 y += listPtr->fontPtr->ascent + listPtr->selBorderWidth;
863 x += margin - elPtr->lBearing - listPtr->xOffset;
864 XDrawString(Tk_Display(tkwin), pixmap, gc, x, y,
865 elPtr->text, elPtr->textLength);
866 }
867
868 /*
869 * Redraw the border for the listbox to make sure that it's on top
870 * of any of the text of the listbox entries.
871 */
872
873 Tk_Draw3DRectangle(Tk_Display(tkwin), pixmap,
874 listPtr->normalBorder, 0, 0, Tk_Width(tkwin),
875 Tk_Height(tkwin), listPtr->borderWidth,
876 listPtr->relief);
877 XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin),
878 listPtr->textGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
879 0, 0);
880 XFreePixmap(Tk_Display(tkwin), pixmap);
881 }
882 \f
883 /*
884 *----------------------------------------------------------------------
885 *
886 * InsertEls --
887 *
888 * Add new elements to a listbox widget.
889 *
890 * Results:
891 * None.
892 *
893 * Side effects:
894 * New information gets added to listPtr; it will be redisplayed
895 * soon, but not immediately.
896 *
897 *----------------------------------------------------------------------
898 */
899
900 static void
901 InsertEls (
902 register Listbox *listPtr, /* Listbox that is to get the new
903 * elements. */
904 int index, /* Add the new elements before this
905 * element. */
906 int argc, /* Number of new elements to add. */
907 char **argv /* New elements (one per entry). */
908 )
909 {
910 register Element *prevPtr, *newPtr;
911 int length, dummy, i, oldMaxWidth;
912 XCharStruct bbox;
913
914 /*
915 * Find the element before which the new ones will be inserted.
916 */
917
918 if (index <= 0) {
919 index = 0;
920 }
921 if (index > listPtr->numElements) {
922 index = listPtr->numElements;
923 }
924 if (index == 0) {
925 prevPtr = NULL;
926 } else {
927 for (prevPtr = listPtr->elementPtr, i = index - 1; i > 0; i--) {
928 prevPtr = prevPtr->nextPtr;
929 }
930 }
931
932 /*
933 * For each new element, create a record, initialize it, and link
934 * it into the list of elements.
935 */
936
937 oldMaxWidth = listPtr->maxWidth;
938 for (i = argc ; i > 0; i--, argv++, prevPtr = newPtr) {
939 length = strlen(*argv);
940 newPtr = (Element *) ckalloc(ElementSize(length));
941 newPtr->textLength = length;
942 strcpy(newPtr->text, *argv);
943 XTextExtents(listPtr->fontPtr, newPtr->text, newPtr->textLength,
944 &dummy, &dummy, &dummy, &bbox);
945 newPtr->lBearing = bbox.lbearing;
946 newPtr->pixelWidth = bbox.lbearing + bbox.rbearing;
947 if (newPtr->pixelWidth > listPtr->maxWidth) {
948 listPtr->maxWidth = newPtr->pixelWidth;
949 }
950 if (prevPtr == NULL) {
951 newPtr->nextPtr = listPtr->elementPtr;
952 listPtr->elementPtr = newPtr;
953 } else {
954 newPtr->nextPtr = prevPtr->nextPtr;
955 prevPtr->nextPtr = newPtr;
956 }
957 }
958 listPtr->numElements += argc;
959
960 /*
961 * Update the selection to account for the renumbering that has just
962 * occurred. Then arrange for the new information to be displayed.
963 */
964
965 if (index <= listPtr->selectFirst) {
966 listPtr->selectFirst += argc;
967 }
968 if (index <= listPtr->selectLast) {
969 listPtr->selectLast += argc;
970 }
971 listPtr->flags |= UPDATE_V_SCROLLBAR;
972 if (listPtr->maxWidth != oldMaxWidth) {
973 listPtr->flags |= UPDATE_H_SCROLLBAR;
974 }
975 ListboxRedrawRange(listPtr, index, listPtr->numElements-1);
976 }
977 \f
978 /*
979 *----------------------------------------------------------------------
980 *
981 * DeleteEls --
982 *
983 * Remove one or more elements from a listbox widget.
984 *
985 * Results:
986 * None.
987 *
988 * Side effects:
989 * Memory gets freed, the listbox gets modified and (eventually)
990 * redisplayed.
991 *
992 *----------------------------------------------------------------------
993 */
994
995 static void
996 DeleteEls (
997 register Listbox *listPtr, /* Listbox widget to modify. */
998 int first, /* Index of first element to delete. */
999 int last /* Index of last element to delete. */
1000 )
1001 {
1002 register Element *prevPtr, *elPtr;
1003 int count, i, widthChanged;
1004
1005 /*
1006 * Adjust the range to fit within the existing elements of the
1007 * listbox, and make sure there's something to delete.
1008 */
1009
1010 if (first < 0) {
1011 first = 0;
1012 }
1013 if (last >= listPtr->numElements) {
1014 last = listPtr->numElements-1;
1015 }
1016 count = last + 1 - first;
1017 if (count <= 0) {
1018 return;
1019 }
1020
1021 /*
1022 * Find the element just before the ones to delete.
1023 */
1024
1025 if (first == 0) {
1026 prevPtr = NULL;
1027 } else {
1028 for (i = first-1, prevPtr = listPtr->elementPtr; i > 0; i--) {
1029 prevPtr = prevPtr->nextPtr;
1030 }
1031 }
1032
1033 /*
1034 * Delete the requested number of elements.
1035 */
1036
1037 widthChanged = 0;
1038 for (i = count; i > 0; i--) {
1039 if (prevPtr == NULL) {
1040 elPtr = listPtr->elementPtr;
1041 listPtr->elementPtr = elPtr->nextPtr;
1042 } else {
1043 elPtr = prevPtr->nextPtr;
1044 prevPtr->nextPtr = elPtr->nextPtr;
1045 }
1046 if (elPtr->pixelWidth == listPtr->maxWidth) {
1047 widthChanged = 1;
1048 }
1049 ckfree((char *) elPtr);
1050 }
1051 listPtr->numElements -= count;
1052
1053 /*
1054 * Update the selection and viewing information to reflect the change
1055 * in the element numbering, and redisplay to slide information up over
1056 * the elements that were deleted.
1057 */
1058
1059 if (first <= listPtr->selectFirst) {
1060 listPtr->selectFirst -= count;
1061 if (listPtr->selectFirst < first) {
1062 listPtr->selectFirst = first;
1063 }
1064 }
1065 if (first <= listPtr->selectLast) {
1066 listPtr->selectLast -= count;
1067 if (listPtr->selectLast < first) {
1068 listPtr->selectLast = first-1;
1069 }
1070 }
1071 if (listPtr->selectLast < listPtr->selectFirst) {
1072 listPtr->selectFirst = -1;
1073 }
1074 if (first <= listPtr->topIndex) {
1075 listPtr->topIndex -= count;
1076 if (listPtr->topIndex < first) {
1077 listPtr->topIndex = first;
1078 }
1079 }
1080 listPtr->flags |= UPDATE_V_SCROLLBAR;
1081 if (widthChanged) {
1082 ListboxComputeWidths(listPtr, 0);
1083 listPtr->flags |= UPDATE_H_SCROLLBAR;
1084 }
1085 ListboxRedrawRange(listPtr, first, listPtr->numElements-1);
1086 }
1087 \f
1088 /*
1089 *--------------------------------------------------------------
1090 *
1091 * ListboxEventProc --
1092 *
1093 * This procedure is invoked by the Tk dispatcher for various
1094 * events on listboxes.
1095 *
1096 * Results:
1097 * None.
1098 *
1099 * Side effects:
1100 * When the window gets deleted, internal structures get
1101 * cleaned up. When it gets exposed, it is redisplayed.
1102 *
1103 *--------------------------------------------------------------
1104 */
1105
1106 static void
1107 ListboxEventProc (
1108 ClientData clientData, /* Information about window. */
1109 XEvent *eventPtr /* Information about event. */
1110 )
1111 {
1112 Listbox *listPtr = (Listbox *) clientData;
1113
1114 if (eventPtr->type == Expose) {
1115 ListboxRedrawRange(listPtr,
1116 NearestListboxElement(listPtr, eventPtr->xexpose.y),
1117 NearestListboxElement(listPtr, eventPtr->xexpose.y
1118 + eventPtr->xexpose.height));
1119 } else if (eventPtr->type == DestroyNotify) {
1120 Tcl_DeleteCommand(listPtr->interp, Tk_PathName(listPtr->tkwin));
1121 listPtr->tkwin = NULL;
1122 if (listPtr->flags & REDRAW_PENDING) {
1123 Tk_CancelIdleCall(DisplayListbox, (ClientData) listPtr);
1124 }
1125 Tk_EventuallyFree((ClientData) listPtr, DestroyListbox);
1126 } else if (eventPtr->type == ConfigureNotify) {
1127 Tk_Preserve((ClientData) listPtr);
1128 listPtr->numLines = (Tk_Height(listPtr->tkwin)
1129 - 2*listPtr->borderWidth) / listPtr->lineHeight;
1130 listPtr->flags |= UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR;
1131 ListboxRedrawRange(listPtr, 0, listPtr->numElements-1);
1132 Tk_Release((ClientData) listPtr);
1133 }
1134 }
1135 \f
1136 /*
1137 *--------------------------------------------------------------
1138 *
1139 * GetListboxIndex --
1140 *
1141 * Parse an index into a listbox and return either its value
1142 * or an error.
1143 *
1144 * Results:
1145 * A standard Tcl result. If all went well, then *indexPtr is
1146 * filled in with the index (into listPtr) corresponding to
1147 * string. Otherwise an error message is left in interp->result.
1148 *
1149 * Side effects:
1150 * None.
1151 *
1152 *--------------------------------------------------------------
1153 */
1154
1155 static int
1156 GetListboxIndex (
1157 Tcl_Interp *interp, /* For error messages. */
1158 Listbox *listPtr, /* Listbox for which the index is being
1159 * specified. */
1160 char *string, /* Numerical index into listPtr's element
1161 * list, or "end" to refer to last element. */
1162 int *indexPtr /* Where to store converted index. */
1163 )
1164 {
1165 if (string[0] == 'e') {
1166 if (strncmp(string, "end", strlen(string)) != 0) {
1167 badIndex:
1168 Tcl_AppendResult(interp, "bad listbox index \"", string,
1169 "\"", (char *) NULL);
1170 return TCL_ERROR;
1171 }
1172 *indexPtr = listPtr->numElements;
1173 if (listPtr->numElements <= 0) {
1174 *indexPtr = 0;
1175 }
1176 } else {
1177 if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
1178 Tcl_ResetResult(interp);
1179 goto badIndex;
1180 }
1181 }
1182 return TCL_OK;
1183 }
1184 \f
1185 /*
1186 *----------------------------------------------------------------------
1187 *
1188 * ChangeListboxView --
1189 *
1190 * Change the view on a listbox widget.
1191 *
1192 * Results:
1193 * None.
1194 *
1195 * Side effects:
1196 * What's displayed on the screen is changed. If there is a
1197 * scrollbar associated with this widget, then the scrollbar
1198 * is instructed to change its display too.
1199 *
1200 *----------------------------------------------------------------------
1201 */
1202
1203 static void
1204 ChangeListboxView (
1205 register Listbox *listPtr, /* Information about widget. */
1206 int index /* Index of element in listPtr. */
1207 )
1208 {
1209 if (listPtr->tkwin == NULL) {
1210 return;
1211 }
1212
1213 if (index >= listPtr->numElements) {
1214 index = listPtr->numElements-1;
1215 }
1216 if (index < 0) {
1217 index = 0;
1218 }
1219 if (listPtr->topIndex != index) {
1220 if (!(listPtr->flags & REDRAW_PENDING)) {
1221 Tk_DoWhenIdle(DisplayListbox, (ClientData) listPtr);
1222 listPtr->flags |= REDRAW_PENDING;
1223 }
1224 listPtr->topIndex = index;
1225 ListboxUpdateVScrollbar(listPtr);
1226 }
1227 }
1228 \f
1229 /*
1230 *----------------------------------------------------------------------
1231 *
1232 * ChangListboxOffset --
1233 *
1234 * Change the horizontal offset for a listbox.
1235 *
1236 * Results:
1237 * None.
1238 *
1239 * Side effects:
1240 * The listbox may be redrawn to reflect its new horizontal
1241 * offset.
1242 *
1243 *----------------------------------------------------------------------
1244 */
1245
1246 static void
1247 ChangeListboxOffset (
1248 register Listbox *listPtr, /* Information about widget. */
1249 int offset /* Desired new "xOffset" for
1250 * listbox. */
1251 )
1252 {
1253 int maxOffset;
1254
1255 if (listPtr->tkwin == NULL) {
1256 return;
1257 }
1258
1259 /*
1260 * Make sure that the new offset is within the allowable range, and
1261 * round it off to an even multiple of xScrollUnit.
1262 */
1263
1264 maxOffset = listPtr->maxWidth + (listPtr->xScrollUnit-1)
1265 - (Tk_Width(listPtr->tkwin) - 2*listPtr->borderWidth
1266 - 2*listPtr->selBorderWidth - listPtr->xScrollUnit);
1267 if (offset > maxOffset) {
1268 offset = maxOffset;
1269 }
1270 if (offset < 0) {
1271 offset = 0;
1272 }
1273 offset -= offset%listPtr->xScrollUnit;
1274 if (offset != listPtr->xOffset) {
1275 listPtr->xOffset = offset;
1276 listPtr->flags |= UPDATE_H_SCROLLBAR;
1277 ListboxRedrawRange(listPtr, 0, listPtr->numElements);
1278 }
1279 }
1280 \f
1281 /*
1282 *----------------------------------------------------------------------
1283 *
1284 * ListboxScanTo --
1285 *
1286 * Given a point (presumably of the curent mouse location)
1287 * drag the view in the window to implement the scan operation.
1288 *
1289 * Results:
1290 * None.
1291 *
1292 * Side effects:
1293 * The view in the window may change.
1294 *
1295 *----------------------------------------------------------------------
1296 */
1297
1298 static void
1299 ListboxScanTo (
1300 register Listbox *listPtr, /* Information about widget. */
1301 int x, /* X-coordinate to use for scan
1302 * operation. */
1303 int y /* Y-coordinate to use for scan
1304 * operation. */
1305 )
1306 {
1307 int newTopIndex, newOffset;
1308
1309 /*
1310 * Compute new top line for screen by amplifying the difference
1311 * between the current position and the place where the scan
1312 * started (the "mark" position). If we run off the top or bottom
1313 * of the list, then reset the mark point so that the current
1314 * position continues to correspond to the edge of the window.
1315 * This means that the picture will start dragging as soon as the
1316 * mouse reverses direction (without this reset, might have to slide
1317 * mouse a long ways back before the picture starts moving again).
1318 */
1319
1320 newTopIndex = listPtr->scanMarkYIndex
1321 - (10*(y - listPtr->scanMarkY))/listPtr->lineHeight;
1322 if (newTopIndex >= listPtr->numElements) {
1323 newTopIndex = listPtr->scanMarkYIndex = listPtr->numElements-1;
1324 listPtr->scanMarkY = y;
1325 } else if (newTopIndex < 0) {
1326 newTopIndex = listPtr->scanMarkYIndex = 0;
1327 listPtr->scanMarkY = y;
1328 }
1329 ChangeListboxView(listPtr, newTopIndex);
1330
1331 /*
1332 * Compute new left edge for display in a similar fashion by amplifying
1333 * the difference between the current position and the place where the
1334 * scan started.
1335 */
1336
1337 newOffset = listPtr->scanMarkXOffset - (10*(x - listPtr->scanMarkX));
1338 if (newOffset >= listPtr->maxWidth) {
1339 newOffset = listPtr->scanMarkXOffset = listPtr->maxWidth;
1340 listPtr->scanMarkX = x;
1341 } else if (newOffset < 0) {
1342 newOffset = listPtr->scanMarkXOffset = 0;
1343 listPtr->scanMarkX = x;
1344 }
1345 ChangeListboxOffset(listPtr, newOffset);
1346 }
1347 \f
1348 /*
1349 *----------------------------------------------------------------------
1350 *
1351 * NearestListboxElement --
1352 *
1353 * Given a y-coordinate inside a listbox, compute the index of
1354 * the element under that y-coordinate (or closest to that
1355 * y-coordinate).
1356 *
1357 * Results:
1358 * The return value is an index of an element of listPtr. If
1359 * listPtr has no elements, then 0 is always returned.
1360 *
1361 * Side effects:
1362 * None.
1363 *
1364 *----------------------------------------------------------------------
1365 */
1366
1367 static int
1368 NearestListboxElement (
1369 register Listbox *listPtr, /* Information about widget. */
1370 int y /* Y-coordinate in listPtr's window. */
1371 )
1372 {
1373 int index;
1374
1375 index = (y - listPtr->borderWidth)/listPtr->lineHeight;
1376 if (index >= listPtr->numLines) {
1377 index = listPtr->numLines-1;
1378 }
1379 if (index < 0) {
1380 index = 0;
1381 }
1382 index += listPtr->topIndex;
1383 if (index >= listPtr->numElements) {
1384 index = listPtr->numElements-1;
1385 }
1386 return index;
1387 }
1388 \f
1389 /*
1390 *----------------------------------------------------------------------
1391 *
1392 * ListboxSelectFrom --
1393 *
1394 * Start a new selection in a listbox.
1395 *
1396 * Results:
1397 * None.
1398 *
1399 * Side effects:
1400 * ListPtr claims the selection, and the selection becomes the
1401 * single element given by index.
1402 *
1403 *----------------------------------------------------------------------
1404 */
1405
1406 static void
1407 ListboxSelectFrom (
1408 register Listbox *listPtr, /* Information about widget. */
1409 int index /* Index of element that is to
1410 * become the new selection. */
1411 )
1412 {
1413 /*
1414 * Make sure the index is within the proper range for the listbox.
1415 */
1416
1417 if (index <= 0) {
1418 index = 0;
1419 }
1420 if (index >= listPtr->numElements) {
1421 index = listPtr->numElements-1;
1422 }
1423
1424 if (listPtr->selectFirst != -1) {
1425 ListboxRedrawRange(listPtr, listPtr->selectFirst, listPtr->selectLast);
1426 } else if (listPtr->exportSelection) {
1427 Tk_OwnSelection(listPtr->tkwin, ListboxLostSelection,
1428 (ClientData) listPtr);
1429 }
1430
1431 listPtr->selectFirst = listPtr->selectLast = index;
1432 listPtr->selectAnchor = index;
1433 ListboxRedrawRange(listPtr, index, index);
1434 }
1435 \f
1436 /*
1437 *----------------------------------------------------------------------
1438 *
1439 * ListboxSelectTo --
1440 *
1441 * Modify the selection by moving its un-anchored end. This could
1442 * make the selection either larger or smaller.
1443 *
1444 * Results:
1445 * None.
1446 *
1447 * Side effects:
1448 * The selection changes.
1449 *
1450 *----------------------------------------------------------------------
1451 */
1452
1453 static void
1454 ListboxSelectTo (
1455 register Listbox *listPtr, /* Information about widget. */
1456 int index /* Index of element that is to
1457 * become the "other" end of the
1458 * selection. */
1459 )
1460 {
1461 int newFirst, newLast;
1462
1463 /*
1464 * Make sure the index is within the proper range for the listbox.
1465 */
1466
1467 if (index <= 0) {
1468 index = 0;
1469 }
1470 if (index >= listPtr->numElements) {
1471 index = listPtr->numElements-1;
1472 }
1473
1474 /*
1475 * We should already own the selection, but grab it if we don't.
1476 */
1477
1478 if (listPtr->selectFirst == -1) {
1479 ListboxSelectFrom(listPtr, index);
1480 }
1481
1482 if (listPtr->selectAnchor < index) {
1483 newFirst = listPtr->selectAnchor;
1484 newLast = index;
1485 } else {
1486 newFirst = index;
1487 newLast = listPtr->selectAnchor;
1488 }
1489 if ((listPtr->selectFirst == newFirst)
1490 && (listPtr->selectLast == newLast)) {
1491 return;
1492 }
1493 if (listPtr->selectFirst != newFirst) {
1494 if (listPtr->selectFirst < newFirst) {
1495 ListboxRedrawRange(listPtr, listPtr->selectFirst, newFirst-1);
1496 } else {
1497 ListboxRedrawRange(listPtr, newFirst, listPtr->selectFirst-1);
1498 }
1499 listPtr->selectFirst = newFirst;
1500 }
1501 if (listPtr->selectLast != newLast) {
1502 if (listPtr->selectLast < newLast) {
1503 ListboxRedrawRange(listPtr, listPtr->selectLast+1, newLast);
1504 } else {
1505 ListboxRedrawRange(listPtr, newLast+1, listPtr->selectLast);
1506 }
1507 listPtr->selectLast = newLast;
1508 }
1509 }
1510 \f
1511 /*
1512 *----------------------------------------------------------------------
1513 *
1514 * ListboxFetchSelection --
1515 *
1516 * This procedure is called back by Tk when the selection is
1517 * requested by someone. It returns part or all of the selection
1518 * in a buffer provided by the caller.
1519 *
1520 * Results:
1521 * The return value is the number of non-NULL bytes stored
1522 * at buffer. Buffer is filled (or partially filled) with a
1523 * NULL-terminated string containing part or all of the selection,
1524 * as given by offset and maxBytes. The selection is returned
1525 * as a Tcl list with one list element for each element in the
1526 * listbox.
1527 *
1528 * Side effects:
1529 * None.
1530 *
1531 *----------------------------------------------------------------------
1532 */
1533
1534 static int
1535 ListboxFetchSelection (
1536 ClientData clientData, /* Information about listbox widget. */
1537 int offset, /* Offset within selection of first
1538 * byte to be returned. */
1539 char *buffer, /* Location in which to place
1540 * selection. */
1541 int maxBytes /* Maximum number of bytes to place
1542 * at buffer, not including terminating
1543 * NULL character. */
1544 )
1545 {
1546 register Listbox *listPtr = (Listbox *) clientData;
1547 register Element *elPtr;
1548 char **argv, *selection;
1549 int src, dst, length, count, argc;
1550
1551 if ((listPtr->selectFirst == -1) || !listPtr->exportSelection) {
1552 return -1;
1553 }
1554
1555 /*
1556 * Use Tcl_Merge to format the listbox elements into a suitable
1557 * Tcl list.
1558 */
1559
1560 argc = listPtr->selectLast - listPtr->selectFirst + 1;
1561 argv = (char **) ckalloc((unsigned) (argc*sizeof(char *)));
1562 for (src = 0, dst = 0, elPtr = listPtr->elementPtr; ;
1563 src++, elPtr = elPtr->nextPtr) {
1564 if (src < listPtr->selectFirst) {
1565 continue;
1566 }
1567 if (src > listPtr->selectLast) {
1568 break;
1569 }
1570 argv[dst] = elPtr->text;
1571 dst++;
1572 }
1573 selection = Tcl_Merge(argc, argv);
1574
1575 /*
1576 * Copy the requested portion of the selection to the buffer.
1577 */
1578
1579 length = strlen(selection);
1580 count = length - offset;
1581 if (count <= 0) {
1582 count = 0;
1583 goto done;
1584 }
1585 if (count > maxBytes) {
1586 count = maxBytes;
1587 }
1588 memcpy((VOID *) buffer, (VOID *) (selection + offset), count);
1589
1590 done:
1591 buffer[count] = '\0';
1592 ckfree(selection);
1593 ckfree((char *) argv);
1594 return count;
1595 }
1596 \f
1597 /*
1598 *----------------------------------------------------------------------
1599 *
1600 * ListboxLostSelection --
1601 *
1602 * This procedure is called back by Tk when the selection is
1603 * grabbed away from a listbox widget.
1604 *
1605 * Results:
1606 * None.
1607 *
1608 * Side effects:
1609 * The existing selection is unhighlighted, and the window is
1610 * marked as not containing a selection.
1611 *
1612 *----------------------------------------------------------------------
1613 */
1614
1615 static void
1616 ListboxLostSelection (
1617 ClientData clientData /* Information about listbox widget. */
1618 )
1619 {
1620 register Listbox *listPtr = (Listbox *) clientData;
1621
1622 if ((listPtr->selectFirst >= 0) && listPtr->exportSelection) {
1623 ListboxRedrawRange(listPtr, listPtr->selectFirst, listPtr->selectLast);
1624 listPtr->selectFirst = -1;
1625 }
1626 }
1627 \f
1628 /*
1629 *----------------------------------------------------------------------
1630 *
1631 * ListboxRedrawRange --
1632 *
1633 * Ensure that a given range of elements is eventually redrawn on
1634 * the display (if those elements in fact appear on the display).
1635 *
1636 * Results:
1637 * None.
1638 *
1639 * Side effects:
1640 * Information gets redisplayed.
1641 *
1642 *----------------------------------------------------------------------
1643 */
1644
1645 /* ARGSUSED */
1646 static void
1647 ListboxRedrawRange (
1648 register Listbox *listPtr, /* Information about widget. */
1649 int first, /* Index of first element in list
1650 * that needs to be redrawn. */
1651 int last /* Index of last element in list
1652 * that needs to be redrawn. May
1653 * be less than first;
1654 * these just bracket a range. */
1655 )
1656 {
1657 if ((listPtr->tkwin == NULL) || !Tk_IsMapped(listPtr->tkwin)
1658 || (listPtr->flags & REDRAW_PENDING)) {
1659 return;
1660 }
1661 Tk_DoWhenIdle(DisplayListbox, (ClientData) listPtr);
1662 listPtr->flags |= REDRAW_PENDING;
1663 }
1664 \f
1665 /*
1666 *----------------------------------------------------------------------
1667 *
1668 * ListboxUpdateVScrollbar --
1669 *
1670 * This procedure is invoked whenever information has changed in
1671 * a listbox in a way that would invalidate a vertical scrollbar
1672 * display. If there is an associated scrollbar, then this command
1673 * updates it by invoking a Tcl command.
1674 *
1675 * Results:
1676 * None.
1677 *
1678 * Side effects:
1679 * A Tcl command is invoked, and an additional command may be
1680 * invoked to process errors in the command.
1681 *
1682 *----------------------------------------------------------------------
1683 */
1684
1685 static void
1686 ListboxUpdateVScrollbar (
1687 register Listbox *listPtr /* Information about widget. */
1688 )
1689 {
1690 char string[60];
1691 int result, last;
1692
1693 if (listPtr->yScrollCmd == NULL) {
1694 return;
1695 }
1696 last = listPtr->topIndex + listPtr->numLines - 1;
1697 if (last >= listPtr->numElements) {
1698 last = listPtr->numElements-1;
1699 }
1700 if (last < listPtr->topIndex) {
1701 last = listPtr->topIndex;
1702 }
1703 sprintf(string, " %d %d %d %d", listPtr->numElements, listPtr->numLines,
1704 listPtr->topIndex, last);
1705 result = Tcl_VarEval(listPtr->interp, listPtr->yScrollCmd, string,
1706 (char *) NULL);
1707 if (result != TCL_OK) {
1708 TkBindError(listPtr->interp);
1709 }
1710 }
1711 \f
1712 /*
1713 *----------------------------------------------------------------------
1714 *
1715 * ListboxUpdateHScrollbar --
1716 *
1717 * This procedure is invoked whenever information has changed in
1718 * a listbox in a way that would invalidate a horizontal scrollbar
1719 * display. If there is an associated horizontal scrollbar, then
1720 * this command updates it by invoking a Tcl command.
1721 *
1722 * Results:
1723 * None.
1724 *
1725 * Side effects:
1726 * A Tcl command is invoked, and an additional command may be
1727 * invoked to process errors in the command.
1728 *
1729 *----------------------------------------------------------------------
1730 */
1731
1732 static void
1733 ListboxUpdateHScrollbar (
1734 register Listbox *listPtr /* Information about widget. */
1735 )
1736 {
1737 char string[60];
1738 int result, totalUnits, windowUnits, first, last;
1739
1740 if (listPtr->xScrollCmd == NULL) {
1741 return;
1742 }
1743 totalUnits = 1 + (listPtr->maxWidth-1)/listPtr->xScrollUnit;
1744 windowUnits = 1 + (Tk_Width(listPtr->tkwin)
1745 - 2*(listPtr->borderWidth + listPtr->selBorderWidth)-1)
1746 /listPtr->xScrollUnit;
1747 first = listPtr->xOffset/listPtr->xScrollUnit;
1748 last = first + windowUnits - 1;
1749 if (last < first) {
1750 last = first;
1751 }
1752 sprintf(string, " %d %d %d %d", totalUnits, windowUnits, first, last);
1753 result = Tcl_VarEval(listPtr->interp, listPtr->xScrollCmd, string,
1754 (char *) NULL);
1755 if (result != TCL_OK) {
1756 TkBindError(listPtr->interp);
1757 }
1758 }
1759 \f
1760 /*
1761 *----------------------------------------------------------------------
1762 *
1763 * ListboxComputeWidths --
1764 *
1765 * This procedure is invoked to completely recompute width
1766 * information used for displaying listboxes and for horizontal
1767 * scrolling.
1768 *
1769 * Results:
1770 * None.
1771 *
1772 * Side effects:
1773 * If "fontChanged" is non-zero then the widths of the individual
1774 * elements are all recomputed. In addition, listPtr->maxWidth is
1775 * recomputed.
1776 *
1777 *----------------------------------------------------------------------
1778 */
1779
1780 static void
1781 ListboxComputeWidths (
1782 Listbox *listPtr, /* Listbox whose geometry is to be
1783 * recomputed. */
1784 int fontChanged /* Non-zero means the font may have changed
1785 * so per-element width information also
1786 * has to be computed. */
1787 )
1788 {
1789 register Element *elPtr;
1790 int dummy;
1791 XCharStruct bbox;
1792
1793 listPtr->xScrollUnit = XTextWidth(listPtr->fontPtr, "0", 1);
1794 listPtr->maxWidth = 0;
1795 for (elPtr = listPtr->elementPtr; elPtr != NULL; elPtr = elPtr->nextPtr) {
1796 if (fontChanged) {
1797 XTextExtents(listPtr->fontPtr, elPtr->text, elPtr->textLength,
1798 &dummy, &dummy, &dummy, &bbox);
1799 elPtr->lBearing = bbox.lbearing;
1800 elPtr->pixelWidth = bbox.lbearing + bbox.rbearing;
1801 }
1802 if (elPtr->pixelWidth > listPtr->maxWidth) {
1803 listPtr->maxWidth = elPtr->pixelWidth;
1804 }
1805 }
1806 }
Impressum, Datenschutz