FileDocCategorySizeDatePackage
TextLayoutStrategy.javaAPI DocJava SE 5 API17158Fri Aug 26 14:58:16 BST 2005javax.swing.text

TextLayoutStrategy

public class TextLayoutStrategy extends FlowView$FlowStrategy
A flow strategy that uses java.awt.font.LineBreakMeasureer to produce java.awt.font.TextLayout for i18n capable rendering. If the child view being placed into the flow is of type GlyphView and can be rendered by TextLayout, a GlyphPainter that uses TextLayout is plugged into the GlyphView.
author
Timothy Prinzing
version
1.22 05/05/04

Fields Summary
private LineBreakMeasurer
measurer
private AttributedSegment
text
Constructors Summary
public TextLayoutStrategy()
Constructs a layout strategy for paragraphs based upon java.awt.font.LineBreakMeasurer.

	text = new AttributedSegment();
    
Methods Summary
protected voidadjustRow(javax.swing.text.FlowView fv, int rowIndex, int desiredSpan, int x)
Adjusts the given row if possible to fit within the layout span. Since all adjustments were already calculated by the LineBreakMeasurer, this is implemented to do nothing.

param
r the row to adjust to the current layout span.
param
desiredSpan the current layout span >= 0
param
x the location r starts at.

    
public voidchangedUpdate(javax.swing.text.FlowView fv, javax.swing.event.DocumentEvent e, java.awt.Rectangle alloc)
Gives notification from the document that attributes were changed in a location that this view is responsible for.

param
changes the change information from the associated document
param
a the current allocation of the view
param
f the factory to use to rebuild if the view has children
see
View#changedUpdate

        sync(fv);
        super.changedUpdate(fv, e, alloc);
    
protected javax.swing.text.ViewcreateView(javax.swing.text.FlowView fv, int startOffset, int spanLeft, int rowIndex)
Creates a unidirectional view that can be used to represent the current chunk. This can be either an entire view from the logical view, or a fragment of the view.

param
fv the view holding the flow
param
startOffset the start location for the view being created
param
spanLeft the about of span left to fill in the row
param
rowIndex the row the view will be placed into

	// Get the child view that contains the given starting position
	View lv = getLogicalView(fv);
	View row = fv.getView(rowIndex);
	boolean requireNextWord = (row.getViewCount() == 0) ? false : true;
	int childIndex = lv.getViewIndex(startOffset, Position.Bias.Forward);
	View v = lv.getView(childIndex);

	int endOffset = getLimitingOffset(v, startOffset, spanLeft, requireNextWord);
	if (endOffset == startOffset) {
	    return null;
	}

	View frag;
	if ((startOffset==v.getStartOffset()) && (endOffset == v.getEndOffset())) {
	    // return the entire view
	    frag = v;
	} else {
	    // return a unidirectional fragment.
	    frag = v.createFragment(startOffset, endOffset);
	}

	if ((frag instanceof GlyphView) && (measurer != null)) {
	    // install a TextLayout based renderer if the view is responsible
	    // for glyphs.  If the view represents a tab, the default 
	    // glyph painter is used (may want to handle tabs differently).
	    boolean isTab = false;
	    int p0 = frag.getStartOffset();
	    int p1 = frag.getEndOffset();
	    if ((p1 - p0) == 1) {
		// check for tab
		Segment s = ((GlyphView)frag).getText(p0, p1);
		char ch = s.first();
		if (ch == '\t") {
		    isTab = true;
		}
	    }
	    TextLayout tl = (isTab) ? null : 
		measurer.nextLayout(spanLeft, text.toIteratorIndex(endOffset), 
				    requireNextWord);
	    if (tl != null) {
		((GlyphView)frag).setGlyphPainter(new GlyphPainter2(tl));
	    }
	}
	return frag;
    
intgetLimitingOffset(javax.swing.text.View v, int startOffset, int spanLeft, boolean requireNextWord)
Calculate the limiting offset for the next view fragment. At most this would be the entire view (i.e. the limiting offset would be the end offset in that case). If the range contains a tab or a direction change, that will limit the offset to something less. This value is then fed to the LineBreakMeasurer as a limit to consider in addition to the remaining span.

param
v the logical view representing the starting offset.
param
startOffset the model location to start at.

	int endOffset = v.getEndOffset();

	// check for direction change
	Document doc = v.getDocument();
	if (doc instanceof AbstractDocument) {
	    AbstractDocument d = (AbstractDocument) doc;
            Element bidiRoot = d.getBidiRootElement();
            if( bidiRoot.getElementCount() > 1 ) {
                int bidiIndex = bidiRoot.getElementIndex( startOffset );
                Element bidiElem = bidiRoot.getElement( bidiIndex );
                endOffset = Math.min( bidiElem.getEndOffset(), endOffset );
            }
        }

	// check for tab
	if (v instanceof GlyphView) {
	    Segment s = ((GlyphView)v).getText(startOffset, endOffset);
	    char ch = s.first();
	    if (ch == '\t") {
		// if the first character is a tab, create a dedicated 
		// view for just the tab
		endOffset = startOffset + 1;
	    } else {
		for (ch = s.next(); ch != Segment.DONE; ch = s.next()) {
		    if (ch == '\t") {
			// found a tab, don't include it in the text
			endOffset = startOffset + s.getIndex() - s.getBeginIndex();
			break;
		    }
		}
	    }
	}

	// determine limit from LineBreakMeasurer
	int limitIndex = text.toIteratorIndex(endOffset);
	if (measurer != null) {
	    int index = text.toIteratorIndex(startOffset);
	    if (measurer.getPosition() != index) {
		measurer.setPosition(index);
	    }
	    limitIndex = measurer.nextOffset(spanLeft, limitIndex, requireNextWord);
	}
	int pos = text.toModelPosition(limitIndex);
	return pos;
    
