OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [tags/] [start/] [insight/] [tk/] [generic/] [tkListbox.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
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 (c) 1990-1994 The Regents of the University of California.
9
 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
10
 *
11
 * See the file "license.terms" for information on usage and redistribution
12
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13
 *
14
 * RCS: @(#) $Id: tkListbox.c,v 1.1.1.1 2002-01-16 10:25:52 markom Exp $
15
 */
16
 
17
#include "tkPort.h"
18
#include "default.h"
19
#include "tkInt.h"
20
 
21
/*
22
 * One record of the following type is kept for each element
23
 * associated with a listbox widget:
24
 */
25
 
26
typedef struct Element {
27
    int textLength;             /* # non-NULL characters in text. */
28
    int lBearing;               /* Distance from first character's
29
                                 * origin to left edge of character. */
30
    int pixelWidth;             /* Total width of element in pixels (including
31
                                 * left bearing and right bearing). */
32
    int selected;               /* 1 means this item is selected, 0 means
33
                                 * it isn't. */
34
    struct Element *nextPtr;    /* Next in list of all elements of this
35
                                 * listbox, or NULL for last element. */
36
    char text[4];               /* Characters of this element, NULL-
37
                                 * terminated.  The actual space allocated
38
                                 * here will be as large as needed (> 4,
39
                                 * most likely).  Must be the last field
40
                                 * of the record. */
41
} Element;
42
 
43
#define ElementSize(stringLength) \
44
        ((unsigned) (sizeof(Element) - 3 + stringLength))
45
 
46
/*
47
 * A data structure of the following type is kept for each listbox
48
 * widget managed by this file:
49
 */
50
 
51
typedef struct {
52
    Tk_Window tkwin;            /* Window that embodies the listbox.  NULL
53
                                 * means that the window has been destroyed
54
                                 * but the data structures haven't yet been
55
                                 * cleaned up.*/
56
    Display *display;           /* Display containing widget.  Used, among
57
                                 * other things, so that resources can be
58
                                 * freed even after tkwin has gone away. */
59
    Tcl_Interp *interp;         /* Interpreter associated with listbox. */
60
    Tcl_Command widgetCmd;      /* Token for listbox's widget command. */
61
    int numElements;            /* Total number of elements in this listbox. */
62
    Element *firstPtr;          /* First in list of elements (NULL if no
63
                                 * elements). */
64
    Element *lastPtr;           /* Last in list of elements (NULL if no
65
                                 * elements). */
66
 
67
    /*
68
     * Information used when displaying widget:
69
     */
70
 
71
    Tk_3DBorder normalBorder;   /* Used for drawing border around whole
72
                                 * window, plus used for background. */
73
    int borderWidth;            /* Width of 3-D border around window. */
74
    int relief;                 /* 3-D effect: TK_RELIEF_RAISED, etc. */
75
    int highlightWidth;         /* Width in pixels of highlight to draw
76
                                 * around widget when it has the focus.
77
                                 * <= 0 means don't draw a highlight. */
78
    XColor *highlightBgColorPtr;
79
                                /* Color for drawing traversal highlight
80
                                 * area when highlight is off. */
81
    XColor *highlightColorPtr;  /* Color for drawing traversal highlight. */
82
    int inset;                  /* Total width of all borders, including
83
                                 * traversal highlight and 3-D border.
84
                                 * Indicates how much interior stuff must
85
                                 * be offset from outside edges to leave
86
                                 * room for borders. */
87
    Tk_Font tkfont;             /* Information about text font, or NULL. */
88
    XColor *fgColorPtr;         /* Text color in normal mode. */
89
    GC textGC;                  /* For drawing normal text. */
90
    Tk_3DBorder selBorder;      /* Borders and backgrounds for selected
91
                                 * elements. */
92
    int selBorderWidth;         /* Width of border around selection. */
93
    XColor *selFgColorPtr;      /* Foreground color for selected elements. */
94
    GC selTextGC;               /* For drawing selected text. */
95
    int width;                  /* Desired width of window, in characters. */
96
    int height;                 /* Desired height of window, in lines. */
97
    int lineHeight;             /* Number of pixels allocated for each line
98
                                 * in display. */
99
    int topIndex;               /* Index of top-most element visible in
100
                                 * window. */
101
    int fullLines;              /* Number of lines that fit are completely
102
                                 * visible in window.  There may be one
103
                                 * additional line at the bottom that is
104
                                 * partially visible. */
105
    int partialLine;            /* 0 means that the window holds exactly
106
                                 * fullLines lines.  1 means that there is
107
                                 * one additional line that is partially
108
                                 * visble. */
109
    int setGrid;                /* Non-zero means pass gridding information
110
                                 * to window manager. */
111
 
112
    /*
113
     * Information to support horizontal scrolling:
114
     */
115
 
116
    int maxWidth;               /* Width (in pixels) of widest string in
117
                                 * listbox. */
118
    int xScrollUnit;            /* Number of pixels in one "unit" for
119
                                 * horizontal scrolling (window scrolls
120
                                 * horizontally in increments of this size).
121
                                 * This is an average character size. */
122
    int xOffset;                /* The left edge of each string in the
123
                                 * listbox is offset to the left by this
124
                                 * many pixels (0 means no offset, positive
125
                                 * means there is an offset). */
126
 
127
    /*
128
     * Information about what's selected or active, if any.
129
     */
130
 
131
    Tk_Uid selectMode;          /* Selection style: single, browse, multiple,
132
                                 * or extended.  This value isn't used in C
133
                                 * code, but the Tcl bindings use it. */
134
    int numSelected;            /* Number of elements currently selected. */
135
    int selectAnchor;           /* Fixed end of selection (i.e. element
136
                                 * at which selection was started.) */
137
    int exportSelection;        /* Non-zero means tie internal listbox
138
                                 * to X selection. */
139
    int active;                 /* Index of "active" element (the one that
140
                                 * has been selected by keyboard traversal).
141
                                 * -1 means none. */
142
 
143
    /*
144
     * Information for scanning:
145
     */
146
 
147
    int scanMarkX;              /* X-position at which scan started (e.g.
148
                                 * button was pressed here). */
149
    int scanMarkY;              /* Y-position at which scan started (e.g.
150
                                 * button was pressed here). */
151
    int scanMarkXOffset;        /* Value of "xOffset" field when scan
152
                                 * started. */
153
    int scanMarkYIndex;         /* Index of line that was at top of window
154
                                 * when scan started. */
155
 
156
    /*
157
     * Miscellaneous information:
158
     */
159
 
160
    Tk_Cursor cursor;           /* Current cursor for window, or None. */
161
    char *takeFocus;            /* Value of -takefocus option;  not used in
162
                                 * the C code, but used by keyboard traversal
163
                                 * scripts.  Malloc'ed, but may be NULL. */
164
    char *yScrollCmd;           /* Command prefix for communicating with
165
                                 * vertical scrollbar.  NULL means no command
166
                                 * to issue.  Malloc'ed. */
167
    char *xScrollCmd;           /* Command prefix for communicating with
168
                                 * horizontal scrollbar.  NULL means no command
169
                                 * to issue.  Malloc'ed. */
170
    int flags;                  /* Various flag bits:  see below for
171
                                 * definitions. */
172
} Listbox;
173
 
174
/*
175
 * Flag bits for listboxes:
176
 *
177
 * REDRAW_PENDING:              Non-zero means a DoWhenIdle handler
178
 *                              has already been queued to redraw
179
 *                              this window.
180
 * UPDATE_V_SCROLLBAR:          Non-zero means vertical scrollbar needs
181
 *                              to be updated.
182
 * UPDATE_H_SCROLLBAR:          Non-zero means horizontal scrollbar needs
183
 *                              to be updated.
184
 * GOT_FOCUS:                   Non-zero means this widget currently
185
 *                              has the input focus.
186
 */
187
 
188
#define REDRAW_PENDING          1
189
#define UPDATE_V_SCROLLBAR      2
190
#define UPDATE_H_SCROLLBAR      4
191
#define GOT_FOCUS               8
192
 
193
/*
194
 * Information used for argv parsing:
195
 */
196
 
197
static Tk_ConfigSpec configSpecs[] = {
198
    {TK_CONFIG_BORDER, "-background", "background", "Background",
199
        DEF_LISTBOX_BG_COLOR, Tk_Offset(Listbox, normalBorder),
200
        TK_CONFIG_COLOR_ONLY},
201
    {TK_CONFIG_BORDER, "-background", "background", "Background",
202
        DEF_LISTBOX_BG_MONO, Tk_Offset(Listbox, normalBorder),
203
        TK_CONFIG_MONO_ONLY},
204
    {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
205
        (char *) NULL, 0, 0},
206
    {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
207
        (char *) NULL, 0, 0},
208
    {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
209
        DEF_LISTBOX_BORDER_WIDTH, Tk_Offset(Listbox, borderWidth), 0},
210
    {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
211
        DEF_LISTBOX_CURSOR, Tk_Offset(Listbox, cursor), TK_CONFIG_NULL_OK},
212
    {TK_CONFIG_BOOLEAN, "-exportselection", "exportSelection",
213
        "ExportSelection", DEF_LISTBOX_EXPORT_SELECTION,
214
        Tk_Offset(Listbox, exportSelection), 0},
215
    {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
216
        (char *) NULL, 0, 0},
217
    {TK_CONFIG_FONT, "-font", "font", "Font",
218
        DEF_LISTBOX_FONT, Tk_Offset(Listbox, tkfont), 0},
219
    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
220
        DEF_LISTBOX_FG, Tk_Offset(Listbox, fgColorPtr), 0},
221
    {TK_CONFIG_INT, "-height", "height", "Height",
222
        DEF_LISTBOX_HEIGHT, Tk_Offset(Listbox, height), 0},
223
    {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
224
        "HighlightBackground", DEF_LISTBOX_HIGHLIGHT_BG,
225
        Tk_Offset(Listbox, highlightBgColorPtr), 0},
226
    {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
227
        DEF_LISTBOX_HIGHLIGHT, Tk_Offset(Listbox, highlightColorPtr), 0},
228
    {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
229
        "HighlightThickness",
230
        DEF_LISTBOX_HIGHLIGHT_WIDTH, Tk_Offset(Listbox, highlightWidth), 0},
231
    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
232
        DEF_LISTBOX_RELIEF, Tk_Offset(Listbox, relief), 0},
233
    {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
234
        DEF_LISTBOX_SELECT_COLOR, Tk_Offset(Listbox, selBorder),
235
        TK_CONFIG_COLOR_ONLY},
236
    {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
237
        DEF_LISTBOX_SELECT_MONO, Tk_Offset(Listbox, selBorder),
238
        TK_CONFIG_MONO_ONLY},
239
    {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
240
        DEF_LISTBOX_SELECT_BD, Tk_Offset(Listbox, selBorderWidth), 0},
241
    {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
242
        DEF_LISTBOX_SELECT_FG_COLOR, Tk_Offset(Listbox, selFgColorPtr),
243
        TK_CONFIG_COLOR_ONLY},
244
    {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
245
        DEF_LISTBOX_SELECT_FG_MONO, Tk_Offset(Listbox, selFgColorPtr),
246
        TK_CONFIG_MONO_ONLY},
247
    {TK_CONFIG_UID, "-selectmode", "selectMode", "SelectMode",
248
        DEF_LISTBOX_SELECT_MODE, Tk_Offset(Listbox, selectMode), 0},
249
    {TK_CONFIG_BOOLEAN, "-setgrid", "setGrid", "SetGrid",
250
        DEF_LISTBOX_SET_GRID, Tk_Offset(Listbox, setGrid), 0},
251
    {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
252
        DEF_LISTBOX_TAKE_FOCUS, Tk_Offset(Listbox, takeFocus),
253
        TK_CONFIG_NULL_OK},
254
    {TK_CONFIG_INT, "-width", "width", "Width",
255
        DEF_LISTBOX_WIDTH, Tk_Offset(Listbox, width), 0},
256
    {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
257
        DEF_LISTBOX_SCROLL_COMMAND, Tk_Offset(Listbox, xScrollCmd),
258
        TK_CONFIG_NULL_OK},
259
    {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
260
        DEF_LISTBOX_SCROLL_COMMAND, Tk_Offset(Listbox, yScrollCmd),
261
        TK_CONFIG_NULL_OK},
262
    {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
263
        (char *) NULL, 0, 0}
264
};
265
 
