TextLayoutStrategypublic 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. |
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 void | adjustRow(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.
| public void | changedUpdate(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.
sync(fv);
super.changedUpdate(fv, e, alloc);
| protected javax.swing.text.View | createView(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.
// Get the child view that contains the given starting position
View lv = getLogicalView(fv);
View row = fv.getView(rowIndex);
boolean requireNextWord = (viewBuffer.size() == 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;
| int | getLimitingOffset(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.
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 void | insertUpdate(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).
sync(fv);
super.insertUpdate(fv, e, alloc);
| public void | layout(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.
super.layout(fv);
| protected int | layoutRow(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.
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 void | removeUpdate(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.
sync(fv);
super.removeUpdate(fv, e, alloc);
| void | sync(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 = sun.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());
|
|