home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / TCL / BLT / BLT1.7L1 / BLT1 / blt-1.7 / src / bltHtext.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-04-14  |  101.0 KB  |  3,521 lines

  1. /*
  2.  * bltHtext.c --
  3.  *
  4.  *    This module implements a hypertext widget for the
  5.  *    Tk toolkit.
  6.  *
  7.  * Copyright 1991-1994 by AT&T Bell Laboratories.
  8.  * Permission to use, copy, modify, and distribute this software
  9.  * and its documentation for any purpose and without fee is hereby
  10.  * granted, provided that the above copyright notice appear in all
  11.  * copies and that both that the copyright notice and warranty
  12.  * disclaimer appear in supporting documentation, and that the
  13.  * names of AT&T Bell Laboratories any of their entities not be used
  14.  * in advertising or publicity pertaining to distribution of the
  15.  * software without specific, written prior permission.
  16.  *
  17.  * AT&T disclaims all warranties with regard to this software, including
  18.  * all implied warranties of merchantability and fitness.  In no event
  19.  * shall AT&T be liable for any special, indirect or consequential
  20.  * damages or any damages whatsoever resulting from loss of use, data
  21.  * or profits, whether in an action of contract, negligence or other
  22.  * tortious action, arising out of or in connection with the use or
  23.  * performance of this software.
  24.  *
  25.  * Hypertext widget created by George Howlett.
  26.  */
  27.  
  28. /*
  29.  * To do:
  30.  *
  31.  * 1) Fix scroll unit round off errors.
  32.  *
  33.  * 2) Bug in reporting errors in Tcl evaluations.
  34.  *
  35.  * 3) Selections of text. (characters, word, line)
  36.  *
  37.  * 4) Tabstops for easier placement of text and child widgets.
  38.  *    Use variable "tabstops" to set/reset tabstops.
  39.  *
  40.  * 5) Better error checking.
  41.  *
  42.  */
  43.  
  44. #include "blt.h"
  45. #include <X11/Xutil.h>
  46.  
  47. #ifndef HTEXT_VERSION
  48. #define HTEXT_VERSION "2.3"
  49. #endif
  50.  
  51. #define HTEXT_CMDNAME     "blt_htext"
  52.  
  53. #define LINES_ALLOC_CHUNK 512
  54.  
  55. #define BLT_MIN(a,b)    (((a)<(b))?(a):(b))
  56. #define BLT_MAX(a,b)    (((a)>(b))?(a):(b))
  57.  
  58. /*
  59.  * Flags passed to TkMeasureChars: taken from tkInt.h
  60.  */
  61. #define TK_WHOLE_WORDS          1
  62. #define TK_AT_LEAST_ONE         2
  63. #define TK_PARTIAL_OK           4
  64.  
  65. /*
  66.  * The following enumerated values are used as bit flags.
  67.  */
  68. typedef enum {
  69.     FILL_NONE, FILL_X, FILL_Y, FILL_BOTH
  70. } FillFlags;
  71.  
  72. static int ParseFill _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
  73.     Tk_Window tkwin, char *value, char *widgRec, int offset));
  74. static char *PrintFill _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
  75.     char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
  76.  
  77. static Tk_CustomOption FillOption =
  78. {
  79.     ParseFill, PrintFill, (ClientData)0
  80. };
  81.  
  82. /*
  83.  * Justify option values
  84.  */
  85. typedef enum {
  86.     JUSTIFY_CENTER, JUSTIFY_TOP, JUSTIFY_BOTTOM
  87. } Justify;
  88.  
  89. static int ParseJustify _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *value, char *widgRec, int offset));
  90. static char *PrintJustify _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
  91.  
  92. static Tk_CustomOption JustifyOption =
  93. {
  94.     ParseJustify, PrintJustify, (ClientData)0
  95. };
  96.  
  97. static char *fillStrings[] =
  98. {
  99.     "none", "x", "y", "both"
  100. };
  101. static char *justifyStrings[] =
  102. {
  103.     "center", "top", "bottom"
  104. };
  105.  
  106. typedef struct Child {
  107.     struct Htext *textPtr;    /* Pointer to parent's Htext structure */
  108.     Tk_Window tkwin;        /* Widget window */
  109.     int flags;
  110.  
  111.     int x, y;            /* Origin of child subwindow in text */
  112.  
  113.     /* Dimensions of the cavity surrounding the child window */
  114.  
  115.     unsigned int cavityWidth, cavityHeight;
  116.  
  117.     /*
  118.      * Dimensions of the child window.  Saved for later comparisons to
  119.      * check for resizing.
  120.      */
  121.     unsigned int windowWidth, windowHeight;
  122.  
  123.     int precedingTextEnd;    /* Number of characters of text */
  124.     int precedingTextWidth;    /* Width of normal text preceding child */
  125.  
  126.     Tk_Anchor anchor;
  127.     Justify justify;        /* Justification of region wrt to line */
  128.  
  129.     int reqWidth, reqHeight;    /* Requested dimension of cavity */
  130.     double relWidth, relHeight;    /* Relative dimensions of cavity.
  131.                  * Sizes are calculated with respect
  132.                  * the size of the viewport */
  133.     int padX, padY;        /* Extra padding to frame around */
  134.     int ipadX, ipadY;        /* Internal padding for window */
  135.  
  136.     FillFlags fill;        /* Fill style flag */
  137.  
  138. } Child;
  139.  
  140. /*
  141.  * Flag bits children subwindows:
  142.  */
  143. #define VISIBLE        4    /* Subwindow is visible in the viewport. */
  144.  
  145. /*
  146.  * Information used for parsing configuration specs:
  147.  */
  148.  
  149. /*
  150.  * Defaults for children:
  151.  */
  152.  
  153. #define DEF_CHILD_ANCHOR        "center"
  154. #define DEF_CHILD_FILL        "none"
  155. #define DEF_CHILD_HEIGHT    "0"
  156. #define DEF_CHILD_JUSTIFY    "center"
  157. #define DEF_CHILD_PAD_X        "0"
  158. #define DEF_CHILD_PAD_Y        "0"
  159. #define DEF_CHILD_REL_HEIGHT    "0.0"
  160. #define DEF_CHILD_REL_WIDTH      "0.0"
  161. #define DEF_CHILD_WIDTH      "0"
  162.  
  163. static Tk_ConfigSpec childConfigSpecs[] =
  164. {
  165.     {TK_CONFIG_ANCHOR, "-anchor", (char *)NULL, (char *)NULL,
  166.     DEF_CHILD_ANCHOR, Tk_Offset(Child, anchor), 
  167.         TK_CONFIG_DONT_SET_DEFAULT},
  168.     {TK_CONFIG_CUSTOM, "-fill", (char *)NULL, (char *)NULL,
  169.     DEF_CHILD_FILL, Tk_Offset(Child, fill),
  170.     TK_CONFIG_DONT_SET_DEFAULT, &FillOption},
  171.     {TK_CONFIG_PIXELS, "-height", (char *)NULL, (char *)NULL,
  172.     DEF_CHILD_HEIGHT, Tk_Offset(Child, reqHeight), 
  173.         TK_CONFIG_DONT_SET_DEFAULT},
  174.     {TK_CONFIG_CUSTOM, "-justify", (char *)NULL, (char *)NULL,
  175.     DEF_CHILD_JUSTIFY, Tk_Offset(Child, justify),
  176.     TK_CONFIG_DONT_SET_DEFAULT, &JustifyOption},
  177.     {TK_CONFIG_PIXELS, "-padx", (char *)NULL, (char *)NULL,
  178.     DEF_CHILD_PAD_X, Tk_Offset(Child, padX), 
  179.         TK_CONFIG_DONT_SET_DEFAULT},
  180.     {TK_CONFIG_PIXELS, "-pady", (char *)NULL, (char *)NULL,
  181.     DEF_CHILD_PAD_Y, Tk_Offset(Child, padY),
  182.         TK_CONFIG_DONT_SET_DEFAULT},
  183.     {TK_CONFIG_DOUBLE, "-relheight", (char *)NULL, (char *)NULL,
  184.     DEF_CHILD_REL_HEIGHT, Tk_Offset(Child, relHeight),
  185.         TK_CONFIG_DONT_SET_DEFAULT},
  186.     {TK_CONFIG_DOUBLE, "-relwidth", (char *)NULL, (char *)NULL,
  187.     DEF_CHILD_REL_WIDTH, Tk_Offset(Child, relWidth),
  188.         TK_CONFIG_DONT_SET_DEFAULT},
  189.     {TK_CONFIG_PIXELS, "-width", (char *)NULL, (char *)NULL,
  190.     DEF_CHILD_WIDTH, Tk_Offset(Child, reqWidth),
  191.         TK_CONFIG_DONT_SET_DEFAULT},
  192.     {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
  193.     (char *)NULL, 0, 0}
  194. };
  195.  
  196. /*
  197.  * Structure to contain the contents of a single line of text and the
  198.  * children on that line.
  199.  *
  200.  * Individual lines are not configurable, although changes to the size
  201.  * of children do effect its values.
  202. */
  203.  
  204. typedef struct {
  205.     int offset;            /* Pixel offset from world coordinate 0,0 */
  206.  
  207.     short int height;        /* Height of line */
  208.     short int width;        /* Width of line */
  209.     short int baseline;        /* Baseline of text */
  210.     short int numChars;        /* Number of characters in normal text */
  211.     char *text;            /* The plain text on the line */
  212.  
  213.     Blt_LinkedList windowList;    /* List of subwindows on the line of text */
  214.  
  215. } Line;
  216.  
  217.  
  218. typedef struct {
  219.     Tk_3DBorder border;        /* Border and background for selected
  220.                      * characters. */
  221.     int borderWidth;        /* Width of border around selection. */
  222.     XColor *activeFg;        /* Foreground color for selected text. */
  223.     GC gc;            /* For drawing selected text. */
  224.  
  225.     int first;            /* Position of first selected character
  226.                  * (-1 means nothing is selected). */
  227.     int last;            /* Position of last selected character.
  228.                  * (-1  means nothing is selected). */
  229.     int anchor;            /* Fixed end of selection
  230.                      * (i.e. "select to" operation will
  231.                      * use this as one end of the selection).*/
  232. } Selection;
  233.  
  234. typedef struct {
  235.     long int x, y;
  236.     long int width, height;
  237. } ViewPort;
  238.  
  239. /*
  240.  * Hypertext widget.
  241.  */
  242. typedef struct Htext {
  243.     Tk_Window tkwin;        /* Window that embodies the widget.
  244.                                  * NULL means that the window has been
  245.                                  * destroyed but the data structures
  246.                                  * haven't yet been cleaned up.*/
  247.     Display *display;        /* Display containing widget; needed,
  248.                                  * among other things, to release
  249.                                  * resources after tkwin has already
  250.                                  * gone away. */
  251.     Tcl_Interp *interp;        /* Interpreter associated with widget. */
  252.     unsigned int flags;
  253.  
  254.     /* User-configurable fields */
  255.  
  256.     Tk_3DBorder border;        /* Don't current use borders (only color) */
  257.     XColor *normalFg;
  258.     int reqLineNum;        /* Line requested by "gotoline" command */
  259.     int reqWidth, reqHeight;    /* Requested dimensions of the text window */
  260.     int maxWidth, maxHeight;
  261.     Cursor cursor;        /* X Cursor for child */
  262.  
  263.     char *yScrollCmd;        /* Name of vertical scrollbar to invoke */
  264.     int scrollY;        /* # of pixels per vert scroll */
  265.     char *xScrollCmd;        /* Name of horizontal scroll bar to invoke */
  266.     int scrollX;        /* # of pixels per horiz scroll */
  267.     int lineSpacing;        /* # of pixels between lines */
  268.     int specChar;        /* Special character designating a TCL
  269.                      * command block in a hypertext file. */
  270.     XFontStruct *fontPtr;    /* Font for normal text */
  271.     char *fileName;        /* Name of hypertext file  */
  272.     char *text;            /* Text */
  273.  
  274.  
  275.     /*
  276.      * The view port is the width and height of the window and the
  277.      * origin of the viewport (upper left corner) in world coordinates.
  278.      */
  279.     ViewPort vPort;        /* Position and size of viewport in
  280.                  * virtual coordinates */
  281.     int pendingX, pendingY;    /* New upper-left corner (origin) of
  282.                  * the viewport (not yet posted) */
  283.  
  284.     int first, last;        /* Range of lines displayed */
  285.  
  286.     Tcl_HashTable subwindows;    /* Table of child sub-windows */
  287.     GC gc;            /* Graphics context for normal text */
  288.  
  289. #ifdef notdef
  290.     Selection selection;
  291. #endif
  292.     /*
  293.      * Scanning information:
  294.      */
  295.     XPoint scanMark;        /* Anchor position of scan */
  296.     XPoint scanPt;        /* x,y position where the scan started. */
  297.  
  298.     unsigned int width, height;    /* Size of the window: saved to recognize
  299.                  * when the viewport is resized. */
  300.     char *intBuffer;        /* Internal text buffer */
  301.     Line **linePtrPtr;        /* Array of text lines */
  302.     unsigned int numLines;    /* # of line entered into array. */
  303.     unsigned int arraySize;    /* Size of array allocated. */
  304.  
  305. } Htext;
  306.  
  307. /*
  308.  * Bit flags for the hypertext widget:
  309.  */
  310. #define REDRAW_PENDING    1    /* A DoWhenIdle handler has already
  311.                  * been queued to redraw the window */
  312. #define IGNORE_EXPOSURES (1<<1)    /* Ignore exposure events in the text
  313.                  * window.  Potentially many expose
  314.                  * events can occur while rearranging
  315.                  * subwindows during a single call to
  316.                  * the DisplayText.  */
  317. #define VIEW_RESIZED    (1<<2)    /* Size of viewport (window) has
  318.                    changed */
  319. #define VIEW_MOVED     (1<<3)    /* Position of viewport has moved.
  320.                  * This occurs when scrolling or
  321.                  * goto-ing a new line */
  322. #define REQUEST_LAYOUT     (1<<4)    /* Something has happened which
  323.                  * requires the layout of text and
  324.                  * child window positions to be
  325.                  * recalculated.  The following
  326.                  * actions may cause this:
  327.                  *
  328.                  * 1) the contents of the hypertext
  329.                  *    has changed by either the -file or
  330.                  *    -text options.
  331.                  *
  332.                  * 2) a text attribute has changed
  333.                  *    (line spacing, font, etc)
  334.                  *
  335.                  * 3) a subwindow has been resized or
  336.                  *    moved.
  337.                  *
  338.                  * 4) a child configuration option has
  339.                  *    changed.
  340.                  */
  341. #define LAYOUT_CHANGED     (1<<5)    /* The layout was recalculated and the
  342.                  * size of the world (text layout) has
  343.                  * changed. */
  344. #define REQUEST_GOTO     (1<<6)    /* Indicates the starting text line
  345.                  * number has changed. To be reflected
  346.                  * the next time the widget is
  347.                  * redrawn. */
  348.  
  349. #define DEF_HTEXT_BG_COLOR    BISQUE1
  350. #define DEF_HTEXT_BG_MONO    WHITE
  351. #define DEF_HTEXT_CURSOR    "pencil"
  352. #define DEF_HTEXT_FG_COLOR    BLACK
  353. #define DEF_HTEXT_FG_MONO    BLACK
  354. #define DEF_HTEXT_FILE_NAME    (char *)NULL
  355. #define DEF_HTEXT_FONT        "*-Helvetica-Bold-R-Normal-*-14-*"
  356. #define DEF_HTEXT_HEIGHT    "0"
  357. #define DEF_HTEXT_LINE_SPACING    "1"
  358. #define DEF_HTEXT_MAX_HEIGHT    "9.5i"
  359. #define DEF_HTEXT_MAX_WIDTH     "6.5i"
  360. #define DEF_HTEXT_SCROLL_UNITS    "10"
  361. #define DEF_HTEXT_SPEC_CHAR    "0x25"
  362. #define DEF_HTEXT_TEXT        (char *)NULL
  363. #define DEF_HTEXT_WIDTH        "0"
  364.  
  365. static Tk_ConfigSpec configSpecs[] =
  366. {
  367.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  368.     DEF_HTEXT_BG_COLOR, Tk_Offset(Htext, border),
  369.     TK_CONFIG_COLOR_ONLY},
  370.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  371.     DEF_HTEXT_BG_MONO, Tk_Offset(Htext, border), TK_CONFIG_MONO_ONLY},
  372.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
  373.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  374.     DEF_HTEXT_CURSOR, Tk_Offset(Htext, cursor), TK_CONFIG_NULL_OK},
  375.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
  376.     {TK_CONFIG_STRING, "-filename", "fileName", "FileName",
  377.     DEF_HTEXT_FILE_NAME, Tk_Offset(Htext, fileName), TK_CONFIG_NULL_OK},
  378.     {TK_CONFIG_FONT, "-font", "font", "Font",
  379.     DEF_HTEXT_FONT, Tk_Offset(Htext, fontPtr), 0},
  380.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  381.     DEF_HTEXT_FG_COLOR, Tk_Offset(Htext, normalFg), TK_CONFIG_COLOR_ONLY},
  382.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  383.     DEF_HTEXT_FG_MONO, Tk_Offset(Htext, normalFg), TK_CONFIG_MONO_ONLY},
  384.     {TK_CONFIG_PIXELS, "-height", "height", "Height",
  385.     DEF_HTEXT_HEIGHT, Tk_Offset(Htext, reqHeight), 
  386.         TK_CONFIG_DONT_SET_DEFAULT},
  387.     {TK_CONFIG_PIXELS, "-linespacing", "lineSpacing", "LineSpacing",
  388.     DEF_HTEXT_LINE_SPACING, Tk_Offset(Htext, lineSpacing), 
  389.         TK_CONFIG_DONT_SET_DEFAULT},
  390.     {TK_CONFIG_PIXELS, "-maxheight", "maxHeight", "MaxHeight",
  391.     DEF_HTEXT_MAX_HEIGHT, Tk_Offset(Htext, maxHeight), 0},
  392.     {TK_CONFIG_PIXELS, "-maxwidth", "maxWidth", "MaxWidth",
  393.     DEF_HTEXT_MAX_WIDTH, Tk_Offset(Htext, maxWidth), 0},
  394.     {TK_CONFIG_INT, "-specialchar", "specialChar", "SpecialChar",
  395.     DEF_HTEXT_SPEC_CHAR, Tk_Offset(Htext, specChar), 0},
  396.     {TK_CONFIG_STRING, "-text", "text", "Text",
  397.     DEF_HTEXT_TEXT, Tk_Offset(Htext, text), TK_CONFIG_NULL_OK},
  398.     {TK_CONFIG_PIXELS, "-width", "width", "Width",
  399.     DEF_HTEXT_WIDTH, Tk_Offset(Htext, reqWidth), 
  400.         TK_CONFIG_DONT_SET_DEFAULT},
  401.     {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
  402.     (char *)NULL, Tk_Offset(Htext, xScrollCmd), TK_CONFIG_NULL_OK},
  403.     {TK_CONFIG_PIXELS, "-xscrollunits", "xScrollUnits", "ScrollUnits",
  404.     DEF_HTEXT_SCROLL_UNITS, Tk_Offset(Htext, scrollX), 
  405.         TK_CONFIG_DONT_SET_DEFAULT},
  406.     {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
  407.     (char *)NULL, Tk_Offset(Htext, yScrollCmd), TK_CONFIG_NULL_OK},
  408.     {TK_CONFIG_PIXELS, "-yscrollunits", "yScrollUnits", "yScrollUnits",
  409.     DEF_HTEXT_SCROLL_UNITS, Tk_Offset(Htext, scrollY), 
  410.         TK_CONFIG_DONT_SET_DEFAULT},
  411.     {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
  412.     (char *)NULL, 0, 0}
  413. };
  414.  
  415. /* Forward Declarations */
  416.  
  417. static int TextWidgetCmd _ANSI_ARGS_((ClientData clientData,
  418.     Tcl_Interp *interp, int argc, char **argv));
  419. static void DestroyText _ANSI_ARGS_((ClientData clientdata));
  420. static int ConfigureHtext _ANSI_ARGS_((Tcl_Interp *interp,
  421.     Htext *textPtr, int argc, char **argv, int flags));
  422. static void TextEventProc _ANSI_ARGS_((ClientData clientdata,
  423.     XEvent *eventPtr));
  424. static void EventuallyRedraw _ANSI_ARGS_((Htext *textPtr));
  425. static int IncludeText _ANSI_ARGS_((Tcl_Interp *, Htext *, char *));
  426. static int ParseInput _ANSI_ARGS_((Tcl_Interp *, Htext *, char *));
  427. static char *ReadFile _ANSI_ARGS_((Tcl_Interp *interp, char *fileName));
  428. static int AppendChild _ANSI_ARGS_((Htext *, Tcl_Interp *, int, char **));
  429. static Child *CreateChild _ANSI_ARGS_((Htext *textPtr, char *pathName));
  430. static void DestroyChild _ANSI_ARGS_((Child *childPtr));
  431. static void ChildStructureProc _ANSI_ARGS_((ClientData clientdata,
  432.     XEvent *eventPtr));
  433. static Child *FindChild _ANSI_ARGS_((Htext *textPtr, char *childName));
  434. static char *CollectCommand _ANSI_ARGS_((Htext *textPtr, char *inputPtr,
  435.     char *command));
  436. static Line *NewLine _ANSI_ARGS_((Htext *textPtr));
  437. static void DestroyLine _ANSI_ARGS_((Line *linePtr));
  438. static Line *GetLastLine _ANSI_ARGS_((Htext *textPtr));
  439. static void SetTextInfo _ANSI_ARGS_((Line *linePtr, char *line, int size));
  440. static void GetTextInfo _ANSI_ARGS_((Line *linePtr, char *line, int *size));
  441. static void ComputeLayout _ANSI_ARGS_((Htext *textPtr));
  442. static void LayoutLine _ANSI_ARGS_((Htext *textPtr, Line *linePtr));
  443. static void FreeAllLines _ANSI_ARGS_((Htext *textPtr));
  444. static void AdjustLinesAllocated _ANSI_ARGS_((Htext *textPtr));
  445. static void DisplayText _ANSI_ARGS_((ClientData clientData));
  446. static void DrawPage _ANSI_ARGS_((Htext *textPtr, int deltaY));
  447. static void MoveChild _ANSI_ARGS_((Child *childPtr, int offset));
  448. static void UpdateScrollbar _ANSI_ARGS_((Tcl_Interp *interp, char *cmd,
  449.     int total, int window, int first, int units));
  450. static int GetVisibleLines _ANSI_ARGS_((Htext *textPtr));
  451. static int LineSearch _ANSI_ARGS_((Htext *textPtr, int position,
  452.     int low, int high));
  453. static int ResizeArray _ANSI_ARGS_((char **memPtr, unsigned int elemSize,
  454.     unsigned int oldCount, unsigned int newCount));
  455. static void CreateTraces _ANSI_ARGS_((Htext *textPtr));
  456. static void DeleteTraces _ANSI_ARGS_((Htext *textPtr));
  457. static void ChildGeometryProc _ANSI_ARGS_((ClientData clientData,
  458.     Tk_Window tkwin));
  459. static void SendBogusEvent _ANSI_ARGS_((Tk_Window tkwin));
  460.  
  461. extern void TkDisplayChars _ANSI_ARGS_((Display * display, Drawable drawable,
  462.     GC gc, XFontStruct *fontStructPtr, char *string, int numChars,
  463.     int x, int y, int flags));
  464. extern int TkMeasureChars _ANSI_ARGS_((XFontStruct *fontStructPtr,
  465.     char *source, int maxChars, int startX, int maxX, int flags,
  466.     int *nextXPtr));
  467.  
  468. extern int Blt_OptionChanged _ANSI_ARGS_(VARARGS);
  469.  
  470. /* end of Forward Declarations */
  471.  
  472. /*
  473.  *----------------------------------------------------------------------
  474.  *
  475.  * ParseFill --
  476.  *
  477.  *    Converts the fill style string into its numeric representation.
  478.  *    This configuration option affects how the slave window is expanded
  479.  *    if there is extra space in the cubicle in which it sits.
  480.  *
  481.  *    Valid style strings are:
  482.  *
  483.  *        "none"   Don't expand the window to fill the cubicle.
  484.  *         "x"         Expand only the window's width.
  485.  *        "y"         Expand only the window's height.
  486.  *        "both"   Expand both the window's height and width.
  487.  *
  488.  *----------------------------------------------------------------------
  489.  */
  490. /*ARGSUSED*/
  491. static int
  492. ParseFill(clientData, interp, tkwin, value, widgRec, offset)
  493.     ClientData clientData;    /* not used */
  494.     Tcl_Interp *interp;        /* Interpreter to send results back to */
  495.     Tk_Window tkwin;        /* not used */
  496.     char *value;        /* Fill style string */
  497.     char *widgRec;        /* Cubicle structure record */
  498.     int offset;            /* Offset of style in record */
  499. {
  500.     FillFlags *fillPtr = (FillFlags *)(widgRec + offset);
  501.     int length;
  502.     char c;
  503.  
  504.     c = value[0];
  505.     length = strlen(value);
  506.     if ((c == 'n') && (strncmp(value, "none", length) == 0)) {
  507.     *fillPtr = FILL_NONE;
  508.     } else if ((c == 'x') && (strncmp(value, "x", length) == 0)) {
  509.     *fillPtr = FILL_X;
  510.     } else if ((c == 'y') && (strncmp(value, "y", length) == 0)) {
  511.     *fillPtr = FILL_Y;
  512.     } else if ((c == 'b') && (strncmp(value, "both", length) == 0)) {
  513.     *fillPtr = FILL_BOTH;
  514.     } else {
  515.     Tcl_AppendResult(interp, "bad fill argument \"", value,
  516.         "\": should be none, x, y, or both", (char *)NULL);
  517.     return TCL_ERROR;
  518.     }
  519.     return (TCL_OK);
  520. }
  521.  
  522. /*
  523.  *----------------------------------------------------------------------
  524.  *
  525.  * PrintFill --
  526.  *
  527.  *    Returns the fill style string based upon the fill flags.
  528.  *
  529.  * Results:
  530.  *    The fill style string is returned.
  531.  *
  532.  *----------------------------------------------------------------------
  533.  */
  534. /*ARGSUSED*/
  535. static char *
  536. PrintFill(clientData, tkwin, widgRec, offset, freeProcPtr)
  537.     ClientData clientData;    /* not used */
  538.     Tk_Window tkwin;        /* not used */
  539.     char *widgRec;        /* Row/column structure record */
  540.     int offset;            /* Offset of fill in Partition record */
  541.     Tcl_FreeProc **freeProcPtr;    /* not used */
  542. {
  543.     FillFlags fill = *(FillFlags *)(widgRec + offset);
  544.  
  545.     return (fillStrings[(int)fill]);
  546. }
  547.  
  548. /*
  549.  *----------------------------------------------------------------------
  550.  *
  551.  * ParseJustify --
  552.  *
  553.  *     Converts the justification string into its numeric
  554.  *     representation. This configuration option affects how the
  555.  *    slave window is positioned with respect to the line on which
  556.  *    it sits.
  557.  *
  558.  *    Valid style strings are:
  559.  *
  560.  *    "top"      Uppermost point of region is top of the line's
  561.  *           text
  562.  *     "center"   Center point of region is line's baseline.
  563.  *    "bottom"   Lowermost point of region is bottom of the
  564.  *           line's text
  565.  *
  566.  * Returns:
  567.  *    A standard Tcl result.  If the value was not valid
  568.  *
  569.  *---------------------------------------------------------------------- */
  570. /*ARGSUSED*/
  571. static int
  572. ParseJustify(clientData, interp, tkwin, value, widgRec, offset)
  573.     ClientData clientData;    /* not used */
  574.     Tcl_Interp *interp;        /* Interpreter to send results back to */
  575.     Tk_Window tkwin;        /* not used */
  576.     char *value;        /* Justification string */
  577.     char *widgRec;        /* Structure record */
  578.     int offset;            /* Offset of justify in record */
  579. {
  580.     Justify *justPtr = (Justify *)(widgRec + offset);
  581.     int length;
  582.     char c;
  583.  
  584.     c = value[0];
  585.     length = strlen(value);
  586.     if ((c == 'c') && (strncmp(value, "center", length) == 0)) {
  587.     *justPtr = JUSTIFY_CENTER;
  588.     } else if ((c == 't') && (strncmp(value, "top", length) == 0)) {
  589.     *justPtr = JUSTIFY_TOP;
  590.     } else if ((c == 'b') && (strncmp(value, "bottom", length) == 0)) {
  591.     *justPtr = JUSTIFY_BOTTOM;
  592.     } else {
  593.     Tcl_AppendResult(interp, "bad justification argument \"", value,
  594.         "\": should be center, top, or bottom", (char *)NULL);
  595.     return TCL_ERROR;
  596.     }
  597.     return (TCL_OK);
  598. }
  599.  
  600. /*
  601.  *----------------------------------------------------------------------
  602.  *
  603.  * PrintJustify --
  604.  *
  605.  *    Returns the justification style string based upon the value.
  606.  *
  607.  * Results:
  608.  *    The justification style string is returned.
  609.  *
  610.  *----------------------------------------------------------------------
  611.  */
  612. /*ARGSUSED*/
  613. static char *
  614. PrintJustify(clientData, tkwin, widgRec, offset, freeProcPtr)
  615.     ClientData clientData;    /* not used */
  616.     Tk_Window tkwin;        /* not used */
  617.     char *widgRec;        /* Structure record */
  618.     int offset;            /* Offset of justify record */
  619.     Tcl_FreeProc **freeProcPtr;    /* not used */
  620. {
  621.     Justify justify = *(Justify *)(widgRec + offset);
  622.  
  623.     return (justifyStrings[(int)justify]);
  624. }
  625.  
  626. /*
  627.  *----------------------------------------------------------------------
  628.  *
  629.  * EventuallyRedraw --
  630.  *
  631.  *    Queues a request to redraw the text window at the next idle
  632.  *    point.
  633.  *
  634.  * Results:
  635.  *    None.
  636.  *
  637.  * Side effects:
  638.  *    Information gets redisplayed.  Right now we don't do selective
  639.  *    redisplays:  the whole window will be redrawn.  This doesn't
  640.  *    seem to hurt performance noticeably, but if it does then this
  641.  *    could be changed.
  642.  *
  643.  *----------------------------------------------------------------------
  644.  */
  645. static void
  646. EventuallyRedraw(textPtr)
  647.     register Htext *textPtr;    /* Information about widget. */
  648. {
  649.     if ((textPtr->tkwin != NULL) && !(textPtr->flags & REDRAW_PENDING)) {
  650.     textPtr->flags |= REDRAW_PENDING;
  651.     Tk_DoWhenIdle(DisplayText, (ClientData)textPtr);
  652.     }
  653. }
  654.  
  655. /*
  656.  * ----------------------------------------------------------------------
  657.  *
  658.  * DestroyText --
  659.  *
  660.  *     This procedure is invoked by Tk_EventuallyFree or Tk_Release
  661.  *    to clean up the internal structure of a Htext at a safe time
  662.  *    (when no-one is using it anymore).
  663.  *
  664.  * Results:
  665.  *    None.
  666.  *
  667.  * Side effects:
  668.  *    Everything associated with the widget is freed up.
  669.  *
  670.  * ----------------------------------------------------------------------
  671.  */
  672.  
  673. static void
  674. DestroyText(clientData)
  675.     ClientData clientData;    /* Info about hypertext widget. */
  676. {
  677.     register Htext *textPtr = (Htext *)clientData;
  678.  
  679.     if (textPtr->gc != NULL) {
  680.     Tk_FreeGC(textPtr->display, textPtr->gc);
  681.     }
  682.     Tk_FreeOptions(configSpecs, (char *)textPtr, textPtr->display, 0);
  683.     FreeAllLines(textPtr);
  684.     Tcl_DeleteHashTable(&(textPtr->subwindows));
  685.     free((char *)textPtr);
  686. }
  687.  
  688. /*
  689.  * --------------------------------------------------------------
  690.  *
  691.  * TextEventProc --
  692.  *
  693.  *     This procedure is invoked by the Tk dispatcher for various
  694.  *     events on hypertext widgets.
  695.  *
  696.  * Results:
  697.  *    None.
  698.  *
  699.  * Side effects:
  700.  *    When the window gets deleted, internal structures get
  701.  *    cleaned up.  When it gets exposed, it is redisplayed.
  702.  *
  703.  * --------------------------------------------------------------
  704.  */
  705.  
  706. static void
  707. TextEventProc(clientData, eventPtr)
  708.     ClientData clientData;    /* Information about window. */
  709.     XEvent *eventPtr;        /* Information about event. */
  710. {
  711.     Htext *textPtr = (Htext *)clientData;
  712.  
  713.     if (eventPtr->type == ConfigureNotify) {
  714.     if ((textPtr->width != Tk_Width(textPtr->tkwin)) ||
  715.         (textPtr->height != Tk_Height(textPtr->tkwin))) {
  716.         textPtr->flags |= (REQUEST_LAYOUT | VIEW_RESIZED);
  717.         EventuallyRedraw(textPtr);
  718.     }
  719.     } else if (eventPtr->type == Expose) {
  720.  
  721.     /*
  722.      * If the Expose event was synthetic (i.e. we manufactured it
  723.      * ourselves during a redraw operation), toggle the bit flag
  724.      * which controls redraws.
  725.      */
  726.  
  727.     if (eventPtr->xexpose.send_event) {
  728.         textPtr->flags ^= IGNORE_EXPOSURES;
  729.         return;
  730.     }
  731.     if ((eventPtr->xexpose.count == 0) &&
  732.         !(textPtr->flags & IGNORE_EXPOSURES)) {
  733.         EventuallyRedraw(textPtr);
  734.     }
  735.     } else if (eventPtr->type == DestroyNotify) {
  736.     Tcl_DeleteCommand(textPtr->interp, Tk_PathName(textPtr->tkwin));
  737.     textPtr->tkwin = NULL;
  738.     if (textPtr->flags & REDRAW_PENDING) {
  739.         Tk_CancelIdleCall(DisplayText, (ClientData)textPtr);
  740.     }
  741.     Tk_EventuallyFree((ClientData)textPtr, DestroyText);
  742.     }
  743. }
  744.  
  745. /*
  746.  * ----------------------------------------------------------------------
  747.  *
  748.  * ConfigureHtext --
  749.  *
  750.  *     This procedure is called to process an argv/argc list, plus
  751.  *    the Tk option database, in order to configure (or reconfigure)
  752.  *    a hypertext widget.
  753.  *
  754.  *     The layout of the text must be calculated (by ComputeLayout)
  755.  *    whenever particular options change; -font, -filename, -linespacing
  756.  *    and -text options. If the user has changes one of these options,
  757.  *    it must be detected so that the layout can be recomputed. Since the
  758.  *    coordinates of the layout are virtual, there is no need to adjust
  759.  *    them if physical window attributes (window size, etc.)
  760.  *    change.
  761.  *
  762.  * Results:
  763.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  764.  *     returned, then interp->result contains an error message.
  765.  *
  766.  * Side effects:
  767.  *    Configuration information, such as text string, colors, font,
  768.  *     etc. get set for textPtr;  old resources get freed, if there were any.
  769.  *     The hypertext is redisplayed.
  770.  *
  771.  * ----------------------------------------------------------------------
  772.  */
  773.  
  774.  
  775. static int
  776. ConfigureHtext(interp, textPtr, argc, argv, flags)
  777.     Tcl_Interp *interp;        /* Used for error reporting. */
  778.     Htext *textPtr;        /* Information about widget; may or may not
  779.                      * already have values for some fields. */
  780.     int argc;            /* Number of valid entries in argv. */
  781.     char **argv;        /* Arguments. */
  782.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  783. {
  784.     XGCValues gcValues;
  785.     unsigned long valueMask;
  786.     GC newGC;
  787.  
  788.     if (Tk_ConfigureWidget(interp, textPtr->tkwin, configSpecs, argc, argv,
  789.         (char *)textPtr, flags) != TCL_OK) {
  790.     return TCL_ERROR;
  791.     }
  792.     Tk_SetBackgroundFromBorder(textPtr->tkwin, textPtr->border);
  793.     if (Blt_OptionChanged(configSpecs, "-font", "-linespacing", (char *)NULL)) {
  794.     textPtr->flags |= REQUEST_LAYOUT;
  795.     }
  796.     gcValues.font = textPtr->fontPtr->fid;
  797.     gcValues.foreground = textPtr->normalFg->pixel;
  798.     valueMask = GCForeground | GCFont;
  799.     newGC = Tk_GetGC(textPtr->tkwin, valueMask, &gcValues);
  800.     if (textPtr->gc != NULL) {
  801.     Tk_FreeGC(textPtr->display, textPtr->gc);
  802.     }
  803.     textPtr->gc = newGC;
  804.  
  805.     /*
  806.      * If the either the -text or -file option changed, read in the
  807.      * new text.  The -text option supersedes any -file option.
  808.      */
  809.     if (Blt_OptionChanged(configSpecs, "-filename", "-text", (char *)NULL)) {
  810.     int result;
  811.  
  812.     FreeAllLines(textPtr);    /* Delete any previous lines */
  813.     CreateTraces(textPtr);    /* Create variable traces */
  814.  
  815.     result = IncludeText(interp, textPtr, textPtr->fileName);
  816.  
  817.     DeleteTraces(textPtr);
  818.     if (result == TCL_ERROR) {
  819.         FreeAllLines(textPtr);
  820.         return TCL_ERROR;
  821.     }
  822.     AdjustLinesAllocated(textPtr);
  823.     textPtr->flags |= REQUEST_LAYOUT;    /* Mark for layout update */
  824.     }
  825.     EventuallyRedraw(textPtr);
  826.     return TCL_OK;
  827. }
  828.  
  829. /*
  830.  * ----------------------------------------------------------------------
  831.  *
  832.  * NewLine --
  833.  *
  834.  *     This procedure creates and initializes a new line of text.
  835.  *
  836.  * Results:
  837.  *    The return value is a pointer to a structure describing the new
  838.  *     line of text.  If an error occurred, then the return value is NULL
  839.  *    and an error message is left in interp->result.
  840.  *
  841.  * Side effects:
  842.  *    Memory is allocated.
  843.  *
  844.  * ----------------------------------------------------------------------
  845.  */
  846. static Line *
  847. NewLine(textPtr)
  848.     Htext *textPtr;
  849. {
  850.     Line *linePtr;
  851.  
  852.     if (textPtr->numLines >= textPtr->arraySize) {
  853.     if (textPtr->arraySize == 0) {
  854.         textPtr->arraySize = LINES_ALLOC_CHUNK;
  855.     } else {
  856.         textPtr->arraySize += textPtr->arraySize;
  857.     }
  858.     ResizeArray((char **)&(textPtr->linePtrPtr), sizeof(Line *),
  859.         textPtr->arraySize, textPtr->numLines);
  860.     }
  861.     /* Create new line entry and add to table */
  862.     linePtr = (Line *)calloc(1, sizeof(Line));
  863.     if (linePtr == NULL) {
  864.     textPtr->interp->result = "can't allocate line structure";
  865.     return NULL;
  866.     }
  867.     Blt_InitLinkedList(&(linePtr->windowList), TCL_ONE_WORD_KEYS);
  868.     textPtr->linePtrPtr[textPtr->numLines++] = linePtr;
  869.     return (linePtr);
  870. }
  871.  
  872. /*
  873.  * ----------------------------------------------------------------------
  874.  *
  875.  * DestroyLine --
  876.  *
  877.  *     This procedure is invoked by FreeAllLines to clean up the
  878.  *     internal structure of a line.
  879.  *
  880.  * Results: None.
  881.  *
  882.  * Side effects:
  883.  *    Everything associated with the line (text and children) is
  884.  *    freed up.
  885.  *
  886.  * ----------------------------------------------------------------------
  887.  */
  888. static void
  889. DestroyLine(linePtr)
  890.     register Line *linePtr;
  891. {
  892.     Blt_ListEntry *entryPtr;
  893.     Child *childPtr;
  894.  
  895.     /* Free the list of child structures */
  896.  
  897.     for (entryPtr = Blt_FirstListEntry(&(linePtr->windowList));
  898.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  899.     childPtr = (Child *)Blt_GetListValue(entryPtr);
  900.     DestroyChild(childPtr);
  901.     }
  902.     Blt_ClearList(&(linePtr->windowList));
  903.     if (linePtr->text != NULL) {
  904.     free(linePtr->text);
  905.     }
  906.     free((char *)linePtr);
  907. }
  908.  
  909. /*
  910.  * ----------------------------------------------------------------------
  911.  *
  912.  * AppendChild --
  913.  *
  914.  *     This procedure creates and initializes a new hyper text child.
  915.  *
  916.  * Results:
  917.  *    The return value is a standard Tcl result.
  918.  *
  919.  * Side effects:
  920.  *    Memory is allocated.  Child gets configured.
  921.  *
  922.  * ----------------------------------------------------------------------
  923.  */
  924. static int
  925. AppendChild(textPtr, interp, argc, argv)
  926.     Htext *textPtr;        /* Hypertext widget */
  927.     Tcl_Interp *interp;        /* Interpreter associated with widget */
  928.     int argc;            /* Number of arguments. */
  929.     char **argv;        /* Argument strings. */
  930. {
  931.     Line *linePtr;
  932.     Child *childPtr;
  933.     Blt_ListEntry *entryPtr;
  934.  
  935.     if (argc < 3) {
  936.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  937.         " append pathName ?options?\"", (char *)NULL);
  938.     return TCL_ERROR;
  939.     }
  940.     childPtr = CreateChild(textPtr, argv[2]);
  941.     if (childPtr == NULL) {
  942.     return TCL_ERROR;
  943.     }
  944.     if (Tk_ConfigureWidget(interp, textPtr->tkwin, childConfigSpecs,
  945.         argc - 3, argv + 3, (char *)childPtr, 0) != TCL_OK) {
  946.     return TCL_ERROR;
  947.     }
  948.     /*
  949.      * Append child to list of subwindows of the last line.
  950.      */
  951.     linePtr = GetLastLine(textPtr);
  952.     entryPtr = Blt_CreateListEntry((char *)childPtr->tkwin);
  953.     Blt_LinkListAfter(&(linePtr->windowList), entryPtr,
  954.     (Blt_ListEntry *)NULL);
  955.     Blt_SetListValue(entryPtr, childPtr);
  956.     linePtr->width += childPtr->cavityWidth;
  957.     childPtr->precedingTextEnd = linePtr->numChars;
  958.  
  959.     textPtr->flags |= REQUEST_LAYOUT;
  960.     EventuallyRedraw(textPtr);
  961.     return TCL_OK;
  962. }
  963.  
  964. /*
  965.  * -----------------------------------------------------------------
  966.  *
  967.  * TranslateAnchor --
  968.  *
  969.  *     Translate the coordinates of a given bounding box based
  970.  *    upon the anchor specified.  The anchor indicates where
  971.  *    the given xy position is in relation to the bounding box.
  972.  *
  973.  *          nw --- n --- ne
  974.  *          |            |     x,y ---+
  975.  *          w   center   e      |     |
  976.  *          |            |      +-----+
  977.  *          sw --- s --- se
  978.  *
  979.  * Results:
  980.  *    The translated coordinates of the bounding box are returned.
  981.  *
  982.  * -----------------------------------------------------------------
  983.  */
  984. static XPoint
  985. TranslateAnchor(deltaX, deltaY, anchor)
  986.     int deltaX, deltaY;        /* Difference between outer and inner regions
  987.                  */
  988.     Tk_Anchor anchor;        /* Direction of the anchor */
  989. {
  990.     XPoint anchorPos;
  991.  
  992.     anchorPos.x = anchorPos.y = 0;
  993.     switch (anchor) {
  994.     case TK_ANCHOR_NW:        /* Upper left corner */
  995.     break;
  996.     case TK_ANCHOR_W:        /* Left center */
  997.     anchorPos.y = (deltaY / 2);
  998.     break;
  999.     case TK_ANCHOR_SW:        /* Lower left corner */
  1000.     anchorPos.y = deltaY;
  1001.     break;
  1002.     case TK_ANCHOR_N:        /* Top center */
  1003.     anchorPos.x = (deltaX / 2);
  1004.     break;
  1005.     case TK_ANCHOR_CENTER:    /* Centered */
  1006.     anchorPos.x = (deltaX / 2);
  1007.     anchorPos.y = (deltaY / 2);
  1008.     break;
  1009.     case TK_ANCHOR_S:        /* Bottom center */
  1010.     anchorPos.x = (deltaX / 2);
  1011.     anchorPos.y = deltaY;
  1012.     break;
  1013.     case TK_ANCHOR_NE:        /* Upper right corner */
  1014.     anchorPos.x = deltaX;
  1015.     break;
  1016.     case TK_ANCHOR_E:        /* Right center */
  1017.     anchorPos.x = deltaX;
  1018.     anchorPos.y = (deltaY / 2);
  1019.     break;
  1020.     case TK_ANCHOR_SE:        /* Lower right corner */
  1021.     anchorPos.x = deltaX;
  1022.     anchorPos.y = deltaY;
  1023.     break;
  1024.     }
  1025.     return (anchorPos);
  1026. }
  1027.  
  1028. /*
  1029.  * ----------------------------------------------------------------------
  1030.  *
  1031.  * CreateChild --
  1032.  *
  1033.  *     This procedure creates and initializes a new child subwindow
  1034.  *    in the hyper text widget.
  1035.  *
  1036.  * Results:
  1037.  *    The return value is a pointer to a structure describing the
  1038.  *    new child.  If an error occurred, then the return value is
  1039.  *      NULL and an error message is left in interp->result.
  1040.  *
  1041.  * Side effects:
  1042.  *    Memory is allocated. Child window is mapped. Callbacks are set
  1043.  *    up for subwindow resizes and geometry requests.
  1044.  *
  1045.  * ----------------------------------------------------------------------
  1046.  */
  1047. static Child *
  1048. CreateChild(textPtr, name)
  1049.     Htext *textPtr;        /* Hypertext widget */
  1050.     char *name;            /* Name of child window */
  1051. {
  1052.     register Child *childPtr;
  1053.     Tk_Window tkwin;
  1054.     char buf[BUFSIZ];
  1055.     Tcl_HashEntry *entryPtr;
  1056.     int dummy;
  1057.  
  1058.     if (name[0] != '.') {    /* Relative path, make absolute */
  1059.     sprintf(buf, "%s.%s", Tk_PathName(textPtr->tkwin), name);
  1060.     name = buf;
  1061.     }
  1062.     /* Get the Tk window and parent Tk window associated with the child */
  1063.     tkwin = Tk_NameToWindow(textPtr->interp, name, textPtr->tkwin);
  1064.     if (tkwin == NULL) {
  1065.     return NULL;
  1066.     }
  1067.     if (FindChild(textPtr, name) != NULL) {
  1068.     Tcl_AppendResult(textPtr->interp, "\"", name,
  1069.         "\" is already appended to ", Tk_PathName(textPtr->tkwin),
  1070.         (char *)NULL);
  1071.     return NULL;
  1072.     }
  1073.     if (textPtr->tkwin != Tk_Parent(tkwin)) {
  1074.     Tcl_AppendResult(textPtr->interp, "\"", name,
  1075.         "\" is not a child of ", Tk_PathName(textPtr->tkwin),
  1076.         (char *)NULL);
  1077.     return NULL;
  1078.     }
  1079.     childPtr = (Child *)calloc(1, sizeof(Child));
  1080.     if (childPtr == NULL) {
  1081.     textPtr->interp->result = "can't create child structure";
  1082.     return NULL;
  1083.     }
  1084.     childPtr->tkwin = tkwin;
  1085.     childPtr->textPtr = textPtr;
  1086.     childPtr->x = childPtr->y = 0;
  1087.     childPtr->fill = FILL_NONE;
  1088.     childPtr->justify = JUSTIFY_CENTER;
  1089.     childPtr->anchor = TK_ANCHOR_CENTER;
  1090.     entryPtr = Tcl_CreateHashEntry(&(textPtr->subwindows), (char *)tkwin,
  1091.     &dummy);
  1092.     Tcl_SetHashValue(entryPtr, (ClientData)childPtr);
  1093.  
  1094.     Tk_ManageGeometry(tkwin, ChildGeometryProc, (ClientData)childPtr);
  1095.     Tk_CreateEventHandler(tkwin, StructureNotifyMask, ChildStructureProc,
  1096.     (ClientData)childPtr);
  1097.     return (childPtr);
  1098. }
  1099.  
  1100. /*
  1101.  * ----------------------------------------------------------------------
  1102.  *
  1103.  * DestroyChild --
  1104.  *
  1105.  *     This procedure is invoked by DestroyLine to clean up the
  1106.  *     internal structure of a child.
  1107.  *
  1108.  * Results:
  1109.  *    None.
  1110.  *
  1111.  * Side effects:
  1112.  *    Everything associated with the widget is freed up.
  1113.  *
  1114.  * ----------------------------------------------------------------------
  1115.  */
  1116. static void
  1117. DestroyChild(childPtr)
  1118.     register Child *childPtr;
  1119. {
  1120.     /* Destroy the child window if it still exists */
  1121.     if (childPtr->tkwin != NULL) {
  1122.     Tcl_HashEntry *entryPtr;
  1123.  
  1124.     Tk_DeleteEventHandler(childPtr->tkwin, StructureNotifyMask,
  1125.         ChildStructureProc, (ClientData)childPtr);
  1126.     entryPtr = Tcl_FindHashEntry(&(childPtr->textPtr->subwindows),
  1127.         (char *)childPtr->tkwin);
  1128.     Tcl_DeleteHashEntry(entryPtr);
  1129.     Tk_DestroyWindow(childPtr->tkwin);
  1130.     }
  1131.     free((char *)childPtr);
  1132. }
  1133.  
  1134. /*
  1135.  * --------------------------------------------------------------
  1136.  *
  1137.  * TextEventProc --
  1138.  *
  1139.  *     This procedure is invoked by the Tk dispatcher for various
  1140.  *     events on hypertext widgets.
  1141.  *
  1142.  * Results:
  1143.  *    None.
  1144.  *
  1145.  * Side effects:
  1146.  *    When the window gets deleted, internal structures get
  1147.  *    cleaned up.  When it gets exposed, it is redisplayed.
  1148.  *
  1149.  * --------------------------------------------------------------
  1150.  */
  1151. static void
  1152. ChildStructureProc(clientData, eventPtr)
  1153.     ClientData clientData;    /* Information about window. */
  1154.     XEvent *eventPtr;        /* Information about event. */
  1155. {
  1156.     register Child *childPtr = (Child *)clientData;
  1157.     Htext *textPtr;
  1158.  
  1159.     if ((childPtr == NULL) || (childPtr->tkwin == NULL)) {
  1160.     return;
  1161.     }
  1162.     textPtr = childPtr->textPtr;
  1163.  
  1164.     if (eventPtr->type == DestroyNotify) {
  1165.     /*
  1166.      * Mark the child as deleted by dereferencing the Tk window
  1167.      * pointer. Zero out the height and width to collapse the area
  1168.      * used by the child.  Redraw the window only if the child is
  1169.      * currently visible and mapped.
  1170.      */
  1171.     childPtr->textPtr->flags |= REQUEST_LAYOUT;
  1172.     if (Tk_IsMapped(childPtr->tkwin) && (childPtr->flags & VISIBLE)) {
  1173.         EventuallyRedraw(textPtr);
  1174.     }
  1175.     Tk_DeleteEventHandler(childPtr->tkwin, StructureNotifyMask,
  1176.         ChildStructureProc, (ClientData)childPtr);
  1177.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&(childPtr->textPtr->subwindows),
  1178.         (char *)childPtr->tkwin));
  1179.     childPtr->tkwin = NULL;
  1180.     } else if (eventPtr->type == ConfigureNotify) {
  1181.  
  1182.     /*
  1183.      * Children can't request new positions by themselves, so worry
  1184.      * only about resizing.
  1185.      */
  1186.  
  1187.     if (childPtr->windowWidth != Tk_Width(childPtr->tkwin) ||
  1188.         childPtr->windowHeight != Tk_Height(childPtr->tkwin)) {
  1189.         EventuallyRedraw(textPtr);
  1190.         textPtr->flags |= REQUEST_LAYOUT;
  1191.     }
  1192.     }
  1193. }
  1194.  
  1195. /*
  1196.  *----------------------------------------------------------------------
  1197.  *
  1198.  * ComputeLayout --
  1199.  *
  1200.  *    This procedure computes the total width and height needed
  1201.  *      to contain the text and children from all the lines of text.
  1202.  *      It merely sums the heights and finds the maximum width of
  1203.  *    all the lines.  The width and height are needed for scrolling.
  1204.  *
  1205.  * Results:
  1206.  *    None.
  1207.  *
  1208.  *----------------------------------------------------------------------
  1209.  */
  1210. static void
  1211. ComputeLayout(textPtr)
  1212.     Htext *textPtr;
  1213. {
  1214.     register int count;
  1215.     register Line **linePtrPtr;
  1216.     register int height, width;
  1217.  
  1218.     width = height = 0;
  1219.     linePtrPtr = textPtr->linePtrPtr;
  1220.     for (count = 0; count < textPtr->numLines; count++) {
  1221.     (*linePtrPtr)->offset = height;
  1222.     LayoutLine(textPtr, *linePtrPtr);
  1223.     height += (*linePtrPtr)->height;
  1224.     if ((*linePtrPtr)->width > width) {
  1225.         width = (*linePtrPtr)->width;
  1226.     }
  1227.     linePtrPtr++;
  1228.     }
  1229.     /*
  1230.      * Set changed flag if new layout changed size of virtual text.
  1231.      */
  1232.     if ((height != textPtr->vPort.height) ||
  1233.     (width != textPtr->vPort.width)) {
  1234.     textPtr->flags |= LAYOUT_CHANGED;
  1235.     textPtr->vPort.height = height;
  1236.     textPtr->vPort.width = width;
  1237.     }
  1238. }
  1239.  
  1240. /*
  1241.  *----------------------------------------------------------------------
  1242.  *
  1243.  * GetReqWidth --
  1244.  *
  1245.  *    Returns the width requested by the child window. The requested
  1246.  *    space also includes any internal padding which has been designated
  1247.  *    for this window.
  1248.  *
  1249.  * Results:
  1250.  *    Returns the requested width of the child window.
  1251.  *
  1252.  *----------------------------------------------------------------------
  1253.  */
  1254.  
  1255. static int
  1256. GetReqWidth(childPtr)
  1257.     Child *childPtr;
  1258. {
  1259.     int width;
  1260.  
  1261.     if (childPtr->reqWidth > 0) {
  1262.     width = childPtr->reqWidth;
  1263.     } else if (childPtr->relWidth > 0.0) {
  1264.     width = (int)((double)Tk_Width(childPtr->textPtr->tkwin) *
  1265.         childPtr->relWidth + 0.5);
  1266.     } else {
  1267.     width = Tk_ReqWidth(childPtr->tkwin) + 2 * (childPtr->ipadX);
  1268.     }
  1269.     return (width);
  1270. }
  1271.  
  1272. /*
  1273.  *----------------------------------------------------------------------
  1274.  *
  1275.  * GetReqHeight --
  1276.  *
  1277.  *    Returns the height requested by the child window. The requested
  1278.  *    space also includes any internal padding which has been designated
  1279.  *    for this window.
  1280.  *
  1281.  * Results:
  1282.  *    Returns the requested height of the child window.
  1283.  *
  1284.  *----------------------------------------------------------------------
  1285.  */
  1286. static int
  1287. GetReqHeight(childPtr)
  1288.     Child *childPtr;
  1289. {
  1290.     int height;
  1291.  
  1292.     if (childPtr->reqHeight > 0) {
  1293.     height = childPtr->reqHeight;
  1294.     } else if (childPtr->relHeight > 0.0) {
  1295.     height = (int)((double)Tk_Height(childPtr->textPtr->tkwin) *
  1296.         childPtr->relHeight + 0.5);
  1297.     } else {
  1298.     height = Tk_ReqHeight(childPtr->tkwin) + 2 * (childPtr->ipadY);
  1299.     }
  1300.     return (height);
  1301. }
  1302.  
  1303. /*
  1304.  *----------------------------------------------------------------------
  1305.  *
  1306.  * GetCavityWidth --
  1307.  *
  1308.  *    Returns the width of the cavity based upon the requested size
  1309.  *    of the child window.  The requested space also includes any
  1310.  *    external padding which has been designated for this window.
  1311.  *
  1312.  * Results:
  1313.  *    Returns the requested width of the cavity.
  1314.  *
  1315.  *----------------------------------------------------------------------
  1316.  */
  1317.  
  1318. static int
  1319. GetCavityWidth(childPtr)
  1320.     Child *childPtr;
  1321. {
  1322.     int width;
  1323.  
  1324.     width = GetReqWidth(childPtr) +
  1325.     (2 * (Tk_Changes(childPtr->tkwin)->border_width + childPtr->padX));
  1326.     return (width);
  1327. }
  1328.  
  1329. /*
  1330.  *----------------------------------------------------------------------
  1331.  *
  1332.  * GetCavityHeight --
  1333.  *
  1334.  *    Returns the height of the cavity based upon the requested size
  1335.  *    of the child window.  The requested space also includes any
  1336.  *    external padding which has been designated for this window.
  1337.  *
  1338.  * Results:
  1339.  *    Returns the requested height of the cavity.
  1340.  *
  1341.  *----------------------------------------------------------------------
  1342.  */
  1343.  
  1344. static int
  1345. GetCavityHeight(childPtr)
  1346.     Child *childPtr;
  1347. {
  1348.     int height;
  1349.  
  1350.     height = GetReqHeight(childPtr) +
  1351.     (2 * (Tk_Changes(childPtr->tkwin)->border_width + childPtr->padY));
  1352.     return (height);
  1353. }
  1354.  
  1355. /*
  1356.  *----------------------------------------------------------------------
  1357.  *
  1358.  * LayoutLine --
  1359.  *
  1360.  *    This procedure computes the total width and height needed
  1361.  *      to contain the text and children for a particular line.
  1362.  *      It also calculates the baseline of the text on the line with
  1363.  *    respect to the other children on the line.
  1364.  *
  1365.  * Results:
  1366.  *    None.
  1367.  *
  1368.  *----------------------------------------------------------------------
  1369.  */
  1370. static void
  1371. LayoutLine(textPtr, linePtr)
  1372.     Htext *textPtr;
  1373.     Line *linePtr;
  1374. {
  1375.     register Child *childPtr;
  1376.     int numChars;
  1377.     int maxAscent, maxDescent, maxHeight;
  1378.     int ascent, descent;
  1379.     register int curPos = 0;
  1380.     int median;            /* Difference of font ascent/descent values */
  1381.     Blt_ListEntry *entryPtr;
  1382.     register int x, y;
  1383.     int newX;
  1384.  
  1385.     /*
  1386.      * Pass 1: Determine the maximum ascent (baseline) and descent
  1387.      * needed for the line.  We'll need this for figuring the top,
  1388.      * bottom, and center anchors.
  1389.      */
  1390.     /* Initialize line defaults */
  1391.     maxAscent = textPtr->fontPtr->ascent;
  1392.     maxDescent = textPtr->fontPtr->descent;
  1393.     median = textPtr->fontPtr->ascent - textPtr->fontPtr->descent;
  1394.     ascent = descent = 0;    /* Suppress compiler warnings */
  1395.     for (entryPtr = Blt_FirstListEntry(&(linePtr->windowList));
  1396.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1397.     childPtr = (Child *)Blt_GetListValue(entryPtr);
  1398.     if (childPtr->tkwin == NULL) {
  1399.         continue;
  1400.     }
  1401.     childPtr->cavityHeight = GetCavityHeight(childPtr);
  1402.     childPtr->cavityWidth = GetCavityWidth(childPtr);
  1403.     switch (childPtr->justify) {
  1404.     case JUSTIFY_TOP:
  1405.         ascent = textPtr->fontPtr->ascent + childPtr->padY;
  1406.         descent = childPtr->cavityHeight - textPtr->fontPtr->ascent;
  1407.         break;
  1408.     case JUSTIFY_CENTER:
  1409.         ascent = (childPtr->cavityHeight + median) / 2;
  1410.         descent = (childPtr->cavityHeight - median) / 2;
  1411.         break;
  1412.     case JUSTIFY_BOTTOM:
  1413.         ascent = childPtr->cavityHeight - textPtr->fontPtr->descent;
  1414.         descent = textPtr->fontPtr->descent;
  1415.         break;
  1416.     }
  1417.     if (descent > maxDescent) {
  1418.         maxDescent = descent;
  1419.     }
  1420.     if (ascent > maxAscent) {
  1421.         maxAscent = ascent;
  1422.     }
  1423.     }
  1424.  
  1425.     maxHeight = maxAscent + maxDescent + textPtr->lineSpacing;
  1426.     x = 0;            /* Always starts from x=0 */
  1427.     y = 0;            /* Suppress compiler warning */
  1428.     curPos = 0;
  1429.     /*
  1430.      * Pass 2: Find the placements of the text and children along each
  1431.      * line.
  1432.      */
  1433.     for (entryPtr = Blt_FirstListEntry(&(linePtr->windowList));
  1434.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1435.     childPtr = (Child *)Blt_GetListValue(entryPtr);
  1436.     if (childPtr->tkwin == NULL) {
  1437.         continue;
  1438.     }
  1439.     /* Get the width of the text leading to the child */
  1440.     numChars = (childPtr->precedingTextEnd - curPos);
  1441.     if ((numChars > 0) && (linePtr->text != NULL)) {
  1442.         TkMeasureChars(textPtr->fontPtr, linePtr->text + curPos,
  1443.         numChars, x, 10000, TK_PARTIAL_OK | TK_AT_LEAST_ONE, &newX);
  1444.         childPtr->precedingTextWidth = newX - x;
  1445.         x = newX;
  1446.     }
  1447.     switch (childPtr->justify) {
  1448.     case JUSTIFY_TOP:
  1449.         y = maxAscent + textPtr->fontPtr->descent -
  1450.         childPtr->cavityHeight;
  1451.         break;
  1452.     case JUSTIFY_CENTER:
  1453.         y = maxAscent - (childPtr->cavityHeight + median) / 2;
  1454.         break;
  1455.     case JUSTIFY_BOTTOM:
  1456.         y = maxAscent + textPtr->fontPtr->descent -
  1457.         childPtr->cavityHeight;
  1458.         break;
  1459.     }
  1460.     childPtr->x = x;
  1461.     childPtr->y = y;
  1462.     curPos = childPtr->precedingTextEnd;
  1463.     x += childPtr->cavityWidth;
  1464.     }
  1465.  
  1466.     /*
  1467.      * This may be piece of line after last child and will also pick
  1468.      * up the entire line if no children occurred on it
  1469.      */
  1470.     numChars = (linePtr->numChars - curPos);
  1471.     if ((numChars > 0) && (linePtr->text != NULL)) {
  1472.     TkMeasureChars(textPtr->fontPtr, linePtr->text + curPos, numChars,
  1473.         x, 10000, TK_PARTIAL_OK | TK_AT_LEAST_ONE, &newX);
  1474.     x = newX;
  1475.     }
  1476.     /* Update line parameters */
  1477.     linePtr->width = (unsigned int)x;
  1478.     linePtr->height = maxHeight;
  1479.     linePtr->baseline = maxAscent;
  1480. }
  1481.  
  1482. /*
  1483.  * ----------------------------------------------------------------------
  1484.  *
  1485.  * MoveChild --
  1486.  *
  1487.  *     Move a child subwindow to a new location in the hypertext
  1488.  *    parent window.  If the window has no geometry (i.e. width,
  1489.  *    or height is 0), simply unmap to window.
  1490.  *
  1491.  * Results:
  1492.  *    None.
  1493.  *
  1494.  * Side effects:
  1495.  *    Each subwindow is moved to its new location, generating
  1496.  *      Expose events in the parent for each child window moved.
  1497.  *
  1498.  * ----------------------------------------------------------------------
  1499.  */
  1500. static void
  1501. MoveChild(childPtr, offset)
  1502.     register Child *childPtr;
  1503.     int offset;
  1504. {
  1505.     int winWidth, winHeight;
  1506.     int width, height;
  1507.     int deltaX, deltaY;
  1508.     int x, y;
  1509.     int padX, padY;
  1510.  
  1511.     if (childPtr->tkwin == NULL) {
  1512.     return;
  1513.     }
  1514.     winWidth = GetReqWidth(childPtr);
  1515.     winHeight = GetReqHeight(childPtr);
  1516.     if ((winWidth < 1) || (winHeight < 1)) {
  1517.     if (Tk_IsMapped(childPtr->tkwin)) {
  1518.         Tk_UnmapWindow(childPtr->tkwin);
  1519.     }
  1520.     return;
  1521.     }
  1522.     padX = (Tk_Changes(childPtr->tkwin)->border_width + childPtr->padX);
  1523.     padY = (Tk_Changes(childPtr->tkwin)->border_width + childPtr->padY);
  1524.     x = padX + childPtr->x - childPtr->textPtr->vPort.x;
  1525.     y = padY + offset + childPtr->y - childPtr->textPtr->vPort.y;
  1526.     width = childPtr->cavityWidth - (2 * padX);
  1527.     height = childPtr->cavityHeight - (2 * padY);
  1528.  
  1529.     if ((width < winWidth) || (childPtr->fill & FILL_X)) {
  1530.     winWidth = width;
  1531.     }
  1532.     if ((height < winHeight) || (childPtr->fill & FILL_Y)) {
  1533.     winHeight = height;
  1534.     }
  1535.     deltaX = deltaY = 0;
  1536.     if (width > winWidth) {
  1537.     deltaX = width - winWidth;
  1538.     }
  1539.     if (height > winHeight) {
  1540.     deltaY = height - winHeight;
  1541.     }
  1542.     if ((deltaX > 0) || (deltaY > 0)) {
  1543.     XPoint anchorPos;
  1544.  
  1545.     anchorPos = TranslateAnchor(deltaX, deltaY, childPtr->anchor);
  1546.     x += anchorPos.x, y += anchorPos.y;
  1547.     }
  1548.     childPtr->windowWidth = winWidth;
  1549.     childPtr->windowHeight = winHeight;
  1550.     if ((x != Tk_X(childPtr->tkwin)) || (y != Tk_Y(childPtr->tkwin)) ||
  1551.     (winWidth != Tk_Width(childPtr->tkwin)) ||
  1552.     (winHeight != Tk_Height(childPtr->tkwin))) {
  1553.     Tk_MoveResizeWindow(childPtr->tkwin, x, y, (unsigned int)winWidth,
  1554.         (unsigned int)winHeight);
  1555.     if (!Tk_IsMapped(childPtr->tkwin)) {
  1556.         Tk_MapWindow(childPtr->tkwin);
  1557.     }
  1558.     }
  1559. }
  1560.  
  1561. /*
  1562.  * ----------------------------------------------------------------------
  1563.  *
  1564.  * DrawPage --
  1565.  *
  1566.  *     This procedure displays the lines of text and moves the child
  1567.  *      windows to their new positions.  It draws lines with regard to
  1568.  *    the direction of the scrolling.  The idea here is to make the
  1569.  *    text and buttons appear to move together. Otherwise you will
  1570.  *    get a "jiggling" effect where the windows appear to bump into
  1571.  *    the next line before that line is moved.  In the worst case, where
  1572.  *    every line has at least one widget, you can get an aquarium effect
  1573.  *      (lines appear to ripple up).
  1574.  *
  1575.  *     The text area may start between line boundaries (to accommodate
  1576.  *    both variable height lines and constant scrolling). Subtract the
  1577.  *    difference of the page offset and the line offset from the starting
  1578.  *    coordinates. For horizontal scrolling, simply subtract the offset
  1579.  *    of the viewport. The window will clip the top of the first line,
  1580.  *    the bottom of the last line, whatever text extends to the left
  1581.  *    or right of the viewport on any line.
  1582.  *
  1583.  * Results:
  1584.  *    None.
  1585.  *
  1586.  * Side effects:
  1587.  *    Commands are output to X to display the line in its current
  1588.  *     mode.
  1589.  *
  1590.  * ----------------------------------------------------------------------
  1591.  */
  1592. static void
  1593. DrawPage(textPtr, deltaY)
  1594.     Htext *textPtr;
  1595.     int deltaY;            /* Change from previous Y coordinate */
  1596. {
  1597.     Line *linePtr;
  1598.     Child *childPtr;
  1599.     Tk_Window tkwin = textPtr->tkwin;
  1600.     int numChars;
  1601.     int curPos;
  1602.     int baseline;
  1603.     Pixmap pixMap;
  1604.     int forceCopy = 0;
  1605.     register int i;
  1606.     int curLine, lastY;
  1607.     register int x, y;
  1608.     Blt_ListEntry *entryPtr;
  1609.  
  1610.     /* Setup: Clear the display */
  1611.     /* Create an off-screen pixmap for semi-smooth scrolling. */
  1612.     pixMap = XCreatePixmap(textPtr->display, Tk_WindowId(tkwin),
  1613.     Tk_Width(tkwin), Tk_Height(tkwin),
  1614.     DefaultDepthOfScreen(Tk_Screen(tkwin)));
  1615.     Tk_Fill3DRectangle(textPtr->display, pixMap, textPtr->border, 0, 0,
  1616.     Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  1617.  
  1618.     x = -(textPtr->vPort.x);
  1619.     y = -(textPtr->vPort.y);
  1620.  
  1621.     if (deltaY >= 0) {
  1622.     y += textPtr->linePtrPtr[textPtr->first]->offset;
  1623.     curLine = textPtr->first;
  1624.     lastY = 0;
  1625.     } else {
  1626.     y += textPtr->linePtrPtr[textPtr->last]->offset;
  1627.     curLine = textPtr->last;
  1628.     lastY = Tk_Height(tkwin);
  1629.     }
  1630.     forceCopy = 0;
  1631.  
  1632.     /* Draw each line */
  1633.     for (i = textPtr->first; i <= textPtr->last; i++) {
  1634.  
  1635.     /* Initialize character position in text buffer to start */
  1636.     curPos = 0;
  1637.     /* Initialize X position */
  1638.     x = -(textPtr->vPort.x);
  1639.  
  1640.     linePtr = textPtr->linePtrPtr[curLine];
  1641.     baseline = y + linePtr->baseline;    /* Base line in window
  1642.                          * coordinates */
  1643.  
  1644.     for (entryPtr = Blt_FirstListEntry(&(linePtr->windowList));
  1645.         entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1646.         childPtr = (Child *)Blt_GetListValue(entryPtr);
  1647.         MoveChild(childPtr, linePtr->offset);
  1648.         childPtr->flags |= VISIBLE;
  1649.         numChars = (childPtr->precedingTextEnd - curPos);
  1650.         if ((numChars > 0) && (linePtr->text != NULL)) {
  1651.         TkDisplayChars(textPtr->display, pixMap, textPtr->gc,
  1652.             textPtr->fontPtr, linePtr->text + curPos,
  1653.             numChars, x, baseline, 0);
  1654.         x += childPtr->precedingTextWidth;
  1655.         }
  1656.         curPos = childPtr->precedingTextEnd;
  1657.         x += childPtr->cavityWidth;
  1658.         forceCopy++;
  1659.     }
  1660.  
  1661.     /*
  1662.      * This may be the text trailing the last child or the entire
  1663.      * line if no children occur on it.
  1664.      */
  1665.     numChars = (linePtr->numChars - curPos);
  1666.     if ((numChars > 0) && (linePtr->text != NULL)) {
  1667. #ifdef notdef
  1668.         if (textPtr->selectFirst >= curLine &&
  1669.         textPtr->selectLast <= curLine) {
  1670.         Tk_Fill3DRectangle(textPtr->display, pixMap,
  1671.             textPtr->selectBorder, y, 0,
  1672.             numChars, Tk_Height(tkwin),
  1673.             0, textPtr->selectBorderWidth);
  1674.         }
  1675. #endif
  1676.         TkDisplayChars(textPtr->display, pixMap, textPtr->gc,
  1677.         textPtr->fontPtr, linePtr->text + curPos,
  1678.         numChars, x, baseline, 0);
  1679.     }
  1680.     /* Go to the top of the next line */
  1681.     if (deltaY >= 0) {
  1682.         y += textPtr->linePtrPtr[curLine++]->height;
  1683.     }
  1684.     if (forceCopy > 0 && !(textPtr->flags & VIEW_RESIZED)) {
  1685.         if (deltaY >= 0) {
  1686.         XCopyArea(textPtr->display, pixMap, Tk_WindowId(tkwin),
  1687.             textPtr->gc, 0, lastY, Tk_Width(tkwin), y - lastY,
  1688.             0, lastY);
  1689.         } else {
  1690.         XCopyArea(textPtr->display, pixMap, Tk_WindowId(tkwin),
  1691.             textPtr->gc, 0, y, Tk_Width(tkwin), lastY - y,
  1692.             0, y);
  1693.         }
  1694.         forceCopy = 0;    /* Reset drawing flag */
  1695.         lastY = y;        /* Record last Y position */
  1696.     }
  1697.     if ((deltaY < 0) && (curLine > 0)) {
  1698.         y -= textPtr->linePtrPtr[--curLine]->height;
  1699.     }
  1700.     }
  1701.     /*
  1702.      * If the viewport was resized, draw the page in one operation.
  1703.      * Otherwise draw any left-over block of text (either at the top
  1704.      * or bottom of the page)
  1705.      */
  1706.     if (textPtr->flags & VIEW_RESIZED) {
  1707.     XCopyArea(textPtr->display, pixMap, Tk_WindowId(tkwin),
  1708.         textPtr->gc, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
  1709.     } else if (lastY != y) {
  1710.     if (deltaY >= 0) {
  1711.         XCopyArea(textPtr->display, pixMap, Tk_WindowId(tkwin),
  1712.         textPtr->gc, 0, lastY, Tk_Width(tkwin),
  1713.         Tk_Height(tkwin) - lastY, 0, lastY);
  1714.     } else {
  1715.         XCopyArea(textPtr->display, pixMap, Tk_WindowId(tkwin),
  1716.         textPtr->gc, 0, 0, Tk_Width(tkwin), lastY, 0, 0);
  1717.     }
  1718.     }
  1719.     XFreePixmap(textPtr->display, pixMap);
  1720. }
  1721.  
  1722. /*
  1723.  * ----------------------------------------------------------------------
  1724.  *
  1725.  * DisplayText --
  1726.  *
  1727.  *     This procedure is invoked to display a hypertext widget.
  1728.  *    Many of the operations which might ordinarily be performed
  1729.  *    elsewhere (e.g. in a configuration routine) are done here
  1730.  *    because of the somewhat unusual interactions occurring between
  1731.  *    the parent and child windows.
  1732.  *
  1733.  *      Recompute the layout of the text if necessary. This is
  1734.  *    necessary if the world coordinate system has changed.
  1735.  *    Specifically, the following may have occurred:
  1736.  *
  1737.  *      -  a text attribute has changed (font, linespacing, etc.).
  1738.  *      -  child option changed (anchor, width, height).
  1739.  *        -  actual child window was resized.
  1740.  *      -  new text string or file.
  1741.  *
  1742.  *      This is deferred to the display routine since potentially
  1743.  *      many of these may occur (especially child window changes).
  1744.  *
  1745.  *    Set the vertical and horizontal scrollbars (if they are
  1746.  *    designated) by issuing a Tcl command.  Done here since
  1747.  *    the text window width and height are needed.
  1748.  *
  1749.  *    If the viewport position or contents have changed in the
  1750.  *    vertical direction,  the now out-of-view child windows
  1751.  *    must be moved off the viewport.  Since child windows will
  1752.  *    obscure the text window, it is imperative that the children
  1753.  *    are moved off before we try to redraw text in the same area.
  1754.  *      This is necessary only for vertical movements.  Horizontal
  1755.  *    child window movements are handled automatically in the
  1756.  *    page drawing routine.
  1757.  *
  1758.  *      Get the new first and last line numbers for the viewport.
  1759.  *      These line numbers may have changed because either a)
  1760.  *      the viewport changed size or position, or b) the text
  1761.  *    (child window sizes or text attributes) have changed.
  1762.  *
  1763.  *    If the viewport has changed vertically (i.e. the first or
  1764.  *      last line numbers have changed), move the now out-of-view
  1765.  *    child windows off the viewport.
  1766.  *
  1767.  *      Potentially many expose events may be generated when the
  1768.  *    the individual child windows are moved and/or resized.
  1769.  *    These events need to be ignored.  Since (I think) expose
  1770.  *     events are guaranteed to happen in order, we can bracket
  1771.  *    them by sending phony events (via XSendEvent). The phony
  1772.  *      event turn on and off flags which indicate if the events
  1773.  *    should be ignored.
  1774.  *
  1775.  *    Finally, the page drawing routine is called.
  1776.  *
  1777.  * Results:
  1778.  *    None.
  1779.  *
  1780.  * Side effects:
  1781.  *     Commands are output to X to display the hypertext in its
  1782.  *    current mode.
  1783.  *
  1784.  * ----------------------------------------------------------------------
  1785.  */
  1786. static void
  1787. DisplayText(clientData)
  1788.     ClientData clientData;    /* Information about widget. */
  1789. {
  1790.     Htext *textPtr = (Htext *)clientData;
  1791.     register Tk_Window tkwin;
  1792.     int oldFirst;        /* First line of old viewport */
  1793.     int oldLast;        /* Last line of old viewport */
  1794.     int deltaY;            /* Change in viewport in Y direction */
  1795.  
  1796.     textPtr->flags &= ~REDRAW_PENDING;
  1797.  
  1798.     tkwin = textPtr->tkwin;
  1799.     if ((tkwin == NULL) || (textPtr->numLines == 0)) {
  1800.     return;
  1801.     }
  1802.     if (textPtr->flags & REQUEST_LAYOUT) {
  1803.     /*
  1804.      * Recompute the layout when children are created, deleted,
  1805.      * moved, or resized.  Also when text attributes (such as
  1806.      * font, linespacing) have changed.
  1807.      */
  1808.     ComputeLayout(textPtr);
  1809.     }
  1810.     textPtr->width = (unsigned int)textPtr->reqWidth;
  1811.     textPtr->height = (unsigned int)textPtr->reqHeight;
  1812.     if (textPtr->width == 0) {
  1813.     textPtr->width = BLT_MIN(textPtr->vPort.width, textPtr->maxWidth);
  1814.     }
  1815.     if (textPtr->height == 0) {
  1816.     textPtr->height = BLT_MIN(textPtr->vPort.height, textPtr->maxHeight);
  1817.     }
  1818.     if ((textPtr->width != Tk_ReqWidth(tkwin)) ||
  1819.     (textPtr->height != Tk_ReqHeight(tkwin))) {
  1820.     Tk_GeometryRequest(tkwin, textPtr->width, textPtr->height);
  1821.     textPtr->flags |= REQUEST_LAYOUT;
  1822.     EventuallyRedraw(textPtr);
  1823.     return;
  1824.     }
  1825.     if (!Tk_IsMapped(tkwin)) {
  1826.     return;
  1827.     }
  1828.     /*
  1829.      * Turn off layout requests here, after the text window has been
  1830.      * mapped Otherwise, relative slave window height and width
  1831.      * requests wrt to the text window will be wrong.
  1832.      */
  1833.     textPtr->flags &= ~REQUEST_LAYOUT;
  1834.  
  1835.     /* Is there a pending gotoline request? */
  1836.     if (textPtr->flags & REQUEST_GOTO) {
  1837.     textPtr->pendingY = textPtr->linePtrPtr[textPtr->reqLineNum]->offset;
  1838.     textPtr->flags &= ~REQUEST_GOTO;
  1839.     }
  1840.     deltaY = textPtr->pendingY - textPtr->vPort.y;
  1841.     oldFirst = textPtr->first, oldLast = textPtr->last;
  1842.  
  1843.     /*
  1844.      * If the viewport has changed size or position, or the text
  1845.      * and/or child subwindows have changed, adjust the scrollbars to
  1846.      * new positions.
  1847.      */
  1848.     if (textPtr->flags & (VIEW_MOVED | VIEW_RESIZED | LAYOUT_CHANGED)) {
  1849.     /* Reset viewport origin and world extents */
  1850.     textPtr->vPort.x = textPtr->pendingX;
  1851.     textPtr->vPort.y = textPtr->pendingY;
  1852.     if (textPtr->xScrollCmd != NULL)
  1853.         UpdateScrollbar(textPtr->interp, textPtr->xScrollCmd,
  1854.         textPtr->vPort.width, Tk_Width(tkwin), textPtr->vPort.x,
  1855.         textPtr->scrollX);
  1856.     if (textPtr->yScrollCmd != NULL)
  1857.         UpdateScrollbar(textPtr->interp, textPtr->yScrollCmd,
  1858.         textPtr->vPort.height, Tk_Height(tkwin),
  1859.         textPtr->vPort.y, textPtr->scrollY);
  1860.     /*
  1861.      * Given a new viewport or text height, find the first and
  1862.      * last line numbers of the new viewport.
  1863.      */
  1864.     GetVisibleLines(textPtr);
  1865.     }
  1866.     /*
  1867.      * (This is a kludge.) Send an expose event before and after
  1868.      * drawing the page of text.  Since moving and resizing of the
  1869.      * subwindows will cause redundant expose events in the parent
  1870.      * window, the phony events will bracket them indicating no action
  1871.      * should be taken.
  1872.      */
  1873.     SendBogusEvent(tkwin);
  1874.  
  1875.     /*
  1876.      * If either the position of the viewport has changed or the size
  1877.      * of width or height of the entire text have changed, move the
  1878.      * children from the previous viewport out of the current
  1879.      * viewport. Worry only about the vertical child window movements.
  1880.      * The horizontal moves are handled by the when drawing the page
  1881.      * of text.
  1882.      */
  1883.     if (textPtr->first != oldFirst || textPtr->last != oldLast) {
  1884.     register Line **linePtrPtr;
  1885.     register int i;
  1886.     int first, last;
  1887.     Blt_ListEntry *entryPtr;
  1888.     Child *childPtr;
  1889.  
  1890.     /* Figure out which lines are now out of the viewport */
  1891.  
  1892.     if ((textPtr->first > oldFirst) && (textPtr->first <= oldLast)) {
  1893.         first = oldFirst, last = textPtr->first;
  1894.     } else if ((textPtr->last < oldLast) && (textPtr->last >= oldFirst)) {
  1895.         first = textPtr->last, last = oldLast;
  1896.     } else {
  1897.         first = oldFirst, last = oldLast;
  1898.     }
  1899.  
  1900.     linePtrPtr = &(textPtr->linePtrPtr[first]);
  1901.     for (i = first; i <= last; i++) {
  1902.         for (entryPtr = Blt_FirstListEntry(&((*linePtrPtr)->windowList));
  1903.         entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1904.         childPtr = (Child *)Blt_GetListValue(entryPtr);
  1905.         MoveChild(childPtr, (*linePtrPtr)->offset);
  1906.         childPtr->flags &= ~VISIBLE;
  1907.         }
  1908.         linePtrPtr++;
  1909.     }
  1910.     }
  1911.     DrawPage(textPtr, deltaY);
  1912.     SendBogusEvent(tkwin);
  1913.  
  1914.     /* Reset flags */
  1915.     textPtr->flags &= ~(VIEW_RESIZED | VIEW_MOVED | LAYOUT_CHANGED);
  1916. }
  1917.  
  1918. /*
  1919.  * ----------------------------------------------------------------------
  1920.  *
  1921.  * GetVisibleLines --
  1922.  *
  1923.  *     Calculates which lines are visible using the height
  1924.  *      of the viewport and y offset from the top of the text.
  1925.  *
  1926.  * Results:
  1927.  *    None.
  1928.  *
  1929.  * Side effects:
  1930.  *    Only those line between first and last inclusive are
  1931.  *     redrawn.
  1932.  *
  1933.  * ----------------------------------------------------------------------
  1934.  */
  1935. static int
  1936. GetVisibleLines(textPtr)
  1937.     Htext *textPtr;
  1938. {
  1939.     int topLine, bottomLine;
  1940.     int topPos, bottomPos;
  1941.     int lastLine;
  1942.  
  1943.     topPos = textPtr->pendingY;
  1944.     lastLine = textPtr->numLines - 1;
  1945.     /* First line */
  1946.     topLine = LineSearch(textPtr, topPos, 0, lastLine);
  1947.     if (topLine < 0) {
  1948.     /*
  1949.      * This can't be. The newY offset must be corrupted.
  1950.      */
  1951.     fprintf(stderr, "First position not found `%d'", topPos);
  1952.     return TCL_ERROR;
  1953.     }
  1954.     textPtr->first = topLine;
  1955.  
  1956.     /*
  1957.      * If there is less text than window space, the bottom line is the
  1958.      * last line of text.  Otherwise search for the line at the bottom
  1959.      * of the window.
  1960.      */
  1961.     bottomPos = topPos + Tk_Height(textPtr->tkwin) - 1;
  1962.     if (bottomPos < textPtr->vPort.height) {
  1963.     bottomLine = LineSearch(textPtr, bottomPos, topLine, lastLine);
  1964.     } else {
  1965.     bottomLine = lastLine;
  1966.     }
  1967.     if (bottomLine < 0) {
  1968.     /*
  1969.      * This can't be. The newY offset must be corrupted.
  1970.      */
  1971.     fprintf(stderr, "Last position not found `%d'\n", bottomPos);
  1972. #ifdef notdef
  1973.     fprintf(stderr, "vPort.height=%d,height=%d,top=%d,first=%d,last=%d\n",
  1974.         textPtr->vPort.height, Tk_Height(textPtr->tkwin), topPos,
  1975.         textPtr->linePtrPtr[topLine]->offset,
  1976.         textPtr->linePtrPtr[lastLine]->offset);
  1977. #endif
  1978.     return TCL_ERROR;
  1979.     }
  1980.     textPtr->last = bottomLine;
  1981.     return TCL_OK;
  1982. }
  1983.  
  1984. /*
  1985.  * ----------------------------------------------------------------------
  1986.  *
  1987.  * LineSearch --
  1988.  *
  1989.  *     Performs a binary search for the line located at some world
  1990.  *    Y coordinate. The search is limited to those lines between
  1991.  *    low and high inclusive.
  1992.  *
  1993.  * Results:
  1994.  *    Returns the line number at the given Y coordinate. If position
  1995.  *    does not correspond to any of the lines in the given the set,
  1996.  *    -1 is returned.
  1997.  *
  1998.  * ----------------------------------------------------------------------
  1999.  */
  2000. static int
  2001. LineSearch(textPtr, position, low, high)
  2002.     Htext *textPtr;
  2003.     int position;
  2004.     int low, high;
  2005. {
  2006.     register int midPoint;
  2007.     register Line *linePtr;
  2008.  
  2009.     while (low <= high) {
  2010.     midPoint = (low + high) >> 1;
  2011.     linePtr = textPtr->linePtrPtr[midPoint];
  2012.     if (position < linePtr->offset) {
  2013.         high = midPoint - 1;
  2014.     } else if (position >= (linePtr->offset + linePtr->height)) {
  2015.         low = midPoint + 1;
  2016.     } else {
  2017.         return (midPoint);
  2018.     }
  2019.     }
  2020.     return -1;
  2021. }
  2022.  
  2023. /*
  2024.  * ----------------------------------------------------------------------
  2025.  *
  2026.  * UpdateScrollbar --
  2027.  *
  2028.  *     Invoke a Tcl command to the scrollbar, defining the new
  2029.  *    position and length of the scroll. See the Tk documentation
  2030.  *    for further information on the scrollbar.  It is assumed the
  2031.  *    scrollbar command prefix is valid.
  2032.  *
  2033.  * Results:
  2034.  *    None.
  2035.  *
  2036.  * Side Effects:
  2037.  *    Scrollbar is commanded to change position and/or size.
  2038.  *
  2039.  * ----------------------------------------------------------------------
  2040.  */
  2041. static void
  2042. UpdateScrollbar(interp, command, total, window, first, units)
  2043.     Tcl_Interp *interp;
  2044.     char *command;        /* scrollbar command */
  2045.     int total;            /* Total distance */
  2046.     int window;            /* Window distance */
  2047.     int first;            /* Position of viewport */
  2048.     int units;            /* Unit distance */
  2049. {
  2050.     char scrollArgs[1000];
  2051.     int totalUnits, windowUnits;
  2052.     int firstUnit, lastUnit;
  2053.  
  2054.     totalUnits = (total / units) + 1;
  2055.     windowUnits = window / units;
  2056.     firstUnit = first / units;
  2057.     lastUnit = (firstUnit + windowUnits);
  2058.  
  2059.     /* Keep scrolling within range. */
  2060.     if (firstUnit >= totalUnits) {
  2061.     firstUnit = totalUnits;
  2062.     }
  2063.     if (lastUnit > totalUnits) {
  2064.     lastUnit = totalUnits;
  2065.     }
  2066.     sprintf(scrollArgs, " %d %d %d %d", totalUnits, windowUnits,
  2067.     firstUnit, lastUnit);
  2068.     if (Tcl_VarEval(interp, command, scrollArgs, (char *)NULL) != TCL_OK) {
  2069.     Tk_BackgroundError(interp);
  2070.     }
  2071. }
  2072.  
  2073.  
  2074. static void
  2075. FreeAllLines(textPtr)
  2076.     Htext *textPtr;
  2077. {
  2078.     register int i;
  2079.  
  2080.     for (i = 0; i < textPtr->numLines; i++) {
  2081.     DestroyLine(textPtr->linePtrPtr[i]);
  2082.     }
  2083.     if (textPtr->linePtrPtr != NULL) {
  2084.     free((char *)textPtr->linePtrPtr);
  2085.     }
  2086.     textPtr->linePtrPtr = NULL;
  2087.     textPtr->arraySize = textPtr->numLines = 0;
  2088. }
  2089.  
  2090.  
  2091. static void
  2092. AdjustLinesAllocated(textPtr)
  2093.     Htext *textPtr;
  2094. {
  2095.     if (textPtr->arraySize > 0) {
  2096.     ResizeArray((char **)&(textPtr->linePtrPtr), sizeof(Line *),
  2097.         textPtr->numLines, textPtr->arraySize);
  2098.     textPtr->arraySize = textPtr->numLines;
  2099.     textPtr->first = 0;
  2100.     textPtr->last = textPtr->numLines - 1;
  2101.     textPtr->pendingX = textPtr->pendingY = 0;
  2102.     textPtr->vPort.width = textPtr->vPort.height = 0;
  2103.     textPtr->vPort.x = textPtr->vPort.y = 0;
  2104.     }
  2105. }
  2106.  
  2107.  
  2108. static Line *
  2109. GetLastLine(textPtr)
  2110.     Htext *textPtr;
  2111. {
  2112.     Line *linePtr;
  2113.  
  2114.     if (textPtr->numLines == 0) {
  2115.     linePtr = NewLine(textPtr);
  2116.  
  2117.     if (linePtr == NULL) {
  2118.         textPtr->interp->result = "can't allocate line structure";
  2119.         return NULL;
  2120.     }
  2121.     }
  2122.     linePtr = textPtr->linePtrPtr[textPtr->numLines - 1];
  2123.     return linePtr;
  2124. }
  2125.  
  2126. static void
  2127. SetTextInfo(linePtr, text, size)
  2128.     Line *linePtr;
  2129.     char *text;
  2130.     int size;
  2131. {
  2132.     linePtr->numChars = size;
  2133.     if (linePtr->text != NULL) {
  2134.     free((char *)linePtr->text);
  2135.     }
  2136.     linePtr->text = strdup(text);
  2137. }
  2138.  
  2139. static void
  2140. GetTextInfo(linePtr, buffer, sizePtr)
  2141.     Line *linePtr;
  2142.     char *buffer;
  2143.     int *sizePtr;
  2144. {
  2145.     *sizePtr = linePtr->numChars;
  2146.     if (linePtr->numChars > 0) {
  2147.     strcpy(buffer, linePtr->text);
  2148.     }
  2149.     buffer[linePtr->numChars] = 0;
  2150. }
  2151.  
  2152. /*
  2153.  * ----------------------------------------------------------------------
  2154.  *
  2155.  * ParseInput --
  2156.  *
  2157.  *     Parse the input to the Htext structure into an array of lines.
  2158.  *
  2159.  * Results:
  2160.  *    Returns TCL_OK or error depending if the file was read correctly.
  2161.  *
  2162.  * ----------------------------------------------------------------------
  2163.  */
  2164. static int
  2165. ParseInput(interp, textPtr, inputPtr)
  2166.     Tcl_Interp *interp;
  2167.     Htext *textPtr;
  2168.     char *inputPtr;
  2169. {
  2170.     register Line *linePtr;
  2171.     int c;
  2172.  
  2173. #define HUGE_LINE_SIZE 1024
  2174.     char buffer[HUGE_LINE_SIZE];
  2175. #define HUGE_COMMAND_SIZE 10000
  2176.     char command[HUGE_COMMAND_SIZE];
  2177.     int count;
  2178.     register int state;
  2179.  
  2180.     linePtr = GetLastLine(textPtr);
  2181.     if (linePtr == NULL) {
  2182.     return TCL_ERROR;    /* Error allocating line */
  2183.     }
  2184.     GetTextInfo(linePtr, buffer, &count);
  2185.     state = 0;
  2186.     while ((c = *inputPtr++) != '\0') {
  2187.     if (c == textPtr->specChar) {
  2188.         state++;
  2189.     } else if (c == '\n') {
  2190.         state = -1;
  2191.     } else if ((state == 0) && (c == '\\')) {
  2192.         state = 3;
  2193.     } else {
  2194.         state = 0;
  2195.     }
  2196.  
  2197.     switch (state) {
  2198.     case 2:        /* Tcl Command block found */
  2199.         count--;
  2200.         inputPtr = CollectCommand(textPtr, inputPtr, command);
  2201.         if (inputPtr == NULL) {
  2202.         return TCL_ERROR;
  2203.         }
  2204.         linePtr->numChars = count;
  2205.         if (Tcl_Eval(interp, command) != TCL_OK) {
  2206.         return TCL_ERROR;
  2207.         }
  2208.         state = 0;
  2209.         break;
  2210.  
  2211.     case 4:        /* Escaped block designator */
  2212.         buffer[count - 1] = c;
  2213.         state = 0;
  2214.         break;
  2215.  
  2216.     case -1:        /* End of text line  */
  2217.         buffer[count] = '\0';
  2218.         SetTextInfo(linePtr, buffer, count);
  2219.         linePtr = NewLine(textPtr);
  2220.         if (linePtr == NULL) {
  2221.         return TCL_ERROR;
  2222.         }
  2223.         count = state = 0;
  2224.         break;
  2225.  
  2226.     default:        /* Default action, add to text buffer */
  2227.         buffer[count++] = c;
  2228.         break;
  2229.     }
  2230.     if (count == HUGE_LINE_SIZE) {
  2231.         interp->result = "text line is too long";
  2232.         return TCL_ERROR;
  2233.     }
  2234.     }
  2235.     if (count > 0) {
  2236.     buffer[count] = '\0';
  2237.     SetTextInfo(linePtr, buffer, count);
  2238.     }
  2239.     return TCL_OK;
  2240. }
  2241.  
  2242.  
  2243. static char *
  2244. ReadFile(interp, fileName)
  2245.     Tcl_Interp *interp;
  2246.     char *fileName;
  2247. {
  2248.     FILE *fp;
  2249.     int arraySize;
  2250.     register int numBytes;
  2251.     char *buffer;
  2252.     register int count;
  2253.     char *result = NULL;
  2254.  
  2255.     fp = fopen(fileName, "r");
  2256.     if (fp == NULL) {
  2257.     Tcl_AppendResult(interp, "can't open \"", fileName,
  2258.         "\" for reading: ", Tcl_PosixError(interp), (char *)NULL);
  2259.     return NULL;
  2260.     }
  2261.     count = 0;
  2262.     arraySize = BUFSIZ;        /* Initial array size */
  2263.     if (ResizeArray(&buffer, sizeof(char), arraySize, count) != TCL_OK) {
  2264.     interp->result = "can't allocate character array";
  2265.     return NULL;
  2266.     }
  2267.     for (;;) {
  2268.     /* Read in next block of text */
  2269.     numBytes = fread(&buffer[count], sizeof(char), BUFSIZ, fp);
  2270.  
  2271.     if (numBytes < 0) {
  2272.         Tcl_AppendResult(interp, "error reading \"", fileName, "\": ",
  2273.         Tcl_PosixError(interp), (char *)NULL);
  2274.         goto error;
  2275.     } else if (numBytes == 0) {
  2276.         break;
  2277.     }
  2278.     count += numBytes;
  2279.     if (count >= arraySize) {
  2280.         /* Reallocate with double the buffer size */
  2281.         arraySize += arraySize;
  2282.         if (ResizeArray(&buffer, sizeof(char),
  2283.             arraySize, count) != TCL_OK) {
  2284.         interp->result = "can't allocate character array";
  2285.         goto error;
  2286.         }
  2287.     }
  2288.     }
  2289.     buffer[count] = '\0';
  2290.     result = buffer;
  2291.   error:
  2292.     fclose(fp);
  2293.     return result;
  2294. }
  2295.  
  2296.  
  2297. static char *
  2298. CollectCommand(textPtr, inputPtr, buffer)
  2299.     Htext *textPtr;
  2300.     char *inputPtr;
  2301.     char *buffer;
  2302. {
  2303.     register int c;
  2304.     register int state;
  2305.     register int count;
  2306.  
  2307.  
  2308.     /* Simply collect the all the characters until %% into a buffer */
  2309.  
  2310.     state = count = 0;
  2311.     while ((c = *inputPtr++) != '\0') {
  2312.     if (c == textPtr->specChar) {
  2313.         state++;
  2314.     } else if ((state == 0) && (c == '\\')) {
  2315.         state = 3;
  2316.     } else {
  2317.         state = 0;
  2318.     }
  2319.  
  2320.     switch (state) {
  2321.     case 2:        /* End of command block found */
  2322.         buffer[count - 1] = '\0';
  2323.         return (inputPtr);
  2324.  
  2325.     case 4:        /* Escaped block designator */
  2326.         buffer[count] = c;
  2327.         state = 0;
  2328.         break;
  2329.  
  2330.     default:        /* Add to command buffer */
  2331.         buffer[count++] = c;
  2332.         break;
  2333.     }
  2334.     if (count == HUGE_COMMAND_SIZE) {
  2335.         textPtr->interp->result = "command block is too long";
  2336.         return NULL;
  2337.     }
  2338.     }
  2339.     textPtr->interp->result = "premature end of TCL command block";
  2340.     return NULL;
  2341. }
  2342.  
  2343. static int
  2344. IncludeText(interp, textPtr, fileName)
  2345.     Tcl_Interp *interp;
  2346.     Htext *textPtr;
  2347.     char *fileName;
  2348. {
  2349.     char *inputPtr;
  2350.     int result;
  2351.  
  2352.     if ((textPtr->text == NULL) && (fileName == NULL)) {
  2353.     return TCL_OK;        /* Empty text string */
  2354.     }
  2355.     inputPtr = textPtr->text;
  2356.     if (fileName != NULL) {
  2357.     inputPtr = ReadFile(interp, fileName);
  2358.     if (inputPtr == NULL) {
  2359.         return TCL_ERROR;
  2360.     }
  2361.     }
  2362.     result = ParseInput(interp, textPtr, inputPtr);
  2363.     if (fileName != NULL) {
  2364.     free(inputPtr);
  2365.     }
  2366.     return (result);
  2367. }
  2368.  
  2369. /* ARGSUSED */
  2370. static char *
  2371. HtextVarProc(clientData, interp, name1, name2, flags)
  2372.     ClientData clientData;    /* Information about widget. */
  2373.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  2374.     char *name1;        /* Name of variable. */
  2375.     char *name2;        /* Second part of variable name. */
  2376.     int flags;            /* Information about what happened. */
  2377. {
  2378.     Htext *textPtr = (Htext *)clientData;
  2379.     Htext *lastTextPtr;
  2380.  
  2381.     /* Check to see of this is the most recent trace */
  2382.     lastTextPtr = (Htext *)Tcl_VarTraceInfo2(interp, name1, name2, flags,
  2383.     HtextVarProc, (ClientData)NULL);
  2384.     if (lastTextPtr != textPtr) {
  2385.     return NULL;        /* Ignore all but most current trace */
  2386.     }
  2387.     if (flags & TCL_TRACE_READS) {
  2388.     char c;
  2389.  
  2390.     c = name2[0];
  2391.     if ((c == 'w') && (strcmp(name2, "widget") == 0)) {
  2392.         Tcl_SetVar2(interp, name1, name2, Tk_PathName(textPtr->tkwin),
  2393.         flags);
  2394.     } else if ((c == 'l') && (strcmp(name2, "line") == 0)) {
  2395.         char buf[80];
  2396.  
  2397.         sprintf(buf, "%d", textPtr->numLines);
  2398.         Tcl_SetVar2(interp, name1, name2, buf, flags);
  2399.     } else if ((c == 'f') && (strcmp(name2, "file") == 0)) {
  2400.         register char *fileName;
  2401.  
  2402.         fileName = textPtr->fileName;
  2403.         if (fileName == NULL) {
  2404.         fileName = "";
  2405.         }
  2406.         Tcl_SetVar2(interp, name1, name2, fileName, flags);
  2407.     } else {
  2408.         return ("Unknown variable");
  2409.     }
  2410.     }
  2411.     return NULL;
  2412. }
  2413.  
  2414. static void
  2415. CreateTraces(textPtr)
  2416.     Htext *textPtr;
  2417. {
  2418.     register Tcl_Interp *interp = textPtr->interp;
  2419.     int flags = (TCL_GLOBAL_ONLY | TCL_TRACE_READS);
  2420.     static char globalCmd[] = "global blt_htext";
  2421.  
  2422.     Tcl_TraceVar2(interp, HTEXT_CMDNAME, "widget", flags, HtextVarProc,
  2423.     (ClientData)textPtr);
  2424.     Tcl_TraceVar2(interp, HTEXT_CMDNAME, "line", flags, HtextVarProc,
  2425.     (ClientData)textPtr);
  2426.     Tcl_TraceVar2(interp, HTEXT_CMDNAME, "file", flags, HtextVarProc,
  2427.     (ClientData)textPtr);
  2428.     /*
  2429.      * Make the traced variables global to the widget
  2430.      */
  2431.     Tcl_Eval(interp, globalCmd);
  2432. }
  2433.  
  2434. static void
  2435. DeleteTraces(textPtr)
  2436.     Htext *textPtr;
  2437. {
  2438.     register Tcl_Interp *interp = textPtr->interp;
  2439.     int flags = (TCL_GLOBAL_ONLY | TCL_TRACE_READS);
  2440.  
  2441.     Tcl_UntraceVar2(interp, HTEXT_CMDNAME, "file", flags, HtextVarProc,
  2442.     (ClientData)textPtr);
  2443.     Tcl_UntraceVar2(interp, HTEXT_CMDNAME, "line", flags, HtextVarProc,
  2444.     (ClientData)textPtr);
  2445.     Tcl_UntraceVar2(interp, HTEXT_CMDNAME, "widget", flags, HtextVarProc,
  2446.     (ClientData)textPtr);
  2447.  
  2448. }
  2449.  
  2450. /*
  2451.  * ----------------------------------------------------------------------
  2452.  *
  2453.  * FindChild --
  2454.  *
  2455.  *    Searches for a child widget matching the path name given
  2456.  *    If found, the pointer to the child structure is returned,
  2457.  *    otherwise NULL.
  2458.  *
  2459.  * Results:
  2460.  *    The pointer to the child structure. If not found, NULL.
  2461.  *
  2462.  * ----------------------------------------------------------------------
  2463.  */
  2464. static Child *
  2465. FindChild(textPtr, name)
  2466.     Htext *textPtr;        /* Hypertext widget structure */
  2467.     char *name;            /* Path name of child window  */
  2468. {
  2469.     Tcl_HashEntry *entryPtr;
  2470.     Tk_Window tkwin;
  2471.  
  2472.     tkwin = Tk_NameToWindow(textPtr->interp, name, textPtr->tkwin);
  2473.     if (tkwin == NULL) {
  2474.     return NULL;
  2475.     }
  2476.     entryPtr = Tcl_FindHashEntry(&(textPtr->subwindows), (char *)tkwin);
  2477.     if (entryPtr != NULL) {
  2478.     return (Child *) Tcl_GetHashValue(entryPtr);
  2479.     }
  2480.     return NULL;
  2481. }
  2482.  
  2483. /*
  2484.  *--------------------------------------------------------------
  2485.  *
  2486.  * ChildGeometryProc --
  2487.  *
  2488.  *    This procedure is invoked by Tk_GeometryRequest for
  2489.  *    subwindows managed by the hypertext widget.
  2490.  *
  2491.  * Results:
  2492.  *    None.
  2493.  *
  2494.  * Side effects:
  2495.  *    Arranges for tkwin, and all its managed siblings, to
  2496.  *    be repacked and drawn at the next idle point.
  2497.  *
  2498.  *--------------------------------------------------------------
  2499.  */
  2500.  /* ARGSUSED */
  2501. static void
  2502. ChildGeometryProc(clientData, tkwin)
  2503.     ClientData clientData;    /* Information about window that got new
  2504.                      * preferred geometry.  */
  2505.     Tk_Window tkwin;        /* Other Tk-related information about the
  2506.                      * window. */
  2507. {
  2508.     Child *childPtr = (Child *)clientData;
  2509.  
  2510.     if ((childPtr->windowWidth != GetReqWidth(childPtr)) ||
  2511.     (childPtr->windowHeight != GetReqHeight(childPtr))) {
  2512.     childPtr->textPtr->flags |= REQUEST_LAYOUT;
  2513.     EventuallyRedraw(childPtr->textPtr);
  2514.     }
  2515. }
  2516.  
  2517. static void
  2518. SendBogusEvent(tkwin)
  2519.     Tk_Window tkwin;
  2520. {
  2521.     enum {
  2522.     DontPropagate = 0
  2523.     };
  2524.     XEvent event;
  2525.  
  2526.     event.type = event.xexpose.type = Expose;
  2527.     event.xexpose.window = Tk_WindowId(tkwin);
  2528.     event.xexpose.display = Tk_Display(tkwin);
  2529.     event.xexpose.count = 0;
  2530.     event.xexpose.x = event.xexpose.y = 0;
  2531.     event.xexpose.width = Tk_Width(tkwin);
  2532.     event.xexpose.height = Tk_Height(tkwin);
  2533.     XSendEvent(Tk_Display(tkwin), Tk_WindowId(tkwin), DontPropagate,
  2534.     ExposureMask, &event);
  2535. }
  2536.  
  2537. /*
  2538.  * --------------------------------------------------------------------
  2539.  *
  2540.  * ResizeArray --
  2541.  *
  2542.  *    Reallocates memory to the new size given.  New memory
  2543.  *    is also cleared (zeros).
  2544.  *
  2545.  * Results:
  2546.  *    Returns a pointer to the new object or NULL if an error occurred.
  2547.  *
  2548.  * Side Effects:
  2549.  *    Memory is re/allocated.
  2550.  *
  2551.  * --------------------------------------------------------------------
  2552.  */
  2553. static int
  2554. ResizeArray(arrayPtr, elemSize, newLength, prevLength)
  2555.     char **arrayPtr;
  2556.     unsigned int elemSize;
  2557.     unsigned int newLength;
  2558.     unsigned int prevLength;
  2559. {
  2560.     char *newPtr;
  2561.  
  2562.     if (newLength == prevLength) {
  2563.     return TCL_OK;
  2564.     }
  2565.     if (newLength == 0) {    /* Free entire array */
  2566.     free(*arrayPtr);
  2567.     *arrayPtr = NULL;
  2568.     return TCL_OK;
  2569.     }
  2570.     newPtr = (char *)calloc(elemSize, newLength);
  2571.     if (newPtr == NULL) {
  2572.     return TCL_ERROR;
  2573.     }
  2574.     if ((prevLength > 0) && (*arrayPtr != NULL)) {
  2575.     unsigned int size;
  2576.  
  2577.     size = BLT_MIN(prevLength, newLength) * elemSize;
  2578.     if (size > 0) {
  2579.         memcpy(newPtr, *arrayPtr, size);
  2580.     }
  2581.     free(*arrayPtr);
  2582.     }
  2583.     *arrayPtr = newPtr;
  2584.     return TCL_OK;
  2585. }
  2586.  
  2587. #ifdef notdef
  2588. static int
  2589. TextXYToIndex(TextPtr, x, y)
  2590.     Text *TextPtr;
  2591.     int x, y;
  2592.  
  2593. {
  2594.     Line *line = TextPtr->topLine;
  2595.     int result, dummy;
  2596.  
  2597.     /* locate the line */
  2598.  
  2599.     y -= TextPtr->borderWidth + 1;
  2600.  
  2601.     if (y > 0) {
  2602.     int count = y / TextPtr->pmHeight;
  2603.  
  2604.     while (line->length > 0 && count--)
  2605.         line = line->next;
  2606.     }
  2607.     if (line->length == 0)
  2608.     result = TextPtr->numChars;
  2609.     else
  2610.     result = line->index +
  2611.         TkMeasureChars(TextPtr->fontPtr,
  2612.         line->text,
  2613.         line->length,
  2614.         TextPtr->offset,
  2615.         x + TextPtr->leftIndex * TextPtr->avgWidth,
  2616.         0, &dummy);
  2617.  
  2618.     return result;
  2619. }
  2620.  
  2621. /*
  2622.  *--------------------------------------------------------------
  2623.  *
  2624.  * GetTextIndex --
  2625.  *
  2626.  *    Parse an index into a Text widget and return either its value
  2627.  *    or an error.
  2628.  *
  2629.  * Results:
  2630.  *    A standard Tcl result.  If all went well, then *indexPtr is
  2631.  *    filled in with the index (into TextPtr) corresponding to
  2632.  *    string.  Otherwise an error message is left in interp->result.
  2633.  *
  2634.  * Side effects:
  2635.  *    None.
  2636.  *
  2637.  *--------------------------------------------------------------
  2638.  */
  2639. static int
  2640. GetTextIndex(interp, TextPtr, string, indexPtr)
  2641.     Tcl_Interp *interp;        /* For error messages. */
  2642.     Text *TextPtr;        /* Text for which the index is being
  2643.                  * specified. */
  2644.     char *string;        /* Numerical index into TextPtr's element
  2645.                  * list, or "end" to refer to last element. */
  2646.     int *indexPtr;        /* Where to store converted relief. */
  2647. {
  2648.     int length;
  2649.     char c;
  2650.  
  2651.     length = strlen(string);
  2652.     c = string[0];
  2653.  
  2654.     if ((c == 'e') && (strncmp(string, "end", length) == 0)) {
  2655.     *indexPtr = TextPtr->numChars;
  2656.     } else if ((c == 'c') && (strncmp(string, "cursor", length) == 0)) {
  2657.     *indexPtr = TextPtr->cursorPos;
  2658.     } else if ((c == 's') && (length > 4) &&
  2659.     (strncmp(string, "sel.first", length) == 0)) {
  2660.  
  2661.     if (TextPtr->selectFirst == -1) {
  2662.         interp->result = "selection isn't in Text";
  2663.         return TCL_ERROR;
  2664.     }
  2665.     } else if ((c == 's') && (length > 4) &&
  2666.     (strncmp(string, "sel.last", length) == 0)) {
  2667.     *indexPtr = TextPtr->selectLast;
  2668.     } else if ((c == 't') && (strncmp(string, "top", length) == 0)) {
  2669.     *indexPtr = TextPtr->topLine->index;
  2670.     } else if (c == '@') {
  2671.     char *comma;
  2672.     int x, y;
  2673.  
  2674.     string++;
  2675.     if ((comma = strchr(string, ',')) == NULL) {
  2676.         goto badIndex;
  2677.     }
  2678.     *comma = '\0';
  2679.     if ((Tcl_GetInt(interp, string, &x) != TCL_OK) ||
  2680.         (Tcl_GetInt(interp, comma + 1, &y) != TCL_OK)) {
  2681.         goto badIndex;
  2682.     }
  2683.     if (TextPtr->numChars == 0) {
  2684.         *indexPtr = 0;
  2685.     } else {
  2686.         *indexPtr = TextXYToIndex(TextPtr, x, y);
  2687.     }
  2688.     } else {
  2689.     if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
  2690.         goto badIndex;
  2691.     }
  2692.     if (*indexPtr < 0) {
  2693.         *indexPtr = 0;
  2694.     } else if (*indexPtr > TextPtr->numChars) {
  2695.         *indexPtr = TextPtr->numChars;
  2696.     }
  2697.     }
  2698.     return TCL_OK;
  2699.   badIndex:
  2700.  
  2701.     /*
  2702.      * Some of the paths here leave messages in interp->result, so we
  2703.      * have to clear it out before storing our own message.
  2704.      */
  2705.     Tcl_SetResult(interp, (char *)NULL, TCL_STATIC);
  2706.     Tcl_AppendResult(interp, "bad text index \"", string,
  2707.     "\"", (char *)NULL);
  2708.     return TCL_ERROR;
  2709. }
  2710.  
  2711. /*
  2712.  *----------------------------------------------------------------------
  2713.  *
  2714.  * TextSelectTo --
  2715.  *
  2716.  *    Modify the selection by moving its un-anchored end.  This could
  2717.  *    make the selection either larger or smaller.
  2718.  *
  2719.  * Results:
  2720.  *    None.
  2721.  *
  2722.  * Side effects:
  2723.  *    The selection changes.
  2724.  *
  2725.  *----------------------------------------------------------------------
  2726.  */
  2727. static void
  2728. TextSelectTo(TextPtr, index)
  2729.     register Text *TextPtr;    /* Information about widget. */
  2730.     int index;            /* Index of element that is to
  2731.                  * become the "other" end of the
  2732.                  * selection. */
  2733. {
  2734.     int newFirst, newLast;
  2735.  
  2736.     /*
  2737.      * Grab the selection if we don't own it already.
  2738.      */
  2739.  
  2740.     if (TextPtr->selectFirst == -1) {
  2741.     Tk_OwnSelection(TextPtr->tkwin, TextLostSelection,
  2742.         (ClientData)TextPtr);
  2743.     }
  2744.     if (index < 0) {
  2745.     index = 0;
  2746.     }
  2747.     if (index >= TextPtr->numChars) {
  2748.     index = TextPtr->numChars - 1;
  2749.     }
  2750.     if (TextPtr->selectAnchor > TextPtr->numChars) {
  2751.     TextPtr->selectAnchor = TextPtr->numChars;
  2752.     }
  2753.     if (TextPtr->selectAnchor <= index) {
  2754.     newFirst = TextPtr->selectAnchor;
  2755.     newLast = index;
  2756.     } else {
  2757.     newFirst = index;
  2758.     newLast = TextPtr->selectAnchor - 1;
  2759.     if (newLast < 0) {
  2760.         newFirst = newLast = -1;
  2761.     }
  2762.     }
  2763.     if ((TextPtr->selectFirst == newFirst)
  2764.     && (TextPtr->selectLast == newLast)) {
  2765.     return;
  2766.     }
  2767.     TextPtr->selectFirst = newFirst;
  2768.     TextPtr->selectLast = newLast;
  2769.     TextPtr->flags |= REFRESH_NEEDED;
  2770.     EventuallyRedraw(TextPtr);
  2771. }
  2772.  
  2773. /*
  2774.  *----------------------------------------------------------------------
  2775.  *
  2776.  * TextFetchSelection --
  2777.  *
  2778.  *    This procedure is called back by Tk when the selection is
  2779.  *    requested by someone.  It returns part or all of the selection
  2780.  *    in a buffer provided by the caller.
  2781.  *
  2782.  * Results:
  2783.  *    The return value is the number of non-NULL bytes stored
  2784.  *    at buffer.  Buffer is filled (or partially filled) with a
  2785.  *    NULL-terminated string containing part or all of the selection,
  2786.  *    as given by offset and maxBytes.
  2787.  *
  2788.  * Side effects:
  2789.  *    None.
  2790.  *
  2791.  *----------------------------------------------------------------------
  2792.  */
  2793. static int
  2794. TextFetchSelection(clientData, offset, buffer, maxBytes)
  2795.     ClientData clientData;    /* Information about Text widget. */
  2796.     int offset;            /* Offset within selection of first
  2797.                  * character to be returned. */
  2798.     char *buffer;        /* Location in which to place
  2799.                  * selection. */
  2800.     int maxBytes;        /* Maximum number of bytes to place
  2801.                  * at buffer, not including terminating
  2802.                  * NULL character. */
  2803. {
  2804.     Text *TextPtr = (Text *) clientData;
  2805.     char *text;
  2806.     int count;
  2807.  
  2808.     if (TextPtr->selectFirst < 0) {
  2809.     return -1;
  2810.     }
  2811.     count = TextPtr->selectLast + 1 - TextPtr->selectFirst - offset;
  2812.     if (count > maxBytes) {
  2813.     count = maxBytes;
  2814.     }
  2815.     if (count <= 0) {
  2816.     return 0;
  2817.     }
  2818.     text = GetText(TextPtr, TextPtr->selectFirst + offset, count);
  2819.  
  2820.     if (text != NULL) {
  2821.     strcpy(buffer, text);
  2822.     free(text);
  2823.     } else
  2824.     count = 0;
  2825.  
  2826.     return count;
  2827. }
  2828.  
  2829. /*
  2830.  *----------------------------------------------------------------------
  2831.  *
  2832.  * TextLostSelection --
  2833.  *
  2834.  *    This procedure is called back by Tk when the selection is
  2835.  *    grabbed away from a Text widget.
  2836.  *
  2837.  * Results:
  2838.  *    None.
  2839.  *
  2840.  * Side effects:
  2841.  *    The existing selection is unhighlighted, and the window is
  2842.  *    marked as not containing a selection.
  2843.  *
  2844.  *----------------------------------------------------------------------
  2845.  */
  2846. static void
  2847. TextLostSelection(clientData)
  2848.     ClientData clientData;    /* Information about Text widget. */
  2849. {
  2850.     Text *TextPtr = (Text *) clientData;
  2851.  
  2852.     TextPtr->selectFirst = -1;
  2853.     TextPtr->selectLast = -1;
  2854.     EventuallyRedraw(TextPtr);
  2855. }
  2856.  
  2857. #endif
  2858.  
  2859. /*
  2860.  *----------------------------------------------------------------------
  2861.  *
  2862.  * GotoLine --
  2863.  *
  2864.  *    Move the top line of the viewport to the new location based
  2865.  *    upon the given line number.  Force outlier requests to the
  2866.  *    top or bottom of text.
  2867.  *
  2868.  * Results:
  2869.  *    Stanard Tcl result. If TCL_OK, interp->result contains the
  2870.  *    current line number.
  2871.  *
  2872.  * Side effects:
  2873.  *    At the next idle point, the text viewport will be move to the
  2874.  *    new line.
  2875.  *
  2876.  *----------------------------------------------------------------------
  2877.  */
  2878. static int
  2879. GotoLine(textPtr, interp, argc, argv)
  2880.     Htext *textPtr;
  2881.     Tcl_Interp *interp;
  2882.     int argc;
  2883.     char **argv;
  2884. {
  2885.     int line;
  2886.  
  2887.     if (argc > 3) {
  2888.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  2889.         " gotoline ?line?\"", (char *)NULL);
  2890.     return TCL_ERROR;
  2891.     }
  2892.     line = textPtr->first;
  2893.     if (argc == 3) {
  2894.     if ((argv[2][0] == 'e') && (strcmp(argv[2], "end") == 0)) {
  2895.         line = textPtr->numLines - 1;
  2896.     } else {
  2897.         if (Tcl_GetInt(textPtr->interp, argv[2], &line) != TCL_OK) {
  2898.         return TCL_ERROR;
  2899.         }
  2900.         /* Keep the line request within the current range of lines */
  2901.         if (line < 1) {
  2902.         line = 1;
  2903.         } else if (line > textPtr->numLines) {
  2904.         line = textPtr->numLines;
  2905.         }
  2906.         line--;
  2907.     }
  2908.     textPtr->reqLineNum = line;
  2909.     textPtr->flags |= VIEW_MOVED;
  2910.  
  2911.     /*
  2912.      * Make only a request for a change in the viewport.  Defer
  2913.      * the actual scrolling until the text layout is adjusted at
  2914.      * the next idle point.
  2915.      */
  2916.     if (line != textPtr->first) {
  2917.         textPtr->flags |= REQUEST_GOTO;
  2918.         EventuallyRedraw(textPtr);
  2919.     }
  2920.     }
  2921.     sprintf(textPtr->interp->result, "%d", line + 1);
  2922.     return TCL_OK;
  2923. }
  2924.  
  2925. static int
  2926. ScrollX(textPtr, string)
  2927.     Htext *textPtr;
  2928.     char *string;
  2929. {
  2930.     int x;
  2931.  
  2932.     x = textPtr->vPort.x;
  2933.     if (string != NULL) {
  2934.     if (Tk_GetPixels(textPtr->interp,
  2935.         textPtr->tkwin, string, &x) != TCL_OK) {
  2936.         return TCL_ERROR;
  2937.     }
  2938.     x *= textPtr->scrollX;    /* Convert to pixels */
  2939.     if (x > textPtr->vPort.width) {
  2940.         x = textPtr->vPort.width - 1;
  2941.     } else if (x < 0) {
  2942.         x = 0;
  2943.     }
  2944.     if (x != textPtr->vPort.x) {
  2945.         textPtr->pendingX = x;
  2946.         textPtr->flags |= VIEW_MOVED;
  2947.         EventuallyRedraw(textPtr);
  2948.     }
  2949.     }
  2950.     sprintf(textPtr->interp->result, "%d", x / textPtr->scrollX);
  2951.     return TCL_OK;
  2952. }
  2953.  
  2954.  
  2955. static int
  2956. ScrollY(textPtr, string)
  2957.     Htext *textPtr;
  2958.     char *string;
  2959. {
  2960.     int y;
  2961.  
  2962.     y = textPtr->vPort.y;
  2963.     if (string != NULL) {
  2964.     if (Tk_GetPixels(textPtr->interp,
  2965.         textPtr->tkwin, string, &y) != TCL_OK) {
  2966.         return TCL_ERROR;
  2967.     }
  2968.     y *= textPtr->scrollY;    /* Convert to pixels */
  2969.     if (y > textPtr->vPort.height) {
  2970.         y = textPtr->vPort.height - 1;
  2971.     } else if (y < 0) {
  2972.         y = 0;
  2973.     }
  2974.     if (y != textPtr->vPort.y) {
  2975.         textPtr->pendingY = y;
  2976.         textPtr->flags |= VIEW_MOVED;
  2977.         EventuallyRedraw(textPtr);
  2978.     }
  2979.     }
  2980.     sprintf(textPtr->interp->result, "%d", y / textPtr->scrollY);
  2981.     return TCL_OK;
  2982. }
  2983.  
  2984. /*
  2985.  *----------------------------------------------------------------------
  2986.  *
  2987.  * GetChildWindows --
  2988.  *
  2989.  *    Returns a list of all the pathNames of child windows of the
  2990.  *    Htext widget.  If a pattern argument is given, only the names
  2991.  *    of windows matching it will be placed into the list.
  2992.  *
  2993.  * Results:
  2994.  *    Standard Tcl result.  If TCL_OK, interp->result will contain
  2995.  *    the list of the child window pathnames.  Otherwise it will
  2996.  *    contain an error message.
  2997.  *
  2998.  *----------------------------------------------------------------------
  2999.  */
  3000. static int
  3001. GetChildren(textPtr, interp, argc, argv)
  3002.     Htext *textPtr;        /* Hypertext widget record */
  3003.     Tcl_Interp *interp;        /* Interpreter associated with widget */
  3004.     int argc;
  3005.     char **argv;
  3006. {
  3007.     Child *childPtr;
  3008.     Tcl_HashEntry *entryPtr;
  3009.     Tcl_HashSearch searchId;
  3010.     char *name;
  3011.  
  3012.     if ((argc != 2) && (argc != 3)) {
  3013.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3014.         " children ?pattern?\"", (char *)NULL);
  3015.     return TCL_ERROR;
  3016.     }
  3017.     for (entryPtr = Tcl_FirstHashEntry(&(textPtr->subwindows), &searchId);
  3018.     entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&searchId)) {
  3019.     childPtr = (Child *)Tcl_GetHashValue(entryPtr);
  3020.     if (childPtr->tkwin == NULL) {
  3021.         fprintf(stderr, "window `%s' is null\n",
  3022.         Tk_PathName(Tcl_GetHashKey(&(textPtr->subwindows), entryPtr)));
  3023.         continue;
  3024.     }
  3025.     name = Tk_PathName(childPtr->tkwin);
  3026.     if ((argc == 2) || (Tcl_StringMatch(name, argv[2]))) {
  3027.         Tcl_AppendElement(interp, name);
  3028.     }
  3029.     }
  3030.     return TCL_OK;
  3031. }
  3032.  
  3033. /*
  3034.  *----------------------------------------------------------------------
  3035.  *
  3036.  * ConfigureParent --
  3037.  *
  3038.  *     This procedure is called to process an argv/argc list, plus
  3039.  *    the Tk option database, in order to configure (or reconfigure)
  3040.  *    a hypertext widget.
  3041.  *
  3042.  * Results:
  3043.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  3044.  *     returned, then interp->result contains an error message.
  3045.  *
  3046.  * Side effects:
  3047.  *    Configuration information, such as text string, colors, font,
  3048.  *     etc. get set for textPtr;  old resources get freed, if there were any.
  3049.  *     The hypertext is redisplayed.
  3050.  *
  3051.  *----------------------------------------------------------------------
  3052.  */
  3053. static int
  3054. ConfigureParent(textPtr, interp, argc, argv)
  3055.     Htext *textPtr;
  3056.     Tcl_Interp *interp;
  3057.     int argc;
  3058.     char **argv;
  3059. {
  3060.     if (argc == 2) {
  3061.     return (Tk_ConfigureInfo(interp, textPtr->tkwin, configSpecs,
  3062.         (char *)textPtr, (char *)NULL, 0));
  3063.     } else if (argc == 3) {
  3064.     return (Tk_ConfigureInfo(interp, textPtr->tkwin, configSpecs,
  3065.         (char *)textPtr, argv[2], 0));
  3066.     } else {
  3067.     if (ConfigureHtext(interp, textPtr, argc - 2, argv + 2,
  3068.         TK_CONFIG_ARGV_ONLY) != TCL_OK) {
  3069.         return TCL_ERROR;
  3070.     }
  3071.     EventuallyRedraw(textPtr);
  3072.     }
  3073.     return TCL_OK;
  3074. }
  3075.  
  3076. /*
  3077.  * ----------------------------------------------------------------------
  3078.  *
  3079.  * ConfigureChild --
  3080.  *
  3081.  *     This procedure is called to process an argv/argc list, plus
  3082.  *    the Tk option database, in order to configure (or reconfigure)
  3083.  *    a hypertext child.
  3084.  *
  3085.  * Results:
  3086.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  3087.  *     returned, then interp->result contains an error message.
  3088.  *
  3089.  * Side effects:
  3090.  *    Configuration information get set for the child cavity; old
  3091.  *    resources get freed, if there were any. The child is marked
  3092.  *    for redisplay.
  3093.  *
  3094.  *----------------------------------------------------------------------
  3095.  */
  3096. static int
  3097. ConfigureChild(textPtr, interp, argc, argv)
  3098.     Htext *textPtr;
  3099.     Tcl_Interp *interp;
  3100.     int argc;
  3101.     char **argv;
  3102. {
  3103.     Child *childPtr;
  3104.  
  3105.     if (argc < 3) {
  3106.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3107.         " childconfigure childName ?args...?\"", (char *)NULL);
  3108.     return TCL_ERROR;
  3109.     }
  3110.     childPtr = FindChild(textPtr, argv[2]);
  3111.     if (childPtr == NULL) {
  3112.     Tcl_AppendResult(interp, "can't find window \"", argv[2], "\" in \"",
  3113.         Tk_PathName(textPtr->tkwin), "\"", (char *)NULL);
  3114.     return TCL_ERROR;
  3115.     }
  3116.     if (argc == 3) {
  3117.     return (Tk_ConfigureInfo(interp, textPtr->tkwin, childConfigSpecs,
  3118.         (char *)childPtr, (char *)NULL, 0));
  3119.     } else if (argc == 4) {
  3120.     return (Tk_ConfigureInfo(interp, textPtr->tkwin, childConfigSpecs,
  3121.         (char *)childPtr, argv[3], 0));
  3122.     } else {
  3123.     if (Tk_ConfigureWidget(interp, textPtr->tkwin, childConfigSpecs,
  3124.         argc - 3, argv + 3, (char *)childPtr,
  3125.         TK_CONFIG_ARGV_ONLY) != TCL_OK) {
  3126.         return TCL_ERROR;
  3127.     }
  3128.     textPtr->flags |= REQUEST_LAYOUT;
  3129.     EventuallyRedraw(textPtr);
  3130.     }
  3131.     return TCL_OK;
  3132. }
  3133.  
  3134. /*
  3135.  *----------------------------------------------------------------------
  3136.  *
  3137.  * ScanText --
  3138.  *
  3139.  *    Implements the quick scan for hypertext widgets.
  3140.  *
  3141.  *----------------------------------------------------------------------
  3142.  */
  3143. static int
  3144. ScanText(textPtr, interp, argc, argv)
  3145.     Htext *textPtr;
  3146.     Tcl_Interp *interp;
  3147.     int argc;
  3148.     char **argv;
  3149. {
  3150.     int markX, markY;
  3151.     char c;
  3152.     int length;
  3153.  
  3154.     if (argc != 5) {
  3155.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3156.         " scan mark|dragto x y\"", (char *)NULL);
  3157.     return TCL_ERROR;
  3158.     }
  3159.     if ((Tcl_GetInt(interp, argv[3], &markX) != TCL_OK) ||
  3160.     (Tcl_GetInt(interp, argv[4], &markY) != TCL_OK)) {
  3161.     return TCL_ERROR;
  3162.     }
  3163.     c = argv[2][0];
  3164.     length = strlen(argv[2]);
  3165.     if ((c == 'm') && (strncmp(argv[2], "mark", length) == 0)) {
  3166.     textPtr->scanMark.x = markX, textPtr->scanMark.y = markY;
  3167.     textPtr->scanPt.x = textPtr->vPort.x;
  3168.     textPtr->scanPt.y = textPtr->vPort.y;
  3169.  
  3170.     } else if ((c == 'd') && (strncmp(argv[2], "dragto", length) == 0)) {
  3171.     int x, y;
  3172.  
  3173.     x = textPtr->scanPt.x - (10 * (markX - textPtr->scanMark.x));
  3174.     y = textPtr->scanPt.y - (10 * (markY - textPtr->scanMark.y));
  3175.  
  3176.     if (x < 0) {
  3177.         x = textPtr->scanPt.x = 0;
  3178.         textPtr->scanMark.x = markX;
  3179.     } else if (x >= textPtr->vPort.width) {
  3180.         x = textPtr->scanPt.x = textPtr->vPort.width - textPtr->scrollX;
  3181.         textPtr->scanMark.x = markX;
  3182.     }
  3183.     if (y < 0) {
  3184.         y = textPtr->scanPt.y = 0;
  3185.         textPtr->scanMark.y = markY;
  3186.     } else if (y >= textPtr->vPort.height) {
  3187.         y = textPtr->scanPt.y = textPtr->vPort.height - textPtr->scrollY;
  3188.         textPtr->scanMark.y = markY;
  3189.     }
  3190.     if ((y != textPtr->pendingY) || (x != textPtr->pendingX)) {
  3191.         textPtr->pendingX = x, textPtr->pendingY = y;
  3192.         textPtr->flags |= VIEW_MOVED;
  3193.         EventuallyRedraw(textPtr);
  3194.     }
  3195.     } else {
  3196.     Tcl_AppendResult(interp, "bad scan option \"", argv[2],
  3197.         "\":  must be mark or dragto", (char *)NULL);
  3198.     return TCL_ERROR;
  3199.     }
  3200.     return TCL_OK;
  3201. }
  3202.  
  3203. /*
  3204.  *----------------------------------------------------------------------
  3205.  *
  3206.  * SearchText --
  3207.  *
  3208.  *    Returns the linenumber of the next line matching the given
  3209.  *    pattern within the range of lines provided.  If the first
  3210.  *    line number is greater than the last, the search is done in
  3211.  *    reverse.
  3212.  *
  3213.  *----------------------------------------------------------------------
  3214.  */
  3215. static int
  3216. SearchText(textPtr, interp, argc, argv)
  3217.     Htext *textPtr;
  3218.     Tcl_Interp *interp;
  3219.     int argc;
  3220.     char **argv;
  3221. {
  3222.     int first, last;
  3223.     register int i;
  3224.     register char *string;
  3225.     int lineNum;
  3226.     enum Direction {
  3227.     REVERSE = -1, FORWARD = 1
  3228.     } direction;        /* Indicates the direction of the search */
  3229.  
  3230.     if ((argc < 3) || (argc > 5)) {
  3231.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3232.         " search pattern ?from? ?to?\"", (char *)NULL);
  3233.     return TCL_ERROR;
  3234.     }
  3235.     first = 0;
  3236.     last = textPtr->numLines - 1;
  3237.  
  3238.     if (argc > 3) {
  3239.     if (argv[3][0] == 'e' && strcmp(argv[3], "end") == 0) {
  3240.         first = textPtr->numLines - 1;
  3241.     } else {
  3242.         if (Tcl_GetInt(interp, argv[3], &first) != TCL_OK) {
  3243.         return TCL_ERROR;
  3244.         }
  3245.         first--;
  3246.         if (first < 0) {
  3247.         first = 0;
  3248.         } else if (first >= textPtr->numLines) {
  3249.         first = textPtr->numLines - 1;
  3250.         }
  3251.     }
  3252.     }
  3253.     if (argc > 4) {
  3254.     if ((argv[4][0] == 'e') && (strcmp(argv[4], "end") == 0)) {
  3255.         last = textPtr->numLines - 1;
  3256.     } else {
  3257.         if (Tcl_GetInt(interp, argv[4], &last) != TCL_OK) {
  3258.         return TCL_ERROR;
  3259.         }
  3260.         last--;
  3261.         if (last < 0) {
  3262.         last = 0;
  3263.         } else if (last >= textPtr->numLines) {
  3264.         last = textPtr->numLines - 1;
  3265.         }
  3266.     }
  3267.     }
  3268.     direction = (first > last) ? REVERSE : FORWARD;
  3269.     lineNum = -1;
  3270.     for (i = first; i != (last + direction); i += direction) {
  3271.     string = textPtr->linePtrPtr[i]->text;
  3272.     if ((string != NULL) && Tcl_StringMatch(string, argv[2])) {
  3273.         lineNum = i + 1;
  3274.         break;
  3275.     }
  3276.     }
  3277.     sprintf(textPtr->interp->result, "%d", lineNum);
  3278.     return TCL_OK;
  3279. }
  3280.  
  3281. /*
  3282.  * --------------------------------------------------------------
  3283.  *
  3284.  * TextWidgetCmd --
  3285.  *
  3286.  *     This procedure is invoked to process the Tcl command that
  3287.  *    corresponds to a widget managed by this module. See the user
  3288.  *     documentation for details on what it does.
  3289.  *
  3290.  * Results:
  3291.  *    A standard Tcl result.
  3292.  *
  3293.  * Side effects:
  3294.  *    See the user documentation.
  3295.  *
  3296.  * --------------------------------------------------------------
  3297.  */
  3298. static int
  3299. TextWidgetCmd(clientData, interp, argc, argv)
  3300.     ClientData clientData;    /* Information about hypertext widget. */
  3301.     Tcl_Interp *interp;        /* Current interpreter. */
  3302.     int argc;            /* Number of arguments. */
  3303.     char **argv;        /* Argument strings. */
  3304. {
  3305.     register Htext *textPtr = (Htext *)clientData;
  3306.     int result = TCL_OK;
  3307.     int length;
  3308.     char c;
  3309.  
  3310.     if (argc < 2) {
  3311.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3312.         " option ?arg arg ...?\"", (char *)NULL);
  3313.     return TCL_ERROR;
  3314.     }
  3315.     Tk_Preserve((ClientData)textPtr);
  3316.     c = argv[1][0];
  3317.     length = strlen(argv[1]);
  3318.  
  3319.     if ((c == 'a') && (strncmp(argv[1], "append", length) == 0)) {
  3320.     result = AppendChild(textPtr, interp, argc, argv);
  3321.     } else if ((c == 'c') && (length > 5) &&
  3322.     (strncmp(argv[1], "childconfigure", length) == 0)) {
  3323.     result = ConfigureChild(textPtr, interp, argc, argv);
  3324.     } else if ((c == 'c') && (length > 5) &&
  3325.     (strncmp(argv[1], "children", length) == 0)) {
  3326.     result = GetChildren(textPtr, interp, argc, argv);
  3327.     } else if ((c == 'c') && (length > 1) &&
  3328.     (strncmp(argv[1], "configure", length) == 0)) {
  3329.     result = ConfigureParent(textPtr, interp, argc, argv);
  3330.     } else if ((c == 'g') && (strncmp(argv[1], "gotoline", length) == 0)) {
  3331.     result = GotoLine(textPtr, interp, argc, argv);
  3332.     } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
  3333.     if (argc > 3) {
  3334.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3335.         " xview ?position?\"", (char *)NULL);
  3336.         goto error;
  3337.     }
  3338.     result = ScrollX(textPtr, argv[2]);
  3339.     } else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)) {
  3340.     if (argc > 3) {
  3341.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3342.         " yview ?position?\"", (char *)NULL);
  3343.         goto error;
  3344.     }
  3345.     result = ScrollY(textPtr, argv[2]);
  3346.     } else if ((c == 's') && (length > 1)
  3347.     && (strncmp(argv[1], "scan", length) == 0)) {
  3348.     result = ScanText(textPtr, interp, argc, argv);
  3349.     } else if ((c == 's') && (length > 2) &&
  3350.     strncmp(argv[1], "search", length) == 0) {
  3351.     result = SearchText(textPtr, interp, argc, argv);
  3352. #ifdef notdef
  3353.     } else if ((c == 's') && (length > 1) &&
  3354.     strncmp(argv[1], "select", length) == 0) {
  3355.     int index;
  3356.  
  3357.     if (argc < 3) {
  3358.         Tcl_AppendResult(interp, "too few args: should be \"",
  3359.         argv[0], " select option ?index?\"", (char *)NULL);
  3360.         goto error;
  3361.     }
  3362.     length = strlen(argv[2]);
  3363.     c = argv[2][0];
  3364.     if ((c == 'c') && (strncmp(argv[2], "clear", length) == 0)) {
  3365.         if (argc != 3) {
  3366.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  3367.             argv[0], " select clear\"", (char *)NULL);
  3368.         goto error;
  3369.         }
  3370.         if (textPtr->selectFirst != -1) {
  3371.         textPtr->selectFirst = textPtr->selectLast = -1;
  3372.         goto redisplay;
  3373.         }
  3374.         goto error;
  3375.     }
  3376.     if (argc >= 4) {
  3377.         if (GetTextIndex(interp, textPtr, argv[3], &index) != TCL_OK) {
  3378.         goto error;
  3379.         }
  3380.     }
  3381.     if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
  3382.         if (argc != 4) {
  3383.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  3384.             argv[0], " select adjust index\"",
  3385.             (char *)NULL);
  3386.         goto error;
  3387.         }
  3388.         TextSelectTo(textPtr, index);
  3389.     } else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
  3390.         if (argc != 4) {
  3391.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  3392.             argv[0], " select from index\"",
  3393.             (char *)NULL);
  3394.         goto error;
  3395.         }
  3396.         textPtr->selectAnchor = index;
  3397.     } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
  3398.         if (argc != 4) {
  3399.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  3400.             argv[0], " select to index\"", (char *)NULL);
  3401.         goto error;
  3402.         }
  3403.         TextSelectTo(textPtr, index);
  3404.     } else {
  3405.         Tcl_AppendResult(interp, "bad select option \"", argv[2],
  3406.         "\": must be adjust, clear, from, or to", (char *)NULL);
  3407.         goto error;
  3408.     }
  3409. #endif
  3410.     } else {
  3411.     Tcl_AppendResult(interp, "bad option \"", argv[1], "\": should be ",
  3412.         "append, configure, childconfigure, gotoline, scan, search, xview, or yview",
  3413.         (char *)NULL);
  3414.     }
  3415.   error:
  3416.     Tk_Release((ClientData)textPtr);
  3417.     return result;
  3418. }
  3419.  
  3420. /*
  3421.  * --------------------------------------------------------------
  3422.  *
  3423.  * HtextCmd --
  3424.  *
  3425.  *     This procedure is invoked to process the "htext" Tcl command.
  3426.  *    See the user documentation for details on what it does.
  3427.  *
  3428.  * Results:
  3429.  *    A standard Tcl result.
  3430.  *
  3431.  * Side effects:
  3432.  *    See the user documentation.
  3433.  *
  3434.  * --------------------------------------------------------------
  3435.  */
  3436.  
  3437. static int
  3438. HtextCmd(clientData, interp, argc, argv)
  3439.     ClientData clientData;    /* Main window associated with interpreter. */
  3440.     Tcl_Interp *interp;        /* Current interpreter. */
  3441.     int argc;            /* Number of arguments. */
  3442.     char **argv;        /* Argument strings. */
  3443. {
  3444.     register Htext *textPtr;
  3445.     Tk_Window mainWindow = (Tk_Window)clientData;
  3446.     Tk_Window tkwin;
  3447.  
  3448.     if (argc < 2) {
  3449.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3450.         " pathName ?options?\"", (char *)NULL);
  3451.     return TCL_ERROR;
  3452.     }
  3453.     textPtr = (Htext *)calloc(1, sizeof(Htext));
  3454.     if (textPtr == NULL) {
  3455.     interp->result = "can't allocate text structure";
  3456.     return TCL_ERROR;
  3457.     }
  3458.     tkwin = Tk_CreateWindowFromPath(interp, mainWindow, argv[1], (char *)NULL);
  3459.     if (tkwin == NULL) {
  3460.     free(textPtr);
  3461.     return TCL_ERROR;
  3462.     }
  3463.     /* Initialize the new hypertext widget */
  3464.  
  3465.     Tk_SetClass(tkwin, "Blt_htext");
  3466.     textPtr->tkwin = tkwin;
  3467.     textPtr->display = Tk_Display(tkwin);
  3468.     textPtr->interp = interp;
  3469.     textPtr->numLines = textPtr->arraySize = 0;
  3470.     textPtr->lineSpacing = 1;
  3471.     textPtr->scrollX = textPtr->scrollY = 10;
  3472.     Tcl_InitHashTable(&(textPtr->subwindows), TCL_ONE_WORD_KEYS);
  3473.  
  3474.     /*
  3475.      * -----------------------------------------------------------------
  3476.      *
  3477.      * Create the widget command before configuring the widget. This
  3478.      * is because the "-file" and "-text" options may have embedded
  3479.      * commands that self-reference the widget through the
  3480.      * "$blt_htext(widget)" variable.
  3481.      *
  3482.      * ------------------------------------------------------------------
  3483.      */
  3484.  
  3485.     Tcl_CreateCommand(interp, argv[1], TextWidgetCmd, (ClientData)textPtr,
  3486.     (Tcl_CmdDeleteProc *)NULL);
  3487.     Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask,
  3488.     TextEventProc, (ClientData)textPtr);
  3489.  
  3490.     if (ConfigureHtext(interp, textPtr, argc - 2, argv + 2, 0) != TCL_OK) {
  3491.     Tk_DestroyWindow(textPtr->tkwin);
  3492.     return TCL_ERROR;
  3493.     }
  3494.     Tcl_SetResult(interp, Tk_PathName(textPtr->tkwin), TCL_STATIC);
  3495.  
  3496.     return TCL_OK;
  3497. }
  3498.  
  3499. int
  3500. Blt_HtextInit(interp)
  3501.     Tcl_Interp *interp;
  3502. {
  3503.     Tk_Window tkwin;
  3504.  
  3505.     if (Blt_FindCmd(interp, HTEXT_CMDNAME, (ClientData *)NULL) == TCL_OK) {
  3506.     Tcl_AppendResult(interp, "\"blt_htext\" command already exists",
  3507.         (char *)NULL);
  3508.     return TCL_ERROR;
  3509.     }
  3510.     tkwin = Tk_MainWindow(interp);
  3511.     if (tkwin == NULL) {
  3512.     Tcl_AppendResult(interp, "\"blt_htext\" requires Tk", (char *)NULL);
  3513.     return TCL_ERROR;
  3514.     }
  3515.     Tcl_SetVar2(interp, "blt_versions", HTEXT_CMDNAME, HTEXT_VERSION,
  3516.     TCL_GLOBAL_ONLY);
  3517.     Tcl_CreateCommand(interp, HTEXT_CMDNAME, HtextCmd, (ClientData)tkwin,
  3518.     (Tcl_CmdDeleteProc *)NULL);
  3519.     return TCL_OK;
  3520. }
  3521.