KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > plaf > basic > BasicHTML


1 /*
2  * @(#)BasicHTML.java 1.22 04/07/23
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package javax.swing.plaf.basic;
8
9 import java.io.*;
10 import java.awt.*;
11 import java.net.URL JavaDoc;
12
13 import javax.swing.*;
14 import javax.swing.text.*;
15 import javax.swing.text.html.*;
16
17 import com.sun.java.swing.SwingUtilities2;
18
19 /**
20  * Support for providing html views for the swing components.
21  * This translates a simple html string to a javax.swing.text.View
22  * implementation that can render the html and provide the necessary
23  * layout semantics.
24  *
25  * @author Timothy Prinzing
26  * @version 1.22 07/23/04
27  */

28 public class BasicHTML {
29
30     /**
31      * Create an html renderer for the given component and
32      * string of html.
33      */

34     public static View createHTMLView(JComponent c, String JavaDoc html) {
35     BasicEditorKit kit = getFactory();
36     Document doc = kit.createDefaultDocument(c.getFont(),
37                                                  c.getForeground());
38     Object JavaDoc base = c.getClientProperty(documentBaseKey);
39     if (base instanceof URL JavaDoc) {
40         ((HTMLDocument)doc).setBase((URL JavaDoc)base);
41     }
42     Reader r = new StringReader(html);
43     try {
44         kit.read(r, doc, 0);
45     } catch (Throwable JavaDoc e) {
46     }
47     ViewFactory f = kit.getViewFactory();
48     View hview = f.create(doc.getDefaultRootElement());
49     View v = new Renderer(c, f, hview);
50     return v;
51     }
52
53     /**
54      * Check the given string to see if it should trigger the
55      * html rendering logic in a non-text component that supports
56      * html rendering.
57      */

58     public static boolean isHTMLString(String JavaDoc s) {
59     if (s != null) {
60         if ((s.length() >= 6) && (s.charAt(0) == '<') && (s.charAt(5) == '>')) {
61         String JavaDoc tag = s.substring(1,5);
62         return tag.equalsIgnoreCase(propertyKey);
63         }
64     }
65     return false;
66     }
67
68     /**
69      * Stash the HTML render for the given text into the client
70      * properties of the given JComponent. If the given text is
71      * <em>NOT HTML</em> the property will be cleared of any
72      * renderer.
73      * <p>
74      * This method is useful for ComponentUI implementations
75      * that are static (i.e. shared) and get their state
76      * entirely from the JComponent.
77      */

78     public static void updateRenderer(JComponent c, String JavaDoc text) {
79     View value = null;
80         View oldValue = (View)c.getClientProperty(BasicHTML.propertyKey);
81         Boolean JavaDoc htmlDisabled = (Boolean JavaDoc) c.getClientProperty(htmlDisable);
82     if (htmlDisabled != Boolean.TRUE && BasicHTML.isHTMLString(text)) {
83         value = BasicHTML.createHTMLView(c, text);
84     }
85         if (value != oldValue && oldValue != null) {
86             for (int i = 0; i < oldValue.getViewCount(); i++) {
87                 oldValue.getView(i).setParent(null);
88             }
89         }
90     c.putClientProperty(BasicHTML.propertyKey, value);
91     }
92
93     /**
94      * If this client property of a JComponent is set to Boolean.TRUE
95      * the component's 'text' property is never treated as HTML.
96      */

97     private static final String JavaDoc htmlDisable = "html.disable";
98
99     /**
100      * Key to use for the html renderer when stored as a
101      * client property of a JComponent.
102      */

103     public static final String JavaDoc propertyKey = "html";
104
105     /**
106      * Key stored as a client property to indicate the base that relative
107      * references are resolved against. For example, lets say you keep
108      * your images in the directory resources relative to the code path,
109      * you would use the following the set the base:
110      * <pre>
111      * jComponent.putClientProperty(documentBaseKey,
112      * xxx.class.getResource("resources/"));
113      * </pre>
114      */

115     public static final String JavaDoc documentBaseKey = "html.base";
116
117     static BasicEditorKit getFactory() {
118     if (basicHTMLFactory == null) {
119             basicHTMLViewFactory = new BasicHTMLViewFactory();
120         basicHTMLFactory = new BasicEditorKit();
121     }
122     return basicHTMLFactory;
123     }
124
125     /**
126      * The source of the html renderers
127      */

128     private static BasicEditorKit basicHTMLFactory;
129
130     /**
131      * Creates the Views that visually represent the model.
132      */

133     private static ViewFactory basicHTMLViewFactory;
134
135     /**
136      * Overrides to the default stylesheet. Should consider
137      * just creating a completely fresh stylesheet.
138      */

139     private static final String JavaDoc styleChanges =
140     "p { margin-top: 0; margin-bottom: 0; margin-left: 0; margin-right: 0 }" +
141     "body { margin-top: 0; margin-bottom: 0; margin-left: 0; margin-right: 0 }";
142
143     /**
144      * The views produced for the ComponentUI implementations aren't
145      * going to be edited and don't need full html support. This kit
146      * alters the HTMLEditorKit to try and trim things down a bit.
147      * It does the following:
148      * <ul>
149      * <li>It doesn't produce Views for things like comments,
150      * head, title, unknown tags, etc.
151      * <li>It installs a different set of css settings from the default
152      * provided by HTMLEditorKit.
153      * </ul>
154      */

155     static class BasicEditorKit extends HTMLEditorKit {
156     /** Shared base style for all documents created by us use. */
157     private static StyleSheet defaultStyles;
158
159     /**
160      * Overriden to return our own slimmed down style sheet.
161      */

162     public StyleSheet getStyleSheet() {
163         if (defaultStyles == null) {
164         defaultStyles = new StyleSheet();
165         StringReader r = new StringReader(styleChanges);
166         try {
167             defaultStyles.loadRules(r, null);
168         } catch (Throwable JavaDoc e) {
169             // don't want to die in static initialization...
170
// just display things wrong.
171
}
172         r.close();
173         defaultStyles.addStyleSheet(super.getStyleSheet());
174         }
175         return defaultStyles;
176     }
177
178     /**
179      * Sets the async policy to flush everything in one chunk, and
180      * to not display unknown tags.
181      */

182         public Document createDefaultDocument(Font defaultFont,
183                                               Color foreground) {
184         StyleSheet styles = getStyleSheet();
185         StyleSheet ss = new StyleSheet();
186         ss.addStyleSheet(styles);
187         BasicDocument doc = new BasicDocument(ss, defaultFont, foreground);
188         doc.setAsynchronousLoadPriority(Integer.MAX_VALUE);
189         doc.setPreservesUnknownTags(false);
190         return doc;
191     }
192
193         /**
194          * Returns the ViewFactory that is used to make sure the Views don't
195          * load in the background.
196          */

197         public ViewFactory getViewFactory() {
198             return basicHTMLViewFactory;
199         }
200     }
201
202
203     /**
204      * BasicHTMLViewFactory extends HTMLFactory to force images to be loaded
205      * synchronously.
206      */

207     static class BasicHTMLViewFactory extends HTMLEditorKit.HTMLFactory {
208         public View create(Element elem) {
209             View view = super.create(elem);
210
211             if (view instanceof ImageView) {
212                 ((ImageView)view).setLoadsSynchronously(true);
213             }
214             return view;
215         }
216     }
217
218
219     /**
220      * The subclass of HTMLDocument that is used as the model. getForeground
221      * is overridden to return the foreground property from the Component this
222      * was created for.
223      */

224     static class BasicDocument extends HTMLDocument {
225     /** The host, that is where we are rendering. */
226     // private JComponent host;
227

228     BasicDocument(StyleSheet s, Font defaultFont, Color foreground) {
229         super(s);
230         setPreservesUnknownTags(false);
231             setFontAndColor(defaultFont, foreground);
232     }
233
234         /**
235          * Sets the default font and default color. These are set by
236          * adding a rule for the body that specifies the font and color.
237          * This allows the html to override these should it wish to have
238          * a custom font or color.
239          */

240     private void setFontAndColor(Font font, Color fg) {
241             getStyleSheet().addRule(com.sun.java.swing.SwingUtilities2.
242                                     displayPropertiesToCSS(font,fg));
243     }
244     }
245
246
247     /**
248      * Root text view that acts as an HTML renderer.
249      */

250     static class Renderer extends View {
251
252         Renderer(JComponent c, ViewFactory f, View v) {
253             super(null);
254         host = c;
255         factory = f;
256         view = v;
257         view.setParent(this);
258         // initially layout to the preferred size
259
setSize(view.getPreferredSpan(X_AXIS), view.getPreferredSpan(Y_AXIS));
260         }
261
262     /**
263      * Fetches the attributes to use when rendering. At the root
264      * level there are no attributes. If an attribute is resolved
265      * up the view hierarchy this is the end of the line.
266      */

267         public AttributeSet getAttributes() {
268         return null;
269     }
270
271         /**
272          * Determines the preferred span for this view along an axis.
273          *
274          * @param axis may be either X_AXIS or Y_AXIS
275          * @return the span the view would like to be rendered into.
276          * Typically the view is told to render into the span
277          * that is returned, although there is no guarantee.
278          * The parent may choose to resize or break the view.
279          */

280         public float getPreferredSpan(int axis) {
281         if (axis == X_AXIS) {
282         // width currently laid out to
283
return width;
284         }
285         return view.getPreferredSpan(axis);
286         }
287
288         /**
289          * Determines the minimum span for this view along an axis.
290          *
291          * @param axis may be either X_AXIS or Y_AXIS
292          * @return the span the view would like to be rendered into.
293          * Typically the view is told to render into the span
294          * that is returned, although there is no guarantee.
295          * The parent may choose to resize or break the view.
296          */

297         public float getMinimumSpan(int axis) {
298         return view.getMinimumSpan(axis);
299         }
300
301         /**
302          * Determines the maximum span for this view along an axis.
303          *
304          * @param axis may be either X_AXIS or Y_AXIS
305          * @return the span the view would like to be rendered into.
306          * Typically the view is told to render into the span
307          * that is returned, although there is no guarantee.
308          * The parent may choose to resize or break the view.
309          */

310         public float getMaximumSpan(int axis) {
311         return Integer.MAX_VALUE;
312         }
313
314         /**
315          * Specifies that a preference has changed.
316          * Child views can call this on the parent to indicate that
317          * the preference has changed. The root view routes this to
318          * invalidate on the hosting component.
319          * <p>
320          * This can be called on a different thread from the
321          * event dispatching thread and is basically unsafe to
322          * propagate into the component. To make this safe,
323          * the operation is transferred over to the event dispatching
324          * thread for completion. It is a design goal that all view
325          * methods be safe to call without concern for concurrency,
326          * and this behavior helps make that true.
327          *
328          * @param child the child view
329          * @param width true if the width preference has changed
330          * @param height true if the height preference has changed
331          */

332         public void preferenceChanged(View child, boolean width, boolean height) {
333             host.revalidate();
334         host.repaint();
335         }
336
337         /**
338          * Determines the desired alignment for this view along an axis.
339          *
340          * @param axis may be either X_AXIS or Y_AXIS
341          * @return the desired alignment, where 0.0 indicates the origin
342          * and 1.0 the full span away from the origin
343          */

344         public float getAlignment(int axis) {
345         return view.getAlignment(axis);
346         }
347
348         /**
349          * Renders the view.
350          *
351          * @param g the graphics context
352          * @param allocation the region to render into
353          */

354         public void paint(Graphics g, Shape allocation) {
355         Rectangle alloc = allocation.getBounds();
356         view.setSize(alloc.width, alloc.height);
357         view.paint(g, allocation);
358         }
359         
360         /**
361          * Sets the view parent.
362          *
363          * @param parent the parent view
364          */

365         public void setParent(View parent) {
366             throw new Error JavaDoc("Can't set parent on root view");
367         }
368
369         /**
370          * Returns the number of views in this view. Since
371          * this view simply wraps the root of the view hierarchy
372          * it has exactly one child.
373          *
374          * @return the number of views
375          * @see #getView
376          */

377         public int getViewCount() {
378             return 1;
379         }
380
381         /**
382          * Gets the n-th view in this container.
383          *
384          * @param n the number of the view to get
385          * @return the view
386          */

387         public View getView(int n) {
388             return view;
389         }
390
391         /**
392          * Provides a mapping from the document model coordinate space
393          * to the coordinate space of the view mapped to it.
394          *
395          * @param pos the position to convert
396          * @param a the allocated region to render into
397          * @return the bounding box of the given position
398          */

399         public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
400         return view.modelToView(pos, a, b);
401         }
402
403     /**
404      * Provides a mapping from the document model coordinate space
405      * to the coordinate space of the view mapped to it.
406      *
407      * @param p0 the position to convert >= 0
408      * @param b0 the bias toward the previous character or the
409      * next character represented by p0, in case the
410      * position is a boundary of two views.
411      * @param p1 the position to convert >= 0
412      * @param b1 the bias toward the previous character or the
413      * next character represented by p1, in case the
414      * position is a boundary of two views.
415      * @param a the allocated region to render into
416      * @return the bounding box of the given position is returned
417      * @exception BadLocationException if the given position does
418      * not represent a valid location in the associated document
419      * @exception IllegalArgumentException for an invalid bias argument
420      * @see View#viewToModel
421      */

422     public Shape modelToView(int p0, Position.Bias b0, int p1,
423                  Position.Bias b1, Shape a) throws BadLocationException {
424         return view.modelToView(p0, b0, p1, b1, a);
425     }
426
427         /**
428          * Provides a mapping from the view coordinate space to the logical
429          * coordinate space of the model.
430          *
431          * @param x x coordinate of the view location to convert
432          * @param y y coordinate of the view location to convert
433          * @param a the allocated region to render into
434          * @return the location within the model that best represents the
435          * given point in the view
436          */

437         public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
438         return view.viewToModel(x, y, a, bias);
439         }
440
441         /**
442          * Returns the document model underlying the view.
443          *
444          * @return the model
445          */