266
/*
267
 * Forward declarations for procedures defined later in this file:
268
 */
269
 
270
static void             ChangeListboxOffset _ANSI_ARGS_((Listbox *listPtr,
271
                            int offset));
272
static void             ChangeListboxView _ANSI_ARGS_((Listbox *listPtr,
273
                            int index));
274
static int              ConfigureListbox _ANSI_ARGS_((Tcl_Interp *interp,
275
                            Listbox *listPtr, int argc, char **argv,
276
                            int flags));
277
static void             DeleteEls _ANSI_ARGS_((Listbox *listPtr, int first,
278
                            int last));
279
static void             DestroyListbox _ANSI_ARGS_((char *memPtr));
280
static void             DisplayListbox _ANSI_ARGS_((ClientData clientData));
281
static int              GetListboxIndex _ANSI_ARGS_((Tcl_Interp *interp,
282
                            Listbox *listPtr, char *string, int endIsSize,
283
                            int *indexPtr));
284
static void             InsertEls _ANSI_ARGS_((Listbox *listPtr, int index,
285
                            int argc, char **argv));
286
static void             ListboxCmdDeletedProc _ANSI_ARGS_((
287
                            ClientData clientData));
288
static void             ListboxComputeGeometry _ANSI_ARGS_((Listbox *listPtr,
289
                            int fontChanged, int maxIsStale, int updateGrid));
290
static void             ListboxEventProc _ANSI_ARGS_((ClientData clientData,
291
                            XEvent *eventPtr));
292
static int              ListboxFetchSelection _ANSI_ARGS_((
293
                            ClientData clientData, int offset, char *buffer,
294
                            int maxBytes));
295
static void             ListboxLostSelection _ANSI_ARGS_((
296
                            ClientData clientData));
297
static void             ListboxRedrawRange _ANSI_ARGS_((Listbox *listPtr,
298
                            int first, int last));
299
static void             ListboxScanTo _ANSI_ARGS_((Listbox *listPtr,
300
                            int x, int y));
301
static void             ListboxSelect _ANSI_ARGS_((Listbox *listPtr,
302
                            int first, int last, int select));
303
static void             ListboxUpdateHScrollbar _ANSI_ARGS_((Listbox *listPtr));
304
static void             ListboxUpdateVScrollbar _ANSI_ARGS_((Listbox *listPtr));
305
static int              ListboxWidgetCmd _ANSI_ARGS_((ClientData clientData,
306
                            Tcl_Interp *interp, int argc, char **argv));
307
static void             ListboxWorldChanged _ANSI_ARGS_((
308
                            ClientData instanceData));
309
static int              NearestListboxElement _ANSI_ARGS_((Listbox *listPtr,
310
                            int y));
311
 
312
/*
313
 * The structure below defines button class behavior by means of procedures
314
 * that can be invoked from generic window code.
315
 */
316
 
317
static TkClassProcs listboxClass = {
318
    NULL,                       /* createProc. */
319
    ListboxWorldChanged,        /* geometryProc. */
320
    NULL                        /* modalProc. */
321
};
322
 
323
 
324
/*
325
 *--------------------------------------------------------------
326
 *
327
 * Tk_ListboxCmd --
328
 *
329
 *      This procedure is invoked to process the "listbox" Tcl
330
 *      command.  See the user documentation for details on what
331
 *      it does.
332
 *
333
 * Results:
334
 *      A standard Tcl result.
335
 *
336
 * Side effects:
337
 *      See the user documentation.
338
 *
339
 *--------------------------------------------------------------
340
 */
341
 
342
int
343
Tk_ListboxCmd(clientData, interp, argc, argv)
344
    ClientData clientData;      /* Main window associated with
345
                                 * interpreter. */
346
    Tcl_Interp *interp;         /* Current interpreter. */
347
    int argc;                   /* Number of arguments. */
348
    char **argv;                /* Argument strings. */
349
{
350
    register Listbox *listPtr;
351
    Tk_Window new;
352
    Tk_Window tkwin = (Tk_Window) clientData;
353
 
354
    if (argc < 2) {
355
        Tcl_AppendResult(interp, "wrong # args: should be \"",
356
                argv[0], " pathName ?options?\"", (char *) NULL);
357
        return TCL_ERROR;
358
    }
359
 
360
    new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
361
    if (new == NULL) {
362
        return TCL_ERROR;
363
    }
364
 
365
    /*
366
     * Initialize the fields of the structure that won't be initialized
367
     * by ConfigureListbox, or that ConfigureListbox requires to be
368
     * initialized already (e.g. resource pointers).
369
     */
370
 
371
    listPtr = (Listbox *) ckalloc(sizeof(Listbox));
372
    listPtr->tkwin = new;
373
    listPtr->display = Tk_Display(new);
374
    listPtr->interp = interp;
375
    listPtr->widgetCmd = Tcl_CreateCommand(interp,
376
            Tk_PathName(listPtr->tkwin), ListboxWidgetCmd,
377
            (ClientData) listPtr, ListboxCmdDeletedProc);
378
    listPtr->numElements = 0;
379
    listPtr->firstPtr = NULL;
380
    listPtr->lastPtr = NULL;
381
    listPtr->normalBorder = NULL;
382
    listPtr->borderWidth = 0;
383
    listPtr->relief = TK_RELIEF_RAISED;
384
    listPtr->highlightWidth = 0;
385
    listPtr->highlightBgColorPtr = NULL;
386
    listPtr->highlightColorPtr = NULL;
387
    listPtr->inset = 0;
388
    listPtr->tkfont = NULL;
389
    listPtr->fgColorPtr = NULL;
390
    listPtr->textGC = None;
391
    listPtr->selBorder = NULL;
392
    listPtr->selBorderWidth = 0;
393
    listPtr->selFgColorPtr = None;
394
    listPtr->selTextGC = None;
395
    listPtr->width = 0;
396
    listPtr->height = 0;
397
    listPtr->lineHeight = 0;
398
    listPtr->topIndex = 0;
399
    listPtr->fullLines = 1;
400
    listPtr->partialLine = 0;
401
    listPtr->setGrid = 0;
402
    listPtr->maxWidth = 0;
403
    listPtr->xScrollUnit = 1;
404
    listPtr->xOffset = 0;
405
    listPtr->selectMode = NULL;
406
    listPtr->numSelected = 0;
407
    listPtr->selectAnchor = 0;
408
    listPtr->exportSelection = 1;
409
    listPtr->active = 0;
410
    listPtr->scanMarkX = 0;
411
    listPtr->scanMarkY = 0;
412
    listPtr->scanMarkXOffset = 0;
413
    listPtr->scanMarkYIndex = 0;
414
    listPtr->cursor = None;
415
    listPtr->takeFocus = NULL;
416
    listPtr->xScrollCmd = NULL;
417
    listPtr->yScrollCmd = NULL;
418
    listPtr->flags = 0;
419
 
420
    Tk_SetClass(listPtr->tkwin, "Listbox");
421
    TkSetClassProcs(listPtr->tkwin, &listboxClass, (ClientData) listPtr);
422
    Tk_CreateEventHandler(listPtr->tkwin,
423
            ExposureMask|StructureNotifyMask|FocusChangeMask,
424
            ListboxEventProc, (ClientData) listPtr);
425
    Tk_CreateSelHandler(listPtr->tkwin, XA_PRIMARY, XA_STRING,
426
            ListboxFetchSelection, (ClientData) listPtr, XA_STRING);
427
    if (ConfigureListbox(interp, listPtr, argc-2, argv+2, 0) != TCL_OK) {
428
        goto error;
429
    }
430
 
431
    interp->result = Tk_PathName(listPtr->tkwin);
432
    return TCL_OK;
433
 
434
    error:
435
    Tk_DestroyWindow(listPtr->tkwin);
436
    return TCL_ERROR;
437
}
438
 
439
/*
440
 *--------------------------------------------------------------
441
 *
442
 * ListboxWidgetCmd --
443
 *
444
 *      This procedure is invoked to process the Tcl command
445
 *      that corresponds to a widget managed by this module.
446
 *      See the user documentation for details on what it does.
447
 *
448
 * Results:
449
 *      A standard Tcl result.
450
 *
451
 * Side effects:
452
 *      See the user documentation.
453
 *
454
 *--------------------------------------------------------------
455
 */
456
 
457
static int
458
ListboxWidgetCmd(clientData, interp, argc, argv)
459
    ClientData clientData;              /* Information about listbox widget. */
460
    Tcl_Interp *interp;                 /* Current interpreter. */
461
    int argc;                           /* Number of arguments. */
462
    char **argv;                        /* Argument strings. */
463
{
464
    register Listbox *listPtr = (Listbox *) clientData;
465
    int result = TCL_OK;
466
    size_t length;
467
    int c;
468
    Tk_FontMetrics fm;
469
 
470
    if (argc < 2) {
471
        Tcl_AppendResult(interp, "wrong # args: should be \"",
472
                argv[0], " option ?arg arg ...?\"", (char *) NULL);
473
        return TCL_ERROR;
474
    }
475
    Tcl_Preserve((ClientData) listPtr);
476
    c = argv[1][0];
477
    length = strlen(argv[1]);
478
    if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)) {
479
        int index;
480
 
481
        if (argc != 3) {
482
            Tcl_AppendResult(interp, "wrong # args: should be \"",
483
                    argv[0], " activate index\"",
484
                    (char *) NULL);
485
            goto error;
486
        }
487
        ListboxRedrawRange(listPtr, listPtr->active, listPtr->active);
488
        if (GetListboxIndex(interp, listPtr, argv[2], 0, &index) != TCL_OK) {
489
            goto error;
490
        }
491
        if (index >= listPtr->numElements) {
492
            index = listPtr->numElements-1;
493
        }
494
        if (index < 0) {
495
            index = 0;
496
        }
497
        listPtr->active = index;
498
        ListboxRedrawRange(listPtr, listPtr->active, listPtr->active);
499
    } else if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) {
500
        int index, x, y, i;
501
        Element *elPtr;
502
 
503
        if (argc != 3) {
504
            Tcl_AppendResult(interp, "wrong # args: should be \"",
505
                    argv[0], " bbox index\"", (char *) NULL);
506
            goto error;
507
        }
508
        if (GetListboxIndex(interp, listPtr, argv[2], 0, &index) != TCL_OK) {
509
            goto error;
510
        }
511
        if ((index >= listPtr->numElements) || (index < 0)) {
512
            goto done;
513
        }
514
        for (i = 0, elPtr = listPtr->firstPtr; i < index;
515
                i++, elPtr = elPtr->nextPtr) {
516
            /* Empty loop body. */
517
        }
518
        if ((index >= listPtr->topIndex) && (index < listPtr->numElements)
519
                    && (index < (listPtr->topIndex + listPtr->fullLines
520
                    + listPtr->partialLine))) {
521
            x = listPtr->inset + listPtr->selBorderWidth - listPtr->xOffset;
522
            y = ((index - listPtr->topIndex)*listPtr->lineHeight)
523
                    + listPtr->inset + listPtr->selBorderWidth;
524
            Tk_GetFontMetrics(listPtr->tkfont, &fm);
525
            sprintf(interp->result, "%d %d %d %d", x, y, elPtr->pixelWidth,
526
                    fm.linespace);
527
        }
528
    } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
