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