1201 lines · 44.8 KB · Java
RawDownload
/* * Copyright (c) 1997, 2006, 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.util.Arrays;import java.awt.*;import java.awt.font.TextAttribute;import javax.swing.event.*;import javax.swing.SizeRequirements; /** * View of a simple line-wrapping paragraph that supports * multiple fonts, colors, components, icons, etc.  It is * basically a vertical box with a margin around it.  The * contents of the box are a bunch of rows which are special * horizontal boxes.  This view creates a collection of * views that represent the child elements of the paragraph * element.  Each of these views are placed into a row * directly if they will fit, otherwise the <code>breakView</code> * method is called to try and carve the view into pieces * that fit. * * @author  Timothy Prinzing * @author  Scott Violet * @author  Igor Kushnirskiy * @see     View */public class ParagraphView extends FlowView implements TabExpander {     /**     * Constructs a <code>ParagraphView</code> for the given element.     *     * @param elem the element that this view is responsible for     */    public ParagraphView(Element elem) {        super(elem, View.Y_AXIS);        setPropertiesFromAttributes();        Document doc = elem.getDocument();        Object i18nFlag = doc.getProperty(AbstractDocument.I18NProperty);        if ((i18nFlag != null) && i18nFlag.equals(Boolean.TRUE)) {            try {                if (i18nStrategy == null) {                    // the classname should probably come from a property file.                    String classname = "javax.swing.text.TextLayoutStrategy";                    ClassLoader loader = getClass().getClassLoader();                    if (loader != null) {                        i18nStrategy = loader.loadClass(classname);                    } else {                        i18nStrategy = Class.forName(classname);                    }                }                Object o = i18nStrategy.newInstance();                if (o instanceof FlowStrategy) {                    strategy = (FlowStrategy) o;                }            } catch (Throwable e) {                throw new StateInvariantError("ParagraphView: Can't create i18n strategy: "                                              + e.getMessage());            }        }    }     /**     * Sets the type of justification.     *     * @param j one of the following values:     * <ul>     * <li><code>StyleConstants.ALIGN_LEFT</code>     * <li><code>StyleConstants.ALIGN_CENTER</code>     * <li><code>StyleConstants.ALIGN_RIGHT</code>     * </ul>     */    protected void setJustification(int j) {        justification = j;    }     /**     * Sets the line spacing.     *     * @param ls the value is a factor of the line hight     */    protected void setLineSpacing(float ls) {        lineSpacing = ls;    }     /**     * Sets the indent on the first line.     *     * @param fi the value in points     */    protected void setFirstLineIndent(float fi) {        firstLineIndent = (int) fi;    }     /**     * Set the cached properties from the attributes.     */    protected void setPropertiesFromAttributes() {        AttributeSet attr = getAttributes();        if (attr != null) {            setParagraphInsets(attr);            Integer a = (Integer)attr.getAttribute(StyleConstants.Alignment);            int alignment;            if (a == null) {                Document doc = getElement().getDocument();                Object o = doc.getProperty(TextAttribute.RUN_DIRECTION);                if ((o != null) && o.equals(TextAttribute.RUN_DIRECTION_RTL)) {                    alignment = StyleConstants.ALIGN_RIGHT;                } else {                    alignment = StyleConstants.ALIGN_LEFT;                }            } else {                alignment = a.intValue();            }            setJustification(alignment);            setLineSpacing(StyleConstants.getLineSpacing(attr));            setFirstLineIndent(StyleConstants.getFirstLineIndent(attr));        }    }     /**     * Returns the number of views that this view is     * responsible for.     * The child views of the paragraph are rows which     * have been used to arrange pieces of the <code>View</code>s     * that represent the child elements.  This is the number     * of views that have been tiled in two dimensions,     * and should be equivalent to the number of child elements     * to the element this view is responsible for.     *     * @return the number of views that this <code>ParagraphView</code>     *          is responsible for     */    protected int getLayoutViewCount() {        return layoutPool.getViewCount();    }     /**     * Returns the view at a given <code>index</code>.     * The child views of the paragraph are rows which     * have been used to arrange pieces of the <code>Views</code>     * that represent the child elements.  This methods returns     * the view responsible for the child element index     * (prior to breaking).  These are the Views that were     * produced from a factory (to represent the child     * elements) and used for layout.     *     * @param index the <code>index</code> of the desired view     * @return the view at <code>index</code>     */    protected View getLayoutView(int index) {        return layoutPool.getView(index);    }     /**     * Returns the next visual position for the cursor, in     * either the east or west direction.     * Overridden from <code>CompositeView</code>.     * @param pos position into the model     * @param b either <code>Position.Bias.Forward</code> or     *          <code>Position.Bias.Backward</code>     * @param a the allocated region to render into     * @param direction either <code>SwingConstants.NORTH</code>     *          or <code>SwingConstants.SOUTH</code>     * @param biasRet an array containing the bias that were checked     *  in this method     * @return the location in the model that represents the     *  next location visual position     */    protected int getNextNorthSouthVisualPositionFrom(int pos, Position.Bias b,                                                      Shape a, int direction,                                                      Position.Bias[] biasRet)                                                throws BadLocationException {        int vIndex;        if(pos == -1) {            vIndex = (direction == NORTH) ?                     getViewCount() - 1 : 0;        }        else {            if(b == Position.Bias.Backward && pos > 0) {                vIndex = getViewIndexAtPosition(pos - 1);            }            else {                vIndex = getViewIndexAtPosition(pos);            }            if(direction == NORTH) {                if(vIndex == 0) {                    return -1;                }                vIndex--;            }            else if(++vIndex >= getViewCount()) {                return -1;            }        }        // vIndex gives index of row to look in.        JTextComponent text = (JTextComponent)getContainer();        Caret c = text.getCaret();        Point magicPoint;        magicPoint = (c != null) ? c.getMagicCaretPosition() : null;        int x;        if(magicPoint == null) {            Shape posBounds;            try {                posBounds = text.getUI().modelToView(text, pos, b);            } catch (BadLocationException exc) {                posBounds = null;            }            if(posBounds == null) {                x = 0;            }            else {                x = posBounds.getBounds().x;            }        }        else {            x = magicPoint.x;        }        return getClosestPositionTo(pos, b, a, direction, biasRet, vIndex, x);    }     /**     * Returns the closest model position to <code>x</code>.     * <code>rowIndex</code> gives the index of the view that corresponds     * that should be looked in.     * @param pos  position into the model     * @param a the allocated region to render into     * @param direction one of the following values:     * <ul>     * <li><code>SwingConstants.NORTH</code>     * <li><code>SwingConstants.SOUTH</code>     * </ul>     * @param biasRet an array containing the bias that were checked     *  in this method     * @param rowIndex the index of the view     * @param x the x coordinate of interest     * @return the closest model position to <code>x</code>     */    // NOTE: This will not properly work if ParagraphView contains    // other ParagraphViews. It won't raise, but this does not message    // the children views with getNextVisualPositionFrom.    protected int getClosestPositionTo(int pos, Position.Bias b, Shape a,                                       int direction, Position.Bias[] biasRet,                                       int rowIndex, int x)              throws BadLocationException {        JTextComponent text = (JTextComponent)getContainer();        Document doc = getDocument();        AbstractDocument aDoc = (doc instanceof AbstractDocument) ?                                (AbstractDocument)doc : null;        View row = getView(rowIndex);        int lastPos = -1;        // This could be made better to check backward positions too.        biasRet[0] = Position.Bias.Forward;        for(int vc = 0, numViews = row.getViewCount(); vc < numViews; vc++) {            View v = row.getView(vc);            int start = v.getStartOffset();            boolean ltr = (aDoc != null) ? aDoc.isLeftToRight                           (start, start + 1) : true;            if(ltr) {                lastPos = start;                for(int end = v.getEndOffset(); lastPos < end; lastPos++) {                    float xx = text.modelToView(lastPos).getBounds().x;                    if(xx >= x) {                        while (++lastPos < end &&                               text.modelToView(lastPos).getBounds().x == xx) {                        }                        return --lastPos;                    }                }                lastPos--;            }            else {                for(lastPos = v.getEndOffset() - 1; lastPos >= start;                    lastPos--) {                    float xx = text.modelToView(lastPos).getBounds().x;                    if(xx >= x) {                        while (--lastPos >= start &&                               text.modelToView(lastPos).getBounds().x == xx) {                        }                        return ++lastPos;                    }                }                lastPos++;            }        }        if(lastPos == -1) {            return getStartOffset();        }        return lastPos;    }     /**     * Determines in which direction the next view lays.     * Consider the <code>View</code> at index n.     * Typically the <code>View</code>s are layed out     * from left to right, so that the <code>View</code>     * to the EAST will be at index n + 1, and the     * <code>View</code> to the WEST will be at index n - 1.     * In certain situations, such as with bidirectional text,     * it is possible that the <code>View</code> to EAST is not     * at index n + 1, but rather at index n - 1,     * or that the <code>View</code> to the WEST is not at     * index n - 1, but index n + 1.  In this case this method     * would return true, indicating the <code>View</code>s are     * layed out in descending order.     * <p>     * This will return true if the text is layed out right     * to left at position, otherwise false.     *     * @param position position into the model     * @param bias either <code>Position.Bias.Forward</code> or     *          <code>Position.Bias.Backward</code>     * @return true if the text is layed out right to left at     *         position, otherwise false.     */    protected boolean flipEastAndWestAtEnds(int position,                                            Position.Bias bias) {        Document doc = getDocument();        if(doc instanceof AbstractDocument &&           !((AbstractDocument)doc).isLeftToRight(getStartOffset(),                                                  getStartOffset() + 1)) {            return true;        }        return false;    }     // --- FlowView methods ---------------------------------------------     /**     * Fetches the constraining span to flow against for     * the given child index.     * @param index the index of the view being queried     * @return the constraining span for the given view at     *  <code>index</code>     * @since 1.3     */    public int getFlowSpan(int index) {        View child = getView(index);        int adjust = 0;        if (child instanceof Row) {            Row row = (Row) child;            adjust = row.getLeftInset() + row.getRightInset();        }        return (layoutSpan == Integer.MAX_VALUE) ? layoutSpan                                                 : (layoutSpan - adjust);    }     /**     * Fetches the location along the flow axis that the     * flow span will start at.     * @param index the index of the view being queried     * @return the location for the given view at     *  <code>index</code>     * @since 1.3     */    public int getFlowStart(int index) {        View child = getView(index);        int adjust = 0;        if (child instanceof Row) {            Row row = (Row) child;            adjust = row.getLeftInset();        }        return tabBase + adjust;    }     /**     * Create a <code>View</code> that should be used to hold a     * a row's worth of children in a flow.     * @return the new <code>View</code>     * @since 1.3     */    protected View createRow() {        return new Row(getElement());    }     // --- TabExpander methods ------------------------------------------     /**     * Returns the next tab stop position given a reference position.     * This view implements the tab coordinate system, and calls     * <code>getTabbedSpan</code> on the logical children in the process     * of layout to determine the desired span of the children.  The     * logical children can delegate their tab expansion upward to     * the paragraph which knows how to expand tabs.     * <code>LabelView</code> is an example of a view that delegates     * its tab expansion needs upward to the paragraph.     * <p>     * This is implemented to try and locate a <code>TabSet</code>     * in the paragraph element's attribute set.  If one can be     * found, its settings will be used, otherwise a default expansion     * will be provided.  The base location for for tab expansion     * is the left inset from the paragraphs most recent allocation     * (which is what the layout of the children is based upon).     *     * @param x the X reference position     * @param tabOffset the position within the text stream     *   that the tab occurred at >= 0     * @return the trailing end of the tab expansion >= 0     * @see TabSet     * @see TabStop     * @see LabelView     */    public float nextTabStop(float x, int tabOffset) {        // If the text isn't left justified, offset by 10 pixels!        if(justification != StyleConstants.ALIGN_LEFT)            return x + 10.0f;        x -= tabBase;        TabSet tabs = getTabSet();        if(tabs == null) {            // a tab every 72 pixels.            return (float)(tabBase + (((int)x / 72 + 1) * 72));        }        TabStop tab = tabs.getTabAfter(x + .01f);        if(tab == null) {            // no tab, do a default of 5 pixels.            // Should this cause a wrapping of the line?            return tabBase + x + 5.0f;        }        int alignment = tab.getAlignment();        int offset;        switch(alignment) {        default:        case TabStop.ALIGN_LEFT:            // Simple case, left tab.            return tabBase + tab.getPosition();        case TabStop.ALIGN_BAR:            // PENDING: what does this mean?            return tabBase + tab.getPosition();        case TabStop.ALIGN_RIGHT:        case TabStop.ALIGN_CENTER:            offset = findOffsetToCharactersInString(tabChars,                                                    tabOffset + 1);            break;        case TabStop.ALIGN_DECIMAL:            offset = findOffsetToCharactersInString(tabDecimalChars,                                                    tabOffset + 1);            break;        }        if (offset == -1) {            offset = getEndOffset();        }        float charsSize = getPartialSize(tabOffset + 1, offset);        switch(alignment) {        case TabStop.ALIGN_RIGHT:        case TabStop.ALIGN_DECIMAL:            // right and decimal are treated the same way, the new            // position will be the location of the tab less the            // partialSize.            return tabBase + Math.max(x, tab.getPosition() - charsSize);        case TabStop.ALIGN_CENTER:            // Similar to right, but half the partialSize.            return tabBase + Math.max(x, tab.getPosition() - charsSize / 2.0f);        }        // will never get here!        return x;    }     /**     * Gets the <code>Tabset</code> to be used in calculating tabs.     *     * @return the <code>TabSet</code>     */    protected TabSet getTabSet() {        return StyleConstants.getTabSet(getElement().getAttributes());    }     /**     * Returns the size used by the views between     * <code>startOffset</code> and <code>endOffset</code>.     * This uses <code>getPartialView</code> to calculate the     * size if the child view implements the     * <code>TabableView</code> interface. If a     * size is needed and a <code>View</code> does not implement     * the <code>TabableView</code> interface,     * the <code>preferredSpan</code> will be used.     *     * @param startOffset the starting document offset >= 0     * @param endOffset the ending document offset >= startOffset

Lines 1–500 of 1201.