home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / x / volume17 / tcleditr / part16 < prev    next >
Encoding:
Text File  |  1992-03-17  |  58.1 KB  |  1,856 lines

  1. Newsgroups: comp.sources.x
  2. Path: uunet!think.com!mips!msi!dcmartin
  3. From: crowley@chaco.cs.unm.edu (Charlie Crowley)
  4. Subject: v17i017: point text editor (TCL and TK), Part16/16
  5. Message-ID: <1992Mar18.142035.28043@msi.com>
  6. Originator: dcmartin@fascet
  7. Sender: dcmartin@msi.com (David C. Martin - Moderator)
  8. Organization: Molecular Simulations, Inc.
  9. References: <csx-17i002-tcl-editor@uunet.UU.NET>
  10. Date: Wed, 18 Mar 1992 14:20:35 GMT
  11. Approved: dcmartin@msi.com
  12.  
  13. Submitted-by: crowley@chaco.cs.unm.edu (Charlie Crowley)
  14. Posting-number: Volume 17, Issue 17
  15. Archive-name: tcl-editor/part16
  16.  
  17. #! /bin/sh
  18. # This is a shell archive.  Remove anything before this line, then unpack
  19. # it by saving it into a file and typing "sh file".  To overwrite existing
  20. # files, type "sh file -c".  You can also feed this as standard input via
  21. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  22. # will see the following message at the end:
  23. #        "End of archive 15 (of 15)."
  24. # Contents:  tkColbox.c
  25. # Wrapped by crowley@chaco.cs.unm.edu on Tue Mar 10 15:05:52 1992
  26. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  27. if test -f 'tkColbox.c' -a "${1}" != "-c" ; then 
  28.   echo shar: Will not clobber existing file \"'tkColbox.c'\"
  29. else
  30. echo shar: Extracting \"'tkColbox.c'\" \(55672 characters\)
  31. sed "s/^X//" >'tkColbox.c' <<'END_OF_FILE'
  32. X/* 
  33. X * tkColbox.c --
  34. X *
  35. X *    This module implements colbox widgets for the Tk
  36. X *    toolkit.  A colbox displays a collection of strings,
  37. X *    N per line (where N is the number of columns chosen by
  38. X *      the user), and provides scrolling and selection.
  39. X *      The items in the list are filled by columns, that is, the
  40. X *      first column is filled before the second column, and so on.
  41. X *      Thus an alphabatical list will be alphabetical by columns.
  42. X *      It would be easy to add on option to fill by rows but I have
  43. X *      not done it.
  44. X *
  45. X *    This module was modified to be a `colbox', that is, a listbox
  46. X *      that will display the list in one or more columns.
  47. X *      In addition, a command to return the indexes of the first and
  48. X *      last selected element was added.  this command is called
  49. X *      'sellimits' and causes the abbreviation 'se' for 'select' to
  50. X *      no longer be a valid abbreviation.
  51. X *      Finally I added a resource to prevent losing the X selection
  52. X *      from deselecting the selected elements.  This is to allow several
  53. X *      list boxes to have selections simultaneously.
  54. X *      The modifications were done in July 1991 by
  55. X *           Charles Crowley
  56. X *           Computer Science Department
  57. X *           University of New Mexico
  58. X *           Albuquerque, NM 87131
  59. X *           505-277-3112
  60. X *           crowley@unmvax.cs.unm.edu
  61. X *
  62. X * Copyright 1990 Regents of the University of California.
  63. X * Permission to use, copy, modify, and distribute this
  64. X * software and its documentation for any purpose and without
  65. X * fee is hereby granted, provided that the above copyright
  66. X * notice appear in all copies.  The University of California
  67. X * makes no representations about the suitability of this
  68. X * software for any purpose.  It is provided "as is" without
  69. X * express or implied warranty.
  70. X */
  71. X
  72. X#include <stdio.h>
  73. X#include <stdlib.h>
  74. X#include <string.h>
  75. X#include <X11/Xatom.h>
  76. X#include "tkConfig.h"
  77. X#include "default.h"
  78. X#include "tkInt.h"
  79. X
  80. X/*
  81. X * One record of the following type is kept for each element
  82. X * associated with a listbox widget:
  83. X */
  84. X
  85. typedef struct Element {
  86. X    int textLength;        /* # non-NULL characters in text. */
  87. X    int lBearing;        /* Distance from first character's
  88. X                 * origin to left edge of character. */
  89. X    struct Element *nextPtr;    /* Next in list of all elements of this
  90. X                 * listbox, or NULL for last element. */
  91. X    char text[4];        /* Characters of this element, NULL-
  92. X                 * terminated.  The actual space allocated
  93. X                 * here will be as large as needed (> 4,
  94. X                 * most likely).  Must be the last field
  95. X                 * of the record. */
  96. X} Element;
  97. X
  98. X#define ElementSize(stringLength) \
  99. X    ((unsigned) (sizeof(Element) - 3 + stringLength))
  100. X
  101. X#define MAX_COLS    50    /* seems big enough */
  102. X
  103. X/*
  104. X * A data structure of the following type is kept for each listbox
  105. X * widget managed by this file:
  106. X */
  107. X
  108. typedef struct {
  109. X    Tk_Window tkwin;        /* Window that embodies the listbox.  NULL
  110. X                 * means that the window has been destroyed
  111. X                 * but the data structures haven't yet been
  112. X                 * cleaned up.*/
  113. X    Tcl_Interp *interp;        /* Interpreter associated with listbox. */
  114. X    int numElements;        /* Total number of elements in this listbox. */
  115. X    Element *elementPtr;    /* First in list of elements (NULL if no
  116. X                 * elements. */
  117. X
  118. X    /*
  119. X     * Information used when displaying widget:
  120. X     */
  121. X
  122. X    Tk_3DBorder normalBorder;    /* Used for drawing border around whole
  123. X                 * window, plus used for background. */
  124. X    int borderWidth;        /* Width of 3-D border around window. */
  125. X    int relief;            /* 3-D effect: TK_RELIEF_RAISED, etc. */
  126. X    XFontStruct *fontPtr;    /* Information about text font, or NULL. */
  127. X    XColor *fgColorPtr;        /* Text color in normal mode. */
  128. X    GC textGC;            /* For drawing normal text. */
  129. X    Tk_3DBorder selBorder;    /* Borders and backgrounds for selected
  130. X                 * elements. */
  131. X    int selBorderWidth;        /* Width of border around selection. */
  132. X    XColor *selFgColorPtr;    /* Foreground color for selected elements. */
  133. X    GC selTextGC;        /* For drawing selected text. */
  134. X    char *geometry;        /* Desired geometry for window.  Malloc'ed. */
  135. X    int lineHeight;        /* Number of pixels allocated for each line
  136. X                 * in display. */
  137. X    int topIndex;        /* Index of top-most element visible in
  138. X                 * window (on the left most column). */
  139. X    int numLines;        /* Number of lines that fit
  140. X                 * in window at one time. */
  141. X    int columns;        /* Number of columns to display. Can be at
  142. X                                 * most MAX_COLS. */
  143. X    int noxsel;            /* If true, then do not deselect elements
  144. X                                 * when we lose the X selection. */
  145. X    int numInColumn;        /* Number of elements in one column. */
  146. X    int redrawFirst;        /* Index (in element list, not on display)
  147. X                 * of first element (in the left most column)
  148. X                 * to redisplay.  -1 means
  149. X                 * no redisplay pending. */
  150. X    int redrawLast;        /* Index of last element to redisplay. */
  151. X    int scrollLines;        /* If non-zero, then DisplayListbox is
  152. X                 * expected to shift the whole picture
  153. X                 * down (if > 0) or up (if < 0) this many
  154. X                 * lines, prior to redisplaying the stuff
  155. X                 * given by redrawFirst and redrawLast. */
  156. X
  157. X    /*
  158. X     * Information about what's selected, if any.
  159. X     */
  160. X
  161. X    int selectFirst;        /* Index of first selected element (-1 means
  162. X                 * nothing selected. */
  163. X    int selectLast;        /* Index of last selected element. */
  164. X    int selectAnchor;        /* Fixed end of selection (i.e. element
  165. X                 * at which selection was started.) */
  166. X
  167. X    /*
  168. X     * Information for scanning:
  169. X     */
  170. X
  171. X    int scanMarkY;        /* Y-position at which scan started (e.g.
  172. X                 * button was pressed here). */
  173. X    int scanMarkIndex;        /* Index of line that was at top of window
  174. X                 * when scan started. */
  175. X
  176. X    /*
  177. X     * Miscellaneous information:
  178. X     */
  179. X
  180. X    char *scrollCmd;        /* Command prefix for communicating with
  181. X                 * scrollbar(s).  NULL means no command
  182. X                 * to issue.  Malloc'ed. */
  183. X    int flags;            /* Various flag bits:  see below for
  184. X                 * definitions. */
  185. X} Listbox;
  186. X
  187. X/*
  188. X * Flag bits for buttons:
  189. X *
  190. X * None currently defined.
  191. X */
  192. X
  193. X/*
  194. X * Information used for argv parsing:
  195. X */
  196. X
  197. static Tk_ConfigSpec configSpecs[] = {
  198. X    {TK_CONFIG_BORDER, "-background", "background", "Background",
  199. X    DEF_LISTBOX_BG_COLOR, Tk_Offset(Listbox, normalBorder),
  200. X    TK_CONFIG_COLOR_ONLY},
  201. X    {TK_CONFIG_BORDER, "-background", "background", "Background",
  202. X    DEF_LISTBOX_BG_MONO, Tk_Offset(Listbox, normalBorder),
  203. X    TK_CONFIG_MONO_ONLY},
  204. X    {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  205. X    (char *) NULL, 0, 0},
  206. X    {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  207. X    (char *) NULL, 0, 0},
  208. X    {TK_CONFIG_INT, "-borderwidth", "borderWidth", "BorderWidth",
  209. X    DEF_LISTBOX_BORDER_WIDTH, Tk_Offset(Listbox, borderWidth), 0},
  210. X    {TK_CONFIG_INT, "-columns", "columns", "Columns",
  211. X    "1", Tk_Offset(Listbox, columns), 0},
  212. X    {TK_CONFIG_INT, "-noxsel", "noxsel", "noxsel",
  213. X    "0", Tk_Offset(Listbox, noxsel), 0},
  214. X    {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  215. X    (char *) NULL, 0, 0},
  216. X    {TK_CONFIG_FONT, "-font", "font", "Font",
  217. X    DEF_LISTBOX_FONT, Tk_Offset(Listbox, fontPtr), 0},
  218. X    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  219. X    DEF_LISTBOX_FG, Tk_Offset(Listbox, fgColorPtr), 0},
  220. X    {TK_CONFIG_STRING, "-geometry", "geometry", "Geometry",
  221. X    DEF_LISTBOX_GEOMETRY, Tk_Offset(Listbox, geometry), 0},
  222. X    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  223. X    DEF_LISTBOX_RELIEF, Tk_Offset(Listbox, relief), 0},
  224. X    {TK_CONFIG_STRING, "-scrollcommand", "scrollCommand", "ScrollCommand",
  225. X    DEF_LISTBOX_SCROLL_COMMAND, Tk_Offset(Listbox, scrollCmd), 0},
  226. X    {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
  227. X    DEF_LISTBOX_SELECT_COLOR, Tk_Offset(Listbox, selBorder),
  228. X    TK_CONFIG_COLOR_ONLY},
  229. X    {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
  230. X    DEF_LISTBOX_SELECT_MONO, Tk_Offset(Listbox, selBorder),
  231. X    TK_CONFIG_MONO_ONLY},
  232. X    {TK_CONFIG_INT, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
  233. X    DEF_LISTBOX_SELECT_BD, Tk_Offset(Listbox, selBorderWidth), 0},
  234. X    {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
  235. X    DEF_LISTBOX_SELECT_FG_COLOR, Tk_Offset(Listbox, selFgColorPtr),
  236. X    TK_CONFIG_COLOR_ONLY},
  237. X    {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
  238. X    DEF_LISTBOX_SELECT_FG_MONO, Tk_Offset(Listbox, selFgColorPtr),
  239. X    TK_CONFIG_MONO_ONLY},
  240. X    {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  241. X    (char *) NULL, 0, 0}
  242. X};
  243. X
  244. X/*
  245. X * Flags for GetListboxIndex procedure:
  246. X */
  247. X
  248. X#define ZERO_OK            1
  249. X#define LAST_PLUS_ONE_OK    2
  250. X
  251. X/*
  252. X * Forward declarations for procedures defined later in this file:
  253. X */
  254. X
  255. static void        ChangeListboxView _ANSI_ARGS_((Listbox *listPtr,
  256. X                            int index, int lineNum));
  257. static int        ConfigureListbox _ANSI_ARGS_((Tcl_Interp *interp,
  258. X                Listbox *listPtr, int argc, char **argv,
  259. X                int flags));
  260. static void        DeleteEls _ANSI_ARGS_((Listbox *listPtr, int index,
  261. X                int count));
  262. static void        DestroyListbox _ANSI_ARGS_((ClientData clientData));
  263. static void        DisplayListbox _ANSI_ARGS_((ClientData clientData));
  264. static int        GetListboxIndex _ANSI_ARGS_((Tcl_Interp *interp,
  265. X                Listbox *listPtr, char *string, int flags,
  266. X                int *indexPtr));
  267. static void        InsertEls _ANSI_ARGS_((Listbox *listPtr, int index,
  268. X                int argc, char **argv));
  269. static void        ListboxEventProc _ANSI_ARGS_((ClientData clientData,
  270. X                XEvent *eventPtr));
  271. static int        ListboxFetchSelection _ANSI_ARGS_((
  272. X                            ClientData clientData, int offset, char *buffer,
  273. X                            int maxBytes));
  274. static void        ListboxLostSelection _ANSI_ARGS_((ClientData clientData));
  275. static void        ListboxMouseProc _ANSI_ARGS_((ClientData clientData,
  276. X                XEvent *eventPtr));
  277. static void        ListboxRedrawRange _ANSI_ARGS_((Listbox *listPtr,
  278. X                int first, int last));
  279. static void        ListboxScanTo _ANSI_ARGS_((Listbox *listPtr, int y));
  280. static void        ListboxSelectFrom _ANSI_ARGS_((Listbox *listPtr,
  281. X                int index));
  282. static void        ListboxSelectTo _ANSI_ARGS_((Listbox *listPtr,
  283. X                            int index));
  284. static void        ListboxUpdateScrollbar _ANSI_ARGS_((Listbox *listPtr));
  285. static int        ListboxWidgetCmd _ANSI_ARGS_((ClientData clientData,
  286. X                Tcl_Interp *interp, int argc, char **argv));
  287. static int        NearestListboxElement _ANSI_ARGS_((Listbox *listPtr,
  288. X                int x, int y));
  289. X
  290. X/*
  291. X *--------------------------------------------------------------
  292. X *
  293. X * Tk_ListboxCmd --
  294. X *
  295. X *    This procedure is invoked to process the "listbox" Tcl
  296. X *    command.  See the user documentation for details on what
  297. X *    it does.
  298. X *
  299. X * Results:
  300. X *    A standard Tcl result.
  301. X *
  302. X * Side effects:
  303. X *    See the user documentation.
  304. X *
  305. X *--------------------------------------------------------------
  306. X */
  307. X
  308. int
  309. Pt_ColboxCmd(clientData, interp, argc, argv)
  310. X    ClientData clientData;    /* Main window associated with
  311. X                 * interpreter. */
  312. X    Tcl_Interp *interp;        /* Current interpreter. */
  313. X    int argc;            /* Number of arguments. */
  314. X    char **argv;        /* Argument strings. */
  315. X{
  316. X    register Listbox *listPtr;
  317. X    Tk_Window new;
  318. X    Tk_Window tkwin = (Tk_Window) clientData;
  319. X
  320. X    if (argc < 2) {
  321. X    Tcl_AppendResult(interp, "wrong # args: should be \"",
  322. X        argv[0], " pathName ?options?\"", (char *) NULL);
  323. X    return TCL_ERROR;
  324. X    }
  325. X
  326. X    new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  327. X    if (new == NULL) {
  328. X    return TCL_ERROR;
  329. X    }
  330. X
  331. X    /*
  332. X     * Initialize the fields of the structure that won't be initialized
  333. X     * by ConfigureListbox, or that ConfigureListbox requires to be
  334. X     * initialized already (e.g. resource pointers).
  335. X     */
  336. X
  337. X    listPtr = (Listbox *) ckalloc(sizeof(Listbox));
  338. X    listPtr->tkwin = new;
  339. X    listPtr->interp = interp;
  340. X    listPtr->numElements = 0;
  341. X    listPtr->numInColumn = 0;
  342. X    listPtr->elementPtr = NULL;
  343. X    listPtr->normalBorder = NULL;
  344. X    listPtr->fontPtr = NULL;
  345. X    listPtr->fgColorPtr = NULL;
  346. X    listPtr->textGC = None;
  347. X    listPtr->selBorder = NULL;
  348. X    listPtr->selFgColorPtr = NULL;
  349. X    listPtr->selTextGC = NULL;
  350. X    listPtr->geometry = NULL;
  351. X    listPtr->topIndex = 0;
  352. X    listPtr->redrawFirst = -1;
  353. X    listPtr->scrollLines = 0;
  354. X    listPtr->selectFirst = -1;
  355. X    listPtr->selectLast = -2;
  356. X    listPtr->scrollCmd = NULL;
  357. X    listPtr->flags = 0;
  358. X
  359. X    Tk_SetClass(listPtr->tkwin, "Colbox");
  360. X    Tk_CreateEventHandler(listPtr->tkwin, ExposureMask|StructureNotifyMask,
  361. X        ListboxEventProc, (ClientData) listPtr);
  362. X    Tk_CreateEventHandler(listPtr->tkwin, ButtonPressMask|ButtonMotionMask,
  363. X        ListboxMouseProc, (ClientData) listPtr);
  364. X    Tk_CreateSelHandler(listPtr->tkwin, XA_STRING, ListboxFetchSelection,
  365. X        (ClientData) listPtr, XA_STRING);
  366. X    Tcl_CreateCommand(interp, Tk_PathName(listPtr->tkwin), ListboxWidgetCmd,
  367. X        (ClientData) listPtr, (void (*)()) NULL);
  368. X    if (ConfigureListbox(interp, listPtr, argc-2, argv+2, 0) != TCL_OK) {
  369. X    goto error;
  370. X    }
  371. X
  372. X    interp->result = Tk_PathName(listPtr->tkwin);
  373. X    return TCL_OK;
  374. X
  375. X    error:
  376. X    Tk_DestroyWindow(listPtr->tkwin);
  377. X    return TCL_ERROR;
  378. X}
  379. X
  380. X/*
  381. X *--------------------------------------------------------------
  382. X *
  383. X * ListboxWidgetCmd --
  384. X *
  385. X *    This procedure is invoked to process the Tcl command
  386. X *    that corresponds to a widget managed by this module.
  387. X *    See the user documentation for details on what it does.
  388. X *
  389. X * Results:
  390. X *    A standard Tcl result.
  391. X *
  392. X * Side effects:
  393. X *    See the user documentation.
  394. X *
  395. X *--------------------------------------------------------------
  396. X */
  397. X
  398. static int
  399. ListboxWidgetCmd(clientData, interp, argc, argv)
  400. X    ClientData clientData;        /* Information about listbox widget. */
  401. X    Tcl_Interp *interp;            /* Current interpreter. */
  402. X    int argc;                /* Number of arguments. */
  403. X    char **argv;            /* Argument strings. */
  404. X{
  405. X    register Listbox *listPtr = (Listbox *) clientData;
  406. X    int result = TCL_OK;
  407. X    int length;
  408. X    char c;
  409. X
  410. X    if (argc < 2) {
  411. X    Tcl_AppendResult(interp, "wrong # args: should be \"",
  412. X        argv[0], " option ?arg arg ...?\"", (char *) NULL);
  413. X    return TCL_ERROR;
  414. X    }
  415. X    Tk_Preserve((ClientData) listPtr);
  416. X    c = argv[1][0];
  417. X    length = strlen(argv[1]);
  418. X    if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  419. X    if (argc == 2) {
  420. X        result = Tk_ConfigureInfo(interp, listPtr->tkwin, configSpecs,
  421. X            (char *) listPtr, (char *) NULL, 0);
  422. X    } else if (argc == 3) {
  423. X        result = Tk_ConfigureInfo(interp, listPtr->tkwin, configSpecs,
  424. X            (char *) listPtr, argv[2], 0);
  425. X    } else {
  426. X        result = ConfigureListbox(interp, listPtr, argc-2, argv+2,
  427. X            TK_CONFIG_ARGV_ONLY);
  428. X    }
  429. X    } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
  430. X    int first, last;
  431. X
  432. X    if ((argc < 3) || (argc > 4)) {
  433. X        Tcl_AppendResult(interp, "wrong # args: should be \"",
  434. X            argv[0], " delete firstIndex ?lastIndex?\"",
  435. X            (char *) NULL);
  436. X        goto error;
  437. X    }
  438. X    if (GetListboxIndex(interp, listPtr, argv[2], 0, &first) != TCL_OK) {
  439. X        return TCL_ERROR;
  440. X    }
  441. X    if (argc == 3) {
  442. X        last = first;
  443. X    } else {
  444. X        if (GetListboxIndex(interp, listPtr, argv[3], 0,
  445. X            &last) != TCL_OK) {
  446. X        goto error;
  447. X        }
  448. X        if (last < first) {
  449. X        Tcl_AppendResult(interp, "bad listbox index \"", argv[3],
  450. X            "\"", (char *) NULL);
  451. X        goto error;
  452. X        }
  453. X    }
  454. X    DeleteEls(listPtr, first, last+1-first);
  455. X    } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
  456. X    int index;
  457. X    register Element *elPtr;
  458. X
  459. X    if (argc != 3) {
  460. X        Tcl_AppendResult(interp, "wrong # args: should be \"",
  461. X            argv[0], " get index\"", (char *) NULL);
  462. X        goto error;
  463. X    }
  464. X    if (GetListboxIndex(interp, listPtr, argv[2], 0, &index) != TCL_OK) {
  465. X        goto error;
  466. X    }
  467. X    for (elPtr = listPtr->elementPtr; index > 0;
  468. X        index--, elPtr = elPtr->nextPtr)
  469. X        /*EMPTY*/
  470. X        ;
  471. X    interp->result = elPtr->text;
  472. X    } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)) {
  473. X    int index;
  474. X
  475. X    if (argc < 3) {
  476. X        Tcl_AppendResult(interp, "wrong # args: should be \"",
  477. X            argv[0], " insert index ?element ?element ...?\"",
  478. X            (char *) NULL);
  479. X        goto error;
  480. X    }
  481. X    if (argc > 3) {
  482. X        if (GetListboxIndex(interp, listPtr, argv[2],
  483. X            ZERO_OK|LAST_PLUS_ONE_OK, &index) != TCL_OK) {
  484. X            goto error;
  485. X        }
  486. X        InsertEls(listPtr, index, argc-3, argv+3);
  487. X    }
  488. X    } else if ((c == 'n') && (strncmp(argv[1], "nearest", length) == 0)) {
  489. X    int index, x, y;
  490. X
  491. X    if (argc != 4) {
  492. X        Tcl_AppendResult(interp, "wrong # args: should be \"",
  493. X            argv[0], " nearest x y\"", (char *) NULL);
  494. X        goto error;
  495. X    }
  496. X    if (Tcl_GetInt(interp, argv[2], &x) != TCL_OK) {
  497. X        goto error;
  498. X    }
  499. X    if (Tcl_GetInt(interp, argv[3], &y) != TCL_OK) {
  500. X        goto error;
  501. X    }
  502. X    index = NearestListboxElement(listPtr, x, y);
  503. X    sprintf(interp->result, "%d", index);
  504. X    } else if ((c == 's') && (length >= 2)
  505. X        && (strncmp(argv[1], "scan", length) == 0)) {
  506. X    int y;
  507. X
  508. X    if (argc != 4) {
  509. X        Tcl_AppendResult(interp, "wrong # args: should be \"",
  510. X            argv[0], " scan mark|dragto y\"", (char *) NULL);
  511. X        goto error;
  512. X    }
  513. X    if (Tcl_GetInt(interp, argv[3], &y) != TCL_OK) {
  514. X        goto error;
  515. X    }
  516. X    if ((argv[2][0] == 'm')
  517. X        && (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
  518. X        listPtr->scanMarkY = y;
  519. X        listPtr->scanMarkIndex = listPtr->topIndex;
  520. X    } else if ((argv[2][0] == 'd')
  521. X        && (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
  522. X        ListboxScanTo(listPtr, y);
  523. X    } else {
  524. X        Tcl_AppendResult(interp, "bad scan option \"", argv[2],
  525. X            "\":  must be mark or dragto", (char *) NULL);
  526. X        goto error;
  527. X    }
  528. X    } else if ((c == 's') && (length >= 4)
  529. X        && (strncmp(argv[1], "sellimits", length) == 0)) {
  530. X    sprintf(interp->result, "%d %d", listPtr->selectFirst,
  531. X                        listPtr->selectLast);
  532. X    } else if ((c == 's') && (length >= 4)
  533. X        && (strncmp(argv[1], "select", length) == 0)) {
  534. X    int index;
  535. X
  536. X    if (argc != 4) {
  537. X        Tcl_AppendResult(interp, "wrong # args: should be \"",
  538. X            argv[0], " select option index\"", (char *) NULL);
  539. X        goto error;
  540. X    }
  541. X    if (GetListboxIndex(interp, listPtr, argv[3], 0, &index) != TCL_OK) {
  542. X        goto error;
  543. X    }
  544. X    length = strlen(argv[2]);
  545. X    c = argv[2][0];
  546. X    if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
  547. X        if (index < (listPtr->selectFirst + listPtr->selectLast)/2) {
  548. X        listPtr->selectAnchor = listPtr->selectLast;
  549. X        } else {
  550. X        listPtr->selectAnchor = listPtr->selectFirst;
  551. X        }
  552. X        ListboxSelectTo(listPtr, index);
  553. X    } else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
  554. X        ListboxSelectFrom(listPtr, index);
  555. X    } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
  556. X        ListboxSelectTo(listPtr, index);
  557. X    } else {
  558. X        Tcl_AppendResult(interp, "bad select option \"", argv[2],
  559. X            "\": must be adjust, from, or to", (char *) NULL);
  560. X        goto error;
  561. X    }
  562. X    } else if ((c == 's') && (length >= 2)
  563. X        && (strncmp(argv[1], "size", length) == 0)) {
  564. X    sprintf(interp->result, "%d", listPtr->numElements);
  565. X    } else if ((c == 'v') && (strncmp(argv[1], "view", length) == 0)) {
  566. X    int index, lineNum;
  567. X
  568. X    if ((argc != 3) && (argc != 4)) {
  569. X        Tcl_AppendResult(interp, "wrong # args: should be \"",
  570. X            argv[0], " view index ?lineNum?\"", (char *) NULL);
  571. X        goto error;
  572. X    }
  573. X    if (GetListboxIndex(interp, listPtr, argv[2], ZERO_OK, &index)
  574. X        != TCL_OK) {
  575. X        goto error;
  576. X    }
  577. X    if (argc == 3) {
  578. X        lineNum = 0;
  579. X    } else {
  580. X        if (Tcl_GetInt(interp, argv[3], &lineNum) != TCL_OK) {
  581. X        goto error;
  582. X        }
  583. X    }
  584. X    ChangeListboxView(listPtr, index, lineNum);
  585. X    } else {
  586. X    Tcl_AppendResult(interp, "bad option \"", argv[1],
  587. X        "\": must be configure, delete, get, insert, ",
  588. X        "nearest, scan, select, size, or view", (char *) NULL);
  589. X    goto error;
  590. X    }
  591. X    Tk_Release((ClientData) listPtr);
  592. X    return result;
  593. X
  594. X    error:
  595. X    Tk_Release((ClientData) listPtr);
  596. X    return TCL_ERROR;
  597. X}
  598. X
  599. X/*
  600. X *----------------------------------------------------------------------
  601. X *
  602. X * DestroyListbox --
  603. X *
  604. X *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  605. X *    to clean up the internal structure of a listbox at a safe time
  606. X *    (when no-one is using it anymore).
  607. X *
  608. X * Results:
  609. X *    None.
  610. X *
  611. X * Side effects:
  612. X *    Everything associated with the listbox is freed up.
  613. X *
  614. X *----------------------------------------------------------------------
  615. X */
  616. X
  617. static void
  618. DestroyListbox(clientData)
  619. X    ClientData clientData;    /* Info about listbox widget. */
  620. X{
  621. X    register Listbox *listPtr = (Listbox *) clientData;
  622. X    register Element *elPtr, *nextPtr;
  623. X
  624. X    for (elPtr = listPtr->elementPtr; elPtr != NULL; ) {
  625. X    nextPtr = elPtr->nextPtr;
  626. X    ckfree((char *) elPtr);
  627. X    elPtr = nextPtr;
  628. X    }
  629. X    if (listPtr->normalBorder != NULL) {
  630. X    Tk_Free3DBorder(listPtr->normalBorder);
  631. X    }
  632. X    if (listPtr->fontPtr != NULL) {
  633. X    Tk_FreeFontStruct(listPtr->fontPtr);
  634. X    }
  635. X    if (listPtr->fgColorPtr != NULL) {
  636. X    Tk_FreeColor(listPtr->fgColorPtr);
  637. X    }
  638. X    if (listPtr->textGC != None) {
  639. X    Tk_FreeGC(listPtr->textGC);
  640. X    }
  641. X    if (listPtr->selBorder != NULL) {
  642. X    Tk_Free3DBorder(listPtr->selBorder);
  643. X    }
  644. X    if (listPtr->selFgColorPtr != NULL) {
  645. X    Tk_FreeColor(listPtr->selFgColorPtr);
  646. X    }
  647. X    if (listPtr->selTextGC != None) {
  648. X    Tk_FreeGC(listPtr->selTextGC);
  649. X    }
  650. X    if (listPtr->geometry != NULL) {
  651. X    ckfree(listPtr->geometry);
  652. X    }
  653. X    if (listPtr->scrollCmd != NULL) {
  654. X    ckfree(listPtr->scrollCmd);
  655. X    }
  656. X    ckfree((char *) listPtr);
  657. X}
  658. X
  659. X/*
  660. X *----------------------------------------------------------------------
  661. X *
  662. X * ConfigureListbox --
  663. X *
  664. X *    This procedure is called to process an argv/argc list, plus
  665. X *    the Tk option database, in order to configure (or reconfigure)
  666. X *    a listbox widget.
  667. X *
  668. X * Results:
  669. X *    The return value is a standard Tcl result.  If TCL_ERROR is
  670. X *    returned, then interp->result contains an error message.
  671. X *
  672. X * Side effects:
  673. X *    Configuration information, such as colors, border width,
  674. X *    etc. get set for listPtr;  old resources get freed,
  675. X *    if there were any.
  676. X *
  677. X *----------------------------------------------------------------------
  678. X */
  679. X
  680. static int
  681. ConfigureListbox(interp, listPtr, argc, argv, flags)
  682. X    Tcl_Interp *interp;        /* Used for error reporting. */
  683. X    register Listbox *listPtr;    /* Information about widget;  may or may
  684. X                 * not already have values for some fields. */
  685. X    int argc;            /* Number of valid entries in argv. */
  686. X    char **argv;        /* Arguments. */
  687. X    int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  688. X{
  689. X    XGCValues gcValues;
  690. X    GC new;
  691. X    int width, charWidth, height, fontHeight;
  692. X
  693. X    if (Tk_ConfigureWidget(interp, listPtr->tkwin, configSpecs,
  694. X        argc, argv, (char *) listPtr, flags) != TCL_OK) {
  695. X    return TCL_ERROR;
  696. X    }
  697. X
  698. X    /*
  699. X     * A few options need special processing, such as parsing the
  700. X     * geometry and setting the background from a 3-D border.
  701. X     */
  702. X
  703. X    Tk_SetBackgroundFromBorder(listPtr->tkwin, listPtr->normalBorder);
  704. X
  705. X
  706. X    if ( listPtr->columns < 1 ) {
  707. X    Tcl_AppendResult(interp, "Number of columns must be >= 1",
  708. X        (char *) NULL);
  709. X    return TCL_ERROR;
  710. X    }
  711. X    if ( listPtr->columns > MAX_COLS ) {
  712. X    Tcl_AppendResult(interp, "Number of columns must be <= 50",
  713. X        (char *) NULL);
  714. X    return TCL_ERROR;
  715. X    }
  716. X    listPtr->numInColumn = ((listPtr->numElements) + (listPtr->columns) - 1)
  717. X                            / listPtr->columns;
  718. X
  719. X    gcValues.foreground = listPtr->fgColorPtr->pixel;
  720. X    gcValues.font = listPtr->fontPtr->fid;
  721. X    new = Tk_GetGC(listPtr->tkwin, GCForeground|GCFont, &gcValues);
  722. X    if (listPtr->textGC != None) {
  723. X    Tk_FreeGC(listPtr->textGC);
  724. X    }
  725. X    listPtr->textGC = new;
  726. X
  727. X    gcValues.foreground = listPtr->selFgColorPtr->pixel;
  728. X    gcValues.font = listPtr->fontPtr->fid;
  729. X    new = Tk_GetGC(listPtr->tkwin, GCForeground|GCFont, &gcValues);
  730. X    if (listPtr->selTextGC != None) {
  731. X    Tk_FreeGC(listPtr->selTextGC);
  732. X    }
  733. X    listPtr->selTextGC = new;
  734. X
  735. X    /*
  736. X     * Register the desired geometry for the window, and arrange for
  737. X     * the window to be redisplayed.
  738. X     */
  739. X
  740. X    if ((sscanf(listPtr->geometry, "%dx%d", &width, &height) != 2)
  741. X        || (width <= 0) || (height <= 0)) {
  742. X    Tcl_AppendResult(interp, "bad geometry \"",
  743. X        listPtr->geometry, "\"", (char *) NULL);
  744. X    return TCL_ERROR;
  745. X    }
  746. X    fontHeight = listPtr->fontPtr->ascent + listPtr->fontPtr->descent;
  747. X    listPtr->lineHeight = fontHeight + 1 + 2*listPtr->selBorderWidth;
  748. X    listPtr->numLines = (Tk_Height(listPtr->tkwin) - 2*listPtr->borderWidth)
  749. X        / listPtr->lineHeight;
  750. X    if (listPtr->numLines < 0) {
  751. X    listPtr->numLines = 0;
  752. X    }
  753. X    charWidth = XTextWidth(listPtr->fontPtr, "0m", 2)/2;
  754. X    width = width*charWidth + (15*fontHeight)/10 + 2*listPtr->borderWidth;
  755. X    height = height*listPtr->lineHeight + 2*listPtr->borderWidth;
  756. X    Tk_GeometryRequest(listPtr->tkwin, width, height);
  757. X    Tk_SetInternalBorder(listPtr->tkwin, listPtr->borderWidth);
  758. X    ListboxRedrawRange(listPtr, 0, listPtr->numInColumn-1);
  759. X    ListboxUpdateScrollbar(listPtr);
  760. X    return TCL_OK;
  761. X}
  762. X
  763. X/*
  764. X *--------------------------------------------------------------
  765. X *
  766. X * DisplayListbox --
  767. X *
  768. X *    This procedure redraws the contents of a listbox window.
  769. X *
  770. X * Results:
  771. X *    None.
  772. X *
  773. X * Side effects:
  774. X *    Information appears on the screen.
  775. X *
  776. X *--------------------------------------------------------------
  777. X */
  778. X
  779. static void
  780. DisplayListbox(clientData)
  781. X    ClientData clientData;    /* Information about window. */
  782. X{
  783. X    register Listbox *listPtr = (Listbox *) clientData;
  784. X    register Tk_Window tkwin = listPtr->tkwin;
  785. X    GC gc;
  786. X    int limit, x, y, margin, height, distance;
  787. X    int colWidth, nInCol;
  788. X    int xinc, yinc, i, j, col, cols;
  789. X    Element *elPtr0, *elPtr[MAX_COLS];
  790. X    int counter[MAX_COLS];
  791. X    Element nullElement;
  792. X
  793. X    if ((listPtr->tkwin == NULL) || !Tk_IsMapped(tkwin) 
  794. X        || ((listPtr->redrawFirst == -1) && (listPtr->scrollLines == 0))) {
  795. X    goto done;
  796. X    }
  797. X
  798. X    /*
  799. X     * If we're going to redraw the whole box, then clear it first.  This
  800. X     * is needed to erase the border, in case its style has changed.
  801. X     */
  802. X
  803. X    if ((listPtr->redrawFirst == 0)
  804. X        && (listPtr->redrawLast == (listPtr->numInColumn-1))) {
  805. X    XClearWindow(Tk_Display(tkwin), Tk_WindowId(tkwin));
  806. X    listPtr->scrollLines = 0;
  807. X    }
  808. X
  809. X    /*
  810. X     * In order to provide the smoothest possible scrolling and
  811. X     * scanning, we use CopyArea operations to avoid redrawing big
  812. X     * chunks of the listbox.  First, see if the window has moved
  813. X     * so much that none of its current bits are of any use.
  814. X     */
  815. X
  816. X    if ((listPtr->scrollLines >= listPtr->numLines)
  817. X        || (listPtr->scrollLines <= -listPtr->numLines)) {
  818. X    ListboxRedrawRange(listPtr, 0, listPtr->numInColumn-1);
  819. X    } else if (listPtr->scrollLines != 0) {
  820. X
  821. X    /*
  822. X     * Copying bits will help.  Figure out how much of the window's
  823. X     * new contents can simply be copied into place, then copy them.
  824. X     * Also figure which areas of the window must be explicitly
  825. X     * redrawn, and arrange for them to be redrawn.
  826. X     */
  827. X
  828. X    distance = listPtr->scrollLines * listPtr->lineHeight;
  829. X    if (listPtr->scrollLines > 0) {
  830. X        y = listPtr->borderWidth;
  831. X        height = Tk_Height(tkwin) - 2*listPtr->borderWidth - distance;
  832. X        ListboxRedrawRange(listPtr, listPtr->topIndex,
  833. X            listPtr->topIndex + listPtr->scrollLines - 1);
  834. X    } else {
  835. X        height = Tk_Height(tkwin) - 2*listPtr->borderWidth + distance;
  836. X        y = listPtr->borderWidth - distance;
  837. X        ListboxRedrawRange(listPtr, listPtr->topIndex
  838. X            + listPtr->numLines + listPtr->scrollLines,
  839. X            listPtr->topIndex + listPtr->numLines - 1);
  840. X    }
  841. X    XCopyArea(Tk_Display(tkwin), Tk_WindowId(tkwin), Tk_WindowId(tkwin),
  842. X        listPtr->textGC, listPtr->borderWidth, y,
  843. X        Tk_Width(tkwin) - 2*listPtr->borderWidth, height,
  844. X        listPtr->borderWidth, y + distance);
  845. X
  846. X    /*
  847. X     * Some of the bits to be copied may have been obscured by other
  848. X     * windows.  Read back the exposure events generated by the
  849. X     * copy operation and schedule updates for the obscured areas.
  850. X     */
  851. X
  852. X    while (1) {
  853. X        XEvent event;
  854. X
  855. X        XWindowEvent(Tk_Display(tkwin), Tk_WindowId(tkwin), ExposureMask,
  856. X            &event);
  857. X        if (event.type == NoExpose) {
  858. X        break;
  859. X        } else if (event.type == GraphicsExpose) {
  860. X        ListboxRedrawRange(listPtr,
  861. X            NearestListboxElement(listPtr, 0,
  862. X                event.xgraphicsexpose.y),
  863. X            NearestListboxElement(listPtr, 0,
  864. X                event.xgraphicsexpose.y
  865. X                + event.xgraphicsexpose.height));
  866. X        if (event.xgraphicsexpose.count ==0) {
  867. X            break;
  868. X        }
  869. X        } else if (event.type == Expose) {
  870. X
  871. X        /* Expose events here are trouble, because there's no way
  872. X         * to tell whether the exposure referred to the pre-copy
  873. X         * window or the post-copy window.  This is a rare occurrence,
  874. X         * so take the easy way out:  redraw everything.
  875. X         */
  876. X
  877. X        ListboxRedrawRange(listPtr, 0, listPtr->numInColumn-1);
  878. X        }
  879. X    }
  880. X    }
  881. X
  882. X    /*
  883. X     * Iterate through all of the elements of the listbox, displaying all
  884. X     * those that need redisplay.  Selected elements use a different
  885. X     * GC and are raised.
  886. X     */
  887. X
  888. X    limit = listPtr->topIndex + listPtr->numLines - 1;
  889. X    if (limit > listPtr->redrawLast) {
  890. X    limit = listPtr->redrawLast;
  891. X    }
  892. X    if (listPtr->redrawFirst < listPtr->topIndex) {
  893. X    listPtr->redrawFirst = listPtr->topIndex;
  894. X    }
  895. X    margin = listPtr->borderWidth
  896. X        + (7*(listPtr->fontPtr->ascent + listPtr->fontPtr->descent))/10;
  897. X    colWidth = (Tk_Width(tkwin) - 2*(listPtr->borderWidth)) / listPtr->columns;
  898. X    nInCol = listPtr->numInColumn;
  899. X    cols = listPtr->columns;
  900. X    /* null element for when the last column(s) end early */
  901. X    nullElement.textLength = 0;
  902. X    nullElement.lBearing = 0;
  903. X    nullElement.nextPtr = NULL;
  904. X    nullElement.text[0] = '\0';
  905. X    
  906. X    /* scan down to the first line to redisplay */
  907. X    for( i = 0, elPtr0 = listPtr->elementPtr;
  908. X            (i < listPtr->redrawFirst) && (elPtr0 != NULL) && (i < limit);
  909. X            elPtr0 = elPtr0->nextPtr, ++i )
  910. X        /*EMPTY*/
  911. X        ;
  912. X    counter[0] = i;
  913. X    elPtr[0] = elPtr0;
  914. X    if( elPtr0 == NULL )
  915. X    elPtr[0] = &nullElement;
  916. X    /* set up an elPtr and counter for each column */
  917. X    for( i = 1; i < cols; ++i ) {
  918. X        elPtr[i] = elPtr[i-1];
  919. X        for( j = 0; j < nInCol; ++j ) {
  920. X            elPtr[i] = elPtr[i]->nextPtr;
  921. X        /* this happens when the last column ends early */
  922. X        if( elPtr[i] == NULL ) {
  923. X            elPtr[i] = &nullElement;
  924. X            break;
  925. X        }
  926. X    }
  927. X        counter[i] = counter[i-1] + nInCol;
  928. X    if( counter[i] >= listPtr->numElements )
  929. X        counter[i] = listPtr->numElements - 1;
  930. X    }
  931. X    x = listPtr->borderWidth;
  932. X    
  933. X    /* loop through each row in the display */
  934. X    while( (elPtr[0] != NULL) && (counter[0] <= limit) ) {
  935. X        y = ((counter[0] - listPtr->topIndex) * listPtr->lineHeight) 
  936. X                + listPtr->borderWidth;
  937. X        /* loop through each column in the row */
  938. X        for( col = 0; col < cols; ++col ) {
  939. X            /* is this a selected element? */
  940. X            if ((listPtr->selectFirst >= 0)
  941. X                    && (counter[col] >= listPtr->selectFirst)
  942. X                    && (counter[col] <= listPtr->selectLast)
  943. X                    && (elPtr[col] != NULL) && (elPtr[col] != &nullElement) ) {
  944. X                /* if so draw the background in the selected color */
  945. X                gc = listPtr->selTextGC; /* remember which gc to use for text */
  946. X                Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
  947. X                    listPtr->selBorder, x + col*colWidth, y,
  948. X                    colWidth,
  949. X                    listPtr->lineHeight, listPtr->selBorderWidth,
  950. X                    TK_RELIEF_RAISED);
  951. X                yinc = listPtr->selBorderWidth;
  952. X                xinc = -1;
  953. X            } else {
  954. X                /* otherwise draw the background in the normal color */
  955. X                gc = listPtr->textGC; /* remember which gc to use for text */
  956. X                Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
  957. X                    listPtr->normalBorder, x + col*colWidth, y,
  958. X                    colWidth,
  959. X                    listPtr->lineHeight, 0, TK_RELIEF_FLAT);
  960. X                yinc = listPtr->selBorderWidth + 1;
  961. X                xinc = 0;
  962. X            }
  963. X            /* the last column may end early so check for NULL */
  964. X            if( elPtr[col] != NULL ) {
  965. X                yinc += listPtr->fontPtr->ascent;
  966. X                xinc += margin - elPtr[col]->lBearing;
  967. X                XDrawString(Tk_Display(tkwin), Tk_WindowId(tkwin), gc,
  968. X                    x + xinc + col*colWidth, y+yinc,
  969. X                    elPtr[col]->text, elPtr[col]->textLength);
  970. X                elPtr[col] = elPtr[col]->nextPtr;
  971. X            }
  972. X            ++counter[col];
  973. X        }
  974. X    }
  975. X
  976. X    /*
  977. X     * Clear empty space past end of list, if there is any, then redraw
  978. X     * the border around the list, if there is one.
  979. X     */
  980. X
  981. X    if ((listPtr->topIndex + listPtr->numLines) > listPtr->numInColumn) {
  982. X    y = ((listPtr->numInColumn - listPtr->topIndex) * listPtr->lineHeight)
  983. X        + listPtr->borderWidth;
  984. X    } else {
  985. X    y = (listPtr->numLines * listPtr->lineHeight) + listPtr->borderWidth;
  986. X    }
  987. X    height = Tk_Height(tkwin) - y - listPtr->borderWidth;
  988. X    if (height > 0) {
  989. X    Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
  990. X        listPtr->normalBorder, listPtr->borderWidth, y,
  991. X        Tk_Width(tkwin) - 2*listPtr->borderWidth, height, 0,
  992. X        TK_RELIEF_FLAT);
  993. X    }
  994. X    if (listPtr->relief != TK_RELIEF_FLAT) {
  995. X    Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
  996. X        listPtr->normalBorder, 0, 0, Tk_Width(tkwin),
  997. X        Tk_Height(tkwin), listPtr->borderWidth,
  998. X        listPtr->relief);
  999. X    } else if (listPtr->borderWidth > 0) {
  1000. X    /*
  1001. X     * Non-zero border width but flat:  must clear out the border
  1002. X     * area on the right, since text could have overflowed into it.
  1003. X     */
  1004. X    XClearArea(Tk_Display(tkwin), Tk_WindowId(tkwin),
  1005. X        Tk_Width(tkwin)-listPtr->borderWidth, 0,
  1006. X        listPtr->borderWidth, Tk_Height(tkwin), False);
  1007. X    }
  1008. X
  1009. X    done:
  1010. X    listPtr->redrawFirst = -1;
  1011. X    listPtr->scrollLines = 0;
  1012. X}
  1013. X
  1014. X/*
  1015. X *----------------------------------------------------------------------
  1016. X *
  1017. X * InsertEls --
  1018. X *
  1019. X *    Add new elements to a listbox widget.
  1020. X *
  1021. X * Results:
  1022. X *    None.
  1023. X *
  1024. X * Side effects:
  1025. X *    New information gets added to listPtr;  it will be redisplayed
  1026. X *    soon, but not immediately.
  1027. X *
  1028. X *----------------------------------------------------------------------
  1029. X */
  1030. X
  1031. static void
  1032. InsertEls(listPtr, index, argc, argv)
  1033. X    register Listbox *listPtr;    /* Listbox that is to get the new
  1034. X                 * elements. */
  1035. X    int index;            /* Add the new elements before this
  1036. X                 * element. */
  1037. X    int argc;            /* Number of new elements to add. */
  1038. X    char **argv;        /* New elements (one per entry). */
  1039. X{
  1040. X    register Element *prevPtr, *newPtr;
  1041. X    int length, dummy, i;
  1042. X    XCharStruct bbox;
  1043. X
  1044. X    /*
  1045. X     * Find the element before which the new ones will be inserted.
  1046. X     */
  1047. X
  1048. X    if (index == 0) {
  1049. X    prevPtr = NULL;
  1050. X    } else {
  1051. X    for (prevPtr = listPtr->elementPtr, index--; index > 0; index--) {
  1052. X        prevPtr = prevPtr->nextPtr;
  1053. X    }
  1054. X    }
  1055. X
  1056. X    /*
  1057. X     * For each new element, create a record, initialize it, and link
  1058. X     * it into the list of elements.
  1059. X     */
  1060. X
  1061. X    for (i = argc ; i > 0; i--, argv++, prevPtr = newPtr) {
  1062. X    length = strlen(*argv);
  1063. X    newPtr = (Element *) ckalloc(ElementSize(length));
  1064. X    newPtr->textLength = length;
  1065. X    XTextExtents(listPtr->fontPtr, *argv, 1, &dummy, &dummy, &dummy,
  1066. X        &bbox);
  1067. X    newPtr->lBearing = bbox.lbearing;
  1068. X    if (prevPtr == NULL) {
  1069. X        newPtr->nextPtr = listPtr->elementPtr;
  1070. X        listPtr->elementPtr = newPtr;
  1071. X    } else {
  1072. X        newPtr->nextPtr = prevPtr->nextPtr;
  1073. X        prevPtr->nextPtr = newPtr;
  1074. X    }
  1075. X    strcpy(newPtr->text, *argv);
  1076. X    }
  1077. X    listPtr->numElements += argc;
  1078. X    listPtr->numInColumn = ((listPtr->numElements) + (listPtr->columns) - 1)
  1079. X                            / (listPtr->columns);
  1080. X
  1081. X    /*
  1082. X     * Update the selection to account for the  renumbering that has just
  1083. X     * occurred.  Then arrange for the new information to be displayed.
  1084. X     */
  1085. X
  1086. X    if (index <= listPtr->selectFirst) {
  1087. X    listPtr->selectFirst += argc;
  1088. X    }
  1089. X    if (index <= listPtr->selectLast) {
  1090. X    listPtr->selectLast += argc;
  1091. X    }
  1092. X    ListboxRedrawRange(listPtr, index, listPtr->numInColumn-1);
  1093. X    ListboxUpdateScrollbar(listPtr);
  1094. X}
  1095. X
  1096. X/*
  1097. X *----------------------------------------------------------------------
  1098. X *
  1099. X * DeleteEls --
  1100. X *
  1101. X *    Remove one or more elements from a listbox widget.
  1102. X *
  1103. X * Results:
  1104. X *    None.
  1105. X *
  1106. X * Side effects:
  1107. X *    Memory gets freed, the listbox gets modified and (eventually)
  1108. X *    redisplayed.
  1109. X *
  1110. X *----------------------------------------------------------------------
  1111. X */
  1112. X
  1113. static void
  1114. DeleteEls(listPtr, index, count)
  1115. X    register Listbox *listPtr;    /* Listbox widget to modify. */
  1116. X    int index;            /* Index of first element to delete. */
  1117. X    int count;            /* How many elements to delete. */
  1118. X{
  1119. X    register Element *prevPtr, *elPtr;
  1120. X    int i;
  1121. X
  1122. X    /*
  1123. X     * Find the element just before the ones to delete.
  1124. X     */
  1125. X
  1126. X    if (index == 0) {
  1127. X    prevPtr = NULL;
  1128. X    } else {
  1129. X    for (i = index-1, prevPtr = listPtr->elementPtr; i > 0; i--) {
  1130. X        prevPtr = prevPtr->nextPtr;
  1131. X    }
  1132. X    }
  1133. X
  1134. X    /*
  1135. X     * Delete the requested number of elements.
  1136. X     */
  1137. X
  1138. X    for (i = count; i > 0; i--) {
  1139. X    if (prevPtr == NULL) {
  1140. X        elPtr = listPtr->elementPtr;
  1141. X        listPtr->elementPtr = elPtr->nextPtr;
  1142. X    } else {
  1143. X        elPtr = prevPtr->nextPtr;
  1144. X        prevPtr->nextPtr = elPtr->nextPtr;
  1145. X    }
  1146. X    ckfree((char *) elPtr);
  1147. X    }
  1148. X    listPtr->numElements -= count;
  1149. X    listPtr->numInColumn = ((listPtr->numElements) + (listPtr->columns) - 1)
  1150. X                            / (listPtr->columns);
  1151. X
  1152. X    /*
  1153. X      * Update the selection and viewing information to reflect the change
  1154. X      * in the element numbering, and redisplay to slide information up over
  1155. X      * the elements that were deleted.
  1156. X     */
  1157. X
  1158. X    if (index <= listPtr->selectFirst) {
  1159. X    listPtr->selectFirst -= count;
  1160. X    if (listPtr->selectFirst < index) {
  1161. X        listPtr->selectFirst = index;
  1162. X    }
  1163. X    }
  1164. X    if (index <= listPtr->selectLast) {
  1165. X    listPtr->selectLast -= count;
  1166. X    if (listPtr->selectLast < index) {
  1167. X        listPtr->selectLast = index-1;
  1168. X    }
  1169. X    }
  1170. X    if (listPtr->selectLast < listPtr->selectFirst) {
  1171. X    listPtr->selectFirst = -1;
  1172. X    }
  1173. X    if (index <= listPtr->topIndex) {
  1174. X    listPtr->topIndex -= count;
  1175. X    if (listPtr->topIndex < index) {
  1176. X        listPtr->topIndex = index;
  1177. X    }
  1178. X    }
  1179. X    ListboxRedrawRange(listPtr, index, listPtr->numInColumn-1);
  1180. X    ListboxUpdateScrollbar(listPtr);
  1181. X}
  1182. X
  1183. X/*
  1184. X *--------------------------------------------------------------
  1185. X *
  1186. X * ListboxEventProc --
  1187. X *
  1188. X *    This procedure is invoked by the Tk dispatcher for various
  1189. X *    events on listboxes.
  1190. X *
  1191. X * Results:
  1192. X *    None.
  1193. X *
  1194. X * Side effects:
  1195. X *    When the window gets deleted, internal structures get
  1196. X *    cleaned up.  When it gets exposed, it is redisplayed.
  1197. X *
  1198. X *--------------------------------------------------------------
  1199. X */
  1200. X
  1201. static void
  1202. ListboxEventProc(clientData, eventPtr)
  1203. X    ClientData clientData;    /* Information about window. */
  1204. X    XEvent *eventPtr;        /* Information about event. */
  1205. X{
  1206. X    Listbox *listPtr = (Listbox *) clientData;
  1207. X
  1208. X    if (eventPtr->type == Expose) {
  1209. X    ListboxRedrawRange(listPtr,
  1210. X        NearestListboxElement(listPtr, 0,
  1211. X            eventPtr->xexpose.y),
  1212. X        NearestListboxElement(listPtr, 0,
  1213. X            eventPtr->xexpose.y + eventPtr->xexpose.height) );
  1214. X    } else if (eventPtr->type == DestroyNotify) {
  1215. X    Tcl_DeleteCommand(listPtr->interp, Tk_PathName(listPtr->tkwin));
  1216. X    listPtr->tkwin = NULL;
  1217. X    if ((listPtr->redrawFirst != -1) || (listPtr->scrollLines != 0)) {
  1218. X        Tk_CancelIdleCall(DisplayListbox, (ClientData) listPtr);
  1219. X    }
  1220. X    Tk_EventuallyFree((ClientData) listPtr, DestroyListbox);
  1221. X    } else if (eventPtr->type == ConfigureNotify) {
  1222. X    Tk_Preserve((ClientData) listPtr);
  1223. X    listPtr->numLines = (Tk_Height(listPtr->tkwin)
  1224. X        - 2*listPtr->borderWidth) / listPtr->lineHeight;
  1225. X    ListboxRedrawRange(listPtr, 0, listPtr->numInColumn-1);
  1226. X    ListboxUpdateScrollbar(listPtr);
  1227. X    Tk_Release((ClientData) listPtr);
  1228. X    }
  1229. X}
  1230. X
  1231. X/*
  1232. X *--------------------------------------------------------------
  1233. X *
  1234. X * GetListboxIndex --
  1235. X *
  1236. X *    Parse an index into a listbox and return either its value
  1237. X *    or an error.
  1238. X *
  1239. X * Results:
  1240. X *    A standard Tcl result.  If all went well, then *indexPtr is
  1241. X *    filled in with the index (into listPtr) corresponding to
  1242. X *    string.  Otherwise an error message is left in interp->result.
  1243. X *
  1244. X * Side effects:
  1245. X *    None.
  1246. X *
  1247. X *--------------------------------------------------------------
  1248. X */
  1249. X
  1250. static int
  1251. GetListboxIndex(interp, listPtr, string, flags, indexPtr)
  1252. X    Tcl_Interp *interp;        /* For error messages. */
  1253. X    Listbox *listPtr;        /* Listbox for which the index is being
  1254. X                 * specified. */
  1255. X    char *string;        /* Numerical index into listPtr's element
  1256. X                 * list, or "end" to refer to last element. */
  1257. X    int flags;            /* OR-ed combination of flag bits:  ZERO_OK
  1258. X                 * means accept index 0, even if the list
  1259. X                 * has no entries;  LAST_PLUS_ONE_OK means
  1260. X                 * accept an index equal to the number of
  1261. X                 * elements, and treat "end" as this index. */
  1262. X    int *indexPtr;        /* Where to store converted relief. */
  1263. X{
  1264. X    int last;
  1265. X
  1266. X    if (flags & LAST_PLUS_ONE_OK) {
  1267. X    last = listPtr->numElements;
  1268. X    } else {
  1269. X    last = listPtr->numElements-1;
  1270. X    }
  1271. X    if (string[0] == 'e') {
  1272. X    if (strncmp(string, "end", strlen(string)) != 0) {
  1273. X        *indexPtr = last;
  1274. X        badIndex:
  1275. X        Tcl_AppendResult(interp, "bad listbox index \"", string,
  1276. X            "\"", (char *) NULL);
  1277. X        return TCL_ERROR;
  1278. X    }
  1279. X    *indexPtr = last;
  1280. X    if (listPtr->numElements <= 0) {
  1281. X        if (flags & ZERO_OK) {
  1282. X        *indexPtr = 0;
  1283. X        } else {
  1284. X        interp->result = "listbox has no entries";
  1285. X        return TCL_ERROR;
  1286. X        }
  1287. X    }
  1288. X    } else {
  1289. X    if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
  1290. X        Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
  1291. X        goto badIndex;
  1292. X    }
  1293. X    if ((*indexPtr < 0) || (*indexPtr > last)) {
  1294. X        if ((*indexPtr != 0) || (listPtr->numElements != 0)
  1295. X            || !(flags & ZERO_OK)) {
  1296. X        goto badIndex;
  1297. X        }
  1298. X    }
  1299. X    }
  1300. X    return TCL_OK;
  1301. X}
  1302. X
  1303. X/*
  1304. X *----------------------------------------------------------------------
  1305. X *
  1306. X * ChangeListboxView --
  1307. X *
  1308. X *    Change the view on a listbox widget.
  1309. X *
  1310. X * Results:
  1311. X *    None.
  1312. X *
  1313. X * Side effects:
  1314. X *    What's displayed on the screen is changed.  If there is a
  1315. X *    scrollbar associated with this widget, then the scrollbar
  1316. X *    is instructed to change its display too.
  1317. X *
  1318. X *----------------------------------------------------------------------
  1319. X */
  1320. X
  1321. static void
  1322. ChangeListboxView(listPtr, index, lineNum)
  1323. X    register Listbox *listPtr;        /* Information about widget. */
  1324. X    int index;                /* Index of element in listPtr. */
  1325. X    int lineNum;            /* Index of line on screen:  make
  1326. X                     * element "elIndex" appear at this
  1327. X                     * position on screen. */
  1328. X{
  1329. X    int newTop;
  1330. X
  1331. X    if (listPtr->tkwin == NULL) {
  1332. X    return;
  1333. X    }
  1334. X
  1335. X    newTop = index - lineNum;
  1336. X    if (newTop >= listPtr->numInColumn) {
  1337. X    newTop = listPtr->numInColumn-1;
  1338. X    }
  1339. X    if (newTop < 0) {
  1340. X    newTop = 0;
  1341. X    }
  1342. X    if (listPtr->topIndex == newTop) {
  1343. X    return;
  1344. X    }
  1345. X    if ((listPtr->scrollLines == 0) && (listPtr->redrawFirst == -1)) {
  1346. X    Tk_DoWhenIdle(DisplayListbox, (ClientData) listPtr);
  1347. X    }
  1348. X    listPtr->scrollLines += listPtr->topIndex - newTop;
  1349. X    listPtr->topIndex = newTop;
  1350. X
  1351. X    ListboxUpdateScrollbar(listPtr);
  1352. X}
  1353. X
  1354. X/*
  1355. X *----------------------------------------------------------------------
  1356. X *
  1357. X * ListboxScanTo --
  1358. X *
  1359. X *    Given a y-coordinate (presumably of the curent mouse location)
  1360. X *    drag the view in the window to implement the scan operation.
  1361. X *
  1362. X * Results:
  1363. X *    None.
  1364. X *
  1365. X * Side effects:
  1366. X *    The view in the window may change.
  1367. X *
  1368. X *----------------------------------------------------------------------
  1369. X */
  1370. X
  1371. static void
  1372. ListboxScanTo(listPtr, y)
  1373. X    register Listbox *listPtr;        /* Information about widget. */
  1374. X    int y;                /* Y-coordinate to use for scan
  1375. X                     * operation. */
  1376. X{
  1377. X    int newTopIndex;
  1378. X
  1379. X    /*
  1380. X     * Compute new top line for screen by amplifying the difference
  1381. X     * between the current position and the place where the scan
  1382. X     * started (the "mark" position).  If we run off the top or bottom
  1383. X     * of the list, then reset the mark point so that the current
  1384. X     * position continues to correspond to the edge of the window.
  1385. X     * This means that the picture will start dragging as soon as the
  1386. X     * mouse reverses direction (without this reset, might have to slide
  1387. X     * mouse a long ways back before the picture starts moving again).
  1388. X     */
  1389. X
  1390. X    newTopIndex = listPtr->scanMarkIndex
  1391. X        - (10*(y - listPtr->scanMarkY))/listPtr->lineHeight;
  1392. X    if (newTopIndex >= listPtr->numInColumn) {
  1393. X    newTopIndex = listPtr->scanMarkIndex = listPtr->numInColumn-1;
  1394. X    listPtr->scanMarkY = y;
  1395. X    }
  1396. X    if (newTopIndex < 0) {
  1397. X    newTopIndex = listPtr->scanMarkIndex = 0;
  1398. X    listPtr->scanMarkY = y;
  1399. X    } 
  1400. X    ChangeListboxView(listPtr, newTopIndex, 0);
  1401. X}
  1402. X
  1403. X/*
  1404. X *----------------------------------------------------------------------
  1405. X *
  1406. X * NearestListboxElement --
  1407. X *
  1408. X *    Given a y-coordinate inside a listbox, compute the index of
  1409. X *    the element under that y-coordinate (or closest to that
  1410. X *    y-coordinate).
  1411. X *
  1412. X * Results:
  1413. X *    The return value is an index of an element of listPtr.  If
  1414. X *    listPtr has no elements, then 0 is always returned.
  1415. X *
  1416. X * Side effects:
  1417. X *    None.
  1418. X *
  1419. X *----------------------------------------------------------------------
  1420. X */
  1421. X
  1422. static int
  1423. NearestListboxElement(listPtr, x, y)
  1424. X    register Listbox *listPtr;        /* Information about widget. */
  1425. X    int x;                /* X-coordinate in listPtr's window. */
  1426. X    int y;                /* Y-coordinate in listPtr's window. */
  1427. X{
  1428. X    int index;
  1429. X    int colWidth;
  1430. X
  1431. X    index = (y - listPtr->borderWidth)/listPtr->lineHeight;
  1432. X    if (index >= listPtr->numLines) {
  1433. X    index = listPtr->numLines-1;
  1434. X    }
  1435. X    if (index < 0) {
  1436. X    index = 0;
  1437. X    }
  1438. X    index += listPtr->topIndex;
  1439. X    colWidth = (Tk_Width(listPtr->tkwin) - 2*(listPtr->borderWidth))
  1440. X                    / (listPtr->columns);
  1441. X    index += (x/colWidth) * (listPtr->numInColumn);
  1442. X    if (index >= listPtr->numElements) {
  1443. X    index = listPtr->numElements-1;
  1444. X    }
  1445. X    return index;
  1446. X}
  1447. X
  1448. X/*
  1449. X *----------------------------------------------------------------------
  1450. X *
  1451. X * ListboxSelectFrom --
  1452. X *
  1453. X *    Start a new selection in a listbox.
  1454. X *
  1455. X * Results:
  1456. X *    None.
  1457. X *
  1458. X * Side effects:
  1459. X *    ListPtr claims the selection, and the selection becomes the
  1460. X *    single element given by index.
  1461. X *
  1462. X *----------------------------------------------------------------------
  1463. X */
  1464. X
  1465. static void
  1466. ListboxSelectFrom(listPtr, index)
  1467. X    register Listbox *listPtr;        /* Information about widget. */
  1468. X    int index;                /* Index of element that is to
  1469. X                     * become the new selection. */
  1470. X{
  1471. X    if (listPtr->selectFirst == -1) {
  1472. X    Tk_OwnSelection(listPtr->tkwin, ListboxLostSelection,
  1473. X        (ClientData) listPtr);
  1474. X    } else {
  1475. X    ListboxRedrawRange(listPtr, listPtr->selectFirst, listPtr->selectLast);
  1476. X    }
  1477. X
  1478. X    listPtr->selectFirst = listPtr->selectLast = index;
  1479. X    listPtr->selectAnchor = index;
  1480. X    ListboxRedrawRange(listPtr, index, index);
  1481. X}
  1482. X
  1483. X/*
  1484. X *----------------------------------------------------------------------
  1485. X *
  1486. X * ListboxSelectTo --
  1487. X *
  1488. X *    Modify the selection by moving its un-anchored end.  This could
  1489. X *    make the selection either larger or smaller.
  1490. X *
  1491. X * Results:
  1492. X *    None.
  1493. X *
  1494. X * Side effects:
  1495. X *    The selection changes.
  1496. X *
  1497. X *----------------------------------------------------------------------
  1498. X */
  1499. X
  1500. static void
  1501. ListboxSelectTo(listPtr, index)
  1502. X    register Listbox *listPtr;        /* Information about widget. */
  1503. X    int index;                /* Index of element that is to
  1504. X                     * become the "other" end of the
  1505. X                     * selection. */
  1506. X{
  1507. X    int newFirst, newLast;
  1508. X
  1509. X    /*
  1510. X     * We should already own the selection, but grab it if we don't.
  1511. X     */
  1512. X
  1513. X    if (listPtr->selectFirst == -1) {
  1514. X    ListboxSelectFrom(listPtr, index);
  1515. X    }
  1516. X
  1517. X    if (listPtr->selectAnchor < index) {
  1518. X    newFirst = listPtr->selectAnchor;
  1519. X    newLast = index;
  1520. X    } else {
  1521. X    newFirst = index;
  1522. X    newLast = listPtr->selectAnchor;
  1523. X    }
  1524. X    if ((listPtr->selectFirst == newFirst)
  1525. X        && (listPtr->selectLast == newLast)) {
  1526. X    return;
  1527. X    }
  1528. X    if (listPtr->selectFirst != newFirst) {
  1529. X    if (listPtr->selectFirst < newFirst) {
  1530. X        ListboxRedrawRange(listPtr, listPtr->selectFirst, newFirst-1);
  1531. X    } else {
  1532. X        ListboxRedrawRange(listPtr, newFirst, listPtr->selectFirst-1);
  1533. X    }
  1534. X    listPtr->selectFirst = newFirst;
  1535. X    }
  1536. X    if (listPtr->selectLast != newLast) {
  1537. X    if (listPtr->selectLast < newLast) {
  1538. X        ListboxRedrawRange(listPtr, listPtr->selectLast+1, newLast);
  1539. X    } else {
  1540. X        ListboxRedrawRange(listPtr, newLast+1, listPtr->selectLast);
  1541. X    }
  1542. X    listPtr->selectLast = newLast;
  1543. X    }
  1544. X}
  1545. X
  1546. X/*
  1547. X *----------------------------------------------------------------------
  1548. X *
  1549. X * ListboxFetchSelection --
  1550. X *
  1551. X *    This procedure is called back by Tk when the selection is
  1552. X *    requested by someone.  It returns part or all of the selection
  1553. X *    in a buffer provided by the caller.
  1554. X *
  1555. X * Results:
  1556. X *    The return value is the number of non-NULL bytes stored
  1557. X *    at buffer.  Buffer is filled (or partially filled) with a
  1558. X *    NULL-terminated string containing part or all of the selection,
  1559. X *    as given by offset and maxBytes.  The selection is returned
  1560. X *    as a Tcl list with one list element for each element in the
  1561. X *    listbox.
  1562. X *
  1563. X * Side effects:
  1564. X *    None.
  1565. X *
  1566. X *----------------------------------------------------------------------
  1567. X */
  1568. X
  1569. static int
  1570. ListboxFetchSelection(clientData, offset, buffer, maxBytes)
  1571. X    ClientData clientData;        /* Information about listbox widget. */
  1572. X    int offset;                /* Offset within selection of first
  1573. X                     * byte to be returned. */
  1574. X    char *buffer;            /* Location in which to place
  1575. X                     * selection. */
  1576. X    int maxBytes;            /* Maximum number of bytes to place
  1577. X                     * at buffer, not including terminating
  1578. X                     * NULL character. */
  1579. X{
  1580. X    register Listbox *listPtr = (Listbox *) clientData;
  1581. X    register Element *elPtr;
  1582. X    char **argv, *selection;
  1583. X    int src, dst, length, count, argc;
  1584. X
  1585. X    if (listPtr->selectFirst == -1) {
  1586. X    return -1;
  1587. X    }
  1588. X
  1589. X    /*
  1590. X     * Use Tcl_Merge to format the listbox elements into a suitable
  1591. X     * Tcl list.
  1592. X     */
  1593. X
  1594. X    argc = listPtr->selectLast - listPtr->selectFirst + 1;
  1595. X    argv = (char **) ckalloc((unsigned) (argc*sizeof(char *)));
  1596. X    for (src = 0, dst = 0, elPtr = listPtr->elementPtr; ;
  1597. X        src++, elPtr = elPtr->nextPtr) {
  1598. X    if (src < listPtr->selectFirst) {
  1599. X        continue;
  1600. X    }
  1601. X    if (src > listPtr->selectLast) {
  1602. X        break;
  1603. X    }
  1604. X    argv[dst] = elPtr->text;
  1605. X    dst++;
  1606. X    }
  1607. X    selection = Tcl_Merge(argc, argv);
  1608. X
  1609. X    /*
  1610. X     * Copy the requested portion of the selection to the buffer.
  1611. X     */
  1612. X
  1613. X    length = strlen(selection);
  1614. X    count = length - offset;
  1615. X    if (count <= 0) {
  1616. X    count = 0;
  1617. X    goto done;
  1618. X    }
  1619. X    if (count > maxBytes) {
  1620. X    count = maxBytes;
  1621. X    }
  1622. X    memcpy((VOID *) buffer, (VOID *) selection + offset, count);
  1623. X    /* WAS: bcopy(selection + offset, buffer, count); */
  1624. X
  1625. X    done:
  1626. X    buffer[count] = '\0';
  1627. X    ckfree(selection);
  1628. X    return count;
  1629. X}
  1630. X
  1631. X/*
  1632. X *----------------------------------------------------------------------
  1633. X *
  1634. X * ListboxLostSelection --
  1635. X *
  1636. X *    This procedure is called back by Tk when the selection is
  1637. X *    grabbed away from a listbox widget.
  1638. X *
  1639. X * Results:
  1640. X *    None.
  1641. X *
  1642. X * Side effects:
  1643. X *      If the variable 'noxsel' is false then
  1644. X *    the existing selection is unhighlighted, and the window is
  1645. X *    marked as not containing a selection.
  1646. X *      Otherwise there is no effect.
  1647. X *
  1648. X *----------------------------------------------------------------------
  1649. X */
  1650. X
  1651. static void
  1652. ListboxLostSelection(clientData)
  1653. X    ClientData clientData;        /* Information about listbox widget. */
  1654. X{
  1655. X    register Listbox *listPtr = (Listbox *) clientData;
  1656. X
  1657. X    if( listPtr->noxsel )
  1658. X    return;
  1659. X    ListboxRedrawRange(listPtr, listPtr->selectFirst, listPtr->selectLast);
  1660. X    listPtr->selectFirst = -1;
  1661. X}
  1662. X
  1663. X/*
  1664. X *----------------------------------------------------------------------
  1665. X *
  1666. X * ListboxMouseProc --
  1667. X *
  1668. X *    This procedure responds to mouse actions to implement selection
  1669. X *    operations and dragging.
  1670. X *
  1671. X * Results:
  1672. X *    None.
  1673. X *
  1674. X * Side effects:
  1675. X *    The selection may change, and the window's view may change.
  1676. X *
  1677. X *----------------------------------------------------------------------
  1678. X */
  1679. X
  1680. static void
  1681. ListboxMouseProc(clientData, eventPtr)
  1682. X    ClientData clientData;        /* Information about window. */
  1683. X    register XEvent *eventPtr;        /* Information about event. */
  1684. X{
  1685. X    Listbox *listPtr = (Listbox *) clientData;
  1686. X    int index;
  1687. X
  1688. X    Tk_Preserve((ClientData) listPtr);
  1689. X    if (eventPtr->type == ButtonPress) {
  1690. X    if (eventPtr->xbutton.button == 1) {
  1691. X        index = NearestListboxElement(listPtr,
  1692. X                eventPtr->xbutton.x, eventPtr->xbutton.y);
  1693. X        if (eventPtr->xbutton.state == 0) {
  1694. X        ListboxSelectFrom(listPtr, index);
  1695. X        } else if (eventPtr->xbutton.state == ShiftMask) {
  1696. X        if (index < (listPtr->selectFirst
  1697. X            + listPtr->selectLast)/2) {
  1698. X            listPtr->selectAnchor = listPtr->selectLast;
  1699. X        } else {
  1700. X            listPtr->selectAnchor = listPtr->selectFirst;
  1701. X        }
  1702. X        ListboxSelectTo(listPtr, index);
  1703. X        }
  1704. X    } else if ((eventPtr->xbutton.button == 3)
  1705. X        || (eventPtr->xbutton.state == 0)) {
  1706. X        listPtr->scanMarkY = eventPtr->xbutton.y;
  1707. X        listPtr->scanMarkIndex = listPtr->topIndex;
  1708. X    }
  1709. X    } else if (eventPtr->type == MotionNotify) {
  1710. X    if ((eventPtr->xmotion.state & ~ShiftMask) == Button1Mask) {
  1711. X        index = NearestListboxElement(listPtr,
  1712. X                    eventPtr->xmotion.x, eventPtr->xmotion.y);
  1713. X        ListboxSelectTo(listPtr, index);
  1714. X    } else if (eventPtr->xmotion.state == Button3Mask) {
  1715. X        ListboxScanTo(listPtr, eventPtr->xmotion.y);
  1716. X    }
  1717. X    }
  1718. X    Tk_Release((ClientData) listPtr);
  1719. X}
  1720. X
  1721. X/*
  1722. X *----------------------------------------------------------------------
  1723. X *
  1724. X * ListboxRedrawRange --
  1725. X *
  1726. X *    Ensure that a given range of elements is eventually redrawn on
  1727. X *    the display (if those elements in fact appear on the display).
  1728. X *
  1729. X * Results:
  1730. X *    None.
  1731. X *
  1732. X * Side effects:
  1733. X *    Information gets redisplayed.
  1734. X *
  1735. X *----------------------------------------------------------------------
  1736. X */
  1737. X
  1738. static void
  1739. ListboxRedrawRange(listPtr, first, last)
  1740. X    register Listbox *listPtr;        /* Information about widget. */
  1741. X    int first;                /* Index of first element in list
  1742. X                     * that needs to be redrawn. */
  1743. X    int last;                /* Index of last element in list
  1744. X                     * that needs to be redrawn.  May
  1745. X                     * be less than first;
  1746. X                     * these just bracket a range. */
  1747. X{
  1748. X    int nCol = listPtr->numInColumn;
  1749. X    int row_first, row_last;
  1750. X    int col_first, col_last;
  1751. X
  1752. X    if ((listPtr->tkwin == NULL) || !Tk_IsMapped(listPtr->tkwin)) {
  1753. X    return;
  1754. X    }
  1755. X    /* adjust for multiple columns */
  1756. X    if( nCol > 0 ) {
  1757. X        row_first = first % nCol;
  1758. X        col_first = first / nCol;
  1759. X        row_last = last % nCol;
  1760. X        col_last = last / nCol;
  1761. X        if( col_first == col_last ) {
  1762. X            first = row_first;
  1763. X            last = row_last;
  1764. X        } else if( col_first < col_last ) {
  1765. X            first = 0;
  1766. X            last = nCol - 1;
  1767. X        }
  1768. X    }
  1769. X
  1770. X    if (listPtr->redrawFirst == -1) {
  1771. X    listPtr->redrawFirst = first;
  1772. X    listPtr->redrawLast = last;
  1773. X    if (listPtr->scrollLines == 0) {
  1774. X        Tk_DoWhenIdle(DisplayListbox, (ClientData) listPtr);
  1775. X    }
  1776. X    } else {
  1777. X    if (first < listPtr->redrawFirst) {
  1778. X        listPtr->redrawFirst = first;
  1779. X    }
  1780. X    if (last > listPtr->redrawLast) {
  1781. X        listPtr->redrawLast = last;
  1782. X    }
  1783. X    }
  1784. X}
  1785. X
  1786. X/*
  1787. X *----------------------------------------------------------------------
  1788. X *
  1789. X * ListboxUpdateScrollbar --
  1790. X *
  1791. X *    This procedure is invoked whenever information has changed in
  1792. X *    a listbox in a way that would invalidate a scrollbar display.
  1793. X *    If there is an associated scrollbar, then this command updates
  1794. X *    it by invoking a Tcl command.
  1795. X *
  1796. X * Results:
  1797. X *    None.
  1798. X *
  1799. X * Side effects:
  1800. X *    A Tcl command is invoked, and an additional command may be
  1801. X *    invoked to process errors in the command.
  1802. X *
  1803. X *----------------------------------------------------------------------
  1804. X */
  1805. X
  1806. static void
  1807. ListboxUpdateScrollbar(listPtr)
  1808. X    register Listbox *listPtr;        /* Information about widget. */
  1809. X{
  1810. X    char string[60];
  1811. X    int result, last;
  1812. X
  1813. X    if (listPtr->scrollCmd == NULL) {
  1814. X    return;
  1815. X    }
  1816. X    last = listPtr->topIndex + listPtr->numLines;
  1817. X    if (last >= listPtr->numInColumn) {
  1818. X    last = listPtr->numInColumn-1;
  1819. X    }
  1820. X    sprintf(string, " %d %d %d %d", listPtr->numInColumn, listPtr->numLines,
  1821. X        listPtr->topIndex, last);
  1822. X    result = Tcl_VarEval(listPtr->interp, listPtr->scrollCmd, string,
  1823. X        (char *) NULL);
  1824. X    if (result != TCL_OK) {
  1825. X    TkBindError(listPtr->interp);
  1826. X    }
  1827. X}
  1828. END_OF_FILE
  1829. if test 55672 -ne `wc -c <'tkColbox.c'`; then
  1830.     echo shar: \"'tkColbox.c'\" unpacked with wrong size!
  1831. fi
  1832. # end of 'tkColbox.c'
  1833. fi
  1834. echo shar: End of archive 15 \(of 15\).
  1835. cp /dev/null ark15isdone
  1836. MISSING=""
  1837. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ; do
  1838.     if test ! -f ark${I}isdone ; then
  1839.     MISSING="${MISSING} ${I}"
  1840.     fi
  1841. done
  1842. if test "${MISSING}" = "" ; then
  1843.     echo You have unpacked all 15 archives.
  1844.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1845. else
  1846.     echo You still need to unpack the following archives:
  1847.     echo "        " ${MISSING}
  1848. fi
  1849. ##  End of shell archive.
  1850. exit 0
  1851. -- 
  1852. --
  1853. Molecular Simulations, Inc.            mail: dcmartin@msi.com
  1854. 796 N. Pastoria Avenue                uucp: uunet!dcmartin
  1855. Sunnyvale, California 94086            at&t: 408/522-9236
  1856.