001    /* InlineView.java -- Renders HTML content
002       Copyright (C) 2006 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing.text.html;
040    
041    import java.awt.FontMetrics;
042    import java.awt.Shape;
043    import java.text.BreakIterator;
044    
045    import javax.swing.event.DocumentEvent;
046    import javax.swing.text.AttributeSet;
047    import javax.swing.text.BadLocationException;
048    import javax.swing.text.Document;
049    import javax.swing.text.Element;
050    import javax.swing.text.LabelView;
051    import javax.swing.text.Segment;
052    import javax.swing.text.View;
053    import 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     */
062    public 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    }