529
            && (length >= 2)) {
530
        if (argc != 3) {
531
            Tcl_AppendResult(interp, "wrong # args: should be \"",
532
                    argv[0], " cget option\"",
533
                    (char *) NULL);
534
            goto error;
535
        }
536
        result = Tk_ConfigureValue(interp, listPtr->tkwin, configSpecs,
537
                (char *) listPtr, argv[2], 0);
538
    } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
539
            && (length >= 2)) {
540
        if (argc == 2) {
541
            result = Tk_ConfigureInfo(interp, listPtr->tkwin, configSpecs,
542
                    (char *) listPtr, (char *) NULL, 0);
543
        } else if (argc == 3) {
544
            result = Tk_ConfigureInfo(interp, listPtr->tkwin, configSpecs,
545
                    (char *) listPtr, argv[2], 0);
546
        } else {
547
            result = ConfigureListbox(interp, listPtr, argc-2, argv+2,
548
                    TK_CONFIG_ARGV_ONLY);
549
        }
550
    } else if ((c == 'c') && (strncmp(argv[1], "curselection", length) == 0)
551
            && (length >= 2)) {
552
        int i, count;
553
        char index[20];
554
        Element *elPtr;
555
 
556
        if (argc != 2) {
557
            Tcl_AppendResult(interp, "wrong # args: should be \"",
558
                    argv[0], " curselection\"",
559
                    (char *) NULL);
560
            goto error;
561
        }
562
        count = 0;
563
        for (i = 0, elPtr = listPtr->firstPtr; elPtr != NULL;
564
                i++, elPtr = elPtr->nextPtr) {
565
            if (elPtr->selected) {
566
                sprintf(index, "%d", i);
567
                Tcl_AppendElement(interp, index);
568
                count++;
569
            }
570
        }
571
        if (count != listPtr->numSelected) {
572
            panic("ListboxWidgetCmd: selection count incorrect");
573
        }
574
    } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
575
        int first, last;
576
 
577
        if ((argc < 3) || (argc > 4)) {
578
            Tcl_AppendResult(interp, "wrong # args: should be \"",
579
                    argv[0], " delete firstIndex ?lastIndex?\"",
580
                    (char *) NULL);
581
            goto error;
582
        }
583
        if (GetListboxIndex(interp, listPtr, argv[2], 0, &first) != TCL_OK) {
584
            goto error;
585
        }
586
        if (first < listPtr->numElements) {
587
            if (argc == 3) {
588
                last = first;
589
            } else {
590
                if (GetListboxIndex(interp, listPtr, argv[3], 0,
591
                        &last) != TCL_OK) {
592
                    goto error;
593
                }
594
                if (last >= listPtr->numElements) {
595
                    last = listPtr->numElements-1;
596
                }
597
            }
598
            DeleteEls(listPtr, first, last);
599
        }
600
    } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
601
        int first, last, i;
602
        Element *elPtr;
603
 
604
        if ((argc != 3) && (argc != 4)) {
605
            Tcl_AppendResult(interp, "wrong # args: should be \"",
606
                    argv[0], " get first ?last?\"", (char *) NULL);
607
            goto error;
608
        }
609
        if (GetListboxIndex(interp, listPtr, argv[2], 0, &first) != TCL_OK) {
610
            goto error;
611
        }
612
        if ((argc == 4) && (GetListboxIndex(interp, listPtr, argv[3],
613
                0, &last) != TCL_OK)) {
614
            goto error;
615
        }
616
        if (first >= listPtr->numElements) {
617
            goto done;
618
        }
619
        if (last >= listPtr->numElements) {
620
            last = listPtr->numElements-1;
621
        }
622
 
623
        for (elPtr = listPtr->firstPtr, i = 0; i < first;
624
                i++, elPtr = elPtr->nextPtr) {
625
            /* Empty loop body. */
626
        }
627
        if (elPtr != NULL) {
628
            if (argc == 3) {
629
                if (first >= 0) {
630
                    interp->result = elPtr->text;
631
                }
632
            } else {
633
                for (  ; i <= last; i++, elPtr = elPtr->nextPtr) {
634
                    Tcl_AppendElement(interp, elPtr->text);
635
                }
636
            }
637
        }
638
    } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
639
            && (length >= 3)) {
640
        int index;
641
 
642
        if (argc != 3) {
643
            Tcl_AppendResult(interp, "wrong # args: should be \"",
644
                    argv[0], " index index\"",
645
                    (char *) NULL);
646
            goto error;
647
        }
648
        if (GetListboxIndex(interp, listPtr, argv[2], 1, &index)
649
                != TCL_OK) {
650
            goto error;
651
        }
652
        sprintf(interp->result, "%d", index);
653
    } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
654
            && (length >= 3)) {
655
        int index;
656
 
657
        if (argc < 3) {
658
            Tcl_AppendResult(interp, "wrong # args: should be \"",
659
                    argv[0], " insert index ?element element ...?\"",
660
                    (char *) NULL);
661
            goto error;
662
        }
663
        if (GetListboxIndex(interp, listPtr, argv[2], 1, &index)
664
                != TCL_OK) {
665
            goto error;
666
        }
667
        InsertEls(listPtr, index, argc-3, argv+3);
668
    } else if ((c == 'n') && (strncmp(argv[1], "nearest", length) == 0)) {
669
        int index, y;
670
 
671
        if (argc != 3) {
672
            Tcl_AppendResult(interp, "wrong # args: should be \"",
673
                    argv[0], " nearest y\"", (char *) NULL);
674
            goto error;
675
        }
676
        if (Tcl_GetInt(interp, argv[2], &y) != TCL_OK) {
677
            goto error;
678
        }
679
        index = NearestListboxElement(listPtr, y);
680
        sprintf(interp->result, "%d", index);
681
    } else if ((c == 's') && (length >= 2)
682
            && (strncmp(argv[1], "scan", length) == 0)) {
683
        int x, y;
684
 
685
        if (argc != 5) {
686
            Tcl_AppendResult(interp, "wrong # args: should be \"",
687
                    argv[0], " scan mark|dragto x y\"", (char *) NULL);
688
            goto error;
689
        }
690
        if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK)
691
                || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)) {
692
            goto error;
693
        }
694
        if ((argv[2][0] == 'm')
695
                && (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
696
            listPtr->scanMarkX = x;
697
            listPtr->scanMarkY = y;
698
            listPtr->scanMarkXOffset = listPtr->xOffset;
699
            listPtr->scanMarkYIndex = listPtr->topIndex;
700
        } else if ((argv[2][0] == 'd')
701
                && (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
702
            ListboxScanTo(listPtr, x, y);
703
        } else {
704
            Tcl_AppendResult(interp, "bad scan option \"", argv[2],
705
                    "\": must be mark or dragto", (char *) NULL);
706
            goto error;
707
        }
708
    } else if ((c == 's') && (strncmp(argv[1], "see", length) == 0)
709
            && (length >= 3)) {
710
        int index, diff;
711
        if (argc != 3) {
712
            Tcl_AppendResult(interp, "wrong # args: should be \"",
713
                    argv[0], " see index\"",
714
                    (char *) NULL);
715
            goto error;
716
        }
717
        if (GetListboxIndex(interp, listPtr, argv[2], 0, &index) != TCL_OK) {
718
            goto error;
719
        }
720
        if (index >= listPtr->numElements) {
721
            index = listPtr->numElements-1;
722
        }
723
        if (index < 0) {
724
            index = 0;
725
        }
726
        diff = listPtr->topIndex-index;
727
        if (diff > 0) {
728
            if (diff <= (listPtr->fullLines/3)) {
729
                ChangeListboxView(listPtr, index);
730
            } else {
731
                ChangeListboxView(listPtr, index - (listPtr->fullLines-1)/2);
732
            }
733
        } else {
734
            diff = index - (listPtr->topIndex + listPtr->fullLines - 1);
735
            if (diff > 0) {
736
                if (diff <= (listPtr->fullLines/3)) {
737
                    ChangeListboxView(listPtr, listPtr->topIndex + diff);
738
                } else {
739
                    ChangeListboxView(listPtr,
740
                            index - (listPtr->fullLines-1)/2);
741
                }
742
            }
743
        }
744
    } else if ((c == 's') && (length >= 3)
745
            && (strncmp(argv[1], "selection", length) == 0)) {
746
        int first, last;
747
 
748
        if ((argc != 4) && (argc != 5)) {
749
            Tcl_AppendResult(interp, "wrong # args: should be \"",
750
                    argv[0], " selection option index ?index?\"",
751
                    (char *) NULL);
752
            goto error;
753
        }
754
        if (GetListboxIndex(interp, listPtr, argv[3], 0, &first) != TCL_OK) {
755
            goto error;
756
        }
757
        if (argc == 5) {
758
            if (GetListboxIndex(interp, listPtr, argv[4], 0, &last) != TCL_OK) {
759
                goto error;
760
            }
761
        } else {
762
            last = first;
763
        }
764
        length = strlen(argv[2]);
765
        c = argv[2][0];
766
        if ((c == 'a') && (strncmp(argv[2], "anchor", length) == 0)) {
767
            if (argc != 4) {
768
                Tcl_AppendResult(interp, "wrong # args: should be \"",
769
                    argv[0], " selection anchor index\"", (char *) NULL);
770
                goto error;
771
            }
772
            if (first >= listPtr->numElements) {
773
                first = listPtr->numElements-1;
774
            }
775
            if (first < 0) {
776
                first = 0;
777
            }
778
            listPtr->selectAnchor = first;
779
        } else if ((c == 'c') && (strncmp(argv[2], "clear", length) == 0)) {
780
            ListboxSelect(listPtr, first, last, 0);
781
        } else if ((c == 'i') && (strncmp(argv[2], "includes", length) == 0)) {
782
            int i;
783
            Element *elPtr;
784
 
785
            if (argc != 4) {
786
                Tcl_AppendResult(interp, "wrong # args: should be \"",
787
                        argv[0], " selection includes index\"", (char *) NULL);
788
                goto error;
789
            }
790
            if ((first < 0) || (first >= listPtr->numElements)) {
791
                interp->result = "0";
792
                goto done;
793
            }
794
            for (elPtr = listPtr->firstPtr, i = 0; i < first;
795
                    i++, elPtr = elPtr->nextPtr) {
796
                /* Empty loop body. */
797
            }
798
            if (elPtr->selected) {
799
                interp->result = "1";
800
            } else {
801
                interp->result = "0";
802
            }
803
        } else if ((c == 's') && (strncmp(argv[2], "set", length) == 0)) {
804
            ListboxSelect(listPtr, first, last, 1);
805
        } else {
806
            Tcl_AppendResult(interp, "bad selection option \"", argv[2],
807
                    "\": must be anchor, clear, includes, or set",
808
                    (char *) NULL);
809
            goto error;
810
        }
811
    } else if ((c == 's') && (length >= 2)
812
            && (strncmp(argv[1], "size", length) == 0)) {
813
        if (argc != 2) {
814
            Tcl_AppendResult(interp, "wrong # args: should be \"",
815
                    argv[0], " size\"", (char *) NULL);
816
            goto error;
817
        }
818
        sprintf(interp->result, "%d", listPtr->numElements);
819
    } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
820
        int index, count, type, windowWidth, windowUnits;
821
        int offset = 0;          /* Initialized to stop gcc warnings. */
822
        double fraction, fraction2;
823
 
824
        windowWidth = Tk_Width(listPtr->tkwin)
825
            - 2*(listPtr->inset + listPtr->selBorderWidth);
826
        if (argc == 2) {
827
            if (listPtr->maxWidth == 0) {
828
                interp->result = "0 1";
829
            } else {
830
                fraction = listPtr->xOffset/((double) listPtr->maxWidth);
831
                fraction2 = (listPtr->xOffset + windowWidth)
832
                        /((double) listPtr->maxWidth);
833
                if (fraction2 > 1.0) {
834
                    fraction2 = 1.0;
835
                }
836
                sprintf(interp->result, "%g %g", fraction, fraction2);
837
            }
838
        } else if (argc == 3) {
839
            if (Tcl_GetInt(interp, argv[2], &index) != TCL_OK) {
840
                goto error;
841
            }
842
            ChangeListboxOffset(listPtr, index*listPtr->xScrollUnit);
843
        } else {
844
            type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
845
            switch (type) {
846
                case TK_SCROLL_ERROR:
847
                    goto error;
848
                case TK_SCROLL_MOVETO:
849
                    offset = (int) (fraction*listPtr->maxWidth + 0.5);
850
                    break;
851
                case TK_SCROLL_PAGES:
852
                    windowUnits = windowWidth/listPtr->xScrollUnit;
853
                    if (windowUnits > 2) {
854
                        offset = listPtr->xOffset
855
                                + count*listPtr->xScrollUnit*(windowUnits-2);
856
                    } else {
857
                        offset = listPtr->xOffset + count*listPtr->xScrollUnit;
858
                    }
859
                    break;
860
                case TK_SCROLL_UNITS:
861
                    offset = listPtr->xOffset + count*listPtr->xScrollUnit;
862
                    break;
863
            }
864
            ChangeListboxOffset(listPtr, offset);
865
        }
866
    } else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)) {
867
        int index, count, type;
868
        double fraction, fraction2;
869
 
870
        if (argc == 2) {
871
            if (listPtr->numElements == 0) {
872
                interp->result = "0 1";
873
            } else {
874
                fraction = listPtr->topIndex/((double) listPtr->numElements);
875
                fraction2 = (listPtr->topIndex+listPtr->fullLines)
876
                        /((double) listPtr->numElements);
877
                if (fraction2 > 1.0) {
878
                    fraction2 = 1.0;
879
                }
880
                sprintf(interp->result, "%g %g", fraction, fraction2);
881
            }
882
        } else if (argc == 3) {
883
            if (GetListboxIndex(interp, listPtr, argv[2], 0, &index)
884
                    != TCL_OK) {
885
                goto error;
886
            }
887
            ChangeListboxView(listPtr, index);
888
        } else {
889
            type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
890
            switch (type) {
891
                case TK_SCROLL_ERROR:
892
                    goto error;
893
                case TK_SCROLL_MOVETO:
894
                    index = (int) (listPtr->numElements*fraction + 0.5);
895
                    break;
896
                case TK_SCROLL_PAGES:
897
                    if (listPtr->fullLines > 2) {
898
                        index = listPtr->topIndex
899
                                + count*(listPtr->fullLines-2);
900
                    } else {
901
                        index = listPtr->topIndex + count;
902
                    }
903
                    break;
904
                case TK_SCROLL_UNITS:
905
                    index = listPtr->topIndex + count;
906
                    break;
907
            }
908
            ChangeListboxView(listPtr, index);
909
        }
910
    } else {
911
        Tcl_AppendResult(interp, "bad option \"", argv[1],
912
                "\": must be activate, bbox, cget, configure, ",
913
                "curselection, delete, get, index, insert, nearest, ",
914
                "scan, see, selection, size, ",
915
                "xview, or yview", (char *) NULL);
916
        goto error;
917
    }