public voidinsertUpdate(javax.swing.text.FlowView fv, javax.swing.event.DocumentEvent e, java.awt.Rectangle alloc)
Gives notification that something was inserted into the document in a location that the given flow view is responsible for. The strategy should update the appropriate changed region (which depends upon the strategy used for repair).

param
e the change information from the associated document
param
alloc the current allocation of the view inside of the insets. This value will be null if the view has not yet been displayed.
see
View#insertUpdate

        sync(fv);
        super.insertUpdate(fv, e, alloc);
    
public voidlayout(javax.swing.text.FlowView fv)
Does a a full layout on the given View. This causes all of the rows (child views) to be rebuilt to match the given constraints for each row. This is called by a FlowView.layout to update the child views in the flow.

param
v the view to reflow

        super.layout(fv);
    
protected intlayoutRow(javax.swing.text.FlowView fv, int rowIndex, int p0)
Creates a row of views that will fit within the layout span of the row. This is implemented to execute the superclass functionality (which fills the row with child views or view fragments) and follow that with bidi reordering of the unidirectional view fragments.

param
row the row to fill in with views. This is assumed to be empty on entry.
param
pos The current position in the children of this views element from which to start.
return
the position to start the next row

	int p1 = super.layoutRow(fv, rowIndex, p0);
	View row = fv.getView(rowIndex);
	Document doc = fv.getDocument();
	Object i18nFlag = doc.getProperty(AbstractDocument.I18NProperty);
	if ((i18nFlag != null) && i18nFlag.equals(Boolean.TRUE)) {
	    int n = row.getViewCount();
	    if (n > 1) {
		AbstractDocument d = (AbstractDocument)fv.getDocument();
		Element bidiRoot = d.getBidiRootElement();
		byte[] levels = new byte[n];
		View[] reorder = new View[n];
		
		for( int i=0; i<n; i++ ) {
		    View v = row.getView(i);
		    int bidiIndex =bidiRoot.getElementIndex(v.getStartOffset());
		    Element bidiElem = bidiRoot.getElement( bidiIndex );
		    levels[i] = (byte)StyleConstants.getBidiLevel(bidiElem.getAttributes());
		    reorder[i] = v;
		}
		
		BidiUtils.reorderVisually( levels, reorder );
		row.replace(0, n, reorder);
	    }
	}
	return p1;
    
public voidremoveUpdate(javax.swing.text.FlowView fv, javax.swing.event.DocumentEvent e, java.awt.Rectangle alloc)
Gives notification that something was removed from the document in a location that the given flow view is responsible for.

param
e the change information from the associated document
param
alloc the current allocation of the view inside of the insets.
see
View#removeUpdate

        sync(fv);
        super.removeUpdate(fv, e, alloc);
    
voidsync(javax.swing.text.FlowView fv)
Synchronize the strategy with its FlowView. Allows the strategy to update its state to account for changes in that portion of the model represented by the FlowView. Also allows the strategy to update the FlowView in response to these changes.

        View lv = getLogicalView(fv);
        text.setView(lv);

        Container container = fv.getContainer();
        FontRenderContext frc = com.sun.java.swing.SwingUtilities2.
                                    getFontRenderContext(container);
        BreakIterator iter;
        Container c = fv.getContainer();
        if (c != null) {
            iter = BreakIterator.getLineInstance(c.getLocale());
        } else {
            iter = BreakIterator.getLineInstance();
        }

        measurer = new LineBreakMeasurer(text, iter, frc);

        // If the children of the FlowView's logical view are GlyphViews, they 
        // need to have their painters updated.
        int n = lv.getViewCount();
        for( int i=0; i<n; i++ ) {
            View child = lv.getView(i);
            if( child instanceof GlyphView ) {
                int p0 = child.getStartOffset();
                int p1 = child.getEndOffset();
                measurer.setPosition(text.toIteratorIndex(p0));
                TextLayout layout 
                    = measurer.nextLayout( Float.MAX_VALUE,
                                           text.toIteratorIndex(p1), false );
                ((GlyphView)child).setGlyphPainter(new GlyphPainter2(layout));
            }
        }

        // Reset measurer.
        measurer.setPosition(text.getBeginIndex());