Logo Search packages:      
Sourcecode: tcltk8.0-ja version File versions  Download package

tkWFont.c

/* 
 * tkWFont.c --
 *
 *      This file has definitions for wide string measuring/displaying
 *      text functions in tkFont.c
 */

#ifdef KANJI

#include "tkInt.h"
#include "tkFont.h"

typedef struct WLayoutChunk {
    CONST wchar *start;       /* Pointer to simple string to be displayed.
                         * This is a pointer into the TkWTextLayout's
                         * string. */
    int numChars;       /* The number of characters in this chunk. */
    int numDisplayChars;      /* The number of characters to display when
                         * this chunk is displayed.  Can be less than
                         * numChars if extra space characters were
                         * absorbed by the end of the chunk.  This
                         * will be < 0 if this is a chunk that is
                         * holding a tab or newline. */
    int x, y;                 /* The origin of the first character in this
                         * chunk with respect to the upper-left hand
                         * corner of the WTextLayout. */
    int totalWidth;           /* Width in pixels of this chunk.  Used
                         * when hit testing the invisible spaces at
                         * the end of a chunk. */
    int displayWidth;         /* Width in pixels of the displayable
                         * characters in this chunk.  Can be less than
                         * width if extra space characters were
                         * absorbed by the end of the chunk. */
} WLayoutChunk;

typedef struct WTextLayout {
    Tk_Font tkfont;           /* The font used when laying out the text. */
    CONST wchar *string;      /* The string that was layed out. */
    int width;                /* The maximum width of all lines in the
                         * text layout. */
    int numChunks;            /* Number of chunks actually used in
                         * following array. */
    WLayoutChunk chunks[1];   /* Array of chunks.  The actual size will
                         * be maxChunks.  THIS FIELD MUST BE THE LAST
                         * IN THE STRUCTURE. */
} WTextLayout;

static WLayoutChunk *   NewWChunk _ANSI_ARGS_((WTextLayout **layoutPtrPtr,
                      int *maxPtr, CONST wchar *start, int numChars,
                      int curX, int newX, int y));


/*
 *---------------------------------------------------------------------------
 *
 * Tk_WTextWidth --
 *
 *    A wrapper function for the more complicated interface of
 *    Tk_MeasureChars.  Computes how much space the given
 *    simple string needs.
 *
 * Results:
 *    The return value is the width (in pixels) of the given string.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */

int
Tk_WTextWidth(tkfont, string, numChars)
    Tk_Font tkfont;           /* Font in which text will be measured. */
    CONST wchar *string;      /* String whose width will be computed. */
    int numChars;       /* Number of characters to consider from
                         * string, or < 0 for Tcl_WStrlen(). */
{
    int width;

    if (numChars < 0) {
      numChars = Tcl_WStrlen((wchar *)string);
    }
    Tk_MeasureWChars(tkfont, string, numChars, 0, 0, &width);
    return width;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_UnderlineWChars --
 *
 *    This procedure draws an underline for a given range of characters
 *    in a given string.  It doesn't draw the characters (which are
 *    assumed to have been displayed previously); it just draws the
 *    underline.  This procedure would mainly be used to quickly
 *    underline a few characters without having to construct an
 *    underlined font.  To produce properly underlined text, the
 *    appropriate underlined font should be constructed and used. 
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Information gets displayed in "drawable".
 *
 *----------------------------------------------------------------------
 */

