Method from javax.swing.text.GlyphView Detail: |
public View breakView(int axis,
int p0,
float pos,
float len) {
if (axis == View.X_AXIS) {
checkPainter();
int p1 = painter.getBoundedPosition(this, p0, pos, len);
int breakSpot = getBreakSpot(p0, p1);
if (breakSpot != -1) {
p1 = breakSpot;
}
// else, no break in the region, return a fragment of the
// bounded region.
if (p0 == getStartOffset() && p1 == getEndOffset()) {
return this;
}
GlyphView v = (GlyphView) createFragment(p0, p1);
v.x = (int) pos;
return v;
}
return this;
}
Breaks this view on the given axis at the given length.
This is implemented to attempt to break on a whitespace
location, and returns a fragment with the whitespace at
the end. If a whitespace location can't be found, the
nearest character is used. |
public void changedUpdate(DocumentEvent e,
Shape a,
ViewFactory f) {
minimumSpan = -1;
syncCR();
preferenceChanged(null, true, true);
}
Gives notification from the document that attributes were changed
in a location that this view is responsible for.
This is implemented to call preferenceChanged along both the
horizontal and vertical axis. |
protected void checkPainter() {
if (painter == null) {
if (defaultPainter == null) {
// the classname should probably come from a property file.
String classname = "javax.swing.text.GlyphPainter1";
try {
Class c;
ClassLoader loader = getClass().getClassLoader();
if (loader != null) {
c = loader.loadClass(classname);
} else {
c = Class.forName(classname);
}
Object o = c.newInstance();
if (o instanceof GlyphPainter) {
defaultPainter = (GlyphPainter) o;
}
} catch (Throwable e) {
throw new StateInvariantError("GlyphView: Can't load glyph painter: "
+ classname);
}
}
setGlyphPainter(defaultPainter.getPainter(this, getStartOffset(),
getEndOffset()));
}
}
Check to see that a glyph painter exists. If a painter
doesn't exist, a default glyph painter will be installed. |
protected final Object clone() {
Object o;
try {
o = super.clone();
} catch (CloneNotSupportedException cnse) {
o = null;
}
return o;
}
Creates a shallow copy. This is used by the
createFragment and breakView methods. |
public View createFragment(int p0,
int p1) {
checkPainter();
Element elem = getElement();
GlyphView v = (GlyphView) clone();
v.offset = p0 - elem.getStartOffset();
v.length = p1 - p0;
v.painter = painter.getPainter(v, p0, p1);
v.justificationInfo = null;
return v;
}
Creates a view that represents a portion of the element.
This is potentially useful during formatting operations
for taking measurements of fragments of the view. If
the view doesn't support fragmenting (the default), it
should return itself.
This view does support fragmenting. It is implemented
to return a nested class that shares state in this view
representing only a portion of the view. |
public float getAlignment(int axis) {
checkPainter();
if (axis == View.Y_AXIS) {
boolean sup = isSuperscript();
boolean sub = isSubscript();
float h = painter.getHeight(this);
float d = painter.getDescent(this);
float a = painter.getAscent(this);
float align;
if (sup) {
align = 1.0f;
} else if (sub) {
align = (h > 0) ? (h - (d + (a / 2))) / h : 0;
} else {
align = (h > 0) ? (h - d) / h : 0;
}
return align;
}
return super.getAlignment(axis);
}
Determines the desired alignment for this view along an
axis. For the label, the alignment is along the font
baseline for the y axis, and the superclasses alignment
along the x axis. |
public Color getBackground() {
Document doc = getDocument();
if (doc instanceof StyledDocument) {
AttributeSet attr = getAttributes();
if (attr.isDefined(StyleConstants.Background)) {
return ((StyledDocument)doc).getBackground(attr);
}
}
return null;
}
Fetch the background color to use to render the
glyphs. If there is no background color, null should
be returned. This is implemented to call
StyledDocument.getBackground if the associated
document is a styled document, otherwise it returns null. |
public int getBreakWeight(int axis,
float pos,
float len) {
if (axis == View.X_AXIS) {
checkPainter();
int p0 = getStartOffset();
int p1 = painter.getBoundedPosition(this, p0, pos, len);
return p1 == p0 ? View.BadBreakWeight :
getBreakSpot(p0, p1) != BreakIterator.DONE ?
View.ExcellentBreakWeight : View.GoodBreakWeight;
}
return super.getBreakWeight(axis, pos, len);
}
Determines how attractive a break opportunity in
this view is. This can be used for determining which
view is the most attractive to call breakView
on in the process of formatting. The
higher the weight, the more attractive the break. A
value equal to or lower than View.BadBreakWeight
should not be considered for a break. A value greater
than or equal to View.ForcedBreakWeight should
be broken.
This is implemented to forward to the superclass for
the Y_AXIS. Along the X_AXIS the following values
may be returned.
- View.ExcellentBreakWeight
- if there is whitespace proceeding the desired break
location.
- View.BadBreakWeight
- if the desired break location results in a break
location of the starting offset.
- View.GoodBreakWeight
- if the other conditions don't occur.
This will normally result in the behavior of breaking
on a whitespace location if one can be found, otherwise
breaking between characters. |
public int getEndOffset() {
Element e = getElement();
return (length > 0) ? e.getStartOffset() + offset + length : e.getEndOffset();
}
Fetches the portion of the model that this view is responsible for. |
public Font getFont() {
Document doc = getDocument();
if (doc instanceof StyledDocument) {
AttributeSet attr = getAttributes();
return ((StyledDocument)doc).getFont(attr);
}
Component c = getContainer();
if (c != null) {
return c.getFont();
}
return null;
}
Fetch the font that the glyphs should be based
upon. This is implemented to call
StyledDocument.getFont if the associated
document is a StyledDocument. If the associated document
is not a StyledDocument, the associated components font
is used. If there is no associated component, null
is returned. |
public Color getForeground() {
Document doc = getDocument();
if (doc instanceof StyledDocument) {
AttributeSet attr = getAttributes();
return ((StyledDocument)doc).getForeground(attr);
}
Component c = getContainer();
if (c != null) {
return c.getForeground();
}
return null;
}
Fetch the foreground color to use to render the
glyphs. If there is no foreground color, null should
be returned. This is implemented to call
StyledDocument.getBackground if the associated
document is a StyledDocument. If the associated document
is not a StyledDocument, the associated components foreground
color is used. If there is no associated component, null
is returned. |
public GlyphPainter getGlyphPainter() {
return painter;
}
Fetch the currently installed glyph painter.
If a painter has not yet been installed, and
a default was not yet needed, null is returned. |
JustificationInfo getJustificationInfo(int rowStartOffset) {
if (justificationInfo != null) {
return justificationInfo;
}
//states for the parsing
final int TRAILING = 0;
final int CONTENT = 1;
final int SPACES = 2;
int startOffset = getStartOffset();
int endOffset = getEndOffset();
Segment segment = getText(startOffset, endOffset);
int txtOffset = segment.offset;
int txtEnd = segment.offset + segment.count - 1;
int startContentPosition = txtEnd + 1;
int endContentPosition = txtOffset - 1;
int lastTabPosition = txtOffset - 1;
int trailingSpaces = 0;
int contentSpaces = 0;
int leadingSpaces = 0;
boolean hasTab = false;
BitSet spaceMap = new BitSet(endOffset - startOffset + 1);
//we parse conent to the right of the rightmost TAB only.
//we are looking for the trailing and leading spaces.
//position after the leading spaces (startContentPosition)
//position before the trailing spaces (endContentPosition)
for (int i = txtEnd, state = TRAILING; i >= txtOffset; i--) {
if (' ' == segment.array[i]) {
spaceMap.set(i - txtOffset);
if (state == TRAILING) {
trailingSpaces++;
} else if (state == CONTENT) {
state = SPACES;
leadingSpaces = 1;
} else if (state == SPACES) {
leadingSpaces++;
}
} else if ('\t' == segment.array[i]) {
hasTab = true;
break;
} else {
if (state == TRAILING) {
if ('\n' != segment.array[i]
&& '\r' != segment.array[i]) {
state = CONTENT;
endContentPosition = i;
}
} else if (state == CONTENT) {
//do nothing
} else if (state == SPACES) {
contentSpaces += leadingSpaces;
leadingSpaces = 0;
}
startContentPosition = i;
}
}
SegmentCache.releaseSharedSegment(segment);
int startJustifiableContent = -1;
if (startContentPosition < txtEnd) {
startJustifiableContent =
startContentPosition - txtOffset;
}
int endJustifiableContent = -1;
if (endContentPosition > txtOffset) {
endJustifiableContent =
endContentPosition - txtOffset;
}
justificationInfo =
new JustificationInfo(startJustifiableContent,
endJustifiableContent,
leadingSpaces,
contentSpaces,
trailingSpaces,
hasTab,
spaceMap);
return justificationInfo;
}
|
public float getMinimumSpan(int axis) {
switch (axis) {
case View.X_AXIS:
if (minimumSpan < 0) {
minimumSpan = 0;
int p0 = getStartOffset();
int p1 = getEndOffset();
while (p1 > p0) {
int breakSpot = getBreakSpot(p0, p1);
if (breakSpot == BreakIterator.DONE) {
// the rest of the view is non-breakable
breakSpot = p0;
}
minimumSpan = Math.max(minimumSpan,
getPartialSpan(breakSpot, p1));
// Note: getBreakSpot returns the *last* breakspot
p1 = breakSpot - 1;
}
}
return minimumSpan;
case View.Y_AXIS:
return super.getMinimumSpan(axis);
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}
}
Determines the minimum span for this view along an axis.
This implementation returns the longest non-breakable area within
the view as a minimum span for {@code View.X_AXIS}. |
public int getNextVisualPositionFrom(int pos,
Bias b,
Shape a,
int direction,
Bias[] biasRet) throws BadLocationException {
return painter.getNextVisualPositionFrom(this, pos, b, a, direction, biasRet);
}
Provides a way to determine the next visually represented model
location that one might place a caret. Some views may not be
visible, they might not be in the same order found in the model, or
they just might not allow access to some of the locations in the
model. |
public float getPartialSpan(int p0,
int p1) {
checkPainter();
float width = painter.getSpan(this, p0, p1, expander, x);
return width;
}
Determines the span along the same axis as tab
expansion for a portion of the view. This is
intended for use by the TabExpander for cases
where the tab expansion involves aligning the
portion of text that doesn't have whitespace
relative to the tab stop. There is therefore
an assumption that the range given does not
contain tabs.
This method can be called while servicing the
getTabbedSpan or getPreferredSize. It has to
arrange for its own text buffer to make the
measurements. |
public float getPreferredSpan(int axis) {
if (impliedCR) {
return 0;
}
checkPainter();
int p0 = getStartOffset();
int p1 = getEndOffset();
switch (axis) {
case View.X_AXIS:
if (skipWidth) {
return 0;
}
return painter.getSpan(this, p0, p1, expander, this.x);
case View.Y_AXIS:
float h = painter.getHeight(this);
if (isSuperscript()) {
h += h/3;
}
return h;
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}
}
Determines the preferred span for this view along an
axis. |
public int getStartOffset() {
Element e = getElement();
return (length > 0) ? e.getStartOffset() + offset : e.getStartOffset();
}
Fetches the portion of the model that this view is responsible for. |
public TabExpander getTabExpander() {
return expander;
}
Fetch the TabExpander to use if tabs are present in this view. |
public float getTabbedSpan(float x,
TabExpander e) {
checkPainter();
TabExpander old = expander;
expander = e;
if (expander != old) {
// setting expander can change horizontal span of the view,
// so we have to call preferenceChanged()
preferenceChanged(null, true, false);
}
this.x = (int) x;
int p0 = getStartOffset();
int p1 = getEndOffset();
float width = painter.getSpan(this, p0, p1, expander, x);
return width;
}
Determines the desired span when using the given
tab expansion implementation. |
public Segment getText(int p0,
int p1) {
// When done with the returned Segment it should be released by
// invoking:
// SegmentCache.releaseSharedSegment(segment);
Segment text = SegmentCache.getSharedSegment();
try {
Document doc = getDocument();
doc.getText(p0, p1 - p0, text);
} catch (BadLocationException bl) {
throw new StateInvariantError("GlyphView: Stale view: " + bl);
}
return text;
}
Fetch a reference to the text that occupies
the given range. This is normally used by
the GlyphPainter to determine what characters
it should render glyphs for. |
public void insertUpdate(DocumentEvent e,
Shape a,
ViewFactory f) {
justificationInfo = null;
breakSpots = null;
minimumSpan = -1;
syncCR();
preferenceChanged(null, true, false);
}
Gives notification that something was inserted into
the document in a location that this view is responsible for.
This is implemented to call preferenceChanged along the
axis the glyphs are rendered. |
public boolean isStrikeThrough() {
AttributeSet attr = getAttributes();
return StyleConstants.isStrikeThrough(attr);
}
Determine if the glyphs should have a strikethrough
line. If true, a line should be drawn through the center
of the glyphs. |
public boolean isSubscript() {
AttributeSet attr = getAttributes();
return StyleConstants.isSubscript(attr);
}
Determine if the glyphs should be rendered as superscript. |
public boolean isSuperscript() {
AttributeSet attr = getAttributes();
return StyleConstants.isSuperscript(attr);
}
Determine if the glyphs should be rendered as subscript. |
public boolean isUnderline() {
AttributeSet attr = getAttributes();
return StyleConstants.isUnderline(attr);
}
Determine if the glyphs should be underlined. If true,
an underline should be drawn through the baseline. |
public Shape modelToView(int pos,
Shape a,
Bias b) throws BadLocationException {
checkPainter();
return painter.modelToView(this, pos, b, a);
}
Provides a mapping from the document model coordinate space
to the coordinate space of the view mapped to it. |
public void paint(Graphics g,
Shape a) {
checkPainter();
boolean paintedText = false;
Component c = getContainer();
int p0 = getStartOffset();
int p1 = getEndOffset();
Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();
Color bg = getBackground();
Color fg = getForeground();
if (c != null && ! c.isEnabled()) {
fg = (c instanceof JTextComponent ?
((JTextComponent)c).getDisabledTextColor() :
UIManager.getColor("textInactiveText"));
}
if (bg != null) {
g.setColor(bg);
g.fillRect(alloc.x, alloc.y, alloc.width, alloc.height);
}
if (c instanceof JTextComponent) {
JTextComponent tc = (JTextComponent) c;
Highlighter h = tc.getHighlighter();
if (h instanceof LayeredHighlighter) {
((LayeredHighlighter)h).paintLayeredHighlights
(g, p0, p1, a, tc, this);
}
}
if (Utilities.isComposedTextElement(getElement())) {
Utilities.paintComposedText(g, a.getBounds(), this);
paintedText = true;
} else if(c instanceof JTextComponent) {
JTextComponent tc = (JTextComponent) c;
Color selFG = tc.getSelectedTextColor();
if (// there's a highlighter (bug 4532590), and
(tc.getHighlighter() != null) &&
// selected text color is different from regular foreground
(selFG != null) && !selFG.equals(fg)) {
Highlighter.Highlight[] h = tc.getHighlighter().getHighlights();
if(h.length != 0) {
boolean initialized = false;
int viewSelectionCount = 0;
for (int i = 0; i < h.length; i++) {
Highlighter.Highlight highlight = h[i];
int hStart = highlight.getStartOffset();
int hEnd = highlight.getEndOffset();
if (hStart > p1 || hEnd < p0) {
// the selection is out of this view
continue;
}
if (!SwingUtilities2.useSelectedTextColor(highlight, tc)) {
continue;
}
if (hStart < = p0 && hEnd >= p1){
// the whole view is selected
paintTextUsingColor(g, a, selFG, p0, p1);
paintedText = true;
break;
}
// the array is lazily created only when the view
// is partially selected
if (!initialized) {
initSelections(p0, p1);
initialized = true;
}
hStart = Math.max(p0, hStart);
hEnd = Math.min(p1, hEnd);
paintTextUsingColor(g, a, selFG, hStart, hEnd);
// the array represents view positions [0, p1-p0+1]
// later will iterate this array and sum its
// elements. Positions with sum == 0 are not selected.
selections[hStart-p0]++;
selections[hEnd-p0]--;
viewSelectionCount++;
}
if (!paintedText && viewSelectionCount > 0) {
// the view is partially selected
int curPos = -1;
int startPos = 0;
int viewLen = p1 - p0;
while (curPos++ < viewLen) {
// searching for the next selection start
while(curPos < viewLen &&
selections[curPos] == 0) curPos++;
if (startPos != curPos) {
// paint unselected text
paintTextUsingColor(g, a, fg,
p0 + startPos, p0 + curPos);
}
int checkSum = 0;
// searching for next start position of unselected text
while (curPos < viewLen &&
(checkSum += selections[curPos]) != 0) curPos++;
startPos = curPos;
}
paintedText = true;
}
}
}
}
if(!paintedText)
paintTextUsingColor(g, a, fg, p0, p1);
}
Renders a portion of a text style run. |
final void paintTextUsingColor(Graphics g,
Shape a,
Color c,
int p0,
int p1) {
// render the glyphs
g.setColor(c);
painter.paint(this, g, a, p0, p1);
// render underline or strikethrough if set.
boolean underline = isUnderline();
boolean strike = isStrikeThrough();
if (underline || strike) {
// calculate x coordinates
Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();
View parent = getParent();
if ((parent != null) && (parent.getEndOffset() == p1)) {
// strip whitespace on end
Segment s = getText(p0, p1);
while (Character.isWhitespace(s.last())) {
p1 -= 1;
s.count -= 1;
}
SegmentCache.releaseSharedSegment(s);
}
int x0 = alloc.x;
int p = getStartOffset();
if (p != p0) {
x0 += (int) painter.getSpan(this, p, p0, getTabExpander(), x0);
}
int x1 = x0 + (int) painter.getSpan(this, p0, p1, getTabExpander(), x0);
// calculate y coordinate
int y = alloc.y + alloc.height - (int) painter.getDescent(this);
if (underline) {
int yTmp = y + 1;
g.drawLine(x0, yTmp, x1, yTmp);
}
if (strike) {
// move y coordinate above baseline
int yTmp = y - (int) (painter.getAscent(this) * 0.3f);
g.drawLine(x0, yTmp, x1, yTmp);
}
}
}
Paints the specified region of text in the specified color. |
public void removeUpdate(DocumentEvent e,
Shape a,
ViewFactory f) {
justificationInfo = null;
breakSpots = null;
minimumSpan = -1;
syncCR();
preferenceChanged(null, true, false);
}
Gives notification that something was removed from the document
in a location that this view is responsible for.
This is implemented to call preferenceChanged along the
axis the glyphs are rendered. |
public void setGlyphPainter(GlyphPainter p) {
painter = p;
}
Sets the painter to use for rendering glyphs. |
public int viewToModel(float x,
float y,
Shape a,
Bias[] biasReturn) {
checkPainter();
return painter.viewToModel(this, x, y, a, biasReturn);
}
Provides a mapping from the view coordinate space to the logical
coordinate space of the model. |