Methods Summary |
---|
public java.lang.Object | clone()
try {
TextRunBreaker res = (TextRunBreaker) super.clone();
res.storedSegments = null;
ArrayList<TextRunSegment> newSegments = new ArrayList<TextRunSegment>(runSegments.size());
for (int i = 0; i < runSegments.size(); i++) {
TextRunSegment seg = runSegments.get(i);
newSegments.add((TextRunSegment)seg.clone());
}
res.runSegments = newSegments;
return res;
} catch (CloneNotSupportedException e) {
// awt.3E=Clone not supported
throw new UnsupportedOperationException(Messages.getString("awt.3E")); //$NON-NLS-1$
}
|
public void | createAllSegments()Checks if text run segments are up to date and creates the new segments if not.
if ( !haveAllSegments &&
(logical2segment == null ||
logical2segment.length != segmentsEnd - segmentsStart)
) { // Check if we don't have all segments yet
resetSegments();
createSegments(segmentsStart, segmentsEnd);
}
haveAllSegments = true;
|
public void | createSegments(int runStart, int runEnd)Creates segments for the text run with
constant decoration, font and bidi level
int endStyleRun, endLevelRun;
// TODO - update levels
int pos = runStart, levelPos;
aci.setIndex(pos);
final int firstRunStart = aci.getRunStart();
Object tdd = decorations.get(new Integer(firstRunStart));
Object fontOrGAttr = fonts.get(new Integer(firstRunStart));
logical2segment = new int[runEnd - runStart];
do {
endStyleRun = getStyleRunLimit(pos, runEnd);
// runStart can be non-zero, but all arrays will be indexed from 0
int ajustedPos = pos - runStart;
int ajustedEndStyleRun = endStyleRun - runStart;
levelPos = ajustedPos;
do {
endLevelRun = getLevelRunLimit(levelPos, ajustedEndStyleRun);
if (fontOrGAttr instanceof GraphicAttribute) {
runSegments.add(
new TextRunSegmentImpl.TextRunSegmentGraphic(
(GraphicAttribute)fontOrGAttr,
endLevelRun - levelPos,
levelPos + runStart)
);
Arrays.fill(logical2segment, levelPos, endLevelRun, runSegments.size()-1);
} else {
TextRunSegmentImpl.TextSegmentInfo i =
new TextRunSegmentImpl.TextSegmentInfo(
levels == null ? 0 : levels[ajustedPos],
(Font) fontOrGAttr,
frc,
text,
levelPos + runStart,
endLevelRun + runStart
);
runSegments.add(
new TextRunSegmentImpl.TextRunSegmentCommon(
i,
(TextDecorator.Decoration) tdd
)
);
Arrays.fill(logical2segment, levelPos, endLevelRun, runSegments.size()-1);
}
levelPos = endLevelRun;
} while (levelPos < ajustedEndStyleRun);
// Prepare next iteration
pos = endStyleRun;
tdd = decorations.get(new Integer(pos));
fontOrGAttr = fonts.get(new Integer(pos));
} while (pos < runEnd);
|
void | createStyleRuns()Breaks the text into separate style runs.
// TODO - implement fast and simple case
fonts = new HashMap<Integer, Font>();
decorations = new HashMap<Integer, Decoration>();
////
ArrayList<Integer> forcedFontRunStartsList = null;
Map<? extends Attribute, ?> attributes = null;
// Check justification attribute
Object val = aci.getAttribute(TextAttribute.JUSTIFICATION);
if (val != null) {
justification = ((Float) val).floatValue();
}
for (
int index = segmentsStart, nextRunStart = segmentsStart;
index < segmentsEnd;
index = nextRunStart, aci.setIndex(index)
) {
nextRunStart = aci.getRunLimit();
attributes = unpackAttributes(aci.getAttributes());
TextDecorator.Decoration d = TextDecorator.getDecoration(attributes);
decorations.put(new Integer(index), d);
// Find appropriate font or place GraphicAttribute there
// 1. Try to pick up CHAR_REPLACEMENT (compatibility)
Font value = (Font)attributes.get(TextAttribute.CHAR_REPLACEMENT);
if (value == null) {
// 2. Try to Get FONT
value = (Font)attributes.get(TextAttribute.FONT);
if (value == null) {
// 3. Try to create font from FAMILY
if (attributes.get(TextAttribute.FAMILY) != null) {
value = Font.getFont(attributes);
}
if (value == null) {
// 4. No attributes found, using default.
if (forcedFontRunStartsList == null) {
forcedFontRunStartsList = new ArrayList<Integer>();
}
FontFinder.findFonts(
text,
index,
nextRunStart,
forcedFontRunStartsList,
fonts
);
value = fonts.get(new Integer(index));
}
}
}
fonts.put(new Integer(index), value);
}
// We have added some default fonts, so we have some extra runs in text
if (forcedFontRunStartsList != null) {
forcedFontRunStarts = new int[forcedFontRunStartsList.size()];
for (int i=0; i<forcedFontRunStartsList.size(); i++) {
forcedFontRunStarts[i] =
forcedFontRunStartsList.get(i).intValue();
}
}
|
public void | deleteChar(java.text.AttributedCharacterIterator newParagraph, int deletePos)Deletes character from the managed text.
aci = newParagraph;
Integer key = new Integer(deletePos);
deletePos -= aci.getBeginIndex();
char newText[] = new char[text.length - 1];
System.arraycopy(text, 0, newText, 0, deletePos);
System.arraycopy(text, deletePos+1, newText, deletePos, newText.length - deletePos);
text = newText;
if (fonts.get(key) != null) {
fonts.remove(key);
}
shiftStyleRuns(key, -1);
resetSegments();
segmentsEnd--;
|
public void | drawSegments(java.awt.Graphics2D g2d, float xOffset, float yOffset)Renders the managed text
for (int i=0; i<runSegments.size(); i++) {
runSegments.get(i).draw(g2d, xOffset, yOffset);
}
|
public boolean | equals(java.lang.Object obj)
if (!(obj instanceof TextRunBreaker)) {
return false;
}
TextRunBreaker br = (TextRunBreaker) obj;
if (br.getACI().equals(aci) && br.frc.equals(frc)) {
return true;
}
return false;
|
public java.text.AttributedCharacterIterator | getACI()
return aci;
|
public int | getBaseLevel()
return 0;
|
public java.awt.Shape | getBlackBoxBounds(int firstEndpoint, int secondEndpoint)Creates the black box bounds shape
GeneralPath bounds = new GeneralPath();
TextRunSegment segment;
for (int idx = firstEndpoint; idx < secondEndpoint; idx=segment.getEnd()) {
segment = runSegments.get(logical2segment[idx]);
bounds.append(segment.getCharsBlackBoxBounds(idx, secondEndpoint), false);
}
return bounds;
|
public char | getChar(int index)
return text[index];
|
public int | getCharCount()
return segmentsEnd - segmentsStart;
|
public float | getJustification()
return justification;
|
public int | getLastNonWhitespace()Calculates position of the last non whitespace character
in the managed text.
int lastNonWhitespace = text.length;
while (lastNonWhitespace >= 0) {
lastNonWhitespace--;
if (!Character.isWhitespace(text[lastNonWhitespace])) {
break;
}
}
return lastNonWhitespace;
|
public byte | getLevel(int idx)
if (levels == null) {
return 0;
}
return levels[idx];
|
int | getLevelRunLimit(int runStart, int runEnd)Calculates the end index of the level run, limited by the given text run.
if (levels == null) {
return runEnd;
}
int endLevelRun = runStart + 1;
byte level = levels[runStart];
while (endLevelRun <= runEnd && levels[endLevelRun] == level) {
endLevelRun++;
}
return endLevelRun;
|
public int | getLineBreakIndex(int start, float maxAdvance)Calculates position where line should be broken without
taking into account word boundaries.
int breakIndex;
TextRunSegment s = null;
for (
int segmentIndex = logical2segment[start];
segmentIndex < runSegments.size();
segmentIndex++
) {
s = runSegments.get(segmentIndex);
breakIndex = s.getCharIndexFromAdvance(maxAdvance, start);
if (breakIndex < s.getEnd()) {
return breakIndex;
}
maxAdvance -= s.getAdvanceDelta(start, s.getEnd());
start = s.getEnd();
}
return s.getEnd();
|
public java.awt.geom.Rectangle2D | getLogicalBounds()Creates logical bounds shape
Rectangle2D bounds = null;
for (int i=0; i<runSegments.size(); i++) {
TextRunSegment s = runSegments.get(i);
if (bounds != null) {
Rectangle2D.union(bounds, s.getLogicalBounds(), bounds);
} else {
bounds = s.getLogicalBounds();
}
}
return bounds;
|
int | getLogicalFromVisual(int visual)Visual order of the characters may differ from the logical order.
This method calculates logical position of the character from its visual position.
return (visual2logical == null) ? visual : visual2logical[visual];
|
public java.awt.geom.GeneralPath | getOutline()Creates outline shape for the managed text
GeneralPath outline = new GeneralPath();
TextRunSegment segment;
for (int i = 0; i < runSegments.size(); i++) {
segment = runSegments.get(i);
outline.append(segment.getOutline(), false);
}
return outline;
|
int | getSegmentFromVisualOrder(int visual)Visual order of text segments may differ from the logical order.
This method calculates logical position of the segment from its visual position.
return (visual2segment == null) ? visual : visual2segment[visual];
|
int | getStyleRunLimit(int runStart, int maxPos)Starting from the current position looks for the end of the text run with
constant text attributes.
try {
aci.setIndex(runStart);
} catch(IllegalArgumentException e) { // Index out of bounds
if (runStart < segmentsStart) {
aci.first();
} else {
aci.last();
}
}
// If we have some extra runs we need to check for their limits
if (forcedFontRunStarts != null) {
for (int element : forcedFontRunStarts) {
if (element > runStart) {
maxPos = Math.min(element, maxPos);
break;
}
}
}
return Math.min(aci.getRunLimit(), maxPos);
|
public java.awt.geom.Rectangle2D | getVisualBounds()Creates visual bounds shape
Rectangle2D bounds = null;
for (int i=0; i<runSegments.size(); i++) {
TextRunSegment s = runSegments.get(i);
if (bounds != null) {
Rectangle2D.union(bounds, s.getVisualBounds(), bounds);
} else {
bounds = s.getVisualBounds();
}
}
return bounds;
|
int | getVisualFromLogical(int logical)Visual order of the characters may differ from the logical order.
This method calculates visual position of the character from its logical position.
return (logical2visual == null) ? logical : logical2visual[logical];
|
int | getVisualFromSegmentOrder(int segmentNum)Visual order of text segments may differ from the logical order.
This method calculates visual position of the segment from its logical position.
return (segment2visual == null) ? segmentNum : segment2visual[segmentNum];
|
public int | hashCode()
return HashCode.combine(aci.hashCode(), frc.hashCode());
|
public java.awt.font.TextHitInfo | hitTest(float x, float y)Calculates text hit info from the screen coordinates.
Current implementation totally ignores Y coordinate.
If X coordinate is outside of the layout boundaries, this
method returns leftmost or rightmost hit.
TextRunSegment segment;
double endOfPrevSeg = -1;
for (int i = 0; i < runSegments.size(); i++) {
segment = runSegments.get(i);
Rectangle2D bounds = segment.getVisualBounds();
if ((bounds.getMinX() <= x && bounds.getMaxX() >= x) || // We are in the segment
(endOfPrevSeg < x && bounds.getMinX() > x)) { // We are somewhere between the segments
return segment.hitTest(x,y);
}
endOfPrevSeg = bounds.getMaxX();
}
return isLTR() ? TextHitInfo.trailing(text.length) : TextHitInfo.leading(0);
|
public void | insertChar(java.text.AttributedCharacterIterator newParagraph, int insertPos)Inserts character into the managed text.
aci = newParagraph;
char insChar = aci.setIndex(insertPos);
Integer key = new Integer(insertPos);
insertPos -= aci.getBeginIndex();
char newText[] = new char[text.length + 1];
System.arraycopy(text, 0, newText, 0, insertPos);
newText[insertPos] = insChar;
System.arraycopy(text, insertPos, newText, insertPos+1, text.length - insertPos);
text = newText;
if (aci.getRunStart() == key.intValue() && aci.getRunLimit() == key.intValue() + 1) {
createStyleRuns(); // We have to create one new run, could be optimized
} else {
shiftStyleRuns(key, 1);
}
resetSegments();
segmentsEnd++;
|
public boolean | isLTR()
return true;
|
public void | justify(float gap)Performs justification of the managed text by changing segment positions
and positions of the glyphs inside of the segments.
// Ignore trailing logical whitespace
int firstIdx = segmentsStart;
int lastIdx = getLastNonWhitespace() + segmentsStart;
JustificationInfo jInfos[] = new JustificationInfo[5];
float gapLeft = gap;
int highestPriority = -1;
// GlyphJustificationInfo.PRIORITY_KASHIDA is 0
// GlyphJustificationInfo.PRIORITY_NONE is 3
for (int priority = 0; priority <= GlyphJustificationInfo.PRIORITY_NONE + 1; priority++) {
JustificationInfo jInfo = new JustificationInfo();
jInfo.lastIdx = lastIdx;
jInfo.firstIdx = firstIdx;
jInfo.grow = gap > 0;
jInfo.gapToFill = gapLeft;
if (priority <= GlyphJustificationInfo.PRIORITY_NONE) {
jInfo.priority = priority;
} else {
jInfo.priority = highestPriority; // Last pass
}
for (int i = 0; i < runSegments.size(); i++) {
TextRunSegment segment = runSegments.get(i);
if (segment.getStart() <= lastIdx) {
segment.updateJustificationInfo(jInfo);
}
}
if (jInfo.priority == highestPriority) {
jInfo.absorb = true;
jInfo.absorbedWeight = jInfo.weight;
}
if (jInfo.weight != 0) {
if (highestPriority < 0) {
highestPriority = priority;
}
jInfos[priority] = jInfo;
} else {
continue;
}
gapLeft -= jInfo.growLimit;
if (((gapLeft > 0) ^ jInfo.grow) || gapLeft == 0) {
gapLeft = 0;
jInfo.gapPerUnit = jInfo.gapToFill/jInfo.weight;
break;
}
jInfo.useLimits = true;
if (jInfo.absorbedWeight > 0) {
jInfo.absorb = true;
jInfo.absorbedGapPerUnit =
(jInfo.gapToFill-jInfo.growLimit)/jInfo.absorbedWeight;
break;
}
}
float currJustificationOffset = 0;
for (int i = 0; i < runSegments.size(); i++) {
TextRunSegment segment =
runSegments.get(getSegmentFromVisualOrder(i));
segment.x += currJustificationOffset;
currJustificationOffset += segment.doJustification(jInfos);
}
justification = -1; // Make further justification impossible
|
public void | popSegments()Restores the internal state of the class
if (storedSegments == null) {
return;
}
this.runSegments = storedSegments.runSegments;
this.logical2segment = storedSegments.logical2segment;
this.segment2visual = storedSegments.segment2visual;
this.visual2segment = storedSegments.visual2segment;
this.levels = storedSegments.levels;
this.segmentsStart = storedSegments.segmentsStart;
this.segmentsEnd = storedSegments.segmentsEnd;
storedSegments = null;
if (runSegments.size() == 0 && logical2segment == null) {
haveAllSegments = false;
} else {
haveAllSegments = true;
}
|
public void | pushSegments(int newSegStart, int newSegEnd)Saves the internal state of the class
storedSegments = new SegmentsInfo();
storedSegments.runSegments = this.runSegments;
storedSegments.logical2segment = this.logical2segment;
storedSegments.segment2visual = this.segment2visual;
storedSegments.visual2segment = this.visual2segment;
storedSegments.levels = this.levels;
storedSegments.segmentsStart = segmentsStart;
storedSegments.segmentsEnd = segmentsEnd;
resetSegments();
segmentsStart = newSegStart;
segmentsEnd = newSegEnd;
|
private void | resetSegments()Resets state of the class
runSegments = new ArrayList<TextRunSegment>();
logical2segment = null;
segment2visual = null;
visual2segment = null;
levels = null;
haveAllSegments = false;
|
private void | shiftStyleRuns(java.lang.Integer pos, int shift)Shift all runs after specified position, needed to perfom insertion
or deletion in the managed text
ArrayList<Integer> keys = new ArrayList<Integer>();
Integer key, oldkey;
for (Iterator<Integer> it = fonts.keySet().iterator(); it.hasNext(); ) {
oldkey = it.next();
if (oldkey.intValue() > pos.intValue()) {
keys.add(oldkey);
}
}
for (int i=0; i<keys.size(); i++) {
oldkey = keys.get(i);
key = new Integer(shift + oldkey.intValue());
fonts.put(key, fonts.remove(oldkey));
decorations.put(key, decorations.remove(oldkey));
}
|
java.util.Map | unpackAttributes(java.util.Map attrs)Adds InputMethodHighlight to the attributes
if (attrs.containsKey(TextAttribute.INPUT_METHOD_HIGHLIGHT)) {
Map<TextAttribute, ?> styles = null;
Object val = attrs.get(TextAttribute.INPUT_METHOD_HIGHLIGHT);
if (val instanceof Annotation) {
val = ((Annotation) val).getValue();
}
if (val instanceof InputMethodHighlight) {
InputMethodHighlight ihl = ((InputMethodHighlight) val);
styles = ihl.getStyle();
if (styles == null) {
Toolkit tk = Toolkit.getDefaultToolkit();
styles = tk.mapInputMethodHighlight(ihl);
}
}
if (styles != null) {
HashMap<Attribute, Object> newAttrs = new HashMap<Attribute, Object>();
newAttrs.putAll(attrs);
newAttrs.putAll(styles);
return newAttrs;
}
}
return attrs;
|