home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / tcl / tk3.3b1 / tkCanvText.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-26  |  45.8 KB  |  1,554 lines

  1. /* 
  2.  * tkCanvText.c --
  3.  *
  4.  *    This file implements text items for canvas widgets.
  5.  *
  6.  * Copyright (c) 1991-1993 The Regents of the University of California.
  7.  * All rights reserved.
  8.  *
  9.  * Permission is hereby granted, without written agreement and without
  10.  * license or royalty fees, to use, copy, modify, and distribute this
  11.  * software and its documentation for any purpose, provided that the
  12.  * above copyright notice and the following two paragraphs appear in
  13.  * all copies of this software.
  14.  * 
  15.  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
  16.  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
  17.  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
  18.  * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  19.  *
  20.  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  21.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  22.  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  23.  * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
  24.  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  25.  */
  26.  
  27. #ifndef lint
  28. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkCanvText.c,v 1.25 93/06/26 16:46:37 ouster Exp $ SPRITE (Berkeley)";
  29. #endif
  30.  
  31. #include <stdio.h>
  32. #include "tkInt.h"
  33. #include "tkCanvas.h"
  34. #include "tkConfig.h"
  35.  
  36. /*
  37.  * One of the following structures is kept for each line of text
  38.  * in a text item.  It contains geometry and display information
  39.  * for that line.
  40.  */
  41.  
  42. typedef struct TextLine {
  43.     char *firstChar;        /* Pointer to the first character in this
  44.                  * line (in the "text" field of enclosing
  45.                  * text item). */
  46.     int numChars;        /* Number of characters displayed in this
  47.                  * line. */
  48.     int totalChars;        /* Total number of characters included as
  49.                  * part of this line (may include an extra
  50.                  * space character at the end that isn't
  51.                  * displayed). */
  52.     int x, y;            /* Origin at which to draw line on screen
  53.                  * (in integer pixel units, but in canvas
  54.                  * coordinates, not screen coordinates). */
  55.     int x1, y1;            /* Upper-left pixel that is part of text
  56.                  * line on screen (again, in integer canvas
  57.                  * pixel units). */
  58.     int x2, y2;            /* Lower-left pixel that is part of text
  59.                  * line on screen (again, in integer canvas
  60.                  * pixel units). */
  61. } TextLine;
  62.  
  63. /*
  64.  * The structure below defines the record for each text item.
  65.  */
  66.  
  67. typedef struct TextItem  {
  68.     Tk_Item header;        /* Generic stuff that's the same for all
  69.                  * types.  MUST BE FIRST IN STRUCTURE. */
  70.     char *text;            /* Text for item (malloc-ed). */
  71.     int numChars;        /* Number of non-NULL characters in text. */
  72.     double x, y;        /* Positioning point for text. */
  73.     Tk_Anchor anchor;        /* Where to anchor text relative to (x,y). */
  74.     int width;            /* Width of lines for word-wrap, pixels.
  75.                  * Zero means no word-wrap. */
  76.     Tk_Justify justify;        /* Justification mode for text. */
  77.     int rightEdge;        /* Pixel just to right of right edge of
  78.                  * area of text item.  Used for selecting
  79.                  * up to end of line. */
  80.     XFontStruct *fontPtr;    /* Font for drawing text. */
  81.     XColor *color;        /* Color for text. */
  82.     Pixmap stipple;        /* Stipple bitmap for text, or None. */
  83.     GC gc;            /* Graphics context for drawing text. */
  84.     TextLine *linePtr;        /* Pointer to array of structures describing
  85.                  * individual lines of text item (malloc-ed). */
  86.     int numLines;        /* Number of structs at *linePtr. */
  87.     int insertPos;        /* Insertion cursor is displayed just to left
  88.                  * of character with this index. */
  89.     GC selTextGC;        /* Graphics context for selected text. */
  90. } TextItem;
  91.  
  92. /*
  93.  * Information used for parsing configuration specs:
  94.  */
  95.  
  96. static Tk_ConfigSpec configSpecs[] = {
  97.     {TK_CONFIG_ANCHOR, "-anchor", (char *) NULL, (char *) NULL,
  98.     "center", Tk_Offset(TextItem, anchor),
  99.     TK_CONFIG_DONT_SET_DEFAULT},
  100.     {TK_CONFIG_COLOR, "-fill", (char *) NULL, (char *) NULL,
  101.     "black", Tk_Offset(TextItem, color), 0},
  102.     {TK_CONFIG_FONT, "-font", (char *) NULL, (char *) NULL,
  103.     "-Adobe-Helvetica-Bold-R-Normal--*-120-*",
  104.     Tk_Offset(TextItem, fontPtr), 0},
  105.     {TK_CONFIG_JUSTIFY, "-justify", (char *) NULL, (char *) NULL,
  106.     "left", Tk_Offset(TextItem, justify),
  107.     TK_CONFIG_DONT_SET_DEFAULT},
  108.     {TK_CONFIG_BITMAP, "-stipple", (char *) NULL, (char *) NULL,
  109.     (char *) NULL, Tk_Offset(TextItem, stipple), TK_CONFIG_NULL_OK},
  110.     {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
  111.     (char *) NULL, 0, TK_CONFIG_NULL_OK, &tkCanvasTagsOption},
  112.     {TK_CONFIG_STRING, "-text", (char *) NULL, (char *) NULL,
  113.     "", Tk_Offset(TextItem, text), 0},
  114.     {TK_CONFIG_PIXELS, "-width", (char *) NULL, (char *) NULL,
  115.     "0", Tk_Offset(TextItem, width), TK_CONFIG_DONT_SET_DEFAULT},
  116.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  117.     (char *) NULL, 0, 0}
  118. };
  119.  
  120. /*
  121.  * Prototypes for procedures defined in this file:
  122.  */
  123.  
  124. static void        ComputeTextBbox _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  125.                 TextItem *textPtr));
  126. static int        ConfigureText _ANSI_ARGS_((
  127.                 Tk_Canvas *canvasPtr, Tk_Item *itemPtr, int argc,
  128.                 char **argv, int flags));
  129. static int        CreateText _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  130.                 struct Tk_Item *itemPtr, int argc, char **argv));
  131. static void        DeleteText _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  132.                 Tk_Item *itemPtr));
  133. static void        DisplayText _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  134.                 Tk_Item *itemPtr, Drawable dst));
  135. static int        GetSelText _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  136.                 Tk_Item *itemPtr, int offset, char *buffer,
  137.                 int maxBytes));
  138. static int        GetTextIndex _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  139.                 Tk_Item *itemPtr, char *indexString,
  140.                 int *indexPtr));
  141. static void        LineToPostscript _ANSI_ARGS_((Tcl_Interp *interp,
  142.                 char *string, int numChars));
  143. static void        ScaleText _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  144.                 Tk_Item *itemPtr, double originX, double originY,
  145.                 double scaleX, double scaleY));
  146. static void        SetTextCursor _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  147.                 Tk_Item *itemPtr, int index));
  148. static int        TextCoords _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  149.                 Tk_Item *itemPtr, int argc, char **argv));
  150. static int        TextDeleteChars _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  151.                 Tk_Item *itemPtr, int first, int last));
  152. static int        TextInsert _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  153.                 Tk_Item *itemPtr, int beforeThis, char *string));
  154. static int        TextToArea _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  155.                 Tk_Item *itemPtr, double *rectPtr));
  156. static double        TextToPoint _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  157.                 Tk_Item *itemPtr, double *pointPtr));
  158. static int        TextToPostscript _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  159.                 Tk_Item *itemPtr, Tk_PostscriptInfo *psInfoPtr));
  160. static void        TranslateText _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  161.                 Tk_Item *itemPtr, double deltaX, double deltaY));
  162.  
  163. /*
  164.  * The structures below defines the rectangle and oval item types
  165.  * by means of procedures that can be invoked by generic item code.
  166.  */
  167.  
  168. Tk_ItemType TkTextType = {
  169.     "text",                /* name */
  170.     sizeof(TextItem),            /* itemSize */
  171.     CreateText,                /* createProc */
  172.     configSpecs,            /* configSpecs */
  173.     ConfigureText,            /* configureProc */
  174.     TextCoords,                /* coordProc */
  175.     DeleteText,                /* deleteProc */
  176.     DisplayText,            /* displayProc */
  177.     0,                    /* alwaysRedraw */
  178.     TextToPoint,            /* pointProc */
  179.     TextToArea,                /* areaProc */
  180.     TextToPostscript,            /* postscriptProc */
  181.     ScaleText,                /* scaleProc */
  182.     TranslateText,            /* translateProc */
  183.     GetTextIndex,            /* indexProc */
  184.     SetTextCursor,            /* icursorProc */
  185.     GetSelText,                /* selectionProc */
  186.     TextInsert,                /* insertProc */
  187.     TextDeleteChars,            /* dTextProc */
  188.     (Tk_ItemType *) NULL        /* nextPtr */
  189. };
  190.  
  191. /*
  192.  *--------------------------------------------------------------
  193.  *
  194.  * CreateText --
  195.  *
  196.  *    This procedure is invoked to create a new text item
  197.  *    in a canvas.
  198.  *
  199.  * Results:
  200.  *    A standard Tcl return value.  If an error occurred in
  201.  *    creating the item then an error message is left in
  202.  *    canvasPtr->interp->result;  in this case itemPtr is
  203.  *    left uninitialized so it can be safely freed by the
  204.  *    caller.
  205.  *
  206.  * Side effects:
  207.  *    A new text item is created.
  208.  *
  209.  *--------------------------------------------------------------
  210.  */
  211.  
  212. static int
  213. CreateText(canvasPtr, itemPtr, argc, argv)
  214.     register Tk_Canvas *canvasPtr;    /* Canvas to hold new item. */
  215.     Tk_Item *itemPtr;            /* Record to hold new item;  header
  216.                      * has been initialized by caller. */
  217.     int argc;                /* Number of arguments in argv. */
  218.     char **argv;            /* Arguments describing rectangle. */
  219. {
  220.     register TextItem *textPtr = (TextItem *) itemPtr;
  221.  
  222.     if (argc < 2) {
  223.     Tcl_AppendResult(canvasPtr->interp, "wrong # args:  should be \"",
  224.         Tk_PathName(canvasPtr->tkwin),
  225.         "\" create text x y [options]", (char *) NULL);
  226.     return TCL_ERROR;
  227.     }
  228.  
  229.     /*
  230.      * Carry out initialization that is needed in order to clean
  231.      * up after errors during the the remainder of this procedure.
  232.      */
  233.  
  234.     textPtr->text = NULL;
  235.     textPtr->anchor = TK_ANCHOR_CENTER;
  236.     textPtr->width = 0;
  237.     textPtr->justify = TK_JUSTIFY_LEFT;
  238.     textPtr->fontPtr = NULL;
  239.     textPtr->color = NULL;
  240.     textPtr->stipple = None;
  241.     textPtr->gc = None;
  242.     textPtr->linePtr = NULL;
  243.     textPtr->numLines = 0;
  244.     textPtr->insertPos = 0;
  245.     textPtr->selTextGC = None;
  246.  
  247.     /*
  248.      * Process the arguments to fill in the item record.
  249.      */
  250.  
  251.     if ((TkGetCanvasCoord(canvasPtr, argv[0], &textPtr->x) != TCL_OK)
  252.         || (TkGetCanvasCoord(canvasPtr, argv[1], &textPtr->y) != TCL_OK)) {
  253.     return TCL_ERROR;
  254.     }
  255.  
  256.     if (ConfigureText(canvasPtr, itemPtr, argc-2, argv+2, 0) != TCL_OK) {
  257.     DeleteText(canvasPtr, itemPtr);
  258.     return TCL_ERROR;
  259.     }
  260.     return TCL_OK;
  261. }
  262.  
  263. /*
  264.  *--------------------------------------------------------------
  265.  *
  266.  * TextCoords --
  267.  *
  268.  *    This procedure is invoked to process the "coords" widget
  269.  *    command on text items.  See the user documentation for
  270.  *    details on what it does.
  271.  *
  272.  * Results:
  273.  *    Returns TCL_OK or TCL_ERROR, and sets canvasPtr->interp->result.
  274.  *
  275.  * Side effects:
  276.  *    The coordinates for the given item may be changed.
  277.  *
  278.  *--------------------------------------------------------------
  279.  */
  280.  
  281. static int
  282. TextCoords(canvasPtr, itemPtr, argc, argv)
  283.     register Tk_Canvas *canvasPtr;    /* Canvas containing item. */
  284.     Tk_Item *itemPtr;            /* Item whose coordinates are to be
  285.                      * read or modified. */
  286.     int argc;                /* Number of coordinates supplied in
  287.                      * argv. */
  288.     char **argv;            /* Array of coordinates: x1, y1,
  289.                      * x2, y2, ... */
  290. {
  291.     register TextItem *textPtr = (TextItem *) itemPtr;
  292.     char x[TCL_DOUBLE_SPACE], y[TCL_DOUBLE_SPACE];
  293.  
  294.     if (argc == 0) {
  295.     Tcl_PrintDouble(canvasPtr->interp, textPtr->x, x);
  296.     Tcl_PrintDouble(canvasPtr->interp, textPtr->y, y);
  297.     Tcl_AppendResult(canvasPtr->interp, x, " ", y, (char *) NULL);
  298.     } else if (argc == 2) {
  299.     if ((TkGetCanvasCoord(canvasPtr, argv[0], &textPtr->x) != TCL_OK)
  300.         || (TkGetCanvasCoord(canvasPtr, argv[1],
  301.             &textPtr->y) != TCL_OK)) {
  302.         return TCL_ERROR;
  303.     }
  304.     ComputeTextBbox(canvasPtr, textPtr);
  305.     } else {
  306.     sprintf(canvasPtr->interp->result,
  307.         "wrong # coordinates:  expected 0 or 2, got %d",
  308.         argc);
  309.     return TCL_ERROR;
  310.     }
  311.     return TCL_OK;
  312. }
  313.  
  314. /*
  315.  *--------------------------------------------------------------
  316.  *
  317.  * ConfigureText --
  318.  *
  319.  *    This procedure is invoked to configure various aspects
  320.  *    of a text item, such as its border and background colors.
  321.  *
  322.  * Results:
  323.  *    A standard Tcl result code.  If an error occurs, then
  324.  *    an error message is left in canvasPtr->interp->result.
  325.  *
  326.  * Side effects:
  327.  *    Configuration information, such as colors and stipple
  328.  *    patterns, may be set for itemPtr.
  329.  *
  330.  *--------------------------------------------------------------
  331.  */
  332.  
  333. static int
  334. ConfigureText(canvasPtr, itemPtr, argc, argv, flags)
  335.     Tk_Canvas *canvasPtr;    /* Canvas containing itemPtr. */
  336.     Tk_Item *itemPtr;        /* Rectangle item to reconfigure. */
  337.     int argc;            /* Number of elements in argv.  */
  338.     char **argv;        /* Arguments describing things to configure. */
  339.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  340. {
  341.     register TextItem *textPtr = (TextItem *) itemPtr;
  342.     XGCValues gcValues;
  343.     GC newGC, newSelGC;
  344.     unsigned long mask;
  345.  
  346.     if (Tk_ConfigureWidget(canvasPtr->interp, canvasPtr->tkwin,
  347.         configSpecs, argc, argv, (char *) textPtr, flags) != TCL_OK) {
  348.     return TCL_ERROR;
  349.     }
  350.  
  351.     /*
  352.      * A few of the options require additional processing, such as
  353.      * graphics contexts.
  354.      */
  355.  
  356.     textPtr->numChars = strlen(textPtr->text);
  357.     newGC = newSelGC = None;
  358.     if ((textPtr->color != NULL) && (textPtr->fontPtr != NULL)) {
  359.     gcValues.foreground = textPtr->color->pixel;
  360.     gcValues.font = textPtr->fontPtr->fid;
  361.     mask = GCForeground|GCFont;
  362.     if (textPtr->stipple != None) {
  363.         gcValues.stipple = textPtr->stipple;
  364.         gcValues.fill_style = FillStippled;
  365.         mask |= GCForeground|GCStipple|GCFillStyle;
  366.     }
  367.     newGC = Tk_GetGC(canvasPtr->tkwin, mask, &gcValues);
  368.     gcValues.foreground = canvasPtr->selFgColorPtr->pixel;
  369.     newSelGC = Tk_GetGC(canvasPtr->tkwin, mask, &gcValues);
  370.     }
  371.     if (textPtr->gc != None) {
  372.     Tk_FreeGC(canvasPtr->display, textPtr->gc);
  373.     }
  374.     textPtr->gc = newGC;
  375.     if (textPtr->selTextGC != None) {
  376.     Tk_FreeGC(canvasPtr->display, textPtr->selTextGC);
  377.     }
  378.     textPtr->selTextGC = newSelGC;
  379.  
  380.     /*
  381.      * If the text was changed, move the selection and insertion indices
  382.      * to keep them inside the item.
  383.      */
  384.  
  385.     if (canvasPtr->selItemPtr == itemPtr) {
  386.     if (canvasPtr->selectFirst >= textPtr->numChars) {
  387.         canvasPtr->selItemPtr = NULL;
  388.     } else {
  389.         if (canvasPtr->selectLast >= textPtr->numChars) {
  390.         canvasPtr->selectLast = textPtr->numChars-1;
  391.         }
  392.         if ((canvasPtr->anchorItemPtr == itemPtr)
  393.             && (canvasPtr->selectAnchor >= textPtr->numChars)) {
  394.         canvasPtr->selectAnchor = textPtr->numChars-1;
  395.         }
  396.     }
  397.     }
  398.     if (textPtr->insertPos >= textPtr->numChars) {
  399.     textPtr->insertPos = textPtr->numChars;
  400.     }
  401.  
  402.     ComputeTextBbox(canvasPtr, textPtr);
  403.     return TCL_OK;
  404. }
  405.  
  406. /*
  407.  *--------------------------------------------------------------
  408.  *
  409.  * DeleteText --
  410.  *
  411.  *    This procedure is called to clean up the data structure
  412.  *    associated with a text item.
  413.  *
  414.  * Results:
  415.  *    None.
  416.  *
  417.  * Side effects:
  418.  *    Resources associated with itemPtr are released.
  419.  *
  420.  *--------------------------------------------------------------
  421.  */
  422.  
  423. static void
  424. DeleteText(canvasPtr, itemPtr)
  425.     Tk_Canvas *canvasPtr;        /* Info about overall canvas widget. */
  426.     Tk_Item *itemPtr;            /* Item that is being deleted. */
  427. {
  428.     register TextItem *textPtr = (TextItem *) itemPtr;
  429.  
  430.     if (textPtr->text != NULL) {
  431.     ckfree(textPtr->text);
  432.     }
  433.     if (textPtr->fontPtr != NULL) {
  434.     Tk_FreeFontStruct(textPtr->fontPtr);
  435.     }
  436.     if (textPtr->color != NULL) {
  437.     Tk_FreeColor(textPtr->color);
  438.     }
  439.     if (textPtr->stipple != None) {
  440.     Tk_FreeBitmap(canvasPtr->display, textPtr->stipple);
  441.     }
  442.     if (textPtr->gc != None) {
  443.     Tk_FreeGC(canvasPtr->display, textPtr->gc);
  444.     }
  445.     if (textPtr->linePtr != NULL) {
  446.     ckfree((char *) textPtr->linePtr);
  447.     }
  448.     if (textPtr->selTextGC != None) {
  449.     Tk_FreeGC(canvasPtr->display, textPtr->selTextGC);
  450.     }
  451. }
  452.  
  453. /*
  454.  *--------------------------------------------------------------
  455.  *
  456.  * ComputeTextBbox --
  457.  *
  458.  *    This procedure is invoked to compute the bounding box of
  459.  *    all the pixels that may be drawn as part of a text item.
  460.  *    In addition, it recomputes all of the geometry information
  461.  *    used to display a text item or check for mouse hits.
  462.  *
  463.  * Results:
  464.  *    None.
  465.  *
  466.  * Side effects:
  467.  *    The fields x1, y1, x2, and y2 are updated in the header
  468.  *    for itemPtr, and the linePtr structure is regenerated
  469.  *    for itemPtr.
  470.  *
  471.  *--------------------------------------------------------------
  472.  */
  473.  
  474. static void
  475. ComputeTextBbox(canvasPtr, textPtr)
  476.     register Tk_Canvas *canvasPtr;    /* Canvas that contains item. */
  477.     register TextItem *textPtr;        /* Item whose bbos is to be
  478.                      * recomputed. */
  479. {
  480.     register TextLine *linePtr;
  481. #define MAX_LINES 100
  482.     char *lineStart[MAX_LINES];
  483.     int lineChars[MAX_LINES];
  484.     int linePixels[MAX_LINES];
  485.     int numLines, wrapPixels, maxLinePixels, leftX, topY, y;
  486.     int lineHeight, i, fudge;
  487.     char *p;
  488.     XCharStruct *maxBoundsPtr = &textPtr->fontPtr->max_bounds;
  489.  
  490.     if (textPtr->linePtr != NULL) {
  491.     ckfree((char *) textPtr->linePtr);
  492.     textPtr->linePtr = NULL;
  493.     }
  494.  
  495.     /*
  496.      * Work through the text computing the starting point, number of
  497.      * characters, and number of pixels in each line.
  498.      */
  499.  
  500.     p = textPtr->text;
  501.     if (textPtr->width > 0) {
  502.     wrapPixels = maxLinePixels = textPtr->width;
  503.     } else {
  504.     wrapPixels = 10000000;
  505.     maxLinePixels = 0;
  506.     }
  507.     for (numLines = 0; (numLines < MAX_LINES); numLines++) {
  508.     int numChars, numPixels;
  509.     numChars = TkMeasureChars(textPtr->fontPtr, p,
  510.         (textPtr->text + textPtr->numChars) - p, 0,
  511.         wrapPixels, TK_WHOLE_WORDS|TK_AT_LEAST_ONE, &numPixels);
  512.     if (numPixels > maxLinePixels) {
  513.         maxLinePixels = numPixels;
  514.     }
  515.     lineStart[numLines] = p;
  516.     lineChars[numLines] = numChars;
  517.     linePixels[numLines] = numPixels;
  518.     p += numChars;
  519.  
  520.     /*
  521.      * Skip space character that terminates a line, if there is one.
  522.      * In the case of multiple spaces, all but one will be displayed.
  523.      * This is important to make sure the insertion cursor gets
  524.      * displayed when it is in the middle of a multi-space.
  525.      */
  526.  
  527.     if (isspace(*p)) {
  528.         p++;
  529.     } else if (*p == 0) {
  530.         /*
  531.          * The code below is tricky.  Putting the loop termination
  532.          * here guarantees that there's a TextLine for the last
  533.          * line of text, even if the line is empty (this can
  534.          * also happen if the entire text item is empty).  This is
  535.          * needed so that we can display the insertion cursor on a
  536.          * line even when it is empty.
  537.          */
  538.  
  539.         numLines++;
  540.         break;
  541.     }
  542.     }
  543.  
  544.     /*
  545.      * Use overall geometry information to compute the top-left corner
  546.      * of the bounding box for the text item.
  547.      */
  548.  
  549.     leftX = textPtr->x + 0.5;
  550.     topY = textPtr->y + 0.5;
  551.     lineHeight = textPtr->fontPtr->ascent + textPtr->fontPtr->descent;
  552.     switch (textPtr->anchor) {
  553.     case TK_ANCHOR_NW:
  554.     case TK_ANCHOR_N:
  555.     case TK_ANCHOR_NE:
  556.         break;
  557.  
  558.     case TK_ANCHOR_W:
  559.     case TK_ANCHOR_CENTER:
  560.     case TK_ANCHOR_E:
  561.         topY -= (lineHeight * numLines)/2;
  562.         break;
  563.  
  564.     case TK_ANCHOR_SW:
  565.     case TK_ANCHOR_S:
  566.     case TK_ANCHOR_SE:
  567.         topY -= lineHeight * numLines;
  568.         break;
  569.     }
  570.     switch (textPtr->anchor) {
  571.     case TK_ANCHOR_NW:
  572.     case TK_ANCHOR_W:
  573.     case TK_ANCHOR_SW:
  574.         break;
  575.  
  576.     case TK_ANCHOR_N:
  577.     case TK_ANCHOR_CENTER:
  578.     case TK_ANCHOR_S:
  579.         leftX -= maxLinePixels/2;
  580.         break;
  581.  
  582.     case TK_ANCHOR_NE:
  583.     case TK_ANCHOR_E:
  584.     case TK_ANCHOR_SE:
  585.         leftX -= maxLinePixels;
  586.         break;
  587.     }
  588.     textPtr->rightEdge = leftX + maxLinePixels;
  589.  
  590.     /*
  591.      * Create the new TextLine array and fill it in using the geometry
  592.      * information gathered already.
  593.      */
  594.  
  595.     if (numLines > 0) {
  596.     textPtr->linePtr = (TextLine *) ckalloc((unsigned)
  597.         (numLines * sizeof(TextLine)));
  598.     } else {
  599.     textPtr->linePtr = NULL;
  600.     }
  601.     textPtr->numLines = numLines;
  602.     for (i = 0, linePtr = textPtr->linePtr, y = topY;
  603.         i < numLines; i++, linePtr++, y += lineHeight) {
  604.     linePtr->firstChar = lineStart[i];
  605.     linePtr->numChars = lineChars[i];
  606.     if (i == (numLines-1)) {
  607.         linePtr->totalChars = linePtr->numChars;
  608.     } else {
  609.         linePtr->totalChars = lineStart[i+1] - lineStart[i];
  610.     }
  611.     switch (textPtr->justify) {
  612.         case TK_JUSTIFY_LEFT:
  613.         case TK_JUSTIFY_FILL:
  614.         linePtr->x = leftX;
  615.         break;
  616.         case TK_JUSTIFY_CENTER:
  617.         linePtr->x = leftX + maxLinePixels/2 - linePixels[i]/2;
  618.         break;
  619.         case TK_JUSTIFY_RIGHT:
  620.         linePtr->x = leftX + maxLinePixels - linePixels[i];
  621.         break;
  622.     }
  623.     linePtr->y = y + textPtr->fontPtr->ascent;
  624.     linePtr->x1 = linePtr->x + maxBoundsPtr->lbearing;
  625.     linePtr->y1 = y;
  626.     linePtr->x2 = linePtr->x + linePixels[i];
  627.     linePtr->y2 = linePtr->y + textPtr->fontPtr->descent - 1;
  628.     }
  629.  
  630.     /*
  631.      * Last of all, update the bounding box for the item.  The item's
  632.      * bounding box includes the bounding box of all its lines, plus
  633.      * an extra fudge factor for the cursor border (which could
  634.      * potentially be quite large).
  635.      */
  636.  
  637.     linePtr = textPtr->linePtr;
  638.     textPtr->header.x1 = textPtr->header.x2 = leftX;
  639.     textPtr->header.y1 = topY;
  640.     textPtr->header.y2 = topY + numLines*lineHeight;
  641.     for (linePtr = textPtr->linePtr, i = textPtr->numLines; i > 0;
  642.         i--, linePtr++) {
  643.     if (linePtr->x1 < textPtr->header.x1) {
  644.         textPtr->header.x1 = linePtr->x1;
  645.     }
  646.     if (linePtr->x2 >= textPtr->header.x2) {
  647.         textPtr->header.x2 = linePtr->x2 + 1;
  648.     }
  649.     }
  650.  
  651.     fudge = (canvasPtr->insertWidth+1)/2;
  652.     if (canvasPtr->selBorderWidth > fudge) {
  653.     fudge = canvasPtr->selBorderWidth;
  654.     }
  655.     textPtr->header.x1 -= fudge;
  656.     textPtr->header.x2 += fudge;
  657. }
  658.  
  659. /*
  660.  *--------------------------------------------------------------
  661.  *
  662.  * DisplayText --
  663.  *
  664.  *    This procedure is invoked to draw a text item in a given
  665.  *    drawable.
  666.  *
  667.  * Results:
  668.  *    None.
  669.  *
  670.  * Side effects:
  671.  *    ItemPtr is drawn in drawable using the transformation
  672.  *    information in canvasPtr.
  673.  *
  674.  *--------------------------------------------------------------
  675.  */
  676.  
  677. static void
  678. DisplayText(canvasPtr, itemPtr, drawable)
  679.     register Tk_Canvas *canvasPtr;    /* Canvas that contains item. */
  680.     Tk_Item *itemPtr;            /* Item to be displayed. */
  681.     Drawable drawable;            /* Pixmap or window in which to draw
  682.                      * item. */
  683. {
  684.     register TextItem *textPtr = (TextItem *) itemPtr;
  685.     Display *display = Tk_Display(canvasPtr->tkwin);
  686.     register TextLine *linePtr;
  687.     int i, focusHere, insertX, insertIndex, lineIndex;
  688.     int beforeSelect, inSelect, afterSelect, selStartX, selEndX;
  689.  
  690.     if (textPtr->gc == None) {
  691.     return;
  692.     }
  693.  
  694.     /*
  695.      * If we're stippling, then modify the stipple offset in the GC.  Be
  696.      * sure to reset the offset when done, since the GC is supposed to be
  697.      * read-only.
  698.      */
  699.  
  700.     if (textPtr->stipple != None) {
  701.     XSetTSOrigin(display, textPtr->gc,
  702.         -canvasPtr->drawableXOrigin, -canvasPtr->drawableYOrigin);
  703.     }
  704.  
  705.     focusHere = (canvasPtr->focusItemPtr == itemPtr) &&
  706.         (canvasPtr->flags & GOT_FOCUS);
  707.     for (linePtr = textPtr->linePtr, i = textPtr->numLines;
  708.         i > 0; linePtr++, i--) {
  709.  
  710.     /*
  711.      * If part or all of this line is selected, then draw a special
  712.      * background under the selected part of the line.
  713.      */
  714.  
  715.     lineIndex = linePtr->firstChar - textPtr->text;
  716.     if ((canvasPtr->selItemPtr != itemPtr)
  717.         || (canvasPtr->selectLast < lineIndex)
  718.         || (canvasPtr->selectFirst >= (lineIndex
  719.             + linePtr->totalChars))) {
  720.         beforeSelect = linePtr->numChars;
  721.         inSelect = 0;
  722.     } else {
  723.         beforeSelect = canvasPtr->selectFirst - lineIndex;
  724.         if (beforeSelect <= 0) {
  725.         beforeSelect = 0;
  726.         selStartX = linePtr->x;
  727.         } else {
  728.         (void) TkMeasureChars(textPtr->fontPtr,
  729.             linePtr->firstChar, beforeSelect, 0,
  730.             (int) 1000000, TK_PARTIAL_OK, &selStartX);
  731.         selStartX += linePtr->x;
  732.         }
  733.         inSelect = canvasPtr->selectLast + 1 - (lineIndex + beforeSelect);
  734.  
  735.         /*
  736.          * If the selection spans the end of this line, then display
  737.          * selection background all the way to the end of the line.
  738.          * However, for the last line we only want to display up to
  739.          * the last character, not the end of the line, hence the
  740.          * "i != 1" check.
  741.          */
  742.  
  743.         if (inSelect >= (linePtr->totalChars - beforeSelect)) {
  744.         inSelect = linePtr->numChars - beforeSelect;
  745.         if (i != 1) {
  746.             selEndX = textPtr->rightEdge;
  747.             goto fillSelectBackground;
  748.         }
  749.         }
  750.         (void) TkMeasureChars(textPtr->fontPtr,
  751.             linePtr->firstChar + beforeSelect, inSelect,
  752.             selStartX-linePtr->x, (int) 1000000, TK_PARTIAL_OK,
  753.             &selEndX);
  754.         selEndX += linePtr->x;
  755.         fillSelectBackground:
  756.         Tk_Fill3DRectangle(display, drawable, canvasPtr->selBorder,
  757.             selStartX - canvasPtr->drawableXOrigin
  758.             - canvasPtr->selBorderWidth,
  759.             linePtr->y - canvasPtr->drawableYOrigin
  760.             - textPtr->fontPtr->ascent,
  761.             selEndX - selStartX + 2*canvasPtr->selBorderWidth,
  762.             textPtr->fontPtr->ascent + textPtr->fontPtr->descent,
  763.             canvasPtr->selBorderWidth, TK_RELIEF_RAISED);
  764.     }
  765.  
  766.     /*
  767.      * If the insertion cursor is in this line, then draw a special
  768.      * background for the cursor before drawing the text.  Note:
  769.      * if we're the cursor item but the cursor is turned off, then
  770.      * redraw background over the area of the cursor.  This guarantees
  771.      * that the selection won't make the cursor invisible on mono
  772.      * displays, where both are drawn in the same color.
  773.      */
  774.  
  775.     if (focusHere) {
  776.         insertIndex = textPtr->insertPos
  777.             - (linePtr->firstChar - textPtr->text);
  778.         if ((insertIndex >= 0) && (insertIndex <= linePtr->numChars)) {
  779.         (void) TkMeasureChars(textPtr->fontPtr, linePtr->firstChar,
  780.             insertIndex, 0, (int) 1000000, TK_PARTIAL_OK, &insertX);
  781.         if (canvasPtr->flags & CURSOR_ON) {
  782.             Tk_Fill3DRectangle(display, drawable,
  783.                 canvasPtr->insertBorder,
  784.                 linePtr->x - canvasPtr->drawableXOrigin
  785.                 + insertX  - (canvasPtr->insertWidth)/2,
  786.                 linePtr->y - canvasPtr->drawableYOrigin
  787.                 - textPtr->fontPtr->ascent,
  788.                 canvasPtr->insertWidth,
  789.                 textPtr->fontPtr->ascent
  790.                 + textPtr->fontPtr->descent,
  791.                 canvasPtr->insertBorderWidth, TK_RELIEF_RAISED);
  792.         } else if (Tk_GetColorModel(canvasPtr->tkwin) != TK_COLOR){
  793.             Tk_Fill3DRectangle(display, drawable,
  794.                 canvasPtr->bgBorder,
  795.                 linePtr->x - canvasPtr->drawableXOrigin
  796.                 + insertX  - (canvasPtr->insertWidth)/2,
  797.                 linePtr->y - canvasPtr->drawableYOrigin
  798.                 - textPtr->fontPtr->ascent,
  799.                 canvasPtr->insertWidth,
  800.                 textPtr->fontPtr->ascent
  801.                 + textPtr->fontPtr->descent,
  802.                 0, TK_RELIEF_FLAT);
  803.         }
  804.         }
  805.     }
  806.  
  807.     /*
  808.      * Display the text in three pieces:  the part before the
  809.      * selection, the selected part (which needs a different graphics
  810.      * context), and the part after the selection.
  811.      */
  812.  
  813.     if (beforeSelect != 0) {
  814.         TkDisplayChars(display, drawable, textPtr->gc, textPtr->fontPtr,
  815.             linePtr->firstChar, beforeSelect,
  816.             linePtr->x - canvasPtr->drawableXOrigin,
  817.             linePtr->y - canvasPtr->drawableYOrigin, 0);
  818.     }
  819.     if (inSelect != 0) {
  820.         TkDisplayChars(display, drawable, textPtr->selTextGC,
  821.             textPtr->fontPtr, linePtr->firstChar + beforeSelect,
  822.             inSelect, selStartX - canvasPtr->drawableXOrigin,
  823.             linePtr->y - canvasPtr->drawableYOrigin, 0);
  824.     }
  825.     afterSelect = linePtr->numChars - beforeSelect - inSelect;
  826.     if (afterSelect > 0) {
  827.         TkDisplayChars(display, drawable, textPtr->gc, textPtr->fontPtr,
  828.             linePtr->firstChar + beforeSelect + inSelect,
  829.             afterSelect, selEndX - canvasPtr->drawableXOrigin,
  830.             linePtr->y - canvasPtr->drawableYOrigin, 0);
  831.     }
  832.     }
  833.     if (textPtr->stipple != None) {
  834.     XSetTSOrigin(display, textPtr->gc, 0, 0);
  835.     }
  836. }
  837.  
  838. /*
  839.  *--------------------------------------------------------------
  840.  *
  841.  * TextInsert --
  842.  *
  843.  *    Insert characters into a text item at a given position.
  844.  *
  845.  * Results:
  846.  *    Always returns TCL_OK.
  847.  *
  848.  * Side effects:
  849.  *    The text in the given item is modified.  The cursor and
  850.  *    selection positions are also modified to reflect the
  851.  *    insertion.
  852.  *
  853.  *--------------------------------------------------------------
  854.  */
  855.  
  856. static int
  857. TextInsert(canvasPtr, itemPtr, beforeThis, string)
  858.     Tk_Canvas *canvasPtr;    /* Canvas containing text item. */
  859.     Tk_Item *itemPtr;        /* Text item to be modified. */
  860.     int beforeThis;        /* Index of character before which text is
  861.                  * to be inserted. */
  862.     char *string;        /* New characters to be inserted. */
  863. {
  864.     register TextItem *textPtr = (TextItem *) itemPtr;
  865.     int length;
  866.     char *new;
  867.  
  868.     length = strlen(string);
  869.     if (length == 0) {
  870.     return TCL_OK;
  871.     }
  872.     if (beforeThis < 0) {
  873.     beforeThis = 0;
  874.     }
  875.     if (beforeThis > textPtr->numChars) {
  876.     beforeThis = textPtr->numChars;
  877.     }
  878.  
  879.     new = (char *) ckalloc((unsigned) (textPtr->numChars + length + 1));
  880.     strncpy(new, textPtr->text, beforeThis);
  881.     strcpy(new+beforeThis, string);
  882.     strcpy(new+beforeThis+length, textPtr->text+beforeThis);
  883.     ckfree(textPtr->text);
  884.     textPtr->text = new;
  885.     textPtr->numChars += length;
  886.  
  887.     /*
  888.      * Inserting characters invalidates indices such as those for the
  889.      * selection and cursor.  Update the indices appropriately.
  890.      */
  891.  
  892.     if (canvasPtr->selItemPtr == itemPtr) {
  893.     if (canvasPtr->selectFirst >= beforeThis) {
  894.         canvasPtr->selectFirst += length;
  895.     }
  896.     if (canvasPtr->selectLast >= beforeThis) {
  897.         canvasPtr->selectLast += length;
  898.     }
  899.     if ((canvasPtr->anchorItemPtr == itemPtr)
  900.         && (canvasPtr->selectAnchor >= beforeThis)) {
  901.         canvasPtr->selectAnchor += length;
  902.     }
  903.     }
  904.     if (textPtr->insertPos >= beforeThis) {
  905.     textPtr->insertPos += length;
  906.     }
  907.     ComputeTextBbox(canvasPtr, textPtr);
  908.     return TCL_OK;
  909. }
  910.  
  911. /*
  912.  *--------------------------------------------------------------
  913.  *
  914.  * TextDeleteChars --
  915.  *
  916.  *    Delete one or more characters from a text item.
  917.  *
  918.  * Results:
  919.  *    Always returns TCL_OK.
  920.  *
  921.  * Side effects:
  922.  *    Characters between "first" and "last", inclusive, get
  923.  *    deleted from itemPtr, and things like the selection
  924.  *    position get updated.
  925.  *
  926.  *--------------------------------------------------------------
  927.  */
  928.  
  929. static int
  930. TextDeleteChars(canvasPtr, itemPtr, first, last)
  931.     Tk_Canvas *canvasPtr;    /* Canvas containing itemPtr. */
  932.     Tk_Item *itemPtr;        /* Item in which to delete characters. */
  933.     int first;            /* Index of first character to delete. */
  934.     int last;            /* Index of last character to delete. */
  935. {
  936.     register TextItem *textPtr = (TextItem *) itemPtr;
  937.     int count;
  938.     char *new;
  939.  
  940.     if (first < 0) {
  941.     first = 0;
  942.     }
  943.     if (last >= textPtr->numChars) {
  944.     last = textPtr->numChars-1;
  945.     }
  946.     if (first > last) {
  947.     return TCL_OK;
  948.     }
  949.     count = last + 1 - first;
  950.  
  951.     new = ckalloc((unsigned) (textPtr->numChars + 1 - count));
  952.     strncpy(new, textPtr->text, first);
  953.     strcpy(new+first, textPtr->text+last+1);
  954.     ckfree(textPtr->text);
  955.     textPtr->text = new;
  956.     textPtr->numChars -= count;
  957.  
  958.     /*
  959.      * Update indexes for the selection and cursor to reflect the
  960.      * renumbering of the remaining characters.
  961.      */
  962.  
  963.     if (canvasPtr->selItemPtr == itemPtr) {
  964.     if (canvasPtr->selectFirst > first) {
  965.         canvasPtr->selectFirst -= count;
  966.         if (canvasPtr->selectFirst < first) {
  967.         canvasPtr->selectFirst = first;
  968.         }
  969.     }
  970.     if (canvasPtr->selectLast >= first) {
  971.         canvasPtr->selectLast -= count;
  972.         if (canvasPtr->selectLast < (first-1)) {
  973.         canvasPtr->selectLast = (first-1);
  974.         }
  975.     }
  976.     if (canvasPtr->selectFirst > canvasPtr->selectLast) {
  977.         canvasPtr->selItemPtr = NULL;
  978.     }
  979.     if ((canvasPtr->anchorItemPtr == itemPtr)
  980.         && (canvasPtr->selectAnchor > first)) {
  981.         canvasPtr->selectAnchor -= count;
  982.         if (canvasPtr->selectAnchor < first) {
  983.         canvasPtr->selectAnchor = first;
  984.         }
  985.     }
  986.     }
  987.     if (textPtr->insertPos > first) {
  988.     textPtr->insertPos -= count;
  989.     if (textPtr->insertPos < first) {
  990.         textPtr->insertPos = first;
  991.     }
  992.     }
  993.     ComputeTextBbox(canvasPtr, textPtr);
  994.     return TCL_OK;
  995. }
  996.  
  997. /*
  998.  *--------------------------------------------------------------
  999.  *
  1000.  * TextToPoint --
  1001.  *
  1002.  *    Computes the distance from a given point to a given
  1003.  *    text item, in canvas units.
  1004.  *
  1005.  * Results:
  1006.  *    The return value is 0 if the point whose x and y coordinates
  1007.  *    are pointPtr[0] and pointPtr[1] is inside the arc.  If the
  1008.  *    point isn't inside the arc then the return value is the
  1009.  *    distance from the point to the arc.
  1010.  *
  1011.  * Side effects:
  1012.  *    None.
  1013.  *
  1014.  *--------------------------------------------------------------
  1015.  */
  1016.  
  1017.     /* ARGSUSED */
  1018. static double
  1019. TextToPoint(canvasPtr, itemPtr, pointPtr)
  1020.     Tk_Canvas *canvasPtr;    /* Canvas containing itemPtr. */
  1021.     Tk_Item *itemPtr;        /* Item to check against point. */
  1022.     double *pointPtr;        /* Pointer to x and y coordinates. */
  1023. {
  1024.     TextItem *textPtr = (TextItem *) itemPtr;
  1025.     register TextLine *linePtr;
  1026.     int i;
  1027.     double xDiff, yDiff, dist, minDist;
  1028.  
  1029.     /*
  1030.      * Treat each line in the text item as a rectangle, compute the
  1031.      * distance to that rectangle, and take the minimum of these
  1032.      * distances.  Perform most of the calculations in integer pixel
  1033.      * units, since that's how the dimensions of the text are defined.
  1034.      */
  1035.  
  1036.     minDist = -1.0;
  1037.     for (linePtr = textPtr->linePtr, i = textPtr->numLines;
  1038.         i > 0; linePtr++, i--) {
  1039.  
  1040.     /*
  1041.      * If the point is inside the line's rectangle, then can
  1042.      * return immediately.
  1043.      */
  1044.     
  1045.     if ((pointPtr[0] >= linePtr->x1)
  1046.         && (pointPtr[0] <= linePtr->x2)
  1047.         && (pointPtr[1] >= linePtr->y1)
  1048.         && (pointPtr[1] <= linePtr->y2)) {
  1049.         return 0.0;
  1050.     }
  1051.     
  1052.     /*
  1053.      * Point is outside line's rectangle; compute distance to nearest
  1054.      * side.
  1055.      */
  1056.     
  1057.     if (pointPtr[0] < linePtr->x1) {
  1058.         xDiff = linePtr->x1 - pointPtr[0];
  1059.     } else if (pointPtr[0] > linePtr->x2)  {
  1060.         xDiff = pointPtr[0] - linePtr->x2;
  1061.     } else {
  1062.         xDiff = 0;
  1063.     }
  1064.     
  1065.     if (pointPtr[1] < linePtr->y1) {
  1066.         yDiff = linePtr->y1 - pointPtr[1];
  1067.     } else if (pointPtr[1] > linePtr->y2)  {
  1068.         yDiff = pointPtr[1] - linePtr->y2;
  1069.     } else {
  1070.         yDiff = 0;
  1071.     }
  1072.  
  1073.     dist = hypot(xDiff, yDiff);
  1074.     if ((dist < minDist) || (minDist < 0.0)) {
  1075.         minDist = dist;
  1076.     }
  1077.     }
  1078.     return minDist;
  1079. }
  1080.  
  1081. /*
  1082.  *--------------------------------------------------------------
  1083.  *
  1084.  * TextToArea --
  1085.  *
  1086.  *    This procedure is called to determine whether an item
  1087.  *    lies entirely inside, entirely outside, or overlapping
  1088.  *    a given rectangle.
  1089.  *
  1090.  * Results:
  1091.  *    -1 is returned if the item is entirely outside the area
  1092.  *    given by rectPtr, 0 if it overlaps, and 1 if it is entirely
  1093.  *    inside the given area.
  1094.  *
  1095.  * Side effects:
  1096.  *    None.
  1097.  *
  1098.  *--------------------------------------------------------------
  1099.  */
  1100.  
  1101.     /* ARGSUSED */
  1102. static int
  1103. TextToArea(canvasPtr, itemPtr, rectPtr)
  1104.     Tk_Canvas *canvasPtr;    /* Canvas containing itemPtr. */
  1105.     Tk_Item *itemPtr;        /* Item to check against rectangle. */
  1106.     double *rectPtr;        /* Pointer to array of four coordinates
  1107.                  * (x1, y1, x2, y2) describing rectangular
  1108.                  * area.  */
  1109. {
  1110.     TextItem *textPtr = (TextItem *) itemPtr;
  1111.     register TextLine *linePtr;
  1112.     int i, result;
  1113.  
  1114.     /*
  1115.      * Scan the lines one at a time, seeing whether each line is
  1116.      * entirely in, entirely out, or overlapping the rectangle.  If
  1117.      * an overlap is detected, return immediately;  otherwise wait
  1118.      * until all lines have been processed and see if they were all
  1119.      * inside or all outside.
  1120.      */
  1121.  
  1122.     result = 0;
  1123.     for (linePtr = textPtr->linePtr, i = textPtr->numLines;
  1124.         i > 0; linePtr++, i--) {
  1125.     if ((rectPtr[2] < linePtr->x1) || (rectPtr[0] > linePtr->x2)
  1126.         || (rectPtr[3] < linePtr->y1) || (rectPtr[1] > linePtr->y2)) {
  1127.         if (result == 1) {
  1128.         return 0;
  1129.         }
  1130.         result = -1;
  1131.         continue;
  1132.     }
  1133.     if ((linePtr->x1 < rectPtr[0]) || (linePtr->x2 > rectPtr[2])
  1134.         || (linePtr->y1 < rectPtr[1]) || (linePtr->y2 > rectPtr[3])) {
  1135.         return 0;
  1136.     }
  1137.     if (result == -1) {
  1138.         return 0;
  1139.     }
  1140.     result = 1;
  1141.     }
  1142.     return result;
  1143. }
  1144.  
  1145. /*
  1146.  *--------------------------------------------------------------
  1147.  *
  1148.  * ScaleText --
  1149.  *
  1150.  *    This procedure is invoked to rescale a text item.
  1151.  *
  1152.  * Results:
  1153.  *    None.
  1154.  *
  1155.  * Side effects:
  1156.  *    Scales the position of the text, but not the size
  1157.  *    of the font for the text.
  1158.  *
  1159.  *--------------------------------------------------------------
  1160.  */
  1161.  
  1162.     /* ARGSUSED */
  1163. static void
  1164. ScaleText(canvasPtr, itemPtr, originX, originY, scaleX, scaleY)
  1165.     Tk_Canvas *canvasPtr;        /* Canvas containing rectangle. */
  1166.     Tk_Item *itemPtr;            /* Rectangle to be scaled. */
  1167.     double originX, originY;        /* Origin about which to scale rect. */
  1168.     double scaleX;            /* Amount to scale in X direction. */
  1169.     double scaleY;            /* Amount to scale in Y direction. */
  1170. {
  1171.     register TextItem *textPtr = (TextItem *) itemPtr;
  1172.  
  1173.     textPtr->x = originX + scaleX*(textPtr->x - originX);
  1174.     textPtr->y = originY + scaleY*(textPtr->y - originY);
  1175.     ComputeTextBbox(canvasPtr, textPtr);
  1176.     return;
  1177. }
  1178.  
  1179. /*
  1180.  *--------------------------------------------------------------
  1181.  *
  1182.  * TranslateText --
  1183.  *
  1184.  *    This procedure is called to move a text item by a
  1185.  *    given amount.
  1186.  *
  1187.  * Results:
  1188.  *    None.
  1189.  *
  1190.  * Side effects:
  1191.  *    The position of the text item is offset by (xDelta, yDelta),
  1192.  *    and the bounding box is updated in the generic part of the
  1193.  *    item structure.
  1194.  *
  1195.  *--------------------------------------------------------------
  1196.  */
  1197.  
  1198. static void
  1199. TranslateText(canvasPtr, itemPtr, deltaX, deltaY)
  1200.     Tk_Canvas *canvasPtr;        /* Canvas containing item. */
  1201.     Tk_Item *itemPtr;            /* Item that is being moved. */
  1202.     double deltaX, deltaY;        /* Amount by which item is to be
  1203.                      * moved. */
  1204. {
  1205.     register TextItem *textPtr = (TextItem *) itemPtr;
  1206.  
  1207.     textPtr->x += deltaX;
  1208.     textPtr->y += deltaY;
  1209.     ComputeTextBbox(canvasPtr, textPtr);
  1210. }
  1211.  
  1212. /*
  1213.  *--------------------------------------------------------------
  1214.  *
  1215.  * GetTextIndex --
  1216.  *
  1217.  *    Parse an index into a text item and return either its value
  1218.  *    or an error.
  1219.  *
  1220.  * Results:
  1221.  *    A standard Tcl result.  If all went well, then *indexPtr is
  1222.  *    filled in with the index (into itemPtr) corresponding to
  1223.  *    string.  Otherwise an error message is left in
  1224.  *    canvasPtr->interp->result.
  1225.  *
  1226.  * Side effects:
  1227.  *    None.
  1228.  *
  1229.  *--------------------------------------------------------------
  1230.  */
  1231.  
  1232. static int
  1233. GetTextIndex(canvasPtr, itemPtr, string, indexPtr)
  1234.     Tk_Canvas *canvasPtr;    /* Canvas containing item. */
  1235.     Tk_Item *itemPtr;        /* Item for which the index is being
  1236.                  * specified. */
  1237.     char *string;        /* Specification of a particular character
  1238.                  * in itemPtr's text. */
  1239.     int *indexPtr;        /* Where to store converted index. */
  1240. {
  1241.     register TextItem *textPtr = (TextItem *) itemPtr;
  1242.     int length;
  1243.  
  1244.     length = strlen(string);
  1245.  
  1246.     if (string[0] == 'e') {
  1247.     if (strncmp(string, "end", length) == 0) {
  1248.         *indexPtr = textPtr->numChars;
  1249.     } else {
  1250.         badIndex:
  1251.  
  1252.         /*
  1253.          * Some of the paths here leave messages in
  1254.          * canvasPtr->interp->result, so we have to clear it out
  1255.          * before storing our own message.
  1256.          */
  1257.  
  1258.         Tcl_SetResult(canvasPtr->interp, (char *) NULL, TCL_STATIC);
  1259.         Tcl_AppendResult(canvasPtr->interp, "bad index \"", string,
  1260.             "\"", (char *) NULL);
  1261.         return TCL_ERROR;
  1262.     }
  1263.     } else if (string[0] == 'i') {
  1264.     if (strncmp(string, "insert", length) == 0) {
  1265.         *indexPtr = textPtr->insertPos;
  1266.     } else {
  1267.         goto badIndex;
  1268.     }
  1269.     } else if (string[0] == 's') {
  1270.     if (canvasPtr->selItemPtr != itemPtr) {
  1271.         canvasPtr->interp->result = "selection isn't in item";
  1272.         return TCL_ERROR;
  1273.     }
  1274.     if (length < 5) {
  1275.         goto badIndex;
  1276.     }
  1277.     if (strncmp(string, "sel.first", length) == 0) {
  1278.         *indexPtr = canvasPtr->selectFirst;
  1279.     } else if (strncmp(string, "sel.last", length) == 0) {
  1280.         *indexPtr = canvasPtr->selectLast;
  1281.     } else {
  1282.         goto badIndex;
  1283.     }
  1284.     } else if (string[0] == '@') {
  1285.     int x, y, dummy, i;
  1286.     char *end, *p;
  1287.     register TextLine *linePtr;
  1288.  
  1289.     p = string+1;
  1290.     x = strtol(p, &end, 0);
  1291.     if ((end == p) || (*end != ',')) {
  1292.         goto badIndex;
  1293.     }
  1294.     p = end+1;
  1295.     y = strtol(p, &end, 0);
  1296.     if ((end == p) || (*end != 0)) {
  1297.         goto badIndex;
  1298.     }
  1299.     if ((textPtr->numChars == 0) || (y < textPtr->linePtr[0].y1)) {
  1300.         *indexPtr = 0;
  1301.         return TCL_OK;
  1302.     }
  1303.     for (i = 0, linePtr = textPtr->linePtr; ; i++, linePtr++) {
  1304.         if (i >= textPtr->numLines) {
  1305.         *indexPtr = textPtr->numChars;
  1306.         return TCL_OK;
  1307.         }
  1308.         if (y <= linePtr->y2) {
  1309.         break;
  1310.         }
  1311.     }
  1312.     *indexPtr = TkMeasureChars(textPtr->fontPtr, linePtr->firstChar,
  1313.         linePtr->numChars, linePtr->x, x, 0, &dummy);
  1314.     *indexPtr += linePtr->firstChar - textPtr->text;
  1315.     } else {
  1316.     if (Tcl_GetInt(canvasPtr->interp, string, indexPtr) != TCL_OK) {
  1317.         goto badIndex;
  1318.     }
  1319.     if (*indexPtr < 0){
  1320.         *indexPtr = 0;
  1321.     } else if (*indexPtr > textPtr->numChars) {
  1322.         *indexPtr = textPtr->numChars;
  1323.     }
  1324.     }
  1325.     return TCL_OK;
  1326. }
  1327.  
  1328. /*
  1329.  *--------------------------------------------------------------
  1330.  *
  1331.  * SetTextCursor --
  1332.  *
  1333.  *    Set the position of the insertion cursor in this item.
  1334.  *
  1335.  * Results:
  1336.  *    None.
  1337.  *
  1338.  * Side effects:
  1339.  *    The cursor position will change.
  1340.  *
  1341.  *--------------------------------------------------------------
  1342.  */
  1343.  
  1344.     /* ARGSUSED */
  1345. static void
  1346. SetTextCursor(canvasPtr, itemPtr, index)
  1347.     Tk_Canvas *canvasPtr;        /* Record describing canvas widget. */
  1348.     Tk_Item *itemPtr;            /* Text item in which cursor position
  1349.                      * is to be set. */
  1350.     int index;                /* Index of character just before which
  1351.                      * cursor is to be positioned. */
  1352. {
  1353.     register TextItem *textPtr = (TextItem *) itemPtr;
  1354.  
  1355.     if (index < 0) {
  1356.     textPtr->insertPos = 0;
  1357.     } else  if (index > textPtr->numChars) {
  1358.     textPtr->insertPos = textPtr->numChars;
  1359.     } else {
  1360.     textPtr->insertPos = index;
  1361.     }
  1362. }
  1363.  
  1364. /*
  1365.  *--------------------------------------------------------------
  1366.  *
  1367.  * GetSelText --
  1368.  *
  1369.  *    This procedure is invoked to return the selected portion
  1370.  *    of a text item.  It is only called when this item has
  1371.  *    the selection.
  1372.  *
  1373.  * Results:
  1374.  *    The return value is the number of non-NULL bytes stored
  1375.  *    at buffer.  Buffer is filled (or partially filled) with a
  1376.  *    NULL-terminated string containing part or all of the selection,
  1377.  *    as given by offset and maxBytes.
  1378.  *
  1379.  * Side effects:
  1380.  *    None.
  1381.  *
  1382.  *--------------------------------------------------------------
  1383.  */
  1384.  
  1385. static int
  1386. GetSelText(canvasPtr, itemPtr, offset, buffer, maxBytes)
  1387.     Tk_Canvas *canvasPtr;        /* Canvas containing selection. */
  1388.     Tk_Item *itemPtr;            /* Text item containing selection. */
  1389.     int offset;                /* Offset within selection of first
  1390.                      * character to be returned. */
  1391.     char *buffer;            /* Location in which to place
  1392.                      * selection. */
  1393.     int maxBytes;            /* Maximum number of bytes to place
  1394.                      * at buffer, not including terminating
  1395.                      * NULL character. */
  1396. {
  1397.     TextItem *textPtr = (TextItem *) itemPtr;
  1398.     int count;
  1399.  
  1400.     count = canvasPtr->selectLast + 1 - canvasPtr->selectFirst - offset;
  1401.     if (canvasPtr->selectLast == textPtr->numChars) {
  1402.     count -= 1;
  1403.     }
  1404.     if (count > maxBytes) {
  1405.     count = maxBytes;
  1406.     }
  1407.     if (count <= 0) {
  1408.     return 0;
  1409.     }
  1410.     strncpy(buffer, textPtr->text + canvasPtr->selectFirst + offset, count);
  1411.     buffer[count] = '\0';
  1412.     return count;
  1413. }
  1414.  
  1415. /*
  1416.  *--------------------------------------------------------------
  1417.  *
  1418.  * TextToPostscript --
  1419.  *
  1420.  *    This procedure is called to generate Postscript for
  1421.  *    text items.
  1422.  *
  1423.  * Results:
  1424.  *    The return value is a standard Tcl result.  If an error
  1425.  *    occurs in generating Postscript then an error message is
  1426.  *    left in canvasPtr->interp->result, replacing whatever used
  1427.  *    to be there.  If no error occurs, then Postscript for the
  1428.  *    item is appended to the result.
  1429.  *
  1430.  * Side effects:
  1431.  *    None.
  1432.  *
  1433.  *--------------------------------------------------------------
  1434.  */
  1435.  
  1436. static int
  1437. TextToPostscript(canvasPtr, itemPtr, psInfoPtr)
  1438.     Tk_Canvas *canvasPtr;        /* Information about overall canvas. */
  1439.     Tk_Item *itemPtr;            /* Item for which Postscript is
  1440.                      * wanted. */
  1441.     Tk_PostscriptInfo *psInfoPtr;    /* Information about the Postscript;
  1442.                      * must be passed back to Postscript
  1443.                      * utility procedures. */
  1444. {
  1445.     register TextItem *textPtr = (TextItem *) itemPtr;
  1446.     register TextLine *linePtr;
  1447.     int i;
  1448.     char *xoffset = NULL, *yoffset = NULL;    /* Initializations needed */
  1449.     char *justify = NULL;            /* only to stop compiler
  1450.                             * warnings. */
  1451.     char buffer[500];
  1452.  
  1453.     if (textPtr->color == NULL) {
  1454.     return TCL_OK;
  1455.     }
  1456.  
  1457.     if (TkCanvPsFont(canvasPtr, psInfoPtr, textPtr->fontPtr) != TCL_OK) {
  1458.     return TCL_ERROR;
  1459.     }
  1460.     if (TkCanvPsColor(canvasPtr, psInfoPtr, textPtr->color) != TCL_OK) {
  1461.     return TCL_ERROR;
  1462.     }
  1463.     sprintf(buffer, "%.15g %.15g [\n", textPtr->x,
  1464.         TkCanvPsY(psInfoPtr, textPtr->y));
  1465.     Tcl_AppendResult(canvasPtr->interp, buffer, (char *) NULL);
  1466.     for (i = textPtr->numLines, linePtr = textPtr->linePtr;
  1467.         i > 0; i--, linePtr++) {
  1468.     Tcl_AppendResult(canvasPtr->interp, "    ", (char *) NULL);
  1469.     LineToPostscript(canvasPtr->interp, linePtr->firstChar,
  1470.         linePtr->numChars);
  1471.     Tcl_AppendResult(canvasPtr->interp, "\n", (char *) NULL);
  1472.     }
  1473.     switch (textPtr->anchor) {
  1474.     case TK_ANCHOR_NW:     xoffset = "0";    yoffset = "0";   break;
  1475.     case TK_ANCHOR_N:      xoffset = "-0.5"; yoffset = "0";   break;
  1476.     case TK_ANCHOR_NE:     xoffset = "-1";   yoffset = "0";   break;
  1477.     case TK_ANCHOR_E:      xoffset = "-1";   yoffset = "0.5"; break;
  1478.     case TK_ANCHOR_SE:     xoffset = "-1";   yoffset = "1";   break;
  1479.     case TK_ANCHOR_S:      xoffset = "-0.5"; yoffset = "1";   break;
  1480.     case TK_ANCHOR_SW:     xoffset = "0";    yoffset = "1";   break;
  1481.     case TK_ANCHOR_W:      xoffset = "0";    yoffset = "0.5"; break;
  1482.     case TK_ANCHOR_CENTER: xoffset = "-0.5"; yoffset = "0.5"; break;
  1483.     }
  1484.     switch (textPtr->justify) {
  1485.     case TK_JUSTIFY_LEFT:    justify = "0";   break;
  1486.     case TK_JUSTIFY_FILL:
  1487.     case TK_JUSTIFY_CENTER:    justify = "0.5"; break;
  1488.     case TK_JUSTIFY_RIGHT:    justify = "1";   break;
  1489.     }
  1490.     sprintf(buffer, "] %d %s %s %s () false DrawText\n",
  1491.         textPtr->fontPtr->ascent + textPtr->fontPtr->descent,
  1492.         xoffset, yoffset, justify);
  1493.     Tcl_AppendResult(canvasPtr->interp, buffer, (char *) NULL);
  1494.     return TCL_OK;
  1495. }
  1496.  
  1497. /*
  1498.  *--------------------------------------------------------------
  1499.  *
  1500.  * LineToPostscript --
  1501.  *
  1502.  *    This procedure generates a parenthesized Postscript string
  1503.  *    describing one line of text from a text item.
  1504.  *
  1505.  * Results:
  1506.  *    None. The parenthesized string is appended to
  1507.  *    interp->result.  It generates proper backslash notation so
  1508.  *    that Postscript can interpret the string correctly.
  1509.  *
  1510.  * Side effects:
  1511.  *    None.
  1512.  *
  1513.  *--------------------------------------------------------------
  1514.  */
  1515.  
  1516. static void
  1517. LineToPostscript(interp, string, numChars)
  1518.     Tcl_Interp *interp;        /* Interp whose result is to be appended to. */
  1519.     register char *string;    /* String to Postscript-ify. */
  1520.     int numChars;        /* Number of characters in the string. */
  1521. {
  1522. #define BUFFER_SIZE 100
  1523.     char buffer[BUFFER_SIZE+5];
  1524.     int used, c;
  1525.  
  1526.     buffer[0] = '(';
  1527.     used = 1;
  1528.     for ( ; numChars > 0; string++, numChars--) {
  1529.     c = (*string) & 0xff;
  1530.     if ((c == '(') || (c == ')') || (c == '\\') || (c < 0x20)
  1531.         || (c >= 0x7f)) {
  1532.         /*
  1533.          * Tricky point:  the "03" is necessary in the sprintf below,
  1534.          * so that a full three digits of octal are always generated.
  1535.          * Without the "03", a number following this sequence could
  1536.          * be interpreted by Postscript as part of this sequence.
  1537.          */
  1538.         sprintf(buffer+used, "\\%03o", c);
  1539.         used += strlen(buffer+used);
  1540.     } else {
  1541.         buffer[used] = c;
  1542.         used++;
  1543.     }
  1544.     if (used >= BUFFER_SIZE) {
  1545.         buffer[used] = 0;
  1546.         Tcl_AppendResult(interp, buffer, (char *) NULL);
  1547.         used = 0;
  1548.     }
  1549.     }
  1550.     buffer[used] = ')';
  1551.     buffer[used+1] = 0;
  1552.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  1553. }
  1554.