home *** CD-ROM | disk | FTP | other *** search
- /*
- * tkColbox.c --
- *
- * This module implements colbox widgets for the Tk
- * toolkit. A colbox displays a collection of strings,
- * N per line (where N is the number of columns chosen by
- * the user), and provides scrolling and selection.
- * The items in the list are filled by columns, that is, the
- * first column is filled before the second column, and so on.
- * Thus an alphabatical list will be alphabetical by columns.
- * It would be easy to add on option to fill by rows but I have
- * not done it.
- *
- * This module was modified to be a `colbox', that is, a listbox
- * that will display the list in one or more columns.
- * In addition, a command to return the indexes of the first and
- * last selected element was added. this command is called
- * 'sellimits' and causes the abbreviation 'se' for 'select' to
- * no longer be a valid abbreviation.
- * Finally I added a resource to prevent losing the X selection
- * from deselecting the selected elements. This is to allow several
- * list boxes to have selections simultaneously.
- * The modifications were done in July 1991 by
- * Charles Crowley
- * Computer Science Department
- * University of New Mexico
- * Albuquerque, NM 87131
- * 505-277-3112
- * crowley@unmvax.cs.unm.edu
- *
- * Copyright 1990 Regents of the University of California.
- * Permission to use, copy, modify, and distribute this
- * software and its documentation for any purpose and without
- * fee is hereby granted, provided that the above copyright
- * notice appear in all copies. The University of California
- * makes no representations about the suitability of this
- * software for any purpose. It is provided "as is" without
- * express or implied warranty.
- */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <X11/Xatom.h>
- #include "tkConfig.h"
- #include "default.h"
- #include "tkInt.h"
-
- /*
- * One record of the following type is kept for each element
- * associated with a listbox widget:
- */
-
- typedef struct Element {
- int textLength; /* # non-NULL characters in text. */
- int lBearing; /* Distance from first character's
- * origin to left edge of character. */
- struct Element *nextPtr; /* Next in list of all elements of this
- * listbox, or NULL for last element. */
- char text[4]; /* Characters of this element, NULL-
- * terminated. The actual space allocated
- * here will be as large as needed (> 4,
- * most likely). Must be the last field
- * of the record. */
- } Element;
-
- #define ElementSize(stringLength) \
- ((unsigned) (sizeof(Element) - 3 + stringLength))
-
- #define MAX_COLS 50 /* seems big enough */
-
- /*
- * A data structure of the following type is kept for each listbox
- * widget managed by this file:
- */
-
- typedef struct {
- Tk_Window tkwin; /* Window that embodies the listbox. NULL
- * means that the window has been destroyed
- * but the data structures haven't yet been
- * cleaned up.*/
- Tcl_Interp *interp; /* Interpreter associated with listbox. */
- int numElements; /* Total number of elements in this listbox. */
- Element *elementPtr; /* First in list of elements (NULL if no
- * elements. */
-
- /*
- * Information used when displaying widget:
- */
-
- Tk_3DBorder normalBorder; /* Used for drawing border around whole
- * window, plus used for background. */
- int borderWidth; /* Width of 3-D border around window. */
- int relief; /* 3-D effect: TK_RELIEF_RAISED, etc. */
- XFontStruct *fontPtr; /* Information about text font, or NULL. */
- XColor *fgColorPtr; /* Text color in normal mode. */
- GC textGC; /* For drawing normal text. */
- Tk_3DBorder selBorder; /* Borders and backgrounds for selected
- * elements. */
- int selBorderWidth; /* Width of border around selection. */
- XColor *selFgColorPtr; /* Foreground color for selected elements. */
- GC selTextGC; /* For drawing selected text. */
- char *geometry; /* Desired geometry for window. Malloc'ed. */
- int lineHeight; /* Number of pixels allocated for each line
- * in display. */
- int topIndex; /* Index of top-most element visible in
- * window (on the left most column). */
- int numLines; /* Number of lines that fit
- * in window at one time. */
- int columns; /* Number of columns to display. Can be at
- * most MAX_COLS. */
- int noxsel; /* If true, then do not deselect elements
- * when we lose the X selection. */
- int numInColumn; /* Number of elements in one column. */
- int redrawFirst; /* Index (in element list, not on display)
- * of first element (in the left most column)
- * to redisplay. -1 means
- * no redisplay pending. */
- int redrawLast; /* Index of last element to redisplay. */
- int scrollLines; /* If non-zero, then DisplayListbox is
- * expected to shift the whole picture
- * down (if > 0) or up (if < 0) this many
- * lines, prior to redisplaying the stuff
- * given by redrawFirst and redrawLast. */
-
- /*
- * Information about what's selected, if any.
- */
-
- int selectFirst; /* Index of first selected element (-1 means
- * nothing selected. */
- int selectLast; /* Index of last selected element. */
- int selectAnchor; /* Fixed end of selection (i.e. element
- * at which selection was started.) */
-
- /*
- * Information for scanning:
- */
-
- int scanMarkY; /* Y-position at which scan started (e.g.
- * button was pressed here). */
- int scanMarkIndex; /* Index of line that was at top of window
- * when scan started. */
-
- /*
- * Miscellaneous information:
- */
-
- char *scrollCmd; /* Command prefix for communicating with
- * scrollbar(s). NULL means no command
- * to issue. Malloc'ed. */
- int flags; /* Various flag bits: see below for
- * definitions. */
- } Listbox;
-
- /*
- * Flag bits for buttons:
- *
- * None currently defined.
- */
-
- /*
- * Information used for argv parsing:
- */
-
- static Tk_ConfigSpec configSpecs[] = {
- {TK_CONFIG_BORDER, "-background", "background", "Background",
- DEF_LISTBOX_BG_COLOR, Tk_Offset(Listbox, normalBorder),
- TK_CONFIG_COLOR_ONLY},
- {TK_CONFIG_BORDER, "-background", "background", "Background",
- DEF_LISTBOX_BG_MONO, Tk_Offset(Listbox, normalBorder),
- TK_CONFIG_MONO_ONLY},
- {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
- (char *) NULL, 0, 0},
- {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
- (char *) NULL, 0, 0},
- {TK_CONFIG_INT, "-borderwidth", "borderWidth", "BorderWidth",
- DEF_LISTBOX_BORDER_WIDTH, Tk_Offset(Listbox, borderWidth), 0},
- {TK_CONFIG_INT, "-columns", "columns", "Columns",
- "1", Tk_Offset(Listbox, columns), 0},
- {TK_CONFIG_INT, "-noxsel", "noxsel", "noxsel",
- "0", Tk_Offset(Listbox, noxsel), 0},
- {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
- (char *) NULL, 0, 0},
- {TK_CONFIG_FONT, "-font", "font", "Font",
- DEF_LISTBOX_FONT, Tk_Offset(Listbox, fontPtr), 0},
- {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
- DEF_LISTBOX_FG, Tk_Offset(Listbox, fgColorPtr), 0},
- {TK_CONFIG_STRING, "-geometry", "geometry", "Geometry",
- DEF_LISTBOX_GEOMETRY, Tk_Offset(Listbox, geometry), 0},
- {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
- DEF_LISTBOX_RELIEF, Tk_Offset(Listbox, relief), 0},
- {TK_CONFIG_STRING, "-scrollcommand", "scrollCommand", "ScrollCommand",
- DEF_LISTBOX_SCROLL_COMMAND, Tk_Offset(Listbox, scrollCmd), 0},
- {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
- DEF_LISTBOX_SELECT_COLOR, Tk_Offset(Listbox, selBorder),
- TK_CONFIG_COLOR_ONLY},
- {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
- DEF_LISTBOX_SELECT_MONO, Tk_Offset(Listbox, selBorder),
- TK_CONFIG_MONO_ONLY},
- {TK_CONFIG_INT, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
- DEF_LISTBOX_SELECT_BD, Tk_Offset(Listbox, selBorderWidth), 0},
- {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
- DEF_LISTBOX_SELECT_FG_COLOR, Tk_Offset(Listbox, selFgColorPtr),
- TK_CONFIG_COLOR_ONLY},
- {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
- DEF_LISTBOX_SELECT_FG_MONO, Tk_Offset(Listbox, selFgColorPtr),
- TK_CONFIG_MONO_ONLY},
- {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
- (char *) NULL, 0, 0}
- };
-
- /*
- * Flags for GetListboxIndex procedure:
- */
-
- #define ZERO_OK 1
- #define LAST_PLUS_ONE_OK 2
-
- /*
- * Forward declarations for procedures defined later in this file:
- */
-
- static void ChangeListboxView _ANSI_ARGS_((Listbox *listPtr,
- int index, int lineNum));
- static int ConfigureListbox _ANSI_ARGS_((Tcl_Interp *interp,
- Listbox *listPtr, int argc, char **argv,
- int flags));
- static void DeleteEls _ANSI_ARGS_((Listbox *listPtr, int index,
- int count));
- static void DestroyListbox _ANSI_ARGS_((ClientData clientData));
- static void DisplayListbox _ANSI_ARGS_((ClientData clientData));
- static int GetListboxIndex _ANSI_ARGS_((Tcl_Interp *interp,
- Listbox *listPtr, char *string, int flags,
- int *indexPtr));
- static void InsertEls _ANSI_ARGS_((Listbox *listPtr, int index,
- int argc, char **argv));
- static void ListboxEventProc _ANSI_ARGS_((ClientData clientData,
- XEvent *eventPtr));
- static int ListboxFetchSelection _ANSI_ARGS_((
- ClientData clientData, int offset, char *buffer,
- int maxBytes));
- static void ListboxLostSelection _ANSI_ARGS_((ClientData clientData));
- static void ListboxMouseProc _ANSI_ARGS_((ClientData clientData,
- XEvent *eventPtr));
- static void ListboxRedrawRange _ANSI_ARGS_((Listbox *listPtr,
- int first, int last));
- static void ListboxScanTo _ANSI_ARGS_((Listbox *listPtr, int y));
- static void ListboxSelectFrom _ANSI_ARGS_((Listbox *listPtr,
- int index));
- static void ListboxSelectTo _ANSI_ARGS_((Listbox *listPtr,
- int index));
- static void ListboxUpdateScrollbar _ANSI_ARGS_((Listbox *listPtr));
- static int ListboxWidgetCmd _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, int argc, char **argv));
- static int NearestListboxElement _ANSI_ARGS_((Listbox *listPtr,
- int x, int y));
-
- /*
- *--------------------------------------------------------------
- *
- * Tk_ListboxCmd --
- *
- * This procedure is invoked to process the "listbox" Tcl
- * command. See the user documentation for details on what
- * it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *--------------------------------------------------------------
- */
-
- int
- Pt_ColboxCmd(clientData, interp, argc, argv)
- ClientData clientData; /* Main window associated with
- * interpreter. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- register Listbox *listPtr;
- Tk_Window new;
- Tk_Window tkwin = (Tk_Window) clientData;
-
- if (argc < 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " pathName ?options?\"", (char *) NULL);
- return TCL_ERROR;
- }
-
- new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
- if (new == NULL) {
- return TCL_ERROR;
- }
-
- /*
- * Initialize the fields of the structure that won't be initialized
- * by ConfigureListbox, or that ConfigureListbox requires to be
- * initialized already (e.g. resource pointers).
- */
-
- listPtr = (Listbox *) ckalloc(sizeof(Listbox));
- listPtr->tkwin = new;
- listPtr->interp = interp;
- listPtr->numElements = 0;
- listPtr->numInColumn = 0;
- listPtr->elementPtr = NULL;
- listPtr->normalBorder = NULL;
- listPtr->fontPtr = NULL;
- listPtr->fgColorPtr = NULL;
- listPtr->textGC = None;
- listPtr->selBorder = NULL;
- listPtr->selFgColorPtr = NULL;
- listPtr->selTextGC = NULL;
- listPtr->geometry = NULL;
- listPtr->topIndex = 0;
- listPtr->redrawFirst = -1;
- listPtr->scrollLines = 0;
- listPtr->selectFirst = -1;
- listPtr->selectLast = -2;
- listPtr->scrollCmd = NULL;
- listPtr->flags = 0;
-
- Tk_SetClass(listPtr->tkwin, "Colbox");
- Tk_CreateEventHandler(listPtr->tkwin, ExposureMask|StructureNotifyMask,
- ListboxEventProc, (ClientData) listPtr);
- Tk_CreateEventHandler(listPtr->tkwin, ButtonPressMask|ButtonMotionMask,
- ListboxMouseProc, (ClientData) listPtr);
- Tk_CreateSelHandler(listPtr->tkwin, XA_STRING, ListboxFetchSelection,
- (ClientData) listPtr, XA_STRING);
- Tcl_CreateCommand(interp, Tk_PathName(listPtr->tkwin), ListboxWidgetCmd,
- (ClientData) listPtr, (void (*)()) NULL);
- if (ConfigureListbox(interp, listPtr, argc-2, argv+2, 0) != TCL_OK) {
- goto error;
- }
-
- interp->result = Tk_PathName(listPtr->tkwin);
- return TCL_OK;
-
- error:
- Tk_DestroyWindow(listPtr->tkwin);
- return TCL_ERROR;
- }
-
- /*
- *--------------------------------------------------------------
- *
- * ListboxWidgetCmd --
- *
- * This procedure is invoked to process the Tcl command
- * that corresponds to a widget managed by this module.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *--------------------------------------------------------------
- */
-
- static int
- ListboxWidgetCmd(clientData, interp, argc, argv)
- ClientData clientData; /* Information about listbox widget. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- register Listbox *listPtr = (Listbox *) clientData;
- int result = TCL_OK;
- int length;
- char c;
-
- if (argc < 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " option ?arg arg ...?\"", (char *) NULL);
- return TCL_ERROR;
- }
- Tk_Preserve((ClientData) listPtr);
- c = argv[1][0];
- length = strlen(argv[1]);
- if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
- if (argc == 2) {
- result = Tk_ConfigureInfo(interp, listPtr->tkwin, configSpecs,
- (char *) listPtr, (char *) NULL, 0);
- } else if (argc == 3) {
- result = Tk_ConfigureInfo(interp, listPtr->tkwin, configSpecs,
- (char *) listPtr, argv[2], 0);
- } else {
- result = ConfigureListbox(interp, listPtr, argc-2, argv+2,
- TK_CONFIG_ARGV_ONLY);
- }
- } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
- int first, last;
-
- if ((argc < 3) || (argc > 4)) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " delete firstIndex ?lastIndex?\"",
- (char *) NULL);
- goto error;
- }
- if (GetListboxIndex(interp, listPtr, argv[2], 0, &first) != TCL_OK) {
- return TCL_ERROR;
- }
- if (argc == 3) {
- last = first;
- } else {
- if (GetListboxIndex(interp, listPtr, argv[3], 0,
- &last) != TCL_OK) {
- goto error;
- }
- if (last < first) {
- Tcl_AppendResult(interp, "bad listbox index \"", argv[3],
- "\"", (char *) NULL);
- goto error;
- }
- }
- DeleteEls(listPtr, first, last+1-first);
- } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
- int index;
- register Element *elPtr;
-
- if (argc != 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " get index\"", (char *) NULL);
- goto error;
- }
- if (GetListboxIndex(interp, listPtr, argv[2], 0, &index) != TCL_OK) {
- goto error;
- }
- for (elPtr = listPtr->elementPtr; index > 0;
- index--, elPtr = elPtr->nextPtr)
- /*EMPTY*/
- ;
- interp->result = elPtr->text;
- } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)) {
- int index;
-
- if (argc < 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " insert index ?element ?element ...?\"",
- (char *) NULL);
- goto error;
- }
- if (argc > 3) {
- if (GetListboxIndex(interp, listPtr, argv[2],
- ZERO_OK|LAST_PLUS_ONE_OK, &index) != TCL_OK) {
- goto error;
- }
- InsertEls(listPtr, index, argc-3, argv+3);
- }
- } else if ((c == 'n') && (strncmp(argv[1], "nearest", length) == 0)) {
- int index, x, y;
-
- if (argc != 4) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " nearest x y\"", (char *) NULL);
- goto error;
- }
- if (Tcl_GetInt(interp, argv[2], &x) != TCL_OK) {
- goto error;
- }
- if (Tcl_GetInt(interp, argv[3], &y) != TCL_OK) {
- goto error;
- }
- index = NearestListboxElement(listPtr, x, y);
- sprintf(interp->result, "%d", index);
- } else if ((c == 's') && (length >= 2)
- && (strncmp(argv[1], "scan", length) == 0)) {
- int y;
-
- if (argc != 4) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " scan mark|dragto y\"", (char *) NULL);
- goto error;
- }
- if (Tcl_GetInt(interp, argv[3], &y) != TCL_OK) {
- goto error;
- }
- if ((argv[2][0] == 'm')
- && (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
- listPtr->scanMarkY = y;
- listPtr->scanMarkIndex = listPtr->topIndex;
- } else if ((argv[2][0] == 'd')
- && (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
- ListboxScanTo(listPtr, y);
- } else {
- Tcl_AppendResult(interp, "bad scan option \"", argv[2],
- "\": must be mark or dragto", (char *) NULL);
- goto error;
- }
- } else if ((c == 's') && (length >= 4)
- && (strncmp(argv[1], "sellimits", length) == 0)) {
- sprintf(interp->result, "%d %d", listPtr->selectFirst,
- listPtr->selectLast);
- } else if ((c == 's') && (length >= 4)
- && (strncmp(argv[1], "select", length) == 0)) {
- int index;
-
- if (argc != 4) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " select option index\"", (char *) NULL);
- goto error;
- }
- if (GetListboxIndex(interp, listPtr, argv[3], 0, &index) != TCL_OK) {
- goto error;
- }
- length = strlen(argv[2]);
- c = argv[2][0];
- if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
- if (index < (listPtr->selectFirst + listPtr->selectLast)/2) {
- listPtr->selectAnchor = listPtr->selectLast;
- } else {
- listPtr->selectAnchor = listPtr->selectFirst;
- }
- ListboxSelectTo(listPtr, index);
- } else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
- ListboxSelectFrom(listPtr, index);
- } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
- ListboxSelectTo(listPtr, index);
- } else {
- Tcl_AppendResult(interp, "bad select option \"", argv[2],
- "\": must be adjust, from, or to", (char *) NULL);
- goto error;
- }
- } else if ((c == 's') && (length >= 2)
- && (strncmp(argv[1], "size", length) == 0)) {
- sprintf(interp->result, "%d", listPtr->numElements);
- } else if ((c == 'v') && (strncmp(argv[1], "view", length) == 0)) {
- int index, lineNum;
-
- if ((argc != 3) && (argc != 4)) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " view index ?lineNum?\"", (char *) NULL);
- goto error;
- }
- if (GetListboxIndex(interp, listPtr, argv[2], ZERO_OK, &index)
- != TCL_OK) {
- goto error;
- }
- if (argc == 3) {
- lineNum = 0;
- } else {
- if (Tcl_GetInt(interp, argv[3], &lineNum) != TCL_OK) {
- goto error;
- }
- }
- ChangeListboxView(listPtr, index, lineNum);
- } else {
- Tcl_AppendResult(interp, "bad option \"", argv[1],
- "\": must be configure, delete, get, insert, ",
- "nearest, scan, select, size, or view", (char *) NULL);
- goto error;
- }
- Tk_Release((ClientData) listPtr);
- return result;
-
- error:
- Tk_Release((ClientData) listPtr);
- return TCL_ERROR;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * DestroyListbox --
- *
- * This procedure is invoked by Tk_EventuallyFree or Tk_Release
- * to clean up the internal structure of a listbox at a safe time
- * (when no-one is using it anymore).
- *
- * Results:
- * None.
- *
- * Side effects:
- * Everything associated with the listbox is freed up.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- DestroyListbox(clientData)
- ClientData clientData; /* Info about listbox widget. */
- {
- register Listbox *listPtr = (Listbox *) clientData;
- register Element *elPtr, *nextPtr;
-
- for (elPtr = listPtr->elementPtr; elPtr != NULL; ) {
- nextPtr = elPtr->nextPtr;
- ckfree((char *) elPtr);
- elPtr = nextPtr;
- }
- if (listPtr->normalBorder != NULL) {
- Tk_Free3DBorder(listPtr->normalBorder);
- }
- if (listPtr->fontPtr != NULL) {
- Tk_FreeFontStruct(listPtr->fontPtr);
- }
- if (listPtr->fgColorPtr != NULL) {
- Tk_FreeColor(listPtr->fgColorPtr);
- }
- if (listPtr->textGC != None) {
- Tk_FreeGC(listPtr->textGC);
- }
- if (listPtr->selBorder != NULL) {
- Tk_Free3DBorder(listPtr->selBorder);
- }
- if (listPtr->selFgColorPtr != NULL) {
- Tk_FreeColor(listPtr->selFgColorPtr);
- }
- if (listPtr->selTextGC != None) {
- Tk_FreeGC(listPtr->selTextGC);
- }
- if (listPtr->geometry != NULL) {
- ckfree(listPtr->geometry);
- }
- if (listPtr->scrollCmd != NULL) {
- ckfree(listPtr->scrollCmd);
- }
- ckfree((char *) listPtr);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * ConfigureListbox --
- *
- * This procedure is called to process an argv/argc list, plus
- * the Tk option database, in order to configure (or reconfigure)
- * a listbox widget.
- *
- * Results:
- * The return value is a standard Tcl result. If TCL_ERROR is
- * returned, then interp->result contains an error message.
- *
- * Side effects:
- * Configuration information, such as colors, border width,
- * etc. get set for listPtr; old resources get freed,
- * if there were any.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- ConfigureListbox(interp, listPtr, argc, argv, flags)
- Tcl_Interp *interp; /* Used for error reporting. */
- register Listbox *listPtr; /* Information about widget; may or may
- * not already have values for some fields. */
- int argc; /* Number of valid entries in argv. */
- char **argv; /* Arguments. */
- int flags; /* Flags to pass to Tk_ConfigureWidget. */
- {
- XGCValues gcValues;
- GC new;
- int width, charWidth, height, fontHeight;
-
- if (Tk_ConfigureWidget(interp, listPtr->tkwin, configSpecs,
- argc, argv, (char *) listPtr, flags) != TCL_OK) {
- return TCL_ERROR;
- }
-
- /*
- * A few options need special processing, such as parsing the
- * geometry and setting the background from a 3-D border.
- */
-
- Tk_SetBackgroundFromBorder(listPtr->tkwin, listPtr->normalBorder);
-
-
- if ( listPtr->columns < 1 ) {
- Tcl_AppendResult(interp, "Number of columns must be >= 1",
- (char *) NULL);
- return TCL_ERROR;
- }
- if ( listPtr->columns > MAX_COLS ) {
- Tcl_AppendResult(interp, "Number of columns must be <= 50",
- (char *) NULL);
- return TCL_ERROR;
- }
- listPtr->numInColumn = ((listPtr->numElements) + (listPtr->columns) - 1)
- / listPtr->columns;
-
- gcValues.foreground = listPtr->fgColorPtr->pixel;
- gcValues.font = listPtr->fontPtr->fid;
- new = Tk_GetGC(listPtr->tkwin, GCForeground|GCFont, &gcValues);
- if (listPtr->textGC != None) {
- Tk_FreeGC(listPtr->textGC);
- }
- listPtr->textGC = new;
-
- gcValues.foreground = listPtr->selFgColorPtr->pixel;
- gcValues.font = listPtr->fontPtr->fid;
- new = Tk_GetGC(listPtr->tkwin, GCForeground|GCFont, &gcValues);
- if (listPtr->selTextGC != None) {
- Tk_FreeGC(listPtr->selTextGC);
- }
- listPtr->selTextGC = new;
-
- /*
- * Register the desired geometry for the window, and arrange for
- * the window to be redisplayed.
- */
-
- if ((sscanf(listPtr->geometry, "%dx%d", &width, &height) != 2)
- || (width <= 0) || (height <= 0)) {
- Tcl_AppendResult(interp, "bad geometry \"",
- listPtr->geometry, "\"", (char *) NULL);
- return TCL_ERROR;
- }
- fontHeight = listPtr->fontPtr->ascent + listPtr->fontPtr->descent;
- listPtr->lineHeight = fontHeight + 1 + 2*listPtr->selBorderWidth;
- listPtr->numLines = (Tk_Height(listPtr->tkwin) - 2*listPtr->borderWidth)
- / listPtr->lineHeight;
- if (listPtr->numLines < 0) {
- listPtr->numLines = 0;
- }
- charWidth = XTextWidth(listPtr->fontPtr, "0m", 2)/2;
- width = width*charWidth + (15*fontHeight)/10 + 2*listPtr->borderWidth;
- height = height*listPtr->lineHeight + 2*listPtr->borderWidth;
- Tk_GeometryRequest(listPtr->tkwin, width, height);
- Tk_SetInternalBorder(listPtr->tkwin, listPtr->borderWidth);
- ListboxRedrawRange(listPtr, 0, listPtr->numInColumn-1);
- ListboxUpdateScrollbar(listPtr);
- return TCL_OK;
- }
-
- /*
- *--------------------------------------------------------------
- *
- * DisplayListbox --
- *
- * This procedure redraws the contents of a listbox window.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Information appears on the screen.
- *
- *--------------------------------------------------------------
- */
-
- static void
- DisplayListbox(clientData)
- ClientData clientData; /* Information about window. */
- {
- register Listbox *listPtr = (Listbox *) clientData;
- register Tk_Window tkwin = listPtr->tkwin;
- GC gc;
- int limit, x, y, margin, height, distance;
- int colWidth, nInCol;
- int xinc, yinc, i, j, col, cols;
- Element *elPtr0, *elPtr[MAX_COLS];
- int counter[MAX_COLS];
- Element nullElement;
-
- if ((listPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)
- || ((listPtr->redrawFirst == -1) && (listPtr->scrollLines == 0))) {
- goto done;
- }
-
- /*
- * If we're going to redraw the whole box, then clear it first. This
- * is needed to erase the border, in case its style has changed.
- */
-
- if ((listPtr->redrawFirst == 0)
- && (listPtr->redrawLast == (listPtr->numInColumn-1))) {
- XClearWindow(Tk_Display(tkwin), Tk_WindowId(tkwin));
- listPtr->scrollLines = 0;
- }
-
- /*
- * In order to provide the smoothest possible scrolling and
- * scanning, we use CopyArea operations to avoid redrawing big
- * chunks of the listbox. First, see if the window has moved
- * so much that none of its current bits are of any use.
- */
-
- if ((listPtr->scrollLines >= listPtr->numLines)
- || (listPtr->scrollLines <= -listPtr->numLines)) {
- ListboxRedrawRange(listPtr, 0, listPtr->numInColumn-1);
- } else if (listPtr->scrollLines != 0) {
-
- /*
- * Copying bits will help. Figure out how much of the window's
- * new contents can simply be copied into place, then copy them.
- * Also figure which areas of the window must be explicitly
- * redrawn, and arrange for them to be redrawn.
- */
-
- distance = listPtr->scrollLines * listPtr->lineHeight;
- if (listPtr->scrollLines > 0) {
- y = listPtr->borderWidth;
- height = Tk_Height(tkwin) - 2*listPtr->borderWidth - distance;
- ListboxRedrawRange(listPtr, listPtr->topIndex,
- listPtr->topIndex + listPtr->scrollLines - 1);
- } else {
- height = Tk_Height(tkwin) - 2*listPtr->borderWidth + distance;
- y = listPtr->borderWidth - distance;
- ListboxRedrawRange(listPtr, listPtr->topIndex
- + listPtr->numLines + listPtr->scrollLines,
- listPtr->topIndex + listPtr->numLines - 1);
- }
- XCopyArea(Tk_Display(tkwin), Tk_WindowId(tkwin), Tk_WindowId(tkwin),
- listPtr->textGC, listPtr->borderWidth, y,
- Tk_Width(tkwin) - 2*listPtr->borderWidth, height,
- listPtr->borderWidth, y + distance);
-
- /*
- * Some of the bits to be copied may have been obscured by other
- * windows. Read back the exposure events generated by the
- * copy operation and schedule updates for the obscured areas.
- */
-
- while (1) {
- XEvent event;
-
- XWindowEvent(Tk_Display(tkwin), Tk_WindowId(tkwin), ExposureMask,
- &event);
- if (event.type == NoExpose) {
- break;
- } else if (event.type == GraphicsExpose) {
- ListboxRedrawRange(listPtr,
- NearestListboxElement(listPtr, 0,
- event.xgraphicsexpose.y),
- NearestListboxElement(listPtr, 0,
- event.xgraphicsexpose.y
- + event.xgraphicsexpose.height));
- if (event.xgraphicsexpose.count ==0) {
- break;
- }
- } else if (event.type == Expose) {
-
- /* Expose events here are trouble, because there's no way
- * to tell whether the exposure referred to the pre-copy
- * window or the post-copy window. This is a rare occurrence,
- * so take the easy way out: redraw everything.
- */
-
- ListboxRedrawRange(listPtr, 0, listPtr->numInColumn-1);
- }
- }
- }
-
- /*
- * Iterate through all of the elements of the listbox, displaying all
- * those that need redisplay. Selected elements use a different
- * GC and are raised.
- */
-
- limit = listPtr->topIndex + listPtr->numLines - 1;
- if (limit > listPtr->redrawLast) {
- limit = listPtr->redrawLast;
- }
- if (listPtr->redrawFirst < listPtr->topIndex) {
- listPtr->redrawFirst = listPtr->topIndex;
- }
- margin = listPtr->borderWidth
- + (7*(listPtr->fontPtr->ascent + listPtr->fontPtr->descent))/10;
- colWidth = (Tk_Width(tkwin) - 2*(listPtr->borderWidth)) / listPtr->columns;
- nInCol = listPtr->numInColumn;
- cols = listPtr->columns;
- /* null element for when the last column(s) end early */
- nullElement.textLength = 0;
- nullElement.lBearing = 0;
- nullElement.nextPtr = NULL;
- nullElement.text[0] = '\0';
-
- /* scan down to the first line to redisplay */
- for( i = 0, elPtr0 = listPtr->elementPtr;
- (i < listPtr->redrawFirst) && (elPtr0 != NULL) && (i < limit);
- elPtr0 = elPtr0->nextPtr, ++i )
- /*EMPTY*/
- ;
- counter[0] = i;
- elPtr[0] = elPtr0;
- if( elPtr0 == NULL )
- elPtr[0] = &nullElement;
- /* set up an elPtr and counter for each column */
- for( i = 1; i < cols; ++i ) {
- elPtr[i] = elPtr[i-1];
- for( j = 0; j < nInCol; ++j ) {
- elPtr[i] = elPtr[i]->nextPtr;
- /* this happens when the last column ends early */
- if( elPtr[i] == NULL ) {
- elPtr[i] = &nullElement;
- break;
- }
- }
- counter[i] = counter[i-1] + nInCol;
- if( counter[i] >= listPtr->numElements )
- counter[i] = listPtr->numElements - 1;
- }
- x = listPtr->borderWidth;
-
- /* loop through each row in the display */
- while( (elPtr[0] != NULL) && (counter[0] <= limit) ) {
- y = ((counter[0] - listPtr->topIndex) * listPtr->lineHeight)
- + listPtr->borderWidth;
- /* loop through each column in the row */
- for( col = 0; col < cols; ++col ) {
- /* is this a selected element? */
- if ((listPtr->selectFirst >= 0)
- && (counter[col] >= listPtr->selectFirst)
- && (counter[col] <= listPtr->selectLast)
- && (elPtr[col] != NULL) && (elPtr[col] != &nullElement) ) {
- /* if so draw the background in the selected color */
- gc = listPtr->selTextGC; /* remember which gc to use for text */
- Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
- listPtr->selBorder, x + col*colWidth, y,
- colWidth,
- listPtr->lineHeight, listPtr->selBorderWidth,
- TK_RELIEF_RAISED);
- yinc = listPtr->selBorderWidth;
- xinc = -1;
- } else {
- /* otherwise draw the background in the normal color */
- gc = listPtr->textGC; /* remember which gc to use for text */
- Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
- listPtr->normalBorder, x + col*colWidth, y,
- colWidth,
- listPtr->lineHeight, 0, TK_RELIEF_FLAT);
- yinc = listPtr->selBorderWidth + 1;
- xinc = 0;
- }
- /* the last column may end early so check for NULL */
- if( elPtr[col] != NULL ) {
- yinc += listPtr->fontPtr->ascent;
- xinc += margin - elPtr[col]->lBearing;
- XDrawString(Tk_Display(tkwin), Tk_WindowId(tkwin), gc,
- x + xinc + col*colWidth, y+yinc,
- elPtr[col]->text, elPtr[col]->textLength);
- elPtr[col] = elPtr[col]->nextPtr;
- }
- ++counter[col];
- }
- }
-
- /*
- * Clear empty space past end of list, if there is any, then redraw
- * the border around the list, if there is one.
- */
-
- if ((listPtr->topIndex + listPtr->numLines) > listPtr->numInColumn) {
- y = ((listPtr->numInColumn - listPtr->topIndex) * listPtr->lineHeight)
- + listPtr->borderWidth;
- } else {
- y = (listPtr->numLines * listPtr->lineHeight) + listPtr->borderWidth;
- }
- height = Tk_Height(tkwin) - y - listPtr->borderWidth;
- if (height > 0) {
- Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
- listPtr->normalBorder, listPtr->borderWidth, y,
- Tk_Width(tkwin) - 2*listPtr->borderWidth, height, 0,
- TK_RELIEF_FLAT);
- }
- if (listPtr->relief != TK_RELIEF_FLAT) {
- Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
- listPtr->normalBorder, 0, 0, Tk_Width(tkwin),
- Tk_Height(tkwin), listPtr->borderWidth,
- listPtr->relief);
- } else if (listPtr->borderWidth > 0) {
- /*
- * Non-zero border width but flat: must clear out the border
- * area on the right, since text could have overflowed into it.
- */
- XClearArea(Tk_Display(tkwin), Tk_WindowId(tkwin),
- Tk_Width(tkwin)-listPtr->borderWidth, 0,
- listPtr->borderWidth, Tk_Height(tkwin), False);
- }
-
- done:
- listPtr->redrawFirst = -1;
- listPtr->scrollLines = 0;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * InsertEls --
- *
- * Add new elements to a listbox widget.
- *
- * Results:
- * None.
- *
- * Side effects:
- * New information gets added to listPtr; it will be redisplayed
- * soon, but not immediately.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- InsertEls(listPtr, index, argc, argv)
- register Listbox *listPtr; /* Listbox that is to get the new
- * elements. */
- int index; /* Add the new elements before this
- * element. */
- int argc; /* Number of new elements to add. */
- char **argv; /* New elements (one per entry). */
- {
- register Element *prevPtr, *newPtr;
- int length, dummy, i;
- XCharStruct bbox;
-
- /*
- * Find the element before which the new ones will be inserted.
- */
-
- if (index == 0) {
- prevPtr = NULL;
- } else {
- for (prevPtr = listPtr->elementPtr, index--; index > 0; index--) {
- prevPtr = prevPtr->nextPtr;
- }
- }
-
- /*
- * For each new element, create a record, initialize it, and link
- * it into the list of elements.
- */
-
- for (i = argc ; i > 0; i--, argv++, prevPtr = newPtr) {
- length = strlen(*argv);
- newPtr = (Element *) ckalloc(ElementSize(length));
- newPtr->textLength = length;
- XTextExtents(listPtr->fontPtr, *argv, 1, &dummy, &dummy, &dummy,
- &bbox);
- newPtr->lBearing = bbox.lbearing;
- if (prevPtr == NULL) {
- newPtr->nextPtr = listPtr->elementPtr;
- listPtr->elementPtr = newPtr;
- } else {
- newPtr->nextPtr = prevPtr->nextPtr;
- prevPtr->nextPtr = newPtr;
- }
- strcpy(newPtr->text, *argv);
- }
- listPtr->numElements += argc;
- listPtr->numInColumn = ((listPtr->numElements) + (listPtr->columns) - 1)
- / (listPtr->columns);
-
- /*
- * Update the selection to account for the renumbering that has just
- * occurred. Then arrange for the new information to be displayed.
- */
-
- if (index <= listPtr->selectFirst) {
- listPtr->selectFirst += argc;
- }
- if (index <= listPtr->selectLast) {
- listPtr->selectLast += argc;
- }
- ListboxRedrawRange(listPtr, index, listPtr->numInColumn-1);
- ListboxUpdateScrollbar(listPtr);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * DeleteEls --
- *
- * Remove one or more elements from a listbox widget.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Memory gets freed, the listbox gets modified and (eventually)
- * redisplayed.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- DeleteEls(listPtr, index, count)
- register Listbox *listPtr; /* Listbox widget to modify. */
- int index; /* Index of first element to delete. */
- int count; /* How many elements to delete. */
- {
- register Element *prevPtr, *elPtr;
- int i;
-
- /*
- * Find the element just before the ones to delete.
- */
-
- if (index == 0) {
- prevPtr = NULL;
- } else {
- for (i = index-1, prevPtr = listPtr->elementPtr; i > 0; i--) {
- prevPtr = prevPtr->nextPtr;
- }
- }
-
- /*
- * Delete the requested number of elements.
- */
-
- for (i = count; i > 0; i--) {
- if (prevPtr == NULL) {
- elPtr = listPtr->elementPtr;
- listPtr->elementPtr = elPtr->nextPtr;
- } else {
- elPtr = prevPtr->nextPtr;
- prevPtr->nextPtr = elPtr->nextPtr;
- }
- ckfree((char *) elPtr);
- }
- listPtr->numElements -= count;
- listPtr->numInColumn = ((listPtr->numElements) + (listPtr->columns) - 1)
- / (listPtr->columns);
-
- /*
- * Update the selection and viewing information to reflect the change
- * in the element numbering, and redisplay to slide information up over
- * the elements that were deleted.
- */
-
- if (index <= listPtr->selectFirst) {
- listPtr->selectFirst -= count;
- if (listPtr->selectFirst < index) {
- listPtr->selectFirst = index;
- }
- }
- if (index <= listPtr->selectLast) {
- listPtr->selectLast -= count;
- if (listPtr->selectLast < index) {
- listPtr->selectLast = index-1;
- }
- }
- if (listPtr->selectLast < listPtr->selectFirst) {
- listPtr->selectFirst = -1;
- }
- if (index <= listPtr->topIndex) {
- listPtr->topIndex -= count;
- if (listPtr->topIndex < index) {
- listPtr->topIndex = index;
- }
- }
- ListboxRedrawRange(listPtr, index, listPtr->numInColumn-1);
- ListboxUpdateScrollbar(listPtr);
- }
-
- /*
- *--------------------------------------------------------------
- *
- * ListboxEventProc --
- *
- * This procedure is invoked by the Tk dispatcher for various
- * events on listboxes.
- *
- * Results:
- * None.
- *
- * Side effects:
- * When the window gets deleted, internal structures get
- * cleaned up. When it gets exposed, it is redisplayed.
- *
- *--------------------------------------------------------------
- */
-
- static void
- ListboxEventProc(clientData, eventPtr)
- ClientData clientData; /* Information about window. */
- XEvent *eventPtr; /* Information about event. */
- {
- Listbox *listPtr = (Listbox *) clientData;
-
- if (eventPtr->type == Expose) {
- ListboxRedrawRange(listPtr,
- NearestListboxElement(listPtr, 0,
- eventPtr->xexpose.y),
- NearestListboxElement(listPtr, 0,
- eventPtr->xexpose.y + eventPtr->xexpose.height) );
- } else if (eventPtr->type == DestroyNotify) {
- Tcl_DeleteCommand(listPtr->interp, Tk_PathName(listPtr->tkwin));
- listPtr->tkwin = NULL;
- if ((listPtr->redrawFirst != -1) || (listPtr->scrollLines != 0)) {
- Tk_CancelIdleCall(DisplayListbox, (ClientData) listPtr);
- }
- Tk_EventuallyFree((ClientData) listPtr, DestroyListbox);
- } else if (eventPtr->type == ConfigureNotify) {
- Tk_Preserve((ClientData) listPtr);
- listPtr->numLines = (Tk_Height(listPtr->tkwin)
- - 2*listPtr->borderWidth) / listPtr->lineHeight;
- ListboxRedrawRange(listPtr, 0, listPtr->numInColumn-1);
- ListboxUpdateScrollbar(listPtr);
- Tk_Release((ClientData) listPtr);
- }
- }
-
- /*
- *--------------------------------------------------------------
- *
- * GetListboxIndex --
- *
- * Parse an index into a listbox and return either its value
- * or an error.
- *
- * Results:
- * A standard Tcl result. If all went well, then *indexPtr is
- * filled in with the index (into listPtr) corresponding to
- * string. Otherwise an error message is left in interp->result.
- *
- * Side effects:
- * None.
- *
- *--------------------------------------------------------------
- */
-
- static int
- GetListboxIndex(interp, listPtr, string, flags, indexPtr)
- Tcl_Interp *interp; /* For error messages. */
- Listbox *listPtr; /* Listbox for which the index is being
- * specified. */
- char *string; /* Numerical index into listPtr's element
- * list, or "end" to refer to last element. */
- int flags; /* OR-ed combination of flag bits: ZERO_OK
- * means accept index 0, even if the list
- * has no entries; LAST_PLUS_ONE_OK means
- * accept an index equal to the number of
- * elements, and treat "end" as this index. */
- int *indexPtr; /* Where to store converted relief. */
- {
- int last;
-
- if (flags & LAST_PLUS_ONE_OK) {
- last = listPtr->numElements;
- } else {
- last = listPtr->numElements-1;
- }
- if (string[0] == 'e') {
- if (strncmp(string, "end", strlen(string)) != 0) {
- *indexPtr = last;
- badIndex:
- Tcl_AppendResult(interp, "bad listbox index \"", string,
- "\"", (char *) NULL);
- return TCL_ERROR;
- }
- *indexPtr = last;
- if (listPtr->numElements <= 0) {
- if (flags & ZERO_OK) {
- *indexPtr = 0;
- } else {
- interp->result = "listbox has no entries";
- return TCL_ERROR;
- }
- }
- } else {
- if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
- Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
- goto badIndex;
- }
- if ((*indexPtr < 0) || (*indexPtr > last)) {
- if ((*indexPtr != 0) || (listPtr->numElements != 0)
- || !(flags & ZERO_OK)) {
- goto badIndex;
- }
- }
- }
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * ChangeListboxView --
- *
- * Change the view on a listbox widget.
- *
- * Results:
- * None.
- *
- * Side effects:
- * What's displayed on the screen is changed. If there is a
- * scrollbar associated with this widget, then the scrollbar
- * is instructed to change its display too.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- ChangeListboxView(listPtr, index, lineNum)
- register Listbox *listPtr; /* Information about widget. */
- int index; /* Index of element in listPtr. */
- int lineNum; /* Index of line on screen: make
- * element "elIndex" appear at this
- * position on screen. */
- {
- int newTop;
-
- if (listPtr->tkwin == NULL) {
- return;
- }
-
- newTop = index - lineNum;
- if (newTop >= listPtr->numInColumn) {
- newTop = listPtr->numInColumn-1;
- }
- if (newTop < 0) {
- newTop = 0;
- }
- if (listPtr->topIndex == newTop) {
- return;
- }
- if ((listPtr->scrollLines == 0) && (listPtr->redrawFirst == -1)) {
- Tk_DoWhenIdle(DisplayListbox, (ClientData) listPtr);
- }
- listPtr->scrollLines += listPtr->topIndex - newTop;
- listPtr->topIndex = newTop;
-
- ListboxUpdateScrollbar(listPtr);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * ListboxScanTo --
- *
- * Given a y-coordinate (presumably of the curent mouse location)
- * drag the view in the window to implement the scan operation.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The view in the window may change.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- ListboxScanTo(listPtr, y)
- register Listbox *listPtr; /* Information about widget. */
- int y; /* Y-coordinate to use for scan
- * operation. */
- {
- int newTopIndex;
-
- /*
- * Compute new top line for screen by amplifying the difference
- * between the current position and the place where the scan
- * started (the "mark" position). If we run off the top or bottom
- * of the list, then reset the mark point so that the current
- * position continues to correspond to the edge of the window.
- * This means that the picture will start dragging as soon as the
- * mouse reverses direction (without this reset, might have to slide
- * mouse a long ways back before the picture starts moving again).
- */
-
- newTopIndex = listPtr->scanMarkIndex
- - (10*(y - listPtr->scanMarkY))/listPtr->lineHeight;
- if (newTopIndex >= listPtr->numInColumn) {
- newTopIndex = listPtr->scanMarkIndex = listPtr->numInColumn-1;
- listPtr->scanMarkY = y;
- }
- if (newTopIndex < 0) {
- newTopIndex = listPtr->scanMarkIndex = 0;
- listPtr->scanMarkY = y;
- }
- ChangeListboxView(listPtr, newTopIndex, 0);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * NearestListboxElement --
- *
- * Given a y-coordinate inside a listbox, compute the index of
- * the element under that y-coordinate (or closest to that
- * y-coordinate).
- *
- * Results:
- * The return value is an index of an element of listPtr. If
- * listPtr has no elements, then 0 is always returned.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- NearestListboxElement(listPtr, x, y)
- register Listbox *listPtr; /* Information about widget. */
- int x; /* X-coordinate in listPtr's window. */
- int y; /* Y-coordinate in listPtr's window. */
- {
- int index;
- int colWidth;
-
- index = (y - listPtr->borderWidth)/listPtr->lineHeight;
- if (index >= listPtr->numLines) {
- index = listPtr->numLines-1;
- }
- if (index < 0) {
- index = 0;
- }
- index += listPtr->topIndex;
- colWidth = (Tk_Width(listPtr->tkwin) - 2*(listPtr->borderWidth))
- / (listPtr->columns);
- index += (x/colWidth) * (listPtr->numInColumn);
- if (index >= listPtr->numElements) {
- index = listPtr->numElements-1;
- }
- return index;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * ListboxSelectFrom --
- *
- * Start a new selection in a listbox.
- *
- * Results:
- * None.
- *
- * Side effects:
- * ListPtr claims the selection, and the selection becomes the
- * single element given by index.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- ListboxSelectFrom(listPtr, index)
- register Listbox *listPtr; /* Information about widget. */
- int index; /* Index of element that is to
- * become the new selection. */
- {
- if (listPtr->selectFirst == -1) {
- Tk_OwnSelection(listPtr->tkwin, ListboxLostSelection,
- (ClientData) listPtr);
- } else {
- ListboxRedrawRange(listPtr, listPtr->selectFirst, listPtr->selectLast);
- }
-
- listPtr->selectFirst = listPtr->selectLast = index;
- listPtr->selectAnchor = index;
- ListboxRedrawRange(listPtr, index, index);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * ListboxSelectTo --
- *
- * Modify the selection by moving its un-anchored end. This could
- * make the selection either larger or smaller.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The selection changes.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- ListboxSelectTo(listPtr, index)
- register Listbox *listPtr; /* Information about widget. */
- int index; /* Index of element that is to
- * become the "other" end of the
- * selection. */
- {
- int newFirst, newLast;
-
- /*
- * We should already own the selection, but grab it if we don't.
- */
-
- if (listPtr->selectFirst == -1) {
- ListboxSelectFrom(listPtr, index);
- }
-
- if (listPtr->selectAnchor < index) {
- newFirst = listPtr->selectAnchor;
- newLast = index;
- } else {
- newFirst = index;
- newLast = listPtr->selectAnchor;
- }
- if ((listPtr->selectFirst == newFirst)
- && (listPtr->selectLast == newLast)) {
- return;
- }
- if (listPtr->selectFirst != newFirst) {
- if (listPtr->selectFirst < newFirst) {
- ListboxRedrawRange(listPtr, listPtr->selectFirst, newFirst-1);
- } else {
- ListboxRedrawRange(listPtr, newFirst, listPtr->selectFirst-1);
- }
- listPtr->selectFirst = newFirst;
- }
- if (listPtr->selectLast != newLast) {
- if (listPtr->selectLast < newLast) {
- ListboxRedrawRange(listPtr, listPtr->selectLast+1, newLast);
- } else {
- ListboxRedrawRange(listPtr, newLast+1, listPtr->selectLast);
- }
- listPtr->selectLast = newLast;
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * ListboxFetchSelection --
- *
- * This procedure is called back by Tk when the selection is
- * requested by someone. It returns part or all of the selection
- * in a buffer provided by the caller.
- *
- * Results:
- * The return value is the number of non-NULL bytes stored
- * at buffer. Buffer is filled (or partially filled) with a
- * NULL-terminated string containing part or all of the selection,
- * as given by offset and maxBytes. The selection is returned
- * as a Tcl list with one list element for each element in the
- * listbox.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- ListboxFetchSelection(clientData, offset, buffer, maxBytes)
- ClientData clientData; /* Information about listbox widget. */
- int offset; /* Offset within selection of first
- * byte to be returned. */
- char *buffer; /* Location in which to place
- * selection. */
- int maxBytes; /* Maximum number of bytes to place
- * at buffer, not including terminating
- * NULL character. */
- {
- register Listbox *listPtr = (Listbox *) clientData;
- register Element *elPtr;
- char **argv, *selection;
- int src, dst, length, count, argc;
-
- if (listPtr->selectFirst == -1) {
- return -1;
- }
-
- /*
- * Use Tcl_Merge to format the listbox elements into a suitable
- * Tcl list.
- */
-
- argc = listPtr->selectLast - listPtr->selectFirst + 1;
- argv = (char **) ckalloc((unsigned) (argc*sizeof(char *)));
- for (src = 0, dst = 0, elPtr = listPtr->elementPtr; ;
- src++, elPtr = elPtr->nextPtr) {
- if (src < listPtr->selectFirst) {
- continue;
- }
- if (src > listPtr->selectLast) {
- break;
- }
- argv[dst] = elPtr->text;
- dst++;
- }
- selection = Tcl_Merge(argc, argv);
-
- /*
- * Copy the requested portion of the selection to the buffer.
- */
-
- length = strlen(selection);
- count = length - offset;
- if (count <= 0) {
- count = 0;
- goto done;
- }
- if (count > maxBytes) {
- count = maxBytes;
- }
- memcpy((VOID *) buffer, (VOID *) selection + offset, count);
- /* WAS: bcopy(selection + offset, buffer, count); */
-
- done:
- buffer[count] = '\0';
- ckfree(selection);
- return count;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * ListboxLostSelection --
- *
- * This procedure is called back by Tk when the selection is
- * grabbed away from a listbox widget.
- *
- * Results:
- * None.
- *
- * Side effects:
- * If the variable 'noxsel' is false then
- * the existing selection is unhighlighted, and the window is
- * marked as not containing a selection.
- * Otherwise there is no effect.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- ListboxLostSelection(clientData)
- ClientData clientData; /* Information about listbox widget. */
- {
- register Listbox *listPtr = (Listbox *) clientData;
-
- if( listPtr->noxsel )
- return;
- ListboxRedrawRange(listPtr, listPtr->selectFirst, listPtr->selectLast);
- listPtr->selectFirst = -1;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * ListboxMouseProc --
- *
- * This procedure responds to mouse actions to implement selection
- * operations and dragging.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The selection may change, and the window's view may change.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- ListboxMouseProc(clientData, eventPtr)
- ClientData clientData; /* Information about window. */
- register XEvent *eventPtr; /* Information about event. */
- {
- Listbox *listPtr = (Listbox *) clientData;
- int index;
-
- Tk_Preserve((ClientData) listPtr);
- if (eventPtr->type == ButtonPress) {
- if (eventPtr->xbutton.button == 1) {
- index = NearestListboxElement(listPtr,
- eventPtr->xbutton.x, eventPtr->xbutton.y);
- if (eventPtr->xbutton.state == 0) {
- ListboxSelectFrom(listPtr, index);
- } else if (eventPtr->xbutton.state == ShiftMask) {
- if (index < (listPtr->selectFirst
- + listPtr->selectLast)/2) {
- listPtr->selectAnchor = listPtr->selectLast;
- } else {
- listPtr->selectAnchor = listPtr->selectFirst;
- }
- ListboxSelectTo(listPtr, index);
- }
- } else if ((eventPtr->xbutton.button == 3)
- || (eventPtr->xbutton.state == 0)) {
- listPtr->scanMarkY = eventPtr->xbutton.y;
- listPtr->scanMarkIndex = listPtr->topIndex;
- }
- } else if (eventPtr->type == MotionNotify) {
- if ((eventPtr->xmotion.state & ~ShiftMask) == Button1Mask) {
- index = NearestListboxElement(listPtr,
- eventPtr->xmotion.x, eventPtr->xmotion.y);
- ListboxSelectTo(listPtr, index);
- } else if (eventPtr->xmotion.state == Button3Mask) {
- ListboxScanTo(listPtr, eventPtr->xmotion.y);
- }
- }
- Tk_Release((ClientData) listPtr);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * ListboxRedrawRange --
- *
- * Ensure that a given range of elements is eventually redrawn on
- * the display (if those elements in fact appear on the display).
- *
- * Results:
- * None.
- *
- * Side effects:
- * Information gets redisplayed.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- ListboxRedrawRange(listPtr, first, last)
- register Listbox *listPtr; /* Information about widget. */
- int first; /* Index of first element in list
- * that needs to be redrawn. */
- int last; /* Index of last element in list
- * that needs to be redrawn. May
- * be less than first;
- * these just bracket a range. */
- {
- int nCol = listPtr->numInColumn;
- int row_first, row_last;
- int col_first, col_last;
-
- if ((listPtr->tkwin == NULL) || !Tk_IsMapped(listPtr->tkwin)) {
- return;
- }
- /* adjust for multiple columns */
- if( nCol > 0 ) {
- row_first = first % nCol;
- col_first = first / nCol;
- row_last = last % nCol;
- col_last = last / nCol;
- if( col_first == col_last ) {
- first = row_first;
- last = row_last;
- } else if( col_first < col_last ) {
- first = 0;
- last = nCol - 1;
- }
- }
-
- if (listPtr->redrawFirst == -1) {
- listPtr->redrawFirst = first;
- listPtr->redrawLast = last;
- if (listPtr->scrollLines == 0) {
- Tk_DoWhenIdle(DisplayListbox, (ClientData) listPtr);
- }
- } else {
- if (first < listPtr->redrawFirst) {
- listPtr->redrawFirst = first;
- }
- if (last > listPtr->redrawLast) {
- listPtr->redrawLast = last;
- }
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * ListboxUpdateScrollbar --
- *
- * This procedure is invoked whenever information has changed in
- * a listbox in a way that would invalidate a scrollbar display.
- * If there is an associated scrollbar, then this command updates
- * it by invoking a Tcl command.
- *
- * Results:
- * None.
- *
- * Side effects:
- * A Tcl command is invoked, and an additional command may be
- * invoked to process errors in the command.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- ListboxUpdateScrollbar(listPtr)
- register Listbox *listPtr; /* Information about widget. */
- {
- char string[60];
- int result, last;
-
- if (listPtr->scrollCmd == NULL) {
- return;
- }
- last = listPtr->topIndex + listPtr->numLines;
- if (last >= listPtr->numInColumn) {
- last = listPtr->numInColumn-1;
- }
- sprintf(string, " %d %d %d %d", listPtr->numInColumn, listPtr->numLines,
- listPtr->topIndex, last);
- result = Tcl_VarEval(listPtr->interp, listPtr->scrollCmd, string,
- (char *) NULL);
- if (result != TCL_OK) {
- TkBindError(listPtr->interp);
- }
- }
-