void
Tk_UnderlineWChars(display, drawable, gc, tkfont, string, x, y, firstChar,
      lastChar)
    Display *display;         /* Display on which to draw. */
    Drawable drawable;        /* Window or pixmap in which to draw. */
    GC gc;              /* Graphics context for actually drawing
                         * line. */
    Tk_Font tkfont;           /* Font used in GC;  must have been allocated
                         * by Tk_GetFont().  Used for character
                         * dimensions, etc. */
    CONST wchar *string;      /* String containing characters to be
                         * underlined or overstruck. */
    int x, y;                 /* Coordinates at which first character of
                         * string is drawn. */
    int firstChar;            /* Index of first character. */
    int lastChar;       /* Index of one after the last character. */
{
    TkFont *fontPtr;
    int startX, endX;

    fontPtr = (TkFont *) tkfont;
    
    Tk_MeasureWChars(tkfont, string, firstChar, 0, 0, &startX);
    Tk_MeasureWChars(tkfont, string, lastChar, 0, 0, &endX);

    XFillRectangle(display, drawable, gc, x + startX,
          y + fontPtr->underlinePos, (unsigned int) (endX - startX),
          (unsigned int) fontPtr->underlineHeight);
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_ComputeWTextLayout --
 *
 *    Computes the amount of screen space needed to display a
 *    multi-line, justified string of text.  Records all the
 *    measurements that were done to determine to size and
 *    positioning of the individual lines of text; this information
 *    can be used by the Tk_DrawWTextLayout() procedure to
 *    display the text quickly (without remeasuring it).
 *
 *    This procedure is useful for simple widgets that want to
 *    display single-font, multi-line text and want Tk to handle the
 *    details.
 *
 * Results:
 *    The return value is a Tk_TextLayout token that holds the
 *    measurement information for the given string.  The token is
 *    only valid for the given string.  If the string is freed,
 *    the token is no longer valid and must also be freed.  To free
 *    the token, call Tk_FreeWTextLayout().
 *
 *    The dimensions of the screen area needed to display the text
 *    are stored in *widthPtr and *heightPtr.
 *
 * Side effects:
 *    Memory is allocated to hold the measurement information.  
 *
 *---------------------------------------------------------------------------
 */

Tk_WTextLayout
Tk_ComputeWTextLayout(tkfont, string, numChars, wrapLength, justify, flags,
      widthPtr, heightPtr)
    Tk_Font tkfont;           /* Font that will be used to display text. */
    CONST wchar *string;      /* String whose dimensions are to be
                         * computed. */
    int numChars;       /* Number of characters to consider from
                         * string, or < 0 for Tcl_WStrlen(). */
    int wrapLength;           /* Longest permissible line length, in
                         * pixels.  <= 0 means no automatic wrapping:
                         * just let lines get as long as needed. */
    Tk_Justify justify;       /* How to justify lines. */
    int flags;                /* Flag bits OR-ed together.
                         * TK_IGNORE_TABS means that tab characters
                         * should not be expanded.  TK_IGNORE_NEWLINES
                         * means that newline characters should not
                         * cause a line break. */
    int *widthPtr;            /* Filled with width of string. */
    int *heightPtr;           /* Filled with height of string. */
{
    TkFont *fontPtr;
    CONST wchar *start, *end, *special;
    int n, y, charsThisChunk, maxChunks;
    int baseline, height, curX, newX, maxWidth;
    WTextLayout *layoutPtr;
    WLayoutChunk *chunkPtr;
    CONST TkFontMetrics *fmPtr;
#define MAX_LINES 50
    int staticLineLengths[MAX_LINES];
    int *lineLengths;
    int maxLines, curLine, layoutHeight;

    lineLengths = staticLineLengths;
    maxLines = MAX_LINES;
#ifdef BUGFIX
    /*
     * XXX
     */
    memset((VOID *)staticLineLengths, 0, sizeof(int) * MAX_LINES);
#endif /* BUGFIX */

    fontPtr = (TkFont *) tkfont;
    fmPtr = &fontPtr->fm;

    height = fmPtr->ascent + fmPtr->descent;

    if (numChars < 0) {
      numChars = Tcl_WStrlen((wchar *)string);
    }

    maxChunks = 1;

    layoutPtr = (WTextLayout *) ckalloc(sizeof(WTextLayout)
          + (maxChunks - 1) * sizeof(WLayoutChunk));
    layoutPtr->tkfont       = tkfont;
    layoutPtr->string       = string;
    layoutPtr->numChunks    = 0;

    baseline = fmPtr->ascent;
    maxWidth = 0;

    /*
     * Divide the string up into simple strings and measure each string.
     */

    curX = 0;

    end = string + numChars;
    special = string;

    flags &= TK_IGNORE_TABS | TK_IGNORE_NEWLINES;
    flags |= TK_WHOLE_WORDS | TK_AT_LEAST_ONE;      
    curLine = 0;
    for (start = string; start < end; ) {
      if (start >= special) {
          /*
           * Find the next special character in the string.
           */

          for (special = start; special < end; special++) {
            if (!(flags & TK_IGNORE_NEWLINES)) {
                if ((*special == '\n') || (*special == '\r')) {
                  break;
                }
            }
            if (!(flags & TK_IGNORE_TABS)) {
                if (*special == '\t') {
                  break;
                }
            }
          }
      }

      /*
       * Special points at the next special character (or the end of the
       * string).  Process characters between start and special.
       */

      chunkPtr = NULL;
      if (start < special) {
          charsThisChunk = Tk_MeasureWChars(tkfont, start, special - start,
                wrapLength - curX, flags, &newX);
          newX += curX;
          flags &= ~TK_AT_LEAST_ONE;
          if (charsThisChunk > 0) {
            chunkPtr = NewWChunk(&layoutPtr, &maxChunks, start,
                  charsThisChunk, curX, newX, baseline);
                  
            start += charsThisChunk;
            curX = newX;
          }
      }

      if ((start == special) && (special < end)) {
          /*
           * Handle the special character.
           */

          chunkPtr = NULL;
          if (*special == '\t') {
            newX = curX + fontPtr->tabWidth;
            newX -= newX % fontPtr->tabWidth;
            NewWChunk(&layoutPtr, &maxChunks, start, 1, curX, newX,
                  baseline)->numDisplayChars = -1;
            start++;
            if ((start < end) &&
                  ((wrapLength <= 0) || (newX <= wrapLength))) {
                /*
                 * More chars can still fit on this line.
                 */

                curX = newX;
                flags &= ~TK_AT_LEAST_ONE;
                continue;
            }
          } else {      
            NewWChunk(&layoutPtr, &maxChunks, start, 1, curX, 1000000000,
                  baseline)->numDisplayChars = -1;
            start++;
            goto wrapLine;
          }
      }

      /*
       * No more characters are going to go on this line, either because
       * no more characters can fit or there are no more characters left.
       * Consume all extra spaces at end of line.  
       */

      while ((start < end) && isspace(UCHAR(*start))) {
          if (!(flags & TK_IGNORE_NEWLINES)) {
            if ((*start == '\n') || (*start == '\r')) {
                break;
            }
          }
          if (!(flags & TK_IGNORE_TABS)) {
            if (*start == '\t') {
                break;
            }
          }
          start++;
      }
      if (chunkPtr != NULL) {
          /*
           * Append all the extra spaces on this line to the end of the
           * last text chunk.
           */
          charsThisChunk = start - (chunkPtr->start + chunkPtr->numChars);
          if (charsThisChunk > 0) {
            chunkPtr->numChars += Tk_MeasureWChars(tkfont,
                  chunkPtr->start + chunkPtr->numChars, charsThisChunk,
                  0, 0, &chunkPtr->totalWidth);
            chunkPtr->totalWidth += curX;
          }
      }

        wrapLine: 
      flags |= TK_AT_LEAST_ONE;

      /*
       * Save current line length, then move current position to start of
       * next line.
       */

      if (curX > maxWidth) {
          maxWidth = curX;
      }

      /*
       * Remember width of this line, so that all chunks on this line
       * can be centered or right justified, if necessary.
       */

      if (curLine >= maxLines) {
          int *newLengths;
          
          newLengths = (int *) ckalloc(2 * maxLines * sizeof(int));
#ifdef BUGFIX
          /*
           * XXX
           */
          memset((VOID *)newLengths, 0, 2 * maxLines * sizeof(int));
#endif /* BUGFIX */
          memcpy((void *) newLengths, lineLengths, maxLines * sizeof(int));
          if (lineLengths != staticLineLengths) {
              ckfree((char *) lineLengths);
          }
          lineLengths = newLengths;
          maxLines *= 2;
      }
      lineLengths[curLine] = curX;
      curLine++;

      curX = 0;
      baseline += height;
    }

    /*
     * If last line ends with a newline, then we need to make a 0 width
     * chunk on the next line.  Otherwise "Hello" and "Hello\n" are the
     * same height.
     */

    if ((layoutPtr->numChunks > 0) && ((flags & TK_IGNORE_NEWLINES) == 0)) {
      if (layoutPtr->chunks[layoutPtr->numChunks - 1].start[0] == '\n') {
          chunkPtr = NewWChunk(&layoutPtr, &maxChunks, start, 0, curX,
                1000000000, baseline);
          chunkPtr->numDisplayChars = -1;
          baseline += height;
      }
    }     

    /*
     * Using maximum line length, shift all the chunks so that the lines are
     * all justified correctly.
     */
    
    curLine = 0;
    chunkPtr = layoutPtr->chunks;
    y = chunkPtr->y;
    for (n = 0; n < layoutPtr->numChunks; n++) {
#ifdef BUGFIX
        int extra = 0;
#else
      int extra;
#endif /* BUGFIX */

      if (chunkPtr->y != y) {
          curLine++;
          y = chunkPtr->y;
      }
#ifdef BUGFIX
      /*
       * curLine could be out of range. lineLengths[curLine] must be
       * initialized by zero in such a case.
       */
#endif /* BUGFIX */
      extra = maxWidth - lineLengths[curLine];
      if (justify == TK_JUSTIFY_CENTER) {
          chunkPtr->x += extra / 2;
      } else if (justify == TK_JUSTIFY_RIGHT) {
          chunkPtr->x += extra;
      }
      chunkPtr++;
    }

    layoutPtr->width = maxWidth;
    layoutHeight = baseline - fmPtr->ascent;
    if (layoutPtr->numChunks == 0) {
      layoutHeight = height;

      /*
       * This fake chunk is used by the other procedures so that they can
       * pretend that there is a chunk with no chars in it, which makes
       * the coding simpler.
       */

      layoutPtr->numChunks = 1;
      layoutPtr->chunks[0].start          = string;
      layoutPtr->chunks[0].numChars       = 0;
      layoutPtr->chunks[0].numDisplayChars      = -1;
      layoutPtr->chunks[0].x              = 0;
      layoutPtr->chunks[0].y              = fmPtr->ascent;
      layoutPtr->chunks[0].totalWidth           = 0;
      layoutPtr->chunks[0].displayWidth   = 0;
    }

    if (widthPtr != NULL) {
      *widthPtr = layoutPtr->width;
    }
    if (heightPtr != NULL) {
      *heightPtr = layoutHeight;
    }
    if (lineLengths != staticLineLengths) {
      ckfree((char *) lineLengths);
    }

    return (Tk_WTextLayout) layoutPtr;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_FreeWTextLayout --
 *
 *    This procedure is called to release the storage associated with
 *    a Tk_WTextLayout when it is no longer needed.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Memory is freed.
 *
 *---------------------------------------------------------------------------
 */

void
Tk_FreeWTextLayout(textLayout)
    Tk_WTextLayout textLayout;      /* The text layout to be released. */
{
    WTextLayout *layoutPtr;

    layoutPtr = (WTextLayout *) textLayout;
    if (layoutPtr != NULL) {
      ckfree((char *) layoutPtr);
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_DrawWTextLayout --
 *
 *    Use the information in the Tk_WTextLayout token to display a
 *    multi-line, justified string of text.
 *
 *    This procedure is useful for simple widgets that need to
 *    display single-font, multi-line text and want Tk to handle
 *    the details.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Text drawn on the screen.
 *
 *---------------------------------------------------------------------------
 */

void
Tk_DrawWTextLayout(display, drawable, gc, layout, x, y, firstChar, lastChar)
    Display *display;         /* Display on which to draw. */
    Drawable drawable;        /* Window or pixmap in which to draw. */
    GC gc;              /* Graphics context to use for drawing text. */
    Tk_WTextLayout layout;    /* Layout information, from a previous call
                         * to Tk_ComputeWTextLayout(). */
    int x, y;                 /* Upper-left hand corner of rectangle in
                         * which to draw (pixels). */
    int firstChar;            /* The index of the first character to draw
                         * from the given text item.  0 specfies the
                         * beginning. */
    int lastChar;       /* The index just after the last character
                         * to draw from the given text item.  A number
                         * < 0 means to draw all characters. */
{
    WTextLayout *layoutPtr;
    int i, numDisplayChars, drawX;
    WLayoutChunk *chunkPtr;

    layoutPtr = (WTextLayout *) layout;
    if (layoutPtr == NULL) {
      return;
    }

    if (lastChar < 0) {
      lastChar = 100000000;
    }
    chunkPtr = layoutPtr->chunks;
    for (i = 0; i < layoutPtr->numChunks; i++) {
      numDisplayChars = chunkPtr->numDisplayChars;
      if ((numDisplayChars > 0) && (firstChar < numDisplayChars)) {
          if (firstChar <= 0) {
            drawX = 0;
            firstChar = 0;
          } else {
            Tk_MeasureWChars(layoutPtr->tkfont, chunkPtr->start, firstChar,
                  0, 0, &drawX);
          }
          if (lastChar < numDisplayChars) {
            numDisplayChars = lastChar;
          }
          Tk_DrawWChars(display, drawable, gc, layoutPtr->tkfont,
                chunkPtr->start + firstChar, numDisplayChars - firstChar,
                x + chunkPtr->x + drawX, y + chunkPtr->y);
      }
      firstChar -= chunkPtr->numChars;
      lastChar -= chunkPtr->numChars;
      if (lastChar <= 0) {
          break;
      }
      chunkPtr++;
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_UnderlineWTextLayout --
 *
 *    Use the information in the Tk_WTextLayout token to display an
 *    underline below an individual character.  This procedure does
 *    not draw the text, just the underline.
 *
 *    This procedure is useful for simple widgets that need to
 *    display single-font, multi-line text with an individual
 *    character underlined and want Tk to handle the details.
 *    To display larger amounts of underlined text, construct
 *    and use an underlined font.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Underline drawn on the screen.
 *
 *---------------------------------------------------------------------------
 */

void
Tk_UnderlineWTextLayout(display, drawable, gc, layout, x, y, underline)
    Display *display;         /* Display on which to draw. */
    Drawable drawable;        /* Window or pixmap in which to draw. */
    GC gc;              /* Graphics context to use for drawing text. */
    Tk_WTextLayout layout;    /* Layout information, from a previous call
                         * to Tk_ComputeWTextLayout(). */
    int x, y;                 /* Upper-left hand corner of rectangle in
                         * which to draw (pixels). */
    int underline;            /* Index of the single character to
                         * underline, or -1 for no underline. */
{
    WTextLayout *layoutPtr;
    TkFont *fontPtr;
    int xx, yy, width, height;

    if ((Tk_WCharBbox(layout, underline, &xx, &yy, &width, &height) != 0)
          && (width != 0)) {
      layoutPtr = (WTextLayout *) layout;
      fontPtr = (TkFont *) layoutPtr->tkfont;

      XFillRectangle(display, drawable, gc, x + xx, 
            y + yy + fontPtr->fm.ascent + fontPtr->underlinePos,
            (unsigned int) width, (unsigned int) fontPtr->underlineHeight);
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_PointToWChar --
 *
 *    Use the information in the Tk_WTextLayout token to determine the
 *    character closest to the given point.  The point must be
 *    specified with respect to the upper-left hand corner of the
 *    text layout, which is considered to be located at (0, 0).
 *
 *    Any point whose y-value is less that 0 will be considered closest
 *    to the first character in the text layout; any point whose y-value
 *    is greater than the height of the text layout will be considered
 *    closest to the last character in the text layout.
 *
 *    Any point whose x-value is less than 0 will be considered closest
 *    to the first character on that line; any point whose x-value is
 *    greater than the width of the text layout will be considered
 *    closest to the last character on that line.
 *
 * Results:
 *    The return value is the index of the character that was
 *    closest to the point.  Given a text layout with no characters,
 *    the value 0 will always be returned, referring to a hypothetical
 *    zero-width placeholder character.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */

int
Tk_PointToWChar(layout, x, y)
    Tk_WTextLayout layout;    /* Layout information, from a previous call
                         * to Tk_ComputeWTextLayout(). */
    int x, y;                 /* Coordinates of point to check, with
                         * respect to the upper-left corner of the
                         * text layout. */
{
    WTextLayout *layoutPtr;
    WLayoutChunk *chunkPtr, *lastPtr;
    TkFont *fontPtr;
    int i, n, dummy, baseline, pos;

    if (y < 0) {
      /*
       * Point lies above any line in this layout.  Return the index of
       * the first char.
       */

      return 0;
    }

    /*
     * Find which line contains the point.
     */

    layoutPtr = (WTextLayout *) layout;
    fontPtr = (TkFont *) layoutPtr->tkfont;
    lastPtr = chunkPtr = layoutPtr->chunks;
    for (i = 0; i < layoutPtr->numChunks; i++) {
      baseline = chunkPtr->y;
      if (y < baseline + fontPtr->fm.descent) {
          if (x < chunkPtr->x) {
            /*
             * Point is to the left of all chunks on this line.  Return
             * the index of the first character on this line.
             */

            return chunkPtr->start - layoutPtr->string;
          }
          if (x >= layoutPtr->width) {
            /*
             * If point lies off right side of the text layout, return
             * the last char in the last chunk on this line.  Without
             * this, it might return the index of the first char that
             * was located outside of the text layout.
             */

            x = INT_MAX;
          }

          /*
           * Examine all chunks on this line to see which one contains
           * the specified point.
           */

          lastPtr = chunkPtr;
          while ((i < layoutPtr->numChunks) && (chunkPtr->y == baseline))  {
            if (x < chunkPtr->x + chunkPtr->totalWidth) {
                /*
                 * Point falls on one of the characters in this chunk.
                 */

                if (chunkPtr->numDisplayChars < 0) {
                  /*
                   * This is a special chunk that encapsulates a single
                   * tab or newline char.
                   */

                  return chunkPtr->start - layoutPtr->string;
                }
                n = Tk_MeasureWChars((Tk_Font) fontPtr, chunkPtr->start,
                      chunkPtr->numChars, x + 1 - chunkPtr->x,
                      TK_PARTIAL_OK, &dummy);
                return (chunkPtr->start + n - 1) - layoutPtr->string;
            }
            lastPtr = chunkPtr;
            chunkPtr++;
            i++;
          }

          /*
           * Point is to the right of all chars in all the chunks on this
           * line.  Return the index just past the last char in the last
           * chunk on this line.
           */

          pos = (lastPtr->start + lastPtr->numChars) - layoutPtr->string;
          if (i < layoutPtr->numChunks) {
            pos--;
          }
          return pos;
      }
      lastPtr = chunkPtr;
      chunkPtr++;
    }

    /*
     * Point lies below any line in this text layout.  Return the index
     * just past the last char.
     */

    return (lastPtr->start + lastPtr->numChars) - layoutPtr->string;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_WCharBbox --
 *
 *    Use the information in the Tk_WTextLayout token to return the
 *    bounding box for the character specified by index.  
 *
 *    The width of the bounding box is the advance width of the
 *    character, and does not include and left- or right-bearing.
 *    Any character that extends partially outside of the
 *    text layout is considered to be truncated at the edge.  Any
 *    character which is located completely outside of the text
 *    layout is considered to be zero-width and pegged against
 *    the edge.
 *
 *    The height of the bounding box is the line height for this font,
 *    extending from the top of the ascent to the bottom of the
 *    descent.  Information about the actual height of the individual
 *    letter is not available.
 *
 *    A text layout that contains no characters is considered to
 *    contain a single zero-width placeholder character.
 * 
 * Results:
 *    The return value is 0 if the index did not specify a character
 *    in the text layout, or non-zero otherwise.  In that case,
 *    *bbox is filled with the bounding box of the character.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */

int
Tk_WCharBbox(layout, index, xPtr, yPtr, widthPtr, heightPtr)
    Tk_WTextLayout layout;  /* Layout information, from a previous call to
                       * Tk_ComputeWTextLayout(). */
    int index;              /* The index of the character whose bbox is
                       * desired. */
    int *xPtr, *yPtr;       /* Filled with the upper-left hand corner, in
                       * pixels, of the bounding box for the character
                       * specified by index, if non-NULL. */
    int *widthPtr, *heightPtr;
                      /* Filled with the width and height of the
                       * bounding box for the character specified by
                       * index, if non-NULL. */
{
    WTextLayout *layoutPtr;
    WLayoutChunk *chunkPtr;
    int i, x, w;
    Tk_Font tkfont;
    TkFont *fontPtr;

    if (index < 0) {
      return 0;
    }

    layoutPtr = (WTextLayout *) layout;
    chunkPtr = layoutPtr->chunks;
    tkfont = layoutPtr->tkfont;
    fontPtr = (TkFont *) tkfont;

    for (i = 0; i < layoutPtr->numChunks; i++) {
      if (chunkPtr->numDisplayChars < 0) {
          if (index == 0) {
            x = chunkPtr->x;
            w = chunkPtr->totalWidth;
            goto check;
          }
      } else if (index < chunkPtr->numChars) {
          if (xPtr != NULL) {
            Tk_MeasureWChars(tkfont, chunkPtr->start, index, 0, 0, &x);
            x += chunkPtr->x;
          }
          if (widthPtr != NULL) {
            Tk_MeasureWChars(tkfont, chunkPtr->start + index, 1, 0, 0, &w);
          }
          goto check;
      }
      index -= chunkPtr->numChars;
      chunkPtr++;
    }
    if (index == 0) {
      /*
       * Special case to get location just past last char in layout.
       */

      chunkPtr--;
      x = chunkPtr->x + chunkPtr->totalWidth;
      w = 0;
    } else {
      return 0;
    }

    /*
     * Ensure that the bbox lies within the text layout.  This forces all
     * chars that extend off the right edge of the text layout to have
     * truncated widths, and all chars that are completely off the right
     * edge of the text layout to peg to the edge and have 0 width.
     */
    check:
    if (yPtr != NULL) {
      *yPtr = chunkPtr->y - fontPtr->fm.ascent;
    }
    if (heightPtr != NULL) {
      *heightPtr = fontPtr->fm.ascent + fontPtr->fm.descent;
    }

    if (x > layoutPtr->width) {
      x = layoutPtr->width;
    }
    if (xPtr != NULL) {
      *xPtr = x;
    }
    if (widthPtr != NULL) {
      if (x + w > layoutPtr->width) {
          w = layoutPtr->width - x;
      }
      *widthPtr = w;
    }

    return 1;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_DistanceToWTextLayout --
 *
 *    Computes the distance in pixels from the given point to the
 *    given text layout.  Non-displaying space characters that occur
 *    at the end of individual lines in the text layout are ignored
 *    for hit detection purposes.
 *
 * Results:
 *    The return value is 0 if the point (x, y) is inside the text
 *    layout.  If the point isn't inside the text layout then the
 *    return value is the distance in pixels from the point to the
 *    text item.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */

int
Tk_DistanceToWTextLayout(layout, x, y)
    Tk_WTextLayout layout;    /* Layout information, from a previous call
                         * to Tk_ComputeWTextLayout(). */
    int x, y;                 /* Coordinates of point to check, with
                         * respect to the upper-left corner of the
                         * text layout (in pixels). */
{
    int i, x1, x2, y1, y2, xDiff, yDiff, dist, minDist, ascent, descent;
    WLayoutChunk *chunkPtr;
    WTextLayout *layoutPtr;
    TkFont *fontPtr;

    layoutPtr = (WTextLayout *) layout;
    fontPtr = (TkFont *) layoutPtr->tkfont;
    ascent = fontPtr->fm.ascent;
    descent = fontPtr->fm.descent;
    
    minDist = 0;
    chunkPtr = layoutPtr->chunks;
    for (i = 0; i < layoutPtr->numChunks; i++) {
      if (chunkPtr->start[0] == '\n') {
          /*
           * Newline characters are not counted when computing distance
           * (but tab characters would still be considered).
           */

          chunkPtr++;
          continue;
      }

      x1 = chunkPtr->x;
      y1 = chunkPtr->y - ascent;
      x2 = chunkPtr->x + chunkPtr->displayWidth;
      y2 = chunkPtr->y + descent;

      if (x < x1) {
          xDiff = x1 - x;
      } else if (x >= x2) {
          xDiff = x - x2 + 1;
      } else {
          xDiff = 0;
      }

      if (y < y1) {
          yDiff = y1 - y;
      } else if (y >= y2) {
          yDiff = y - y2 + 1;
      } else {
          yDiff = 0;
      }
      if ((xDiff == 0) && (yDiff == 0)) {
          return 0;
      }
      dist = (int) hypot((double) xDiff, (double) yDiff);
      if ((dist < minDist) || (minDist == 0)) {
          minDist = dist;
      }
      chunkPtr++;
    }
    return minDist;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_IntersectWTextLayout --
 *
 *    Determines whether a text layout lies entirely inside,
 *    entirely outside, or overlaps a given rectangle.  Non-displaying
 *    space characters that occur at the end of individual lines in
 *    the text layout are ignored for intersection calculations.
 *
 * Results:
 *    The return value is -1 if the text layout is entirely outside of
 *    the rectangle, 0 if it overlaps, and 1 if it is entirely inside
 *    of the rectangle.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */

int
Tk_IntersectWTextLayout(layout, x, y, width, height)
    Tk_WTextLayout layout;    /* Layout information, from a previous call
                         * to Tk_ComputeWTextLayout(). */
    int x, y;                 /* Upper-left hand corner, in pixels, of
                         * rectangular area to compare with text
                         * layout.  Coordinates are with respect to
                         * the upper-left hand corner of the text
                         * layout itself. */
    int width, height;        /* The width and height of the above
                         * rectangular area, in pixels. */
{
    int result, i, x1, y1, x2, y2;
    WTextLayout *layoutPtr;
    WLayoutChunk *chunkPtr;
    TkFont *fontPtr;
    int left, top, right, bottom;

    /*
     * Scan the chunks one at a time, seeing whether each is entirely in,
     * entirely out, or overlapping the rectangle.  If an overlap is
     * detected, return immediately; otherwise wait until all chunks have
     * been processed and see if they were all inside or all outside.
     */
    
    layoutPtr = (WTextLayout *) layout;
    chunkPtr = layoutPtr->chunks;
    fontPtr = (TkFont *) layoutPtr->tkfont;

    left    = x;
    top         = y;
    right   = x + width;
    bottom  = y + height;

    result = 0;
    for (i = 0; i < layoutPtr->numChunks; i++) {
      if (chunkPtr->start[0] == '\n') {
          /*
           * Newline characters are not counted when computing area
           * intersection (but tab characters would still be considered).
           */

          chunkPtr++;
          continue;
      }

      x1 = chunkPtr->x;
      y1 = chunkPtr->y - fontPtr->fm.ascent;
      x2 = chunkPtr->x + chunkPtr->displayWidth;
      y2 = chunkPtr->y + fontPtr->fm.descent;

      if ((right < x1) || (left >= x2)
            || (bottom < y1) || (top >= y2)) {
          if (result == 1) {
            return 0;
          }
          result = -1;
      } else if ((x1 < left) || (x2 >= right)
            || (y1 < top) || (y2 >= bottom)) {
          return 0;
      } else if (result == -1) {
          return 0;
      } else {
          result = 1;
      }
      chunkPtr++;
    }
    return result;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_WTextLayoutToPostscript --
 *
 *    Outputs the contents of a text layout in Postscript format.
 *    The set of lines in the text layout will be rendered by the user
 *    supplied Postscript function.  The function should be of the form:
 *
 *        justify x y string  function  --
 *
 *    Justify is -1, 0, or 1, depending on whether the following string
 *    should be left, center, or right justified, x and y is the
 *    location for the origin of the string, string is the sequence
 *    of characters to be printed, and function is the name of the
 *    caller-provided function; the function should leave nothing
 *    on the stack.
 *
 *    The meaning of the origin of the string (x and y) depends on
 *    the justification.  For left justification, x is where the
 *    left edge of the string should appear.  For center justification,
 *    x is where the center of the string should appear.  And for right
 *    justification, x is where the right edge of the string should
 *    appear.  This behavior is necessary because, for example, right
 *    justified text on the screen is justified with screen metrics.
 *    The same string needs to be justified with printer metrics on
 *    the printer to appear in the correct place with respect to other
 *    similarly justified strings.  In all circumstances, y is the
 *    location of the baseline for the string.
 *
 * Results:
 *    Interp->result is modified to hold the Postscript code that
 *    will render the text layout.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */

void
Tk_WTextLayoutToPostscript(interp, layout)
    Tcl_Interp *interp;       /* Filled with Postscript code. */
    Tk_WTextLayout layout;    /* The layout to be rendered. */
{
#define MAXUSE 128
    char buf[MAXUSE+10];
    WLayoutChunk *chunkPtr;
    int i, j, used, c, baseline;
    WTextLayout *layoutPtr;

    layoutPtr = (WTextLayout *) layout;
    chunkPtr = layoutPtr->chunks;
    baseline = chunkPtr->y;
    used = 0;
    buf[used++] = '(';
    for (i = 0; i < layoutPtr->numChunks; i++) {
      if (baseline != chunkPtr->y) {
          buf[used++] = ')';
          buf[used++] = '\n';
          buf[used++] = '(';
          baseline = chunkPtr->y;
      }
      if (chunkPtr->numDisplayChars <= 0) {
          if (chunkPtr->start[0] == '\t') {
            buf[used++] = '\\';
            buf[used++] = 't';
          }
      } else {
          for (j = 0; j < chunkPtr->numDisplayChars; j++) {
            c = chunkPtr->start[j] & 0xffff;
            if ((c == '(') || (c == ')') || (c == '\\') || (c < 0x20)) {
                /*
                 * Tricky point:  the "03" is necessary in the sprintf
                 * below, so that a full three digits of octal are
                 * always generated.  Without the "03", a number
                 * following this sequence could be interpreted by
                 * Postscript as part of this sequence.
                 */

                sprintf(buf + used, "\\%03o", c);
                used += 4;
            } else if (c <= 0x7f) {
                buf[used++] = c;
            } else {
                /* Kanji */
#if defined(__WIN32__) && defined(USE_RKSJ_PSFONT)
                /* use SJIS. (XXX-RKSJ-H) */
                wchar wTmp[2];
                unsigned char sTmp[3];
                unsigned char* p;
                wTmp[0] = c;
                wTmp[1] = 0;
                Tcl_DecodeSJIS(wTmp, sTmp);
                for (p = sTmp; *p; p++) {
                  sprintf(buf + used, "\\%03o", *p);
                  used += 4;
                }
#else
                sprintf(buf + used, "\\%03o", c >> 8);
                used += 4;
                sprintf(buf + used, "\\%03o", c & 0xff);
                used += 4;
#endif /* __WIN32__ && USE_RKSJ_PSFONT */
            }
            if (used >= MAXUSE) {
                buf[used] = '\0';
                Tcl_AppendResult(interp, buf, (char *) NULL);
                used = 0;
            }
          }
      }
      if (used >= MAXUSE) {
          /*
           * If there are a whole bunch of returns or tabs in a row,
           * then buf[] could get filled up.
           */
           
          buf[used] = '\0';
          Tcl_AppendResult(interp, buf, (char *) NULL);
          used = 0;
      }
      chunkPtr++;
    }
    buf[used++] = ')';
    buf[used++] = '\n';
    buf[used] = '\0';
    Tcl_AppendResult(interp, buf, (char *) NULL);
}

/*
 *---------------------------------------------------------------------------
 *
 * NewWChunk --
 *
 *    Helper function for Tk_ComputeWTextLayout().  Encapsulates a
 *    measured set of characters in a chunk that can be quickly
 *    drawn.
 *
 * Results:
 *    A pointer to the new chunk in the text layout.
 *
 * Side effects:
 *    The text layout is reallocated to hold more chunks as necessary.
 *
 *    Currently, Tk_ComputeWTextLayout() stores contiguous ranges of
 *    "normal" characters in a chunk, along with individual tab
 *    and newline chars in their own chunks.  All characters in the
 *    text layout are accounted for.
 *
 *---------------------------------------------------------------------------
 */
static WLayoutChunk *
NewWChunk(layoutPtrPtr, maxPtr, start, numChars, curX, newX, y)
    WTextLayout **layoutPtrPtr;
    int *maxPtr;
    CONST wchar *start;
    int numChars;
    int curX;
    int newX;
    int y;
{
    WTextLayout *layoutPtr;
    WLayoutChunk *chunkPtr;
    int maxChunks;
    size_t s;
    
    layoutPtr = *layoutPtrPtr;
    maxChunks = *maxPtr;
    if (layoutPtr->numChunks == maxChunks) {
      maxChunks *= 2;
      s = sizeof(WTextLayout) + ((maxChunks - 1) * sizeof(WLayoutChunk));
      layoutPtr = (WTextLayout *) ckrealloc((char *) layoutPtr, s);

      *layoutPtrPtr = layoutPtr;
      *maxPtr = maxChunks;
    }
    chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks];
    chunkPtr->start           = start;
    chunkPtr->numChars        = numChars;
    chunkPtr->numDisplayChars = numChars;
    chunkPtr->x               = curX;
    chunkPtr->y               = y;
    chunkPtr->totalWidth      = newX - curX;
    chunkPtr->displayWidth    = newX - curX;
    layoutPtr->numChunks++;

    return chunkPtr;
}

#endif /* KANJI */

Generated by  Doxygen 1.6.0   Back to index