918
    done:
919
    Tcl_Release((ClientData) listPtr);
920
    return result;
921
 
922
    error:
923
    Tcl_Release((ClientData) listPtr);
924
    return TCL_ERROR;
925
}
926
 
927
/*
928
 *----------------------------------------------------------------------
929
 *
930
 * DestroyListbox --
931
 *
932
 *      This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
933
 *      to clean up the internal structure of a listbox at a safe time
934
 *      (when no-one is using it anymore).
935
 *
936
 * Results:
937
 *      None.
938
 *
939
 * Side effects:
940
 *      Everything associated with the listbox is freed up.
941
 *
942
 *----------------------------------------------------------------------
943
 */
944
 
945
static void
946
DestroyListbox(memPtr)
947
    char *memPtr;       /* Info about listbox widget. */
948
{
949
    register Listbox *listPtr = (Listbox *) memPtr;
950
    register Element *elPtr, *nextPtr;
951
 
952
    /*
953
     * Free up all of the list elements.
954
     */
955
 
956
    for (elPtr = listPtr->firstPtr; elPtr != NULL; ) {
957
        nextPtr = elPtr->nextPtr;
958
        ckfree((char *) elPtr);
959
        elPtr = nextPtr;
960
    }
961
 
962
    /*
963
     * Free up all the stuff that requires special handling, then
964
     * let Tk_FreeOptions handle all the standard option-related
965
     * stuff.
966
     */
967
 
968
    if (listPtr->textGC != None) {
969
        Tk_FreeGC(listPtr->display, listPtr->textGC);
970
    }
971
    if (listPtr->selTextGC != None) {
972
        Tk_FreeGC(listPtr->display, listPtr->selTextGC);
973
    }
974
    Tk_FreeOptions(configSpecs, (char *) listPtr, listPtr->display, 0);
975
    ckfree((char *) listPtr);
976
}
977
 
978
/*
979
 *----------------------------------------------------------------------
980
 *
981
 * ConfigureListbox --
982
 *
983
 *      This procedure is called to process an argv/argc list, plus
984
 *      the Tk option database, in order to configure (or reconfigure)
985
 *      a listbox widget.
986
 *
987
 * Results:
988
 *      The return value is a standard Tcl result.  If TCL_ERROR is
989
 *      returned, then interp->result contains an error message.
990
 *
991
 * Side effects:
992
 *      Configuration information, such as colors, border width,
993
 *      etc. get set for listPtr;  old resources get freed,
994
 *      if there were any.
995
 *
996
 *----------------------------------------------------------------------
997
 */
998
 
999
static int
1000
ConfigureListbox(interp, listPtr, argc, argv, flags)
1001
    Tcl_Interp *interp;         /* Used for error reporting. */
1002
    register Listbox *listPtr;  /* Information about widget;  may or may
1003
                                 * not already have values for some fields. */
1004
    int argc;                   /* Number of valid entries in argv. */
1005
    char **argv;                /* Arguments. */
1006
    int flags;                  /* Flags to pass to Tk_ConfigureWidget. */
1007
{
1008
    int oldExport;
1009
 
1010
    oldExport = listPtr->exportSelection;
1011
    if (Tk_ConfigureWidget(interp, listPtr->tkwin, configSpecs,
1012
            argc, argv, (char *) listPtr, flags) != TCL_OK) {
1013
        return TCL_ERROR;
1014
    }
1015
 
1016
    /*
1017
     * A few options need special processing, such as setting the
1018
     * background from a 3-D border.
1019
     */
1020
 
1021
    Tk_SetBackgroundFromBorder(listPtr->tkwin, listPtr->normalBorder);
1022
 
1023
    if (listPtr->highlightWidth < 0) {
1024
        listPtr->highlightWidth = 0;
1025
    }
1026
    listPtr->inset = listPtr->highlightWidth + listPtr->borderWidth;
1027
 
1028
    /*
1029
     * Claim the selection if we've suddenly started exporting it and
1030
     * there is a selection to export.
1031
     */
1032
 
1033
    if (listPtr->exportSelection && !oldExport
1034
            && (listPtr->numSelected != 0)) {
1035
        Tk_OwnSelection(listPtr->tkwin, XA_PRIMARY, ListboxLostSelection,
1036
                (ClientData) listPtr);
1037
    }
1038
 
1039
    ListboxWorldChanged((ClientData) listPtr);
1040
    return TCL_OK;
1041
}
1042
 
1043
/*
1044
 *---------------------------------------------------------------------------
1045
 *
1046
 * ListboxWorldChanged --
1047
 *
1048
 *      This procedure is called when the world has changed in some
1049
 *      way and the widget needs to recompute all its graphics contexts
1050
 *      and determine its new geometry.
1051
 *
1052
 * Results:
1053
 *      None.
1054
 *
1055
 * Side effects:
1056
 *      Listbox will be relayed out and redisplayed.
1057
 *
1058
 *---------------------------------------------------------------------------
1059
 */
1060
 
1061
static void
1062
ListboxWorldChanged(instanceData)
1063
    ClientData instanceData;    /* Information about widget. */
1064
{
1065
    XGCValues gcValues;
1066
    GC gc;
1067
    unsigned long mask;
1068
    Listbox *listPtr;
1069
 
1070
    listPtr = (Listbox *) instanceData;
1071
 
1072
    gcValues.foreground = listPtr->fgColorPtr->pixel;
1073
    gcValues.font = Tk_FontId(listPtr->tkfont);
1074
    gcValues.graphics_exposures = False;
1075
    mask = GCForeground | GCFont | GCGraphicsExposures;
1076
    gc = Tk_GetGCColor(listPtr->tkwin, mask, &gcValues, listPtr->fgColorPtr,
1077
                       NULL);
1078
    if (listPtr->textGC != None) {
1079
        Tk_FreeGC(listPtr->display, listPtr->textGC);
1080
    }
1081
    listPtr->textGC = gc;
1082
 
1083
    gcValues.foreground = listPtr->selFgColorPtr->pixel;
1084
    gcValues.font = Tk_FontId(listPtr->tkfont);
1085
    mask = GCForeground | GCFont;
1086
    gc = Tk_GetGCColor(listPtr->tkwin, mask, &gcValues, listPtr->selFgColorPtr,
1087
                       NULL);
1088
    if (listPtr->selTextGC != None) {
1089
        Tk_FreeGC(listPtr->display, listPtr->selTextGC);
1090
    }
1091
    listPtr->selTextGC = gc;
1092
 
1093
    /*
1094
     * Register the desired geometry for the window and arrange for
1095
     * the window to be redisplayed.
1096
     */
1097
 
1098
    ListboxComputeGeometry(listPtr, 1, 1, 1);
1099
    listPtr->flags |= UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR;
1100
    ListboxRedrawRange(listPtr, 0, listPtr->numElements-1);
1101
}
1102
 
1103
/*
1104
 *--------------------------------------------------------------
1105
 *
1106
 * DisplayListbox --
1107
 *
1108
 *      This procedure redraws the contents of a listbox window.
1109
 *
1110
 * Results:
1111
 *      None.
1112
 *
1113
 * Side effects:
1114
 *      Information appears on the screen.
1115
 *
1116
 *--------------------------------------------------------------
1117
 */
1118
 
1119
static void
1120
DisplayListbox(clientData)
1121
    ClientData clientData;      /* Information about window. */
