GlyphViewpublic class GlyphView extends View implements Cloneable, TabableViewA GlyphView is a styled chunk of text that represents a view
mapped over an element in the text model. This view is generally
responsible for displaying text glyphs using character level
attributes in some way.
An implementation of the GlyphPainter class is used to do the
actual rendering and model/view translations. This separates
rendering from layout and management of the association with
the model.
The view supports breaking for the purpose of formatting.
The fragments produced by breaking share the view that has
primary responsibility for the element (i.e. they are nested
classes and carry only a small amount of state of their own)
so they can share its resources.
Since this view
represents text that may have tabs embedded in it, it implements the
TabableView interface. Tabs will only be
expanded if this view is embedded in a container that does
tab expansion. ParagraphView is an example of a container
that does tab expansion.
|
Fields Summary |
---|
private byte[] | selectionsUsed by paint() to store highlighted view positions | int | offset | int | length | boolean | impliedCR | private static final String | IMPLIED_CR | boolean | skipWidth | TabExpander | expanderhow to expand tabs | int | xlocation for determining tab expansion against. | GlyphPainter | painterGlyph rendering functionality. | static GlyphPainter | defaultPainterThe prototype painter used by default. |
Constructors Summary |
---|
public GlyphView(Element elem)Constructs a new view wrapped on an element.
super(elem);
offset = 0;
length = 0;
Element parent = elem.getParentElement();
AttributeSet attr = elem.getAttributes();
// if there was an implied CR
impliedCR = (attr != null && attr.getAttribute(IMPLIED_CR) != null &&
// if this is non-empty paragraph
parent != null && parent.getElementCount() > 1);
skipWidth = elem.getName().equals("br");
|
Methods Summary |
---|
public javax.swing.text.View | breakView(int axis, int p0, float pos, float len)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.
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;
| public void | changedUpdate(javax.swing.event.DocumentEvent e, java.awt.Shape a, javax.swing.text.ViewFactory f)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.
syncCR();
preferenceChanged(null, true, true);
| protected void | checkPainter()Check to see that a glyph painter exists. If a painter
doesn't exist, a default glyph painter will be installed.
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()));
}
| protected final java.lang.Object | clone()Creates a shallow copy. This is used by the
createFragment and breakView methods.
Object o;
try {
o = super.clone();
} catch (CloneNotSupportedException cnse) {
o = null;
}
return o;
| public javax.swing.text.View | createFragment(int p0, int p1)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.
checkPainter();
Element elem = getElement();
GlyphView v = (GlyphView) clone();
v.offset = p0 - elem.getStartOffset();
v.length = p1 - p0;
v.painter = painter.getPainter(v, p0, p1);
return v;
| public float | getAlignment(int 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.
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);
| public java.awt.Color | getBackground()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.
Document doc = getDocument();
if (doc instanceof StyledDocument) {
AttributeSet attr = getAttributes();
if (attr.isDefined(StyleConstants.Background)) {
return ((StyledDocument)doc).getBackground(attr);
}
}
return null;
| private int | getBreakSpot(int p0, int p1)Returns a location to break at in the passed in region, or -1 if
there isn't a good location to break at in the specified region.
Document doc = getDocument();
if (doc != null && Boolean.TRUE.equals(doc.getProperty(
AbstractDocument.MultiByteProperty))) {
return getBreakSpotUseBreakIterator(p0, p1);
}
return getBreakSpotUseWhitespace(p0, p1);
| private int | getBreakSpotUseBreakIterator(int p0, int p1)Returns the appropriate place to break based on BreakIterator.
// Certain regions require context for BreakIterator, start from
// our parents start offset.
Element parent = getElement().getParentElement();
int parent0;
int parent1;
Container c = getContainer();
BreakIterator breaker;
if (parent == null) {
parent0 = p0;
parent1 = p1;
}
else {
parent0 = parent.getStartOffset();
parent1 = parent.getEndOffset();
}
if (c != null) {
breaker = BreakIterator.getLineInstance(c.getLocale());
}
else {
breaker = BreakIterator.getLineInstance();
}
Segment s = getText(parent0, parent1);
int breakPoint;
// Needed to initialize the Segment.
s.first();
breaker.setText(s);
if (p1 == parent1) {
// This will most likely return the end, the assumption is
// that if parent1 == p1, then we are the last portion of
// a paragraph
breakPoint = breaker.last();
}
else if (p1 + 1 == parent1) {
// assert(s.count > 1)
breakPoint = breaker.next(s.offset + s.count - 2);
if (breakPoint >= s.count + s.offset) {
breakPoint = breaker.preceding(s.offset + s.count - 1);
}
}
else {
breakPoint = breaker.preceding(p1 - parent0 + s.offset + 1);
}
int retValue = -1;
if (breakPoint != BreakIterator.DONE) {
breakPoint = breakPoint - s.offset + parent0;
if (breakPoint > p0) {
if (p0 == parent0 && breakPoint == p0) {
retValue = -1;
}
else if (breakPoint <= p1) {
retValue = breakPoint;
}
}
}
SegmentCache.releaseSharedSegment(s);
return retValue;
| private int | getBreakSpotUseWhitespace(int p0, int p1)Returns the appropriate place to break based on the last whitespace
character encountered.
Segment s = getText(p0, p1);
for (char ch = s.last(); ch != Segment.DONE; ch = s.previous()) {
if (Character.isWhitespace(ch)) {
// found whitespace
SegmentCache.releaseSharedSegment(s);
return s.getIndex() - s.getBeginIndex() + 1 + p0;
}
}
SegmentCache.releaseSharedSegment(s);
return -1;
| public int | getBreakWeight(int axis, float pos, float 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.
if (axis == View.X_AXIS) {
checkPainter();
int p0 = getStartOffset();
int p1 = painter.getBoundedPosition(this, p0, pos, len);
if (p1 == p0) {
// can't even fit a single character
return View.BadBreakWeight;
}
if (getBreakSpot(p0, p1) != -1) {
return View.ExcellentBreakWeight;
}
// Nothing good to break on.
return View.GoodBreakWeight;
}
return super.getBreakWeight(axis, pos, len);
| public int | getEndOffset()Fetches the portion of the model that this view is responsible for.
Element e = getElement();
return (length > 0) ? e.getStartOffset() + offset + length : e.getEndOffset();
| public java.awt.Font | getFont()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.
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;
| public java.awt.Color | getForeground()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.
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;
| public javax.swing.text.GlyphView$GlyphPainter | getGlyphPainter()Fetch the currently installed glyph painter.
If a painter has not yet been installed, and
a default was not yet needed, null is returned.
return painter;
| public int | getNextVisualPositionFrom(int pos, javax.swing.text.Position$Bias b, java.awt.Shape a, int direction, javax.swing.text.Position$Bias[] 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.
return painter.getNextVisualPositionFrom(this, pos, b, a, direction, biasRet);
| public float | getPartialSpan(int p0, int p1)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.
checkPainter();
float width = painter.getSpan(this, p0, p1, expander, x);
return width;
| public float | getPreferredSpan(int axis)Determines the preferred span for this view along an
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);
}
| public int | getStartOffset()Fetches the portion of the model that this view is responsible for.
Element e = getElement();
return (length > 0) ? e.getStartOffset() + offset : e.getStartOffset();
| public javax.swing.text.TabExpander | getTabExpander()Fetch the TabExpander to use if tabs are present in this view.
return expander;
| public float | getTabbedSpan(float x, javax.swing.text.TabExpander e)Determines the desired span when using the given
tab expansion implementation.
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;
| public javax.swing.text.Segment | getText(int p0, int p1)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.
// 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;
| private void | initSelections(int p0, int p1)Lazily initializes the selections field
int viewPosCount = p1 - p0 + 1;
if (selections == null || viewPosCount > selections.length) {
selections = new byte[viewPosCount];
return;
}
for (int i = 0; i < viewPosCount; selections[i++] = 0);
| public void | insertUpdate(javax.swing.event.DocumentEvent e, java.awt.Shape a, javax.swing.text.ViewFactory f)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.
syncCR();
preferenceChanged(null, true, false);
| public boolean | isStrikeThrough()Determine if the glyphs should have a strikethrough
line. If true, a line should be drawn through the center
of the glyphs.
AttributeSet attr = getAttributes();
return StyleConstants.isStrikeThrough(attr);
| public boolean | isSubscript()Determine if the glyphs should be rendered as superscript.
AttributeSet attr = getAttributes();
return StyleConstants.isSubscript(attr);
| public boolean | isSuperscript()Determine if the glyphs should be rendered as subscript.
AttributeSet attr = getAttributes();
return StyleConstants.isSuperscript(attr);
| public boolean | isUnderline()Determine if the glyphs should be underlined. If true,
an underline should be drawn through the baseline.
AttributeSet attr = getAttributes();
return StyleConstants.isUnderline(attr);
| public java.awt.Shape | modelToView(int pos, java.awt.Shape a, javax.swing.text.Position$Bias b)Provides a mapping from the document model coordinate space
to the coordinate space of the view mapped to it.
checkPainter();
return painter.modelToView(this, pos, b, a);
| public void | paint(java.awt.Graphics g, java.awt.Shape a)Renders a portion of a text style run.
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 instanceof JTextComponent) {
JTextComponent tc = (JTextComponent) c;
if (!tc.isEnabled()) {
fg = tc.getDisabledTextColor();
}
}
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);
| final void | paintTextUsingColor(java.awt.Graphics g, java.awt.Shape a, java.awt.Color c, int p0, int p1)Paints the specified region of text in the specified color.
// 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 ((s.count > 0) && (Character.isWhitespace(s.array[s.count-1]))) {
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 d = (int) painter.getDescent(this);
int y = alloc.y + alloc.height - (int) painter.getDescent(this);
if (underline) {
int yTmp = y;
yTmp += 1;
g.drawLine(x0, yTmp, x1, yTmp);
}
if (strike) {
int yTmp = y;
// move y coordinate above baseline
yTmp -= (int) (painter.getAscent(this) * 0.3f);
g.drawLine(x0, yTmp, x1, yTmp);
}
}
| public void | removeUpdate(javax.swing.event.DocumentEvent e, java.awt.Shape a, javax.swing.text.ViewFactory f)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.
syncCR();
preferenceChanged(null, true, false);
| public void | setGlyphPainter(javax.swing.text.GlyphView$GlyphPainter p)Sets the painter to use for rendering glyphs.
painter = p;
| private void | syncCR()
if (impliedCR) {
Element parent = getElement().getParentElement();
impliedCR = (parent != null && parent.getElementCount() > 1);
}
| public int | viewToModel(float x, float y, java.awt.Shape a, javax.swing.text.Position$Bias[] biasReturn)Provides a mapping from the view coordinate space to the logical
coordinate space of the model.
checkPainter();
return painter.viewToModel(this, x, y, a, biasReturn);
|
|