/*
* @(#)InlineView.java 1.25 04/03/05
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package javax.swing.text.html;
import java.awt.*;
import java.text.BreakIterator;
import javax.swing.event.DocumentEvent;
import javax.swing.text.*;
/**
* Displays the inline element styles
* based upon css attributes.
*
* @author Timothy Prinzing
* @version 1.25 03/05/04
*/
public class InlineView extends LabelView {
/**
* Constructs a new view wrapped on an element.
*
* @param elem the element
*/
public InlineView(Element elem) {
super(elem);
StyleSheet sheet = getStyleSheet();
attr = sheet.getViewAttributes(this);
}
/**
* Gives notification that something was inserted into
* the document in a location that this view is responsible for.
* If either parameter is null
, behavior of this method is
* implementation dependent.
*
* @param e the change information from the associated document
* @param a the current allocation of the view
* @param f the factory to use to rebuild if the view has children
* @since 1.5
* @see View#insertUpdate
*/
public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
super.insertUpdate(e, a, f);
longestWordSpan = -1.0f;
}
/**
* Gives notification that something was removed from the document
* in a location that this view is responsible for.
* If either parameter is null
, behavior of this method is
* implementation dependent.
*
* @param e the change information from the associated document
* @param a the current allocation of the view
* @param f the factory to use to rebuild if the view has children
* @since 1.5
* @see View#removeUpdate
*/
public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
super.removeUpdate(e, a, f);
longestWordSpan = -1.0f;
}
/**
* Gives notification from the document that attributes were changed
* in a location that this view is responsible for.
*
* @param e the change information from the associated document
* @param a the current allocation of the view
* @param f the factory to use to rebuild if the view has children
* @see View#changedUpdate
*/
public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
super.changedUpdate(e, a, f);
StyleSheet sheet = getStyleSheet();
attr = sheet.getViewAttributes(this);
longestWordSpan = -1.0f;
preferenceChanged(null, true, true);
}
/**
* Fetches the attributes to use when rendering. This is
* implemented to multiplex the attributes specified in the
* model with a StyleSheet.
*/
public AttributeSet getAttributes() {
return attr;
}
/**
* 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. A view that represents
* text that has whitespace in it might be more attractive
* than a view that has no whitespace, for example. The
* higher the weight, the more attractive the break. A
* value equal to or lower than BadBreakWeight
* should not be considered for a break. A value greater
* than or equal to ForcedBreakWeight
should
* be broken.
*
* This is implemented to provide the default behavior
* of returning BadBreakWeight
unless the length
* is greater than the length of the view in which case the
* entire view represents the fragment. Unless a view has
* been written to support breaking behavior, it is not
* attractive to try and break the view. An example of
* a view that does support breaking is LabelView
.
* An example of a view that uses break weight is
* ParagraphView
.
*
* @param axis may be either View.X_AXIS or View.Y_AXIS
* @param pos the potential location of the start of the
* broken view >= 0. This may be useful for calculating tab
* positions.
* @param len specifies the relative length from pos
* where a potential break is desired >= 0.
* @return the weight, which should be a value between
* ForcedBreakWeight and BadBreakWeight.
* @see LabelView
* @see ParagraphView
* @see javax.swing.text.View#BadBreakWeight
* @see javax.swing.text.View#GoodBreakWeight
* @see javax.swing.text.View#ExcellentBreakWeight
* @see javax.swing.text.View#ForcedBreakWeight
*/
public int getBreakWeight(int axis, float pos, float len) {
if (nowrap) {
return BadBreakWeight;
}
return super.getBreakWeight(axis, pos, len);
}
/**
* Tries to break this view on the given axis. Refer to
* {@link javax.swing.text.View#breakView} for a complete
* description of this method.
*
Behavior of this method is unspecified in case axis
* is neither View.X_AXIS
nor View.Y_AXIS
, and
* in case offset
, pos
, or len
* is null.
*
* @param axis may be either View.X_AXIS
or
* View.Y_AXIS
* @param offset the location in the document model
* that a broken fragment would occupy >= 0. This
* would be the starting offset of the fragment
* returned
* @param pos the position along the axis that the
* broken view would occupy >= 0. This may be useful for
* things like tab calculations
* @param len specifies the distance along the axis
* where a potential break is desired >= 0
* @return the fragment of the view that represents the
* given span.
* @since 1.5
* @see javax.swing.text.View#breakView
*/
public View breakView(int axis, int offset, float pos, float len) {
InlineView view = (InlineView)super.breakView(axis, offset, pos, len);
if (view != this) {
view.longestWordSpan = -1;
}
return view;
}
/**
* Fetch the span of the longest word in the view.
*/
float getLongestWordSpan() {
if (longestWordSpan < 0.0f) {
longestWordSpan = calculateLongestWordSpan();
}
return longestWordSpan;
}
float calculateLongestWordSpan() {
// find the longest word
float span = 0;
try {
Document doc = getDocument();
int p0 = getStartOffset();
int p1 = getEndOffset();
if (p1 > p0) {
Segment segment = new Segment();
doc.getText(p0, p1 - p0, segment);
int word0 = p0;
int word1 = p0;
Container c = getContainer();
BreakIterator words;
if (c != null) {
words = BreakIterator.getWordInstance(c.getLocale());
} else {
words = BreakIterator.getWordInstance();
}
words.setText(segment);
int start = words.first();
for (int end = words.next(); end != BreakIterator.DONE;
start = end, end = words.next()) {
// update longest word boundary
if ((end - start) > (word1 - word0)) {
word0 = start;
word1 = end;
}
}
// calculate the minimum
if ((word1 - word0) > 0) {
FontMetrics metrics = getFontMetrics();
int offs = segment.offset + word0 - segment.getBeginIndex();
span = metrics.charsWidth(segment.array, offs, word1 - word0);
}
}
} catch (BadLocationException ble) {
// If the text can't be retrieved, it can't influence the size.
}
return span;
}
/**
* Set the cached properties from the attributes.
*/
protected void setPropertiesFromAttributes() {
super.setPropertiesFromAttributes();
AttributeSet a = getAttributes();
Object decor = a.getAttribute(CSS.Attribute.TEXT_DECORATION);
boolean u = (decor != null) ?
(decor.toString().indexOf("underline") >= 0) : false;
setUnderline(u);
boolean s = (decor != null) ?
(decor.toString().indexOf("line-through") >= 0) : false;
setStrikeThrough(s);
Object vAlign = a.getAttribute(CSS.Attribute.VERTICAL_ALIGN);
s = (vAlign != null) ? (vAlign.toString().indexOf("sup") >= 0) : false;
setSuperscript(s);
s = (vAlign != null) ? (vAlign.toString().indexOf("sub") >= 0) : false;
setSubscript(s);
Object whitespace = a.getAttribute(CSS.Attribute.WHITE_SPACE);
if ((whitespace != null) && whitespace.equals("nowrap")) {
nowrap = true;
} else {
nowrap = false;
}
HTMLDocument doc = (HTMLDocument)getDocument();
// fetches background color from stylesheet if specified
Color bg = doc.getBackground(a);
if (bg != null) {
setBackground(bg);
}
}
protected StyleSheet getStyleSheet() {
HTMLDocument doc = (HTMLDocument) getDocument();
return doc.getStyleSheet();
}
private boolean nowrap;
private AttributeSet attr;
private float longestWordSpan = -1.0f;
}