|
|
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.