446         public Document getDocument() {
447             return view.getDocument();
448         }
449         
450         /**
451          * Returns the starting offset into the model for this view.
452          *
453          * @return the starting offset
454          */

455         public int getStartOffset() {
456         return view.getStartOffset();
457         }
458
459         /**
460          * Returns the ending offset into the model for this view.
461          *
462          * @return the ending offset
463          */

464         public int getEndOffset() {
465         return view.getEndOffset();
466         }
467
468         /**
469          * Gets the element that this view is mapped to.
470          *
471          * @return the view
472          */

473         public Element getElement() {
474         return view.getElement();
475         }
476
477         /**
478          * Sets the view size.
479          *
480          * @param width the width
481          * @param height the height
482          */

483         public void setSize(float width, float height) {
484         this.width = (int) width;
485         view.setSize(width, height);
486         }
487
488         /**
489          * Fetches the container hosting the view. This is useful for
490          * things like scheduling a repaint, finding out the host
491          * components font, etc. The default implementation
492          * of this is to forward the query to the parent view.
493          *
494          * @return the container
495          */

496         public Container getContainer() {
497             return host;
498         }
499         
500         /**
501          * Fetches the factory to be used for building the
502          * various view fragments that make up the view that
503          * represents the model. This is what determines
504          * how the model will be represented. This is implemented
505          * to fetch the factory provided by the associated
506          * EditorKit.
507          *
508          * @return the factory
509          */

510         public ViewFactory getViewFactory() {
511         return factory;
512         }
513
514     private int width;
515         private View view;
516     private ViewFactory factory;
517     private JComponent host;
518
519     }
520 }
521
Popular Tags