001/* InlineView.java -- Renders HTML content
002   Copyright (C) 2006 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.swing.text.html;
040
041import java.awt.FontMetrics;
042import java.awt.Shape;
043import java.text.BreakIterator;
044
045import javax.swing.event.DocumentEvent;
046import javax.swing.text.AttributeSet;
047import javax.swing.text.BadLocationException;
048import javax.swing.text.Document;
049import javax.swing.text.Element;
050import javax.swing.text.LabelView;
051import javax.swing.text.Segment;
052import javax.swing.text.View;
053import javax.swing.text.ViewFactory;
054
055/**
056 * Renders HTML content (identified by {@link HTML.Tag#CONTENT}). This is
057 * basically a {@link LabelView} that is adjusted to understand styles defined
058 * by stylesheets.
059 *
060 * @author Roman Kennke (kennke@aicas.com)
061 */
062public class InlineView
063  extends LabelView
064{
065
066  /**
067   * The attributes used by this view.
068   */
069  private AttributeSet attributes;
070
071  /**
072   * The span of the longest word in this view.
073   *
074   * @see #getLongestWord()
075   */
076  private float longestWord;
077
078  /**
079   * Indicates if we may wrap or not.
080   */
081  private boolean nowrap;
082
083  /**
084   * Creates a new <code>InlineView</code> that renders the specified element.
085   *
086   * @param element the element for this view
087   */
088  public InlineView(Element element)
089  {
090    super(element);
091  }
092
093  /**
094   * Receives notification that something was inserted into the document in
095   * a location that this view is responsible for.
096   *
097   * @param e the document event
098   * @param a the current allocation of this view
099   * @param f the view factory for creating new views
100   *
101   * @since 1.5
102   */
103  public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f)
104  {
105    // FIXME: What to do here?
106    super.insertUpdate(e, a, f);
107  }
108
109  /**
110   * Receives notification that something was removed from the document in
111   * a location that this view is responsible for.
112   *
113   * @param e the document event
114   * @param a the current allocation of this view
115   * @param f the view factory for creating new views
116   *
117   * @since 1.5
118   */
119  public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f)
120  {
121    // FIXME: What to do here?
122    super.removeUpdate(e, a, f);
123  }
124
125  /**
126   * Receives notification that attributes have changed in the document in
127   * a location that this view is responsible for. This calls
128   * {@link #setPropertiesFromAttributes}.
129   *
130   * @param e the document event
131   * @param a the current allocation of this view
132   * @param f the view factory for creating new views
133   *
134   * @since 1.5
135   */
136  public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f)
137  {
138    super.changedUpdate(e, a, f);
139    StyleSheet ss = getStyleSheet();
140    attributes = ss.getViewAttributes(this);
141    preferenceChanged(null, true, true);
142    setPropertiesFromAttributes();
143  }
144
145  /**
146   * Returns the attributes that are used for rendering. This is implemented
147   * to multiplex the attributes specified in the model with a stylesheet.
148   *
149   * @return the attributes that are used for rendering
150   */
151  public AttributeSet getAttributes()
152  {
153    if (attributes == null)
154      {
155        StyleSheet ss = getStyleSheet();
156        attributes = ss.getViewAttributes(this);
157      }
158    return attributes;
159  }
160
161  
162  public int getBreakWeight(int axis, float pos, float len)
163  {
164    int weight;
165    if (nowrap)
166      weight = BadBreakWeight;
167    else
168      weight = super.getBreakWeight(axis, pos, len);
169    return weight;
170  }
171
172  public View breakView(int axis, int offset, float pos, float len)
173  {
174    // FIXME: Implement this.
175    return super.breakView(axis, offset, pos, len);
176  }
177
178  /**
179   * Loads the character style properties from the stylesheet.
180   */
181  protected void setPropertiesFromAttributes()
182  {
183    super.setPropertiesFromAttributes();
184    AttributeSet atts = getAttributes();
185    Object o = atts.getAttribute(CSS.Attribute.TEXT_DECORATION);
186
187    // Check for underline.
188    boolean b = false;
189    if (o != null && o.toString().contains("underline"))
190      b = true;
191    setUnderline(b);
192
193    // Check for line-through.
194    b = false;
195    if (o != null && o.toString().contains("line-through"))
196      b = true;
197    setStrikeThrough(b);
198
199    // Check for vertical alignment (subscript/superscript).
200    o = atts.getAttribute(CSS.Attribute.VERTICAL_ALIGN);
201
202    // Subscript.
203    b = false;
204    if (o != null && o.toString().contains("sub"))
205      b = true;
206    setSubscript(b);
207
208    // Superscript.
209    b = false;
210    if (o != null && o.toString().contains("sup"))
211      b = true;
212    setSuperscript(b);
213
214    // Fetch nowrap setting.
215    o = atts.getAttribute(CSS.Attribute.WHITE_SPACE);
216    if (o != null && o.equals("nowrap"))
217      nowrap = true;
218    else
219      nowrap = false;
220  }
221
222  /**
223   * Returns the stylesheet used by this view. This returns the stylesheet
224   * of the <code>HTMLDocument</code> that is rendered by this view.
225   *
226   * @return the stylesheet used by this view
227   */
228  protected StyleSheet getStyleSheet()
229  {
230    Document doc = getDocument();
231    StyleSheet styleSheet = null;
232    if (doc instanceof HTMLDocument)
233      styleSheet = ((HTMLDocument) doc).getStyleSheet();
234    return styleSheet;
235  }
236
237  /**
238   * Returns the minimum span for the specified axis. This returns the
239   * width of the longest word for the X axis and the super behaviour for
240   * the Y axis. This is a slight deviation from the reference implementation.
241   * IMO this should improve rendering behaviour so that an InlineView never
242   * gets smaller than the longest word in it.
243   */
244  public float getMinimumSpan(int axis)
245  {
246    float min = super.getMinimumSpan(axis);
247    if (axis == X_AXIS)
248      min = Math.max(getLongestWord(), min);
249    return min;
250  }
251
252  /**
253   * Returns the span of the longest word in this view.
254   *
255   * @return the span of the longest word in this view
256   */
257  private float getLongestWord()
258  {
259    if (longestWord == -1)
260      longestWord = calculateLongestWord();
261    return longestWord;
262  }
263
264  /**
265   * Calculates the span of the longest word in this view.
266   *
267   * @return the span of the longest word in this view
268   */
269  private float calculateLongestWord()
270  {
271    float span = 0;
272    try
273      {
274        Document doc = getDocument();
275        int p0 = getStartOffset();
276        int p1 = getEndOffset();
277        Segment s = new Segment();
278        doc.getText(p0, p1 - p0, s);
279        BreakIterator iter = BreakIterator.getWordInstance();
280        iter.setText(s);
281        int wordStart = p0;
282        int wordEnd = p0;
283        int start = iter.first();
284        for (int end = iter.next(); end != BreakIterator.DONE;
285             start = end, end = iter.next())
286          {
287            if ((end - start) > (wordEnd - wordStart))
288              {
289                wordStart = start;
290                wordEnd = end;
291              }
292          }
293        if (wordEnd - wordStart > 0)
294          {
295            FontMetrics fm = getFontMetrics();
296            int offset = s.offset + wordStart - s.getBeginIndex();
297            span = fm.charsWidth(s.array, offset, wordEnd - wordStart);
298          }
299      }
300    catch (BadLocationException ex)
301      {
302        // Return 0.
303      }
304    return span;
305  }
306
307}