1122
{
1123
    register Listbox *listPtr = (Listbox *) clientData;
1124
    register Tk_Window tkwin = listPtr->tkwin;
1125
    register Element *elPtr;
1126
    GC gc;
1127
    int i, limit, x, y, width, prevSelected;
1128
    Tk_FontMetrics fm;
1129
    int left, right;                    /* Non-zero values here indicate
1130
                                         * that the left or right edge of
1131
                                         * the listbox is off-screen. */
1132
    Pixmap pixmap;
1133
 
1134
    listPtr->flags &= ~REDRAW_PENDING;
1135
    if (listPtr->flags & UPDATE_V_SCROLLBAR) {
1136
        ListboxUpdateVScrollbar(listPtr);
1137
    }
1138
    if (listPtr->flags & UPDATE_H_SCROLLBAR) {
1139
        ListboxUpdateHScrollbar(listPtr);
1140
    }
1141
    listPtr->flags &= ~(REDRAW_PENDING|UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR);
1142
    if ((listPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
1143
        return;
1144
    }
1145
 
1146
    /*
1147
     * Redrawing is done in a temporary pixmap that is allocated
1148
     * here and freed at the end of the procedure.  All drawing is
1149
     * done to the pixmap, and the pixmap is copied to the screen
1150
     * at the end of the procedure.  This provides the smoothest
1151
     * possible visual effects (no flashing on the screen).
1152
     */
1153
 
1154
    pixmap = Tk_GetPixmap(listPtr->display, Tk_WindowId(tkwin),
1155
            Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
1156
    Tk_Fill3DRectangle(tkwin, pixmap, listPtr->normalBorder, 0, 0,
1157
            Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
1158
 
1159
    /*
1160
     * Iterate through all of the elements of the listbox, displaying each
1161
     * in turn.  Selected elements use a different GC and have a raised
1162
     * background.
1163
     */
1164
 
1165
    limit = listPtr->topIndex + listPtr->fullLines + listPtr->partialLine - 1;
1166
    if (limit >= listPtr->numElements) {
1167
        limit = listPtr->numElements-1;
1168
    }
1169
    left = right = 0;
1170
    if (listPtr->xOffset > 0) {
1171
        left = listPtr->selBorderWidth+1;
1172
    }
1173
    if ((listPtr->maxWidth - listPtr->xOffset) > (Tk_Width(listPtr->tkwin)
1174
            - 2*(listPtr->inset + listPtr->selBorderWidth)))  {
1175
        right = listPtr->selBorderWidth+1;
1176
    }
1177
    prevSelected = 0;
1178
    for (elPtr = listPtr->firstPtr, i = 0; (elPtr != NULL) && (i <= limit);
1179
            prevSelected = elPtr->selected, elPtr = elPtr->nextPtr, i++) {
1180
        if (i < listPtr->topIndex) {
1181
            continue;
1182
        }
1183
        x = listPtr->inset;
1184
        y = ((i - listPtr->topIndex) * listPtr->lineHeight)
1185
                + listPtr->inset;
1186
        gc = listPtr->textGC;
1187
        if (elPtr->selected) {
1188
            gc = listPtr->selTextGC;
1189
            width = Tk_Width(tkwin) - 2*listPtr->inset;
1190
            Tk_Fill3DRectangle(tkwin, pixmap, listPtr->selBorder, x, y,
1191
                    width, listPtr->lineHeight, 0, TK_RELIEF_FLAT);
1192
 
1193
            /*
1194
             * Draw beveled edges around the selection, if there are visible
1195
             * edges next to this element.  Special considerations:
1196
             * 1. The left and right bevels may not be visible if horizontal
1197
             *    scrolling is enabled (the "left" and "right" variables
1198
             *    are zero to indicate that the corresponding bevel is
1199
             *    visible).
1200
             * 2. Top and bottom bevels are only drawn if this is the
1201
             *    first or last seleted item.
1202
             * 3. If the left or right bevel isn't visible, then the "left"
1203
             *    and "right" variables, computed above, have non-zero values
1204
             *    that extend the top and bottom bevels so that the mitered
1205
             *    corners are off-screen.
1206
             */
1207
 
1208
            if (left == 0) {
1209
                Tk_3DVerticalBevel(tkwin, pixmap, listPtr->selBorder,
1210
                        x, y, listPtr->selBorderWidth, listPtr->lineHeight,
1211
                        1, TK_RELIEF_RAISED);
1212
            }
1213
            if (right == 0) {
1214
                Tk_3DVerticalBevel(tkwin, pixmap, listPtr->selBorder,
1215
                        x + width - listPtr->selBorderWidth, y,
1216
                        listPtr->selBorderWidth, listPtr->lineHeight,
1217
                        0, TK_RELIEF_RAISED);
1218
            }
1219
            if (!prevSelected) {
1220
                Tk_3DHorizontalBevel(tkwin, pixmap, listPtr->selBorder,
1221
                        x-left, y, width+left+right, listPtr->selBorderWidth,
1222
                        1, 1, 1, TK_RELIEF_RAISED);
1223
            }
1224
            if ((elPtr->nextPtr == NULL) || !elPtr->nextPtr->selected) {
1225
                Tk_3DHorizontalBevel(tkwin, pixmap, listPtr->selBorder, x-left,
1226
                        y + listPtr->lineHeight - listPtr->selBorderWidth,
1227
                        width+left+right, listPtr->selBorderWidth, 0, 0, 0,
1228
                        TK_RELIEF_RAISED);
1229
            }
1230
        }
1231
        Tk_GetFontMetrics(listPtr->tkfont, &fm);
1232
        y += fm.ascent + listPtr->selBorderWidth;
1233
        x = listPtr->inset + listPtr->selBorderWidth - elPtr->lBearing
1234
                - listPtr->xOffset;
1235
        Tk_DrawChars(listPtr->display, pixmap, gc, listPtr->tkfont,
1236
                elPtr->text, elPtr->textLength, x, y);
1237
 
1238
        /*
1239
         * If this is the active element, underline it.
1240
         */
1241
 
1242
        if ((i == listPtr->active) && (listPtr->flags & GOT_FOCUS)) {
1243
            Tk_UnderlineChars(listPtr->display, pixmap, gc, listPtr->tkfont,
1244
                    elPtr->text, x, y, 0, elPtr->textLength);
1245
        }
1246
    }
1247
 
1248
    /*
1249
     * Redraw the border for the listbox to make sure that it's on top
1250
     * of any of the text of the listbox entries.
1251
     */
1252
 
1253
    Tk_Draw3DRectangle(tkwin, pixmap, listPtr->normalBorder,
1254
            listPtr->highlightWidth, listPtr->highlightWidth,
1255
            Tk_Width(tkwin) - 2*listPtr->highlightWidth,
1256
            Tk_Height(tkwin) - 2*listPtr->highlightWidth,
1257
            listPtr->borderWidth, listPtr->relief);
1258
    if (listPtr->highlightWidth > 0) {
1259
        GC gc;
1260
 
1261
        if (listPtr->flags & GOT_FOCUS) {
1262
            gc = Tk_GCForColor(listPtr->highlightColorPtr, pixmap);
1263
        } else {
1264
            gc = Tk_GCForColor(listPtr->highlightBgColorPtr, pixmap);
1265
        }
1266
        Tk_DrawFocusHighlight(tkwin, gc, listPtr->highlightWidth, pixmap);
1267
    }
1268
    XCopyArea(listPtr->display, pixmap, Tk_WindowId(tkwin),
1269
            listPtr->textGC, 0, 0, (unsigned) Tk_Width(tkwin),
1270
            (unsigned) Tk_Height(tkwin), 0, 0);
1271
    Tk_FreePixmap(listPtr->display, pixmap);
1272
}
1273
 
1274
/*
1275
 *----------------------------------------------------------------------
1276
 *
1277
 * ListboxComputeGeometry --
1278
 *
1279
 *      This procedure is invoked to recompute geometry information
1280
 *      such as the sizes of the elements and the overall dimensions
1281
 *      desired for the listbox.
1282
 *
1283
 * Results:
1284
 *      None.
1285
 *
1286
 * Side effects:
1287
 *      Geometry information is updated and a new requested size is
1288
 *      registered for the widget.  Internal border and gridding
1289
 *      information is also set.
1290
 *
1291
 *----------------------------------------------------------------------
1292
 */
1293
 
1294
static void
1295
ListboxComputeGeometry(listPtr, fontChanged, maxIsStale, updateGrid)
1296
    Listbox *listPtr;           /* Listbox whose geometry is to be
1297
                                 * recomputed. */
1298
    int fontChanged;            /* Non-zero means the font may have changed
1299
                                 * so per-element width information also
1300
                                 * has to be computed. */
1301
    int maxIsStale;             /* Non-zero means the "maxWidth" field may
1302
                                 * no longer be up-to-date and must
1303
                                 * be recomputed.  If fontChanged is 1 then
1304
                                 * this must be 1. */
1305
    int updateGrid;             /* Non-zero means call Tk_SetGrid or
1306
                                 * Tk_UnsetGrid to update gridding for
1307
                                 * the window. */
1308
{
1309
    register Element *elPtr;
1310
    int width, height, pixelWidth, pixelHeight;
1311
    Tk_FontMetrics fm;
1312
 
1313
    if (fontChanged  || maxIsStale) {
1314
        listPtr->xScrollUnit = Tk_TextWidth(listPtr->tkfont, "0", 1);
1315
        if (listPtr->xScrollUnit == 0) {
1316
            listPtr->xScrollUnit = 1;
1317
        }
1318
        listPtr->maxWidth = 0;
1319
        for (elPtr = listPtr->firstPtr; elPtr != NULL; elPtr = elPtr->nextPtr) {
1320
            if (fontChanged) {
1321
                elPtr->pixelWidth = Tk_TextWidth(listPtr->tkfont,
1322
                        elPtr->text, elPtr->textLength);
1323
                elPtr->lBearing = 0;
1324
            }
1325
            if (elPtr->pixelWidth > listPtr->maxWidth) {
1326
                listPtr->maxWidth = elPtr->pixelWidth;
1327
            }
1328
        }
1329
    }
1330
 
1331
    Tk_GetFontMetrics(listPtr->tkfont, &fm);
1332
    listPtr->lineHeight = fm.linespace + 1 + 2*listPtr->selBorderWidth;
1333
    width = listPtr->width;
1334
    if (width <= 0) {
1335
        width = (listPtr->maxWidth + listPtr->xScrollUnit - 1)
1336
                /listPtr->xScrollUnit;
1337
        if (width < 1) {
1338
            width = 1;
1339
        }
1340
    }
1341
    pixelWidth = width*listPtr->xScrollUnit + 2*listPtr->inset
1342
            + 2*listPtr->selBorderWidth;
1343
    height = listPtr->height;
1344
    if (listPtr->height <= 0) {
1345
        height = listPtr->numElements;
1346
        if (height < 1) {
1347
            height = 1;
1348
        }
1349
    }
1350
    pixelHeight = height*listPtr->lineHeight + 2*listPtr->inset;
1351
    Tk_GeometryRequest(listPtr->tkwin, pixelWidth, pixelHeight);
1352
    Tk_SetInternalBorder(listPtr->tkwin, listPtr->inset);
1353
    if (updateGrid) {
1354
        if (listPtr->setGrid) {
1355
            Tk_SetGrid(listPtr->tkwin, width, height, listPtr->xScrollUnit,
1356
                    listPtr->lineHeight);
1357
        } else {
1358
            Tk_UnsetGrid(listPtr->tkwin);
1359
        }
1360
    }
1361
}
1362
 
1363
/*
1364
 *----------------------------------------------------------------------
1365
 *
1366
 * InsertEls --
1367
 *
1368
 *      Add new elements to a listbox widget.
1369
 *
1370
 * Results:
1371
 *      None.
1372
 *
1373
 * Side effects:
1374
 *      New information gets added to listPtr;  it will be redisplayed
1375
 *      soon, but not immediately.
1376
 *
1377
 *----------------------------------------------------------------------
1378
 */
1379
 
1380
static void
1381
InsertEls(listPtr, index, argc, argv)
1382
    register Listbox *listPtr;  /* Listbox that is to get the new
1383
                                 * elements. */
1384
    int index;                  /* Add the new elements before this
1385
                                 * element. */
1386
    int argc;                   /* Number of new elements to add. */
1387
    char **argv;                /* New elements (one per entry). */
1388
{
1389
    register Element *prevPtr, *newPtr;
1390
    int length, i, oldMaxWidth;
1391
 
1392
    /*
1393
     * Find the element before which the new ones will be inserted.
1394
     */
1395
 
1396
    if (index <= 0) {
1397
        index = 0;
1398
    }
1399
    if (index > listPtr->numElements) {
1400
        index = listPtr->numElements;
1401
    }
1402
    if (index == 0) {
1403
        prevPtr = NULL;
1404
    } else if (index == listPtr->numElements) {
1405
          prevPtr = listPtr->lastPtr;
1406
    } else {
1407
        for (prevPtr = listPtr->firstPtr, i = index - 1; i > 0; i--) {
1408
            prevPtr = prevPtr->nextPtr;
1409
        }
1410
    }
1411
 
1412
    /*
1413
     * For each new element, create a record, initialize it, and link
1414
     * it into the list of elements.
1415
     */
1416
 
1417
    oldMaxWidth = listPtr->maxWidth;
1418
    for (i = argc ; i > 0; i--, argv++, prevPtr = newPtr) {
1419
        length = strlen(*argv);
1420
        newPtr = (Element *) ckalloc(ElementSize(length));
1421
        newPtr->textLength = length;
1422
        strcpy(newPtr->text, *argv);
1423
        newPtr->pixelWidth = Tk_TextWidth(listPtr->tkfont, newPtr->text,
1424
                newPtr->textLength);
1425
        newPtr->lBearing = 0;
1426
        if (newPtr->pixelWidth > listPtr->maxWidth) {
1427
            listPtr->maxWidth = newPtr->pixelWidth;
1428
        }
1429
        newPtr->selected = 0;
1430
        if (prevPtr == NULL) {
1431
            newPtr->nextPtr = listPtr->firstPtr;
1432
            listPtr->firstPtr = newPtr;
1433
        } else {
1434
            newPtr->nextPtr = prevPtr->nextPtr;
1435
            prevPtr->nextPtr = newPtr;
1436
        }
1437
    }
1438
    if ((prevPtr != NULL) && (prevPtr->nextPtr == NULL)) {
1439
        listPtr->lastPtr = prevPtr;
1440
    }
1441
    listPtr->numElements += argc;
1442
 
1443
    /*
1444
     * Update the selection and other indexes to account for the
1445
     * renumbering that has just occurred.  Then arrange for the new
1446
     * information to be displayed.
1447
     */
1448
 
1449
    if (index <= listPtr->selectAnchor) {
1450
        listPtr->selectAnchor += argc;
1451
    }
1452
    if (index < listPtr->topIndex) {
1453
        listPtr->topIndex += argc;
1454
    }
1455
    if (index <= listPtr->active) {
1456
        listPtr->active += argc;
1457
        if ((listPtr->active >= listPtr->numElements)
1458
                && (listPtr->numElements > 0)) {
1459
            listPtr->active = listPtr->numElements-1;
1460
        }
1461
    }
1462
    listPtr->flags |= UPDATE_V_SCROLLBAR;
1463
    if (listPtr->maxWidth != oldMaxWidth) {
1464
        listPtr->flags |= UPDATE_H_SCROLLBAR;
1465
    }
1466
    ListboxComputeGeometry(listPtr, 0, 0, 0);
1467
    ListboxRedrawRange(listPtr, index, listPtr->numElements-1);
1468
}
1469
 
1470
/*
1471
 *----------------------------------------------------------------------
1472
 *
1473
 * DeleteEls --
1474
 *
1475
 *      Remove one or more elements from a listbox widget.
1476
 *
1477
 * Results:
1478
 *      None.
1479
 *
1480
 * Side effects:
1481
 *      Memory gets freed, the listbox gets modified and (eventually)
1482
 *      redisplayed.
1483
 *
1484
 *----------------------------------------------------------------------
1485
 */
1486
 
1487
static void
1488
DeleteEls(listPtr, first, last)
1489
    register Listbox *listPtr;  /* Listbox widget to modify. */
1490
    int first;                  /* Index of first element to delete. */
1491
    int last;                   /* Index of last element to delete. */
1492
{
1493
    register Element *prevPtr, *elPtr;
1494
    int count, i, widthChanged;
1495
 
1496
    /*
1497
     * Adjust the range to fit within the existing elements of the
1498
     * listbox, and make sure there's something to delete.
1499
     */
1500
 
1501
    if (first < 0) {
1502
        first = 0;
1503
    }
1504
    if (last >= listPtr->numElements) {
1505
        last = listPtr->numElements-1;
1506
    }
1507
    count = last + 1 - first;
1508
    if (count <= 0) {
1509
        return;
1510
    }
1511
 
1512
    /*
1513
     * Find the element just before the ones to delete.
1514
     */
1515
 
1516
    if (first == 0) {
1517
        prevPtr = NULL;
1518
    } else {
1519
        for (i = first-1, prevPtr = listPtr->firstPtr; i > 0; i--) {
1520
            prevPtr = prevPtr->nextPtr;
1521
        }
1522
    }
1523
 
1524
    /*
1525
     * Delete the requested number of elements.
1526
     */
1527
 
1528
    widthChanged = 0;
1529
    for (i = count; i > 0; i--) {
1530
        if (prevPtr == NULL) {
1531
            elPtr = listPtr->firstPtr;
1532
            listPtr->firstPtr = elPtr->nextPtr;
1533
            if (listPtr->firstPtr == NULL) {
1534
                listPtr->lastPtr = NULL;
1535
            }
1536
        } else {
1537
            elPtr = prevPtr->nextPtr;
1538
            prevPtr->nextPtr = elPtr->nextPtr;
1539
            if (prevPtr->nextPtr == NULL) {
1540
                listPtr->lastPtr = prevPtr;
1541
            }
1542
        }
1543
        if (elPtr->pixelWidth == listPtr->maxWidth) {
1544
            widthChanged = 1;
1545
        }
1546
        if (elPtr->selected) {
1547
            listPtr->numSelected -= 1;
1548
        }
1549
        ckfree((char *) elPtr);
1550
    }
1551
    listPtr->numElements -= count;
1552
 
1553
    /*
1554
     * Update the selection and viewing information to reflect the change
1555
     * in the element numbering, and redisplay to slide information up over
1556
     * the elements that were deleted.
1557
     */
1558
 
1559
    if (first <= listPtr->selectAnchor) {
1560
        listPtr->selectAnchor -= count;
1561
        if (listPtr->selectAnchor < first) {
1562
            listPtr->selectAnchor = first;
1563
        }
1564
    }
1565
    if (first <= listPtr->topIndex) {
1566
        listPtr->topIndex -= count;
1567
        if (listPtr->topIndex < first) {
1568
            listPtr->topIndex = first;
1569
        }
1570
    }
1571
    if (listPtr->topIndex > (listPtr->numElements - listPtr->fullLines)) {
1572
        listPtr->topIndex = listPtr->numElements - listPtr->fullLines;
1573
        if (listPtr->topIndex < 0) {
1574
            listPtr->topIndex = 0;
1575
        }
1576
    }
1577
    if (listPtr->active > last) {
1578
        listPtr->active -= count;
1579
    } else if (listPtr->active >= first) {
1580
        listPtr->active = first;
1581
        if ((listPtr->active >= listPtr->numElements)
1582
                && (listPtr->numElements > 0)) {
1583
            listPtr->active = listPtr->numElements-1;
1584
        }
1585
    }
1586
    listPtr->flags |= UPDATE_V_SCROLLBAR;
1587
    ListboxComputeGeometry(listPtr, 0, widthChanged, 0);
1588
    if (widthChanged) {
1589
        listPtr->flags |= UPDATE_H_SCROLLBAR;
1590
    }
1591
    ListboxRedrawRange(listPtr, first, listPtr->numElements-1);
1592
}
1593
 
1594
/*
1595
 *--------------------------------------------------------------
1596
 *
1597
 * ListboxEventProc --
1598
 *
1599
 *      This procedure is invoked by the Tk dispatcher for various
1600
 *      events on listboxes.
1601
 *
1602
 * Results:
1603
 *      None.
1604
 *
1605
 * Side effects:
1606
 *      When the window gets deleted, internal structures get
1607
 *      cleaned up.  When it gets exposed, it is redisplayed.
1608
 *
1609
 *--------------------------------------------------------------
1610
 */
1611
 
1612
static void
1613
ListboxEventProc(clientData, eventPtr)
1614
    ClientData clientData;      /* Information about window. */
1615
    XEvent *eventPtr;           /* Information about event. */
1616
{
1617
    Listbox *listPtr = (Listbox *) clientData;
1618
 
1619
    if (eventPtr->type == Expose) {
1620
        ListboxRedrawRange(listPtr,
1621
                NearestListboxElement(listPtr, eventPtr->xexpose.y),
1622
                NearestListboxElement(listPtr, eventPtr->xexpose.y
1623
                + eventPtr->xexpose.height));
1624
    } else if (eventPtr->type == DestroyNotify) {
1625
        if (listPtr->tkwin != NULL) {
1626
            if (listPtr->setGrid) {
1627
                Tk_UnsetGrid(listPtr->tkwin);
1628
            }
1629
            listPtr->tkwin = NULL;
1630
            Tcl_DeleteCommandFromToken(listPtr->interp, listPtr->widgetCmd);
1631
        }
1632
        if (listPtr->flags & REDRAW_PENDING) {
1633
            Tcl_CancelIdleCall(DisplayListbox, (ClientData) listPtr);
1634
        }
1635
        Tcl_EventuallyFree((ClientData) listPtr, DestroyListbox);
1636
    } else if (eventPtr->type == ConfigureNotify) {
1637
        int vertSpace;
1638
 
1639
        vertSpace = Tk_Height(listPtr->tkwin) - 2*listPtr->inset;
1640
        listPtr->fullLines = vertSpace / listPtr->lineHeight;
1641
        if ((listPtr->fullLines*listPtr->lineHeight) < vertSpace) {
1642
            listPtr->partialLine = 1;
1643
        } else {
1644
            listPtr->partialLine = 0;
1645
        }
1646
        listPtr->flags |= UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR;
1647
        ChangeListboxView(listPtr, listPtr->topIndex);
1648
        ChangeListboxOffset(listPtr, listPtr->xOffset);
1649
 
1650
        /*
1651
         * Redraw the whole listbox.  It's hard to tell what needs
1652
         * to be redrawn (e.g. if the listbox has shrunk then we
1653
         * may only need to redraw the borders), so just redraw
1654
         * everything for safety.
1655
         */
1656
 
1657
        ListboxRedrawRange(listPtr, 0, listPtr->numElements-1);
1658
    } else if (eventPtr->type == FocusIn) {
1659
        if (eventPtr->xfocus.detail != NotifyInferior) {
1660
            listPtr->flags |= GOT_FOCUS;
1661
            ListboxRedrawRange(listPtr, 0, listPtr->numElements-1);
1662
        }
1663
    } else if (eventPtr->type == FocusOut) {
1664
        if (eventPtr->xfocus.detail != NotifyInferior) {
1665
            listPtr->flags &= ~GOT_FOCUS;
1666
            ListboxRedrawRange(listPtr, 0, listPtr->numElements-1);
1667
        }
1668
    }
1669
}
1670
 
1671
/*
1672
 *----------------------------------------------------------------------
1673
 *
1674
 * ListboxCmdDeletedProc --
1675
 *
1676
 *      This procedure is invoked when a widget command is deleted.  If
1677
 *      the widget isn't already in the process of being destroyed,
1678
 *      this command destroys it.
1679
 *
1680
 * Results:
1681
 *      None.
1682
 *
1683
 * Side effects:
1684
 *      The widget is destroyed.
1685
 *
1686
 *----------------------------------------------------------------------
1687
 */
1688
 
1689
static void
1690
ListboxCmdDeletedProc(clientData)
1691
    ClientData clientData;      /* Pointer to widget record for widget. */
1692
{
1693
    Listbox *listPtr = (Listbox *) clientData;
1694
    Tk_Window tkwin = listPtr->tkwin;
1695
 
1696
    /*
1697
     * This procedure could be invoked either because the window was
1698
     * destroyed and the command was then deleted (in which case tkwin
1699
     * is NULL) or because the command was deleted, and then this procedure
1700
     * destroys the widget.
1701
     */
1702
 
1703
    if (tkwin != NULL) {
1704
        if (listPtr->setGrid) {
1705
            Tk_UnsetGrid(listPtr->tkwin);
1706
        }
1707
        listPtr->tkwin = NULL;
1708
        Tk_DestroyWindow(tkwin);
1709
    }
1710
}
1711
 
1712
/*
1713
 *--------------------------------------------------------------
1714
 *
1715
 * GetListboxIndex --
1716
 *
1717
 *      Parse an index into a listbox and return either its value
1718
 *      or an error.
1719
 *
1720
 * Results:
1721
 *      A standard Tcl result.  If all went well, then *indexPtr is
1722
 *      filled in with the index (into listPtr) corresponding to
1723
 *      string.  Otherwise an error message is left in interp->result.
1724
 *
1725
 * Side effects:
1726
 *      None.
1727
 *
1728
 *--------------------------------------------------------------
1729
 */
1730
 
1731
static int
1732
GetListboxIndex(interp, listPtr, string, endIsSize, indexPtr)
1733
    Tcl_Interp *interp;         /* For error messages. */
1734
    Listbox *listPtr;           /* Listbox for which the index is being
1735
                                 * specified. */
1736
    char *string;               /* Specifies an element in the listbox. */
1737
    int endIsSize;              /* If 1, "end" refers to the number of
1738
                                 * entries in the listbox.  If 0, "end"
1739
                                 * refers to 1 less than the number of
1740
                                 * entries. */
1741
    int *indexPtr;              /* Where to store converted index. */
1742
{
1743
    int c;
1744
    size_t length;
1745
 
1746
    length = strlen(string);
1747
    c = string[0];
1748
    if ((c == 'a') && (strncmp(string, "active", length) == 0)
1749
            && (length >= 2)) {
1750
        *indexPtr = listPtr->active;
1751
    } else if ((c == 'a') && (strncmp(string, "anchor", length) == 0)
1752
            && (length >= 2)) {
1753
        *indexPtr = listPtr->selectAnchor;
1754
    } else if ((c == 'e') && (strncmp(string, "end", length) == 0)) {
1755
        if (endIsSize) {
1756
            *indexPtr = listPtr->numElements;
1757
        } else {
1758
            *indexPtr = listPtr->numElements - 1;
1759
        }
1760
    } else if (c == '@') {
1761
        int y;
1762
        char *p, *end;
1763
 
1764
        p = string+1;
1765
        strtol(p, &end, 0);
1766
        if ((end == p) || (*end != ',')) {
1767
            goto badIndex;
1768
        }
1769
        p = end+1;
1770
        y = strtol(p, &end, 0);
1771
        if ((end == p) || (*end != 0)) {
1772
            goto badIndex;
1773
        }
1774
        *indexPtr = NearestListboxElement(listPtr, y);
1775
    } else {
1776
        if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
1777
            Tcl_ResetResult(interp);
1778
            goto badIndex;
1779
        }
1780
    }
1781
    return TCL_OK;
1782
 
1783
    badIndex:
1784
    Tcl_AppendResult(interp, "bad listbox index \"", string,
1785
            "\": must be active, anchor, end, @x,y, or a number",
1786
            (char *) NULL);
1787
    return TCL_ERROR;
1788
}
1789
 
1790
/*
1791
 *----------------------------------------------------------------------
1792
 *
1793
 * ChangeListboxView --
1794
 *
1795
 *      Change the view on a listbox widget so that a given element
1796
 *      is displayed at the top.
1797
 *
1798
 * Results:
1799
 *      None.
1800
 *
1801
 * Side effects:
1802
 *      What's displayed on the screen is changed.  If there is a
1803
 *      scrollbar associated with this widget, then the scrollbar
1804
 *      is instructed to change its display too.
1805
 *
1806
 *----------------------------------------------------------------------
1807
 */
1808
 
1809
static void
1810
ChangeListboxView(listPtr, index)
1811
    register Listbox *listPtr;          /* Information about widget. */
1812
    int index;                          /* Index of element in listPtr
1813
                                         * that should now appear at the
1814
                                         * top of the listbox. */
1815
{
1816
    if (index >= (listPtr->numElements - listPtr->fullLines)) {
1817
        index = listPtr->numElements - listPtr->fullLines;
1818
    }
1819
    if (index < 0) {
1820
        index = 0;
1821
    }
1822
    if (listPtr->topIndex != index) {
1823
        listPtr->topIndex = index;
1824
        if (!(listPtr->flags & REDRAW_PENDING)) {
1825
            Tcl_DoWhenIdle(DisplayListbox, (ClientData) listPtr);
1826
            listPtr->flags |= REDRAW_PENDING;
1827
        }
1828
        listPtr->flags |= UPDATE_V_SCROLLBAR;
1829
    }
1830
}
1831
 
1832
/*
1833
 *----------------------------------------------------------------------
1834
 *
1835
 * ChangListboxOffset --
1836
 *
1837
 *      Change the horizontal offset for a listbox.
1838
 *
1839
 * Results:
1840
 *      None.
1841
 *
1842
 * Side effects:
1843
 *      The listbox may be redrawn to reflect its new horizontal
1844
 *      offset.
1845
 *
1846
 *----------------------------------------------------------------------
1847
 */
1848
 
1849
static void
1850
ChangeListboxOffset(listPtr, offset)
1851
    register Listbox *listPtr;          /* Information about widget. */
1852
    int offset;                         /* Desired new "xOffset" for
1853
                                         * listbox. */
1854
{
1855
    int maxOffset;
1856
 
1857
    /*
1858
     * Make sure that the new offset is within the allowable range, and
1859
     * round it off to an even multiple of xScrollUnit.
1860
     */
1861
 
1862
    maxOffset = listPtr->maxWidth - (Tk_Width(listPtr->tkwin) -
1863
            2*listPtr->inset - 2*listPtr->selBorderWidth)
1864
            + listPtr->xScrollUnit - 1;
1865
    if (offset > maxOffset) {
1866
        offset = maxOffset;
1867
    }
1868
    if (offset < 0) {
1869
        offset = 0;
1870
    }
1871
    offset -= offset % listPtr->xScrollUnit;
1872
    if (offset != listPtr->xOffset) {
1873
        listPtr->xOffset = offset;
1874
        listPtr->flags |= UPDATE_H_SCROLLBAR;
1875
        ListboxRedrawRange(listPtr, 0, listPtr->numElements);
1876
    }
1877
}
1878
 
1879
/*
1880
 *----------------------------------------------------------------------
1881
 *
1882
 * ListboxScanTo --
1883
 *
1884
 *      Given a point (presumably of the curent mouse location)
1885
 *      drag the view in the window to implement the scan operation.
1886
 *
1887
 * Results:
1888
 *      None.
1889
 *
1890
 * Side effects:
1891
 *      The view in the window may change.
1892
 *
1893
 *----------------------------------------------------------------------
1894
 */
1895
 
1896
static void
1897
ListboxScanTo(listPtr, x, y)
1898
    register Listbox *listPtr;          /* Information about widget. */
1899
    int x;                              /* X-coordinate to use for scan
1900
                                         * operation. */
1901
    int y;                              /* Y-coordinate to use for scan
1902
                                         * operation. */
1903
{
1904
    int newTopIndex, newOffset, maxIndex, maxOffset;
1905
 
1906
    maxIndex = listPtr->numElements - listPtr->fullLines;
1907
    maxOffset = listPtr->maxWidth + (listPtr->xScrollUnit - 1)
1908
            - (Tk_Width(listPtr->tkwin) - 2*listPtr->inset
1909
            - 2*listPtr->selBorderWidth - listPtr->xScrollUnit);
1910
 
1911
    /*
1912
     * Compute new top line for screen by amplifying the difference
1913
     * between the current position and the place where the scan
1914
     * started (the "mark" position).  If we run off the top or bottom
1915
     * of the list, then reset the mark point so that the current
1916
     * position continues to correspond to the edge of the window.
1917
     * This means that the picture will start dragging as soon as the
1918
     * mouse reverses direction (without this reset, might have to slide
1919
     * mouse a long ways back before the picture starts moving again).
1920
     */
1921
 
1922
    newTopIndex = listPtr->scanMarkYIndex
1923
            - (10*(y - listPtr->scanMarkY))/listPtr->lineHeight;
1924
    if (newTopIndex > maxIndex) {
1925
        newTopIndex = listPtr->scanMarkYIndex = maxIndex;
1926
        listPtr->scanMarkY = y;
1927
    } else if (newTopIndex < 0) {
1928
        newTopIndex = listPtr->scanMarkYIndex = 0;
1929
        listPtr->scanMarkY = y;
1930
    }
1931
    ChangeListboxView(listPtr, newTopIndex);
1932
 
1933
    /*
1934
     * Compute new left edge for display in a similar fashion by amplifying
1935
     * the difference between the current position and the place where the
1936
     * scan started.
1937
     */
1938
 
1939
    newOffset = listPtr->scanMarkXOffset - (10*(x - listPtr->scanMarkX));
1940
    if (newOffset > maxOffset) {
1941
        newOffset = listPtr->scanMarkXOffset = maxOffset;
1942
        listPtr->scanMarkX = x;
1943
    } else if (newOffset < 0) {
1944
        newOffset = listPtr->scanMarkXOffset = 0;
1945
        listPtr->scanMarkX = x;
1946
    }
1947
    ChangeListboxOffset(listPtr, newOffset);
1948
}
1949
 
1950
/*
1951
 *----------------------------------------------------------------------
1952
 *
1953
 * NearestListboxElement --
1954
 *
1955
 *      Given a y-coordinate inside a listbox, compute the index of
1956
 *      the element under that y-coordinate (or closest to that
1957
 *      y-coordinate).
1958
 *
1959
 * Results:
1960
 *      The return value is an index of an element of listPtr.  If
1961
 *      listPtr has no elements, then 0 is always returned.
1962
 *
1963
 * Side effects:
1964
 *      None.
1965
 *
1966
 *----------------------------------------------------------------------
1967
 */
1968
 
1969
static int
1970
NearestListboxElement(listPtr, y)
1971
    register Listbox *listPtr;          /* Information about widget. */
1972
    int y;                              /* Y-coordinate in listPtr's window. */
1973
{
1974
    int index;
1975
 
1976
    index = (y - listPtr->inset)/listPtr->lineHeight;
1977
    if (index >= (listPtr->fullLines + listPtr->partialLine)) {
1978
        index = listPtr->fullLines + listPtr->partialLine - 1;
1979
    }
1980
    if (index < 0) {
1981
        index = 0;
1982
    }
1983
    index += listPtr->topIndex;
1984
    if (index >= listPtr->numElements) {
1985
        index = listPtr->numElements-1;
1986
    }
1987
    return index;
1988
}
1989
 
1990
/*
1991
 *----------------------------------------------------------------------
1992
 *
1993
 * ListboxSelect --
1994
 *
1995
 *      Select or deselect one or more elements in a listbox..
1996
 *
1997
 * Results:
1998
 *      None.
1999
 *
2000
 * Side effects:
2001
 *      All of the elements in the range between first and last are
2002
 *      marked as either selected or deselected, depending on the
2003
 *      "select" argument.  Any items whose state changes are redisplayed.
2004
 *      The selection is claimed from X when the number of selected
2005
 *      elements changes from zero to non-zero.
2006
 *
2007
 *----------------------------------------------------------------------
2008
 */
2009
 
2010
static void
2011
ListboxSelect(listPtr, first, last, select)
2012
    register Listbox *listPtr;          /* Information about widget. */
2013
    int first;                          /* Index of first element to
2014
                                         * select or deselect. */
2015
    int last;                           /* Index of last element to
2016
                                         * select or deselect. */
2017
    int select;                         /* 1 means select items, 0 means
2018
                                         * deselect them. */
2019
{
2020
    int i, firstRedisplay, increment, oldCount;
2021
    Element *elPtr;
2022
 
2023
    if (last < first) {
2024
        i = first;
2025
        first = last;
2026
        last = i;
2027
    }
2028
    if ((last < 0) || (first >= listPtr->numElements)) {
2029
        return;
2030
    }
2031
    if (first < 0) {
2032
        first = 0;
2033
    }
2034
    if (last >= listPtr->numElements) {
2035
        last = listPtr->numElements - 1;
2036
    }
2037
    oldCount = listPtr->numSelected;
2038
    firstRedisplay = -1;
2039
    increment = select ? 1 : -1;
2040
    for (i = 0, elPtr = listPtr->firstPtr; i < first;
2041
            i++, elPtr = elPtr->nextPtr) {
2042
        /* Empty loop body. */
2043
    }
2044
    for ( ; i <= last; i++, elPtr = elPtr->nextPtr) {
2045
        if (elPtr->selected == select) {
2046
            continue;
2047
        }
2048
        listPtr->numSelected += increment;
2049
        elPtr->selected = select;
2050
        if (firstRedisplay < 0) {
2051
            firstRedisplay = i;
2052
        }
2053
    }
2054
    if (firstRedisplay >= 0) {
2055
        ListboxRedrawRange(listPtr, first, last);
2056
    }
2057
    if ((oldCount == 0) && (listPtr->numSelected > 0)
2058
            && (listPtr->exportSelection)) {
2059
        Tk_OwnSelection(listPtr->tkwin, XA_PRIMARY, ListboxLostSelection,
2060
                (ClientData) listPtr);
2061
    }
2062
}
2063
 
2064
/*
2065
 *----------------------------------------------------------------------
2066
 *
2067
 * ListboxFetchSelection --
2068
 *
2069
 *      This procedure is called back by Tk when the selection is
2070
 *      requested by someone.  It returns part or all of the selection
2071
 *      in a buffer provided by the caller.
2072
 *
2073
 * Results:
2074
 *      The return value is the number of non-NULL bytes stored
2075
 *      at buffer.  Buffer is filled (or partially filled) with a
2076
 *      NULL-terminated string containing part or all of the selection,
2077
 *      as given by offset and maxBytes.  The selection is returned
2078
 *      as a Tcl list with one list element for each element in the
2079
 *      listbox.
2080
 *
2081
 * Side effects:
2082
 *      None.
2083
 *
2084
 *----------------------------------------------------------------------
2085
 */
2086
 
2087
static int
2088
ListboxFetchSelection(clientData, offset, buffer, maxBytes)
2089
    ClientData clientData;              /* Information about listbox widget. */
2090
    int offset;                         /* Offset within selection of first
2091
                                         * byte to be returned. */
2092
    char *buffer;                       /* Location in which to place
2093
                                         * selection. */
2094
    int maxBytes;                       /* Maximum number of bytes to place
2095
                                         * at buffer, not including terminating
2096
                                         * NULL character. */
2097
{
2098
    register Listbox *listPtr = (Listbox *) clientData;
2099
    register Element *elPtr;
2100
    Tcl_DString selection;
2101
    int length, count, needNewline;
2102
 
2103
    if (!listPtr->exportSelection) {
2104
        return -1;
2105
    }
2106
 
2107
    /*
2108
     * Use a dynamic string to accumulate the contents of the selection.
2109
     */
2110
 
2111
    needNewline = 0;
2112
    Tcl_DStringInit(&selection);
2113
    for (elPtr = listPtr->firstPtr; elPtr != NULL; elPtr = elPtr->nextPtr) {
2114
        if (elPtr->selected) {
2115
            if (needNewline) {
2116
                Tcl_DStringAppend(&selection, "\n", 1);
2117
            }
2118
            Tcl_DStringAppend(&selection, elPtr->text, elPtr->textLength);
2119
            needNewline = 1;
2120
        }
2121
    }
2122
 
2123
    length = Tcl_DStringLength(&selection);
2124
    if (length == 0) {
2125
        return -1;
2126
    }
2127
 
2128
    /*
2129
     * Copy the requested portion of the selection to the buffer.
2130
     */
2131
 
2132
    count = length - offset;
2133
    if (count <= 0) {
2134
        count = 0;
2135
    } else {
2136
        if (count > maxBytes) {
2137
            count = maxBytes;
2138
        }
2139
        memcpy((VOID *) buffer,
2140
                (VOID *) (Tcl_DStringValue(&selection) + offset),
2141
                (size_t) count);
2142
    }
2143
    buffer[count] = '\0';
2144
    Tcl_DStringFree(&selection);
2145
    return count;
2146
}
2147
 
2148
/*
2149
 *----------------------------------------------------------------------
2150
 *
2151
 * ListboxLostSelection --
2152
 *
2153
 *      This procedure is called back by Tk when the selection is
2154
 *      grabbed away from a listbox widget.
2155
 *
2156
 * Results:
2157
 *      None.
2158
 *
2159
 * Side effects:
2160
 *      The existing selection is unhighlighted, and the window is
2161
 *      marked as not containing a selection.
2162
 *
2163
 *----------------------------------------------------------------------
2164
 */
2165
 
2166
static void
2167
ListboxLostSelection(clientData)
2168
    ClientData clientData;              /* Information about listbox widget. */
2169
{
2170
    register Listbox *listPtr = (Listbox *) clientData;
2171
 
2172
    if ((listPtr->exportSelection) && (listPtr->numElements > 0)) {
2173
        ListboxSelect(listPtr, 0, listPtr->numElements-1, 0);
2174
    }
2175
}
2176
 
2177
/*
2178
 *----------------------------------------------------------------------
2179
 *
2180
 * ListboxRedrawRange --
2181
 *
2182
 *      Ensure that a given range of elements is eventually redrawn on
2183
 *      the display (if those elements in fact appear on the display).
2184
 *
2185
 * Results:
2186
 *      None.
2187
 *
2188
 * Side effects:
2189
 *      Information gets redisplayed.
2190
 *
2191
 *----------------------------------------------------------------------
2192
 */
2193
 
2194
        /* ARGSUSED */
2195
static void
2196
ListboxRedrawRange(listPtr, first, last)
2197
    register Listbox *listPtr;          /* Information about widget. */
2198
    int first;                          /* Index of first element in list
2199
                                         * that needs to be redrawn. */
2200
    int last;                           /* Index of last element in list
2201
                                         * that needs to be redrawn.  May
2202
                                         * be less than first;
2203
                                         * these just bracket a range. */
2204
{
2205
    if ((listPtr->tkwin == NULL) || !Tk_IsMapped(listPtr->tkwin)
2206
            || (listPtr->flags & REDRAW_PENDING)) {
2207
        return;
2208
    }
2209
    Tcl_DoWhenIdle(DisplayListbox, (ClientData) listPtr);
2210
    listPtr->flags |= REDRAW_PENDING;
2211
}
2212
 
2213
/*
2214
 *----------------------------------------------------------------------
2215
 *
2216
 * ListboxUpdateVScrollbar --
2217
 *
2218
 *      This procedure is invoked whenever information has changed in
2219
 *      a listbox in a way that would invalidate a vertical scrollbar
2220
 *      display.  If there is an associated scrollbar, then this command
2221
 *      updates it by invoking a Tcl command.
2222
 *
2223
 * Results:
2224
 *      None.
2225
 *
2226
 * Side effects:
2227
 *      A Tcl command is invoked, and an additional command may be
2228
 *      invoked to process errors in the command.
2229
 *
2230
 *----------------------------------------------------------------------
2231
 */
2232
 
2233
static void
2234
ListboxUpdateVScrollbar(listPtr)
2235
    register Listbox *listPtr;          /* Information about widget. */
2236
{
2237
    char string[100];
2238
    double first, last;
2239
    int result;
2240
    Tcl_Interp *interp;
2241
 
2242
    if (listPtr->yScrollCmd == NULL) {
2243
        return;
2244
    }
2245
    if (listPtr->numElements == 0) {
2246
        first = 0.0;
2247
        last = 1.0;
2248
    } else {
2249
        first = listPtr->topIndex/((double) listPtr->numElements);
2250
        last = (listPtr->topIndex+listPtr->fullLines)
2251
                /((double) listPtr->numElements);
2252
        if (last > 1.0) {
2253
            last = 1.0;
2254
        }
2255
    }
2256
    sprintf(string, " %g %g", first, last);
2257
 
2258
    /*
2259
     * We must hold onto the interpreter from the listPtr because the data
2260
     * at listPtr might be freed as a result of the Tcl_VarEval.
2261
     */
2262
 
2263
    interp = listPtr->interp;
2264
    Tcl_Preserve((ClientData) interp);
2265
    result = Tcl_VarEval(interp, listPtr->yScrollCmd, string,
2266
            (char *) NULL);
2267
    if (result != TCL_OK) {
2268
        Tcl_AddErrorInfo(interp,
2269
                "\n    (vertical scrolling command executed by listbox)");
2270
        Tcl_BackgroundError(interp);
2271
    }
2272
    Tcl_Release((ClientData) interp);
2273
}
2274
 
2275
/*
2276
 *----------------------------------------------------------------------
2277
 *
2278
 * ListboxUpdateHScrollbar --
2279
 *
2280
 *      This procedure is invoked whenever information has changed in
2281
 *      a listbox in a way that would invalidate a horizontal scrollbar
2282
 *      display.  If there is an associated horizontal scrollbar, then
2283
 *      this command updates it by invoking a Tcl command.
2284
 *
2285
 * Results:
2286
 *      None.
2287
 *
2288
 * Side effects:
2289
 *      A Tcl command is invoked, and an additional command may be
2290
 *      invoked to process errors in the command.
2291
 *
2292
 *----------------------------------------------------------------------
2293
 */
2294
 
2295
static void
2296
ListboxUpdateHScrollbar(listPtr)
2297
    register Listbox *listPtr;          /* Information about widget. */
2298
{
2299
    char string[60];
2300
    int result, windowWidth;
2301
    double first, last;
2302
    Tcl_Interp *interp;
2303
 
2304
    if (listPtr->xScrollCmd == NULL) {
2305
        return;
2306
    }
2307
    windowWidth = Tk_Width(listPtr->tkwin) - 2*(listPtr->inset
2308
            + listPtr->selBorderWidth);
2309
    if (listPtr->maxWidth == 0) {
2310
        first = 0;
2311
        last = 1.0;
2312
    } else {
2313
        first = listPtr->xOffset/((double) listPtr->maxWidth);
2314
        last = (listPtr->xOffset + windowWidth)
2315
                /((double) listPtr->maxWidth);
2316
        if (last > 1.0) {
2317
            last = 1.0;
2318
        }
2319
    }
2320
    sprintf(string, " %g %g", first, last);
2321
 
2322
    /*
2323
     * We must hold onto the interpreter because the data referred to at
2324
     * listPtr might be freed as a result of the call to Tcl_VarEval.
2325
     */
2326
 
2327
    interp = listPtr->interp;
2328
    Tcl_Preserve((ClientData) interp);
2329
    result = Tcl_VarEval(interp, listPtr->xScrollCmd, string,
2330
            (char *) NULL);
2331
    if (result != TCL_OK) {
2332
        Tcl_AddErrorInfo(interp,
2333
                "\n    (horizontal scrolling command executed by listbox)");
2334
        Tcl_BackgroundError(interp);
2335
    }
2336
    Tcl_Release((ClientData) interp);
2337
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.