|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
package javax.swing.text; |
|
|
|
import java.text.*; |
|
import java.awt.*; |
|
import java.awt.font.*; |
|
import java.awt.geom.Rectangle2D; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
class GlyphPainter2 extends GlyphView.GlyphPainter { |
|
|
|
public GlyphPainter2(TextLayout layout) { |
|
this.layout = layout; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public GlyphView.GlyphPainter getPainter(GlyphView v, int p0, int p1) { |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public float getSpan(GlyphView v, int p0, int p1, |
|
TabExpander e, float x) { |
|
|
|
if ((p0 == v.getStartOffset()) && (p1 == v.getEndOffset())) { |
|
return layout.getAdvance(); |
|
} |
|
int p = v.getStartOffset(); |
|
int index0 = p0 - p; |
|
int index1 = p1 - p; |
|
|
|
TextHitInfo hit0 = TextHitInfo.afterOffset(index0); |
|
TextHitInfo hit1 = TextHitInfo.beforeOffset(index1); |
|
float[] locs = layout.getCaretInfo(hit0); |
|
float x0 = locs[0]; |
|
locs = layout.getCaretInfo(hit1); |
|
float x1 = locs[0]; |
|
return (x1 > x0) ? x1 - x0 : x0 - x1; |
|
} |
|
|
|
public float getHeight(GlyphView v) { |
|
return layout.getAscent() + layout.getDescent() + layout.getLeading(); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public float getAscent(GlyphView v) { |
|
return layout.getAscent(); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public float getDescent(GlyphView v) { |
|
return layout.getDescent(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void paint(GlyphView v, Graphics g, Shape a, int p0, int p1) { |
|
if (g instanceof Graphics2D) { |
|
Rectangle2D alloc = a.getBounds2D(); |
|
Graphics2D g2d = (Graphics2D)g; |
|
float y = (float) alloc.getY() + layout.getAscent() + layout.getLeading(); |
|
float x = (float) alloc.getX(); |
|
if( p0 > v.getStartOffset() || p1 < v.getEndOffset() ) { |
|
try { |
|
//TextLayout can't render only part of it's range, so if a |
|
|
|
Shape s = v.modelToView(p0, Position.Bias.Forward, |
|
p1, Position.Bias.Backward, a); |
|
Shape savedClip = g.getClip(); |
|
g2d.clip(s); |
|
layout.draw(g2d, x, y); |
|
g.setClip(savedClip); |
|
} catch (BadLocationException e) {} |
|
} else { |
|
layout.draw(g2d, x, y); |
|
} |
|
} |
|
} |
|
|
|
public Shape modelToView(GlyphView v, int pos, Position.Bias bias, |
|
Shape a) throws BadLocationException { |
|
int offs = pos - v.getStartOffset(); |
|
Rectangle2D alloc = a.getBounds2D(); |
|
TextHitInfo hit = (bias == Position.Bias.Forward) ? |
|
TextHitInfo.afterOffset(offs) : TextHitInfo.beforeOffset(offs); |
|
float[] locs = layout.getCaretInfo(hit); |
|
|
|
// vertical at the baseline, should use slope and check if glyphs |
|
|
|
alloc.setRect(alloc.getX() + locs[0], alloc.getY(), 1, alloc.getHeight()); |
|
return alloc; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public int viewToModel(GlyphView v, float x, float y, Shape a, |
|
Position.Bias[] biasReturn) { |
|
|
|
Rectangle2D alloc = (a instanceof Rectangle2D) ? (Rectangle2D)a : a.getBounds2D(); |
|
//Move the y co-ord of the hit onto the baseline. This is because TextLayout supports |
|
|
|
TextHitInfo hit = layout.hitTestChar(x - (float)alloc.getX(), 0); |
|
int pos = hit.getInsertionIndex(); |
|
|
|
if (pos == v.getEndOffset()) { |
|
pos--; |
|
} |
|
|
|
biasReturn[0] = hit.isLeadingEdge() ? Position.Bias.Forward : Position.Bias.Backward; |
|
return pos + v.getStartOffset(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public int getBoundedPosition(GlyphView v, int p0, float x, float len) { |
|
if( len < 0 ) |
|
throw new IllegalArgumentException("Length must be >= 0."); |
|
// note: this only works because swing uses TextLayouts that are |
|
|
|
TextHitInfo hit; |
|
if (layout.isLeftToRight()) { |
|
hit = layout.hitTestChar(len, 0); |
|
} else { |
|
hit = layout.hitTestChar(layout.getAdvance() - len, 0); |
|
} |
|
return v.getStartOffset() + hit.getCharIndex(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public int getNextVisualPositionFrom(GlyphView v, int pos, |
|
Position.Bias b, Shape a, |
|
int direction, |
|
Position.Bias[] biasRet) |
|
throws BadLocationException { |
|
|
|
Document doc = v.getDocument(); |
|
int startOffset = v.getStartOffset(); |
|
int endOffset = v.getEndOffset(); |
|
Segment text; |
|
boolean viewIsLeftToRight; |
|
TextHitInfo currentHit, nextHit; |
|
|
|
switch (direction) { |
|
case View.NORTH: |
|
break; |
|
case View.SOUTH: |
|
break; |
|
case View.EAST: |
|
viewIsLeftToRight = AbstractDocument.isLeftToRight(doc, startOffset, endOffset); |
|
|
|
if(startOffset == doc.getLength()) { |
|
if(pos == -1) { |
|
biasRet[0] = Position.Bias.Forward; |
|
return startOffset; |
|
} |
|
// End case for bidi text where newline is at beginning |
|
|
|
return -1; |
|
} |
|
if(pos == -1) { |
|
|
|
if( viewIsLeftToRight ) { |
|
biasRet[0] = Position.Bias.Forward; |
|
return startOffset; |
|
} else { |
|
text = v.getText(endOffset - 1, endOffset); |
|
char c = text.array[text.offset]; |
|
SegmentCache.releaseSharedSegment(text); |
|
if(c == '\n') { |
|
biasRet[0] = Position.Bias.Forward; |
|
return endOffset-1; |
|
} |
|
biasRet[0] = Position.Bias.Backward; |
|
return endOffset; |
|
} |
|
} |
|
if( b==Position.Bias.Forward ) |
|
currentHit = TextHitInfo.afterOffset(pos-startOffset); |
|
else |
|
currentHit = TextHitInfo.beforeOffset(pos-startOffset); |
|
nextHit = layout.getNextRightHit(currentHit); |
|
if( nextHit == null ) { |
|
return -1; |
|
} |
|
if( viewIsLeftToRight != layout.isLeftToRight() ) { |
|
// If the layout's base direction is different from |
|
// this view's run direction, we need to use the weak |
|
|
|
nextHit = layout.getVisualOtherHit(nextHit); |
|
} |
|
pos = nextHit.getInsertionIndex() + startOffset; |
|
|
|
if(pos == endOffset) { |
|
// A move to the right from an internal position will |
|
|
|
text = v.getText(endOffset - 1, endOffset); |
|
char c = text.array[text.offset]; |
|
SegmentCache.releaseSharedSegment(text); |
|
if(c == '\n') { |
|
return -1; |
|
} |
|
biasRet[0] = Position.Bias.Backward; |
|
} |
|
else { |
|
biasRet[0] = Position.Bias.Forward; |
|
} |
|
return pos; |
|
case View.WEST: |
|
viewIsLeftToRight = AbstractDocument.isLeftToRight(doc, startOffset, endOffset); |
|
|
|
if(startOffset == doc.getLength()) { |
|
if(pos == -1) { |
|
biasRet[0] = Position.Bias.Forward; |
|
return startOffset; |
|
} |
|
// End case for bidi text where newline is at beginning |
|
|
|
return -1; |
|
} |
|
if(pos == -1) { |
|
|
|
if( viewIsLeftToRight ) { |
|
text = v.getText(endOffset - 1, endOffset); |
|
char c = text.array[text.offset]; |
|
SegmentCache.releaseSharedSegment(text); |
|
if ((c == '\n') || Character.isSpaceChar(c)) { |
|
biasRet[0] = Position.Bias.Forward; |
|
return endOffset - 1; |
|
} |
|
biasRet[0] = Position.Bias.Backward; |
|
return endOffset; |
|
} else { |
|
biasRet[0] = Position.Bias.Forward; |
|
return startOffset; |
|
} |
|
} |
|
if( b==Position.Bias.Forward ) |
|
currentHit = TextHitInfo.afterOffset(pos-startOffset); |
|
else |
|
currentHit = TextHitInfo.beforeOffset(pos-startOffset); |
|
nextHit = layout.getNextLeftHit(currentHit); |
|
if( nextHit == null ) { |
|
return -1; |
|
} |
|
if( viewIsLeftToRight != layout.isLeftToRight() ) { |
|
// If the layout's base direction is different from |
|
// this view's run direction, we need to use the weak |
|
|
|
nextHit = layout.getVisualOtherHit(nextHit); |
|
} |
|
pos = nextHit.getInsertionIndex() + startOffset; |
|
|
|
if(pos == endOffset) { |
|
// A move to the left from an internal position will |
|
|
|
text = v.getText(endOffset - 1, endOffset); |
|
char c = text.array[text.offset]; |
|
SegmentCache.releaseSharedSegment(text); |
|
if(c == '\n') { |
|
return -1; |
|
} |
|
biasRet[0] = Position.Bias.Backward; |
|
} |
|
else { |
|
biasRet[0] = Position.Bias.Forward; |
|
} |
|
return pos; |
|
default: |
|
throw new IllegalArgumentException("Bad direction: " + direction); |
|
} |
|
return pos; |
|
|
|
} |
|
// --- variables --------------------------------------------- |
|
|
|
TextLayout layout; |
|
|
|
} |