Annotation of micropolis/src/tk/tklist.c, revision 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.