Annotation of micropolis/src/tk/tklist.c, revision 1.1.1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.