1078 lines · 43.3 KB · Java
RawDownload
/* * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package javax.swing.text; import java.lang.reflect.Method; import java.awt.Component;import java.awt.Rectangle;import java.awt.Graphics;import java.awt.FontMetrics;import java.awt.Shape;import java.awt.Toolkit;import java.awt.Graphics2D;import java.awt.font.FontRenderContext;import java.awt.font.TextLayout;import java.awt.font.TextAttribute; import java.text.*;import javax.swing.JComponent;import javax.swing.SwingConstants;import javax.swing.text.ParagraphView.Row;import sun.swing.SwingUtilities2; /** * A collection of methods to deal with various text * related activities. * * @author  Timothy Prinzing */public class Utilities {    /**     * If <code>view</code>'s container is a <code>JComponent</code> it     * is returned, after casting.     */    static JComponent getJComponent(View view) {        if (view != null) {            Component component = view.getContainer();            if (component instanceof JComponent) {                return (JComponent)component;            }        }        return null;    }     /**     * Draws the given text, expanding any tabs that are contained     * using the given tab expansion technique.  This particular     * implementation renders in a 1.1 style coordinate system     * where ints are used and 72dpi is assumed.     *     * @param s  the source of the text     * @param x  the X origin >= 0     * @param y  the Y origin >= 0     * @param g  the graphics context     * @param e  how to expand the tabs.  If this value is null,     *   tabs will be expanded as a space character.     * @param startOffset starting offset of the text in the document >= 0     * @return  the X location at the end of the rendered text     */    public static final int drawTabbedText(Segment s, int x, int y, Graphics g,                                           TabExpander e, int startOffset) {        return drawTabbedText(null, s, x, y, g, e, startOffset);    }     /**     * Draws the given text, expanding any tabs that are contained     * using the given tab expansion technique.  This particular     * implementation renders in a 1.1 style coordinate system     * where ints are used and 72dpi is assumed.     *     * @param view View requesting rendering, may be null.     * @param s  the source of the text     * @param x  the X origin >= 0     * @param y  the Y origin >= 0     * @param g  the graphics context     * @param e  how to expand the tabs.  If this value is null,     *   tabs will be expanded as a space character.     * @param startOffset starting offset of the text in the document >= 0     * @return  the X location at the end of the rendered text     */    static final int drawTabbedText(View view,                                Segment s, int x, int y, Graphics g,                                TabExpander e, int startOffset) {        return drawTabbedText(view, s, x, y, g, e, startOffset, null);    }     // In addition to the previous method it can extend spaces for    // justification.    //    // all params are the same as in the preious method except the last    // one:    // @param justificationData justificationData for the row.    // if null not justification is needed    static final int drawTabbedText(View view,                                Segment s, int x, int y, Graphics g,                                TabExpander e, int startOffset,                                int [] justificationData) {        JComponent component = getJComponent(view);        FontMetrics metrics = SwingUtilities2.getFontMetrics(component, g);        int nextX = x;        char[] txt = s.array;        int txtOffset = s.offset;        int flushLen = 0;        int flushIndex = s.offset;        int spaceAddon = 0;        int spaceAddonLeftoverEnd = -1;        int startJustifiableContent = 0;        int endJustifiableContent = 0;        if (justificationData != null) {            int offset = - startOffset + txtOffset;            View parent = null;            if (view != null                  && (parent = view.getParent()) != null) {                offset += parent.getStartOffset();            }            spaceAddon =                justificationData[Row.SPACE_ADDON];            spaceAddonLeftoverEnd =                justificationData[Row.SPACE_ADDON_LEFTOVER_END] + offset;            startJustifiableContent =                justificationData[Row.START_JUSTIFIABLE] + offset;            endJustifiableContent =                justificationData[Row.END_JUSTIFIABLE] + offset;        }        int n = s.offset + s.count;        for (int i = txtOffset; i < n; i++) {            if (txt[i] == '\t'                || ((spaceAddon != 0 || i <= spaceAddonLeftoverEnd)                    && (txt[i] == ' ')                    && startJustifiableContent <= i                    && i <= endJustifiableContent                    )) {                if (flushLen > 0) {                    nextX = SwingUtilities2.drawChars(component, g, txt,                                                flushIndex, flushLen, x, y);                    flushLen = 0;                }                flushIndex = i + 1;                if (txt[i] == '\t') {                    if (e != null) {                        nextX = (int) e.nextTabStop((float) nextX, startOffset + i - txtOffset);                    } else {                        nextX += metrics.charWidth(' ');                    }                } else if (txt[i] == ' ') {                    nextX += metrics.charWidth(' ') + spaceAddon;                    if (i <= spaceAddonLeftoverEnd) {                        nextX++;                    }                }                x = nextX;            } else if ((txt[i] == '\n') || (txt[i] == '\r')) {                if (flushLen > 0) {                    nextX = SwingUtilities2.drawChars(component, g, txt,                                                flushIndex, flushLen, x, y);                    flushLen = 0;                }                flushIndex = i + 1;                x = nextX;            } else {                flushLen += 1;            }        }        if (flushLen > 0) {            nextX = SwingUtilities2.drawChars(component, g,txt, flushIndex,                                              flushLen, x, y);        }        return nextX;    }     /**     * Determines the width of the given segment of text taking tabs     * into consideration.  This is implemented in a 1.1 style coordinate     * system where ints are used and 72dpi is assumed.     *     * @param s  the source of the text     * @param metrics the font metrics to use for the calculation     * @param x  the X origin >= 0     * @param e  how to expand the tabs.  If this value is null,     *   tabs will be expanded as a space character.     * @param startOffset starting offset of the text in the document >= 0     * @return  the width of the text     */    public static final int getTabbedTextWidth(Segment s, FontMetrics metrics, int x,                                               TabExpander e, int startOffset) {        return getTabbedTextWidth(null, s, metrics, x, e, startOffset, null);    }      // In addition to the previous method it can extend spaces for    // justification.    //    // all params are the same as in the preious method except the last    // one:    // @param justificationData justificationData for the row.    // if null not justification is needed    static final int getTabbedTextWidth(View view, Segment s, FontMetrics metrics, int x,                                        TabExpander e, int startOffset,                                        int[] justificationData) {        int nextX = x;        char[] txt = s.array;        int txtOffset = s.offset;        int n = s.offset + s.count;        int charCount = 0;        int spaceAddon = 0;        int spaceAddonLeftoverEnd = -1;        int startJustifiableContent = 0;        int endJustifiableContent = 0;        if (justificationData != null) {            int offset = - startOffset + txtOffset;            View parent = null;            if (view != null                  && (parent = view.getParent()) != null) {                offset += parent.getStartOffset();            }            spaceAddon =                justificationData[Row.SPACE_ADDON];            spaceAddonLeftoverEnd =                justificationData[Row.SPACE_ADDON_LEFTOVER_END] + offset;            startJustifiableContent =                justificationData[Row.START_JUSTIFIABLE] + offset;            endJustifiableContent =                justificationData[Row.END_JUSTIFIABLE] + offset;        }         for (int i = txtOffset; i < n; i++) {            if (txt[i] == '\t'                || ((spaceAddon != 0 || i <= spaceAddonLeftoverEnd)                    && (txt[i] == ' ')                    && startJustifiableContent <= i                    && i <= endJustifiableContent                    )) {                nextX += metrics.charsWidth(txt, i-charCount, charCount);                charCount = 0;                if (txt[i] == '\t') {                    if (e != null) {                        nextX = (int) e.nextTabStop((float) nextX,                                                    startOffset + i - txtOffset);                    } else {                        nextX += metrics.charWidth(' ');                    }                } else if (txt[i] == ' ') {                    nextX += metrics.charWidth(' ') + spaceAddon;                    if (i <= spaceAddonLeftoverEnd) {                        nextX++;                    }                }            } else if(txt[i] == '\n') {            // Ignore newlines, they take up space and we shouldn't be            // counting them.                nextX += metrics.charsWidth(txt, i - charCount, charCount);                charCount = 0;            } else {                charCount++;        }        }        nextX += metrics.charsWidth(txt, n - charCount, charCount);        return nextX - x;    }     /**     * Determines the relative offset into the given text that     * best represents the given span in the view coordinate     * system.  This is implemented in a 1.1 style coordinate     * system where ints are used and 72dpi is assumed.     *     * @param s  the source of the text     * @param metrics the font metrics to use for the calculation     * @param x0 the starting view location representing the start     *   of the given text >= 0.     * @param x  the target view location to translate to an     *   offset into the text >= 0.     * @param e  how to expand the tabs.  If this value is null,     *   tabs will be expanded as a space character.     * @param startOffset starting offset of the text in the document >= 0     * @return  the offset into the text >= 0     */    public static final int getTabbedTextOffset(Segment s, FontMetrics metrics,                                             int x0, int x, TabExpander e,                                             int startOffset) {        return getTabbedTextOffset(s, metrics, x0, x, e, startOffset, true);    }     static final int getTabbedTextOffset(View view, Segment s, FontMetrics metrics,                                         int x0, int x, TabExpander e,                                         int startOffset,                                         int[] justificationData) {        return getTabbedTextOffset(view, s, metrics, x0, x, e, startOffset, true,                                   justificationData);    }     public static final int getTabbedTextOffset(Segment s,                                                FontMetrics metrics,                                                int x0, int x, TabExpander e,                                                int startOffset,                                                boolean round) {        return getTabbedTextOffset(null, s, metrics, x0, x, e, startOffset, round, null);    }     // In addition to the previous method it can extend spaces for    // justification.    //    // all params are the same as in the preious method except the last    // one:    // @param justificationData justificationData for the row.    // if null not justification is needed    static final int getTabbedTextOffset(View view,                                         Segment s,                                         FontMetrics metrics,                                         int x0, int x, TabExpander e,                                         int startOffset,                                         boolean round,                                         int[] justificationData) {        if (x0 >= x) {            // x before x0, return.            return 0;        }        int nextX = x0;        // s may be a shared segment, so it is copied prior to calling        // the tab expander        char[] txt = s.array;        int txtOffset = s.offset;        int txtCount = s.count;        int spaceAddon = 0 ;        int spaceAddonLeftoverEnd = -1;        int startJustifiableContent = 0 ;        int endJustifiableContent = 0;        if (justificationData != null) {            int offset = - startOffset + txtOffset;            View parent = null;            if (view != null                  && (parent = view.getParent()) != null) {                offset += parent.getStartOffset();            }            spaceAddon =                justificationData[Row.SPACE_ADDON];            spaceAddonLeftoverEnd =                justificationData[Row.SPACE_ADDON_LEFTOVER_END] + offset;            startJustifiableContent =                justificationData[Row.START_JUSTIFIABLE] + offset;            endJustifiableContent =                justificationData[Row.END_JUSTIFIABLE] + offset;        }        int n = s.offset + s.count;        for (int i = s.offset; i < n; i++) {            if (txt[i] == '\t'                || ((spaceAddon != 0 || i <= spaceAddonLeftoverEnd)                    && (txt[i] == ' ')                    && startJustifiableContent <= i                    && i <= endJustifiableContent                    )){                if (txt[i] == '\t') {                    if (e != null) {                        nextX = (int) e.nextTabStop((float) nextX,                                                    startOffset + i - txtOffset);                    } else {                        nextX += metrics.charWidth(' ');                    }                } else if (txt[i] == ' ') {                    nextX += metrics.charWidth(' ') + spaceAddon;                    if (i <= spaceAddonLeftoverEnd) {                        nextX++;                    }                }            } else {                nextX += metrics.charWidth(txt[i]);            }            if (x < nextX) {                // found the hit position... return the appropriate side                int offset;                 // the length of the string measured as a whole may differ from                // the sum of individual character lengths, for example if                // fractional metrics are enabled; and we must guard from this.                if (round) {                    offset = i + 1 - txtOffset;                     int width = metrics.charsWidth(txt, txtOffset, offset);                    int span = x - x0;                     if (span < width) {                        while (offset > 0) {                            int nextWidth = offset > 1 ? metrics.charsWidth(txt, txtOffset, offset - 1) : 0;                             if (span >= nextWidth) {                                if (span - nextWidth < width - span) {                                    offset--;                                }                                 break;                            }                             width = nextWidth;                            offset--;                        }                    }                } else {                    offset = i - txtOffset;                     while (offset > 0 && metrics.charsWidth(txt, txtOffset, offset) > (x - x0)) {                        offset--;                    }                }                 return offset;            }        }         // didn't find, return end offset        return txtCount;    }     /**     * Determine where to break the given text to fit     * within the given span. This tries to find a word boundary.     * @param s  the source of the text     * @param metrics the font metrics to use for the calculation     * @param x0 the starting view location representing the start     *   of the given text.     * @param x  the target view location to translate to an     *   offset into the text.     * @param e  how to expand the tabs.  If this value is null,     *   tabs will be expanded as a space character.     * @param startOffset starting offset in the document of the text     * @return  the offset into the given text     */    public static final int getBreakLocation(Segment s, FontMetrics metrics,                                             int x0, int x, TabExpander e,                                             int startOffset) {        char[] txt = s.array;        int txtOffset = s.offset;        int txtCount = s.count;        int index = Utilities.getTabbedTextOffset(s, metrics, x0, x,                                                  e, startOffset, false);         if (index >= txtCount - 1) {            return txtCount;        }         for (int i = txtOffset + index; i >= txtOffset; i--) {            char ch = txt[i];            if (ch < 256) {                // break on whitespace                if (Character.isWhitespace(ch)) {                    index = i - txtOffset + 1;                    break;                }            } else {                // a multibyte char found; use BreakIterator to find line break                BreakIterator bit = BreakIterator.getLineInstance();                bit.setText(s);                int breakPos = bit.preceding(i + 1);                if (breakPos > txtOffset) {                    index = breakPos - txtOffset;                }                break;            }        }        return index;    }     /**     * Determines the starting row model position of the row that contains     * the specified model position.  The component given must have a     * size to compute the result.  If the component doesn't have a size     * a value of -1 will be returned.     *     * @param c the editor     * @param offs the offset in the document >= 0     * @return the position >= 0 if the request can be computed, otherwise     *  a value of -1 will be returned.     * @exception BadLocationException if the offset is out of range     */    public static final int getRowStart(JTextComponent c, int offs) throws BadLocationException {        Rectangle r = c.modelToView(offs);        if (r == null) {            return -1;        }

Lines 1–500 of 1078.