History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/plaf/basic/BasicHTML.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/plaf/basic/BasicHTML.java+644−0
@@ -0,0 +1,644 @@
1+/*
2+ * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+package javax.swing.plaf.basic;
26+
27+import java.io.*;
28+import java.awt.*;
29+import java.net.URL;
30+
31+import javax.swing.*;
32+import javax.swing.text.*;
33+import javax.swing.text.html.*;
34+
35+import sun.swing.SwingUtilities2;
36+
37+/**
38+ * Support for providing html views for the swing components.
39+ * This translates a simple html string to a javax.swing.text.View
40+ * implementation that can render the html and provide the necessary
41+ * layout semantics.
42+ *
43+ * @author Timothy Prinzing
44+ * @since 1.3
45+ */
46+public class BasicHTML {
47+
48+ /**
49+ * Create an html renderer for the given component and
50+ * string of html.
51+ */
52+ public static View createHTMLView(JComponent c, String html) {
53+ BasicEditorKit kit = getFactory();
54+ Document doc = kit.createDefaultDocument(c.getFont(),
55+ c.getForeground());
56+ Object base = c.getClientProperty(documentBaseKey);
57+ if (base instanceof URL) {
58+ ((HTMLDocument)doc).setBase((URL)base);
59+ }
60+ Reader r = new StringReader(html);
61+ try {
62+ kit.read(r, doc, 0);
63+ } catch (Throwable e) {
64+ }
65+ ViewFactory f = kit.getViewFactory();
66+ View hview = f.create(doc.getDefaultRootElement());
67+ View v = new Renderer(c, f, hview);
68+ return v;
69+ }
70+
71+ /**
72+ * Returns the baseline for the html renderer.
73+ *
74+ * @param view the View to get the baseline for
75+ * @param w the width to get the baseline for
76+ * @param h the height to get the baseline for
77+ * @throws IllegalArgumentException if width or height is < 0
78+ * @return baseline or a value < 0 indicating there is no reasonable
79+ * baseline
80+ * @see java.awt.FontMetrics
81+ * @see javax.swing.JComponent#getBaseline(int,int)
82+ * @since 1.6
83+ */
84+ public static int getHTMLBaseline(View view, int w, int h) {
85+ if (w < 0 || h < 0) {
86+ throw new IllegalArgumentException(
87+ "Width and height must be >= 0");
88+ }
89+ if (view instanceof Renderer) {
90+ return getBaseline(view.getView(0), w, h);
91+ }
92+ return -1;
93+ }
94+
95+ /**
96+ * Gets the baseline for the specified component. This digs out
97+ * the View client property, and if non-null the baseline is calculated
98+ * from it. Otherwise the baseline is the value <code>y + ascent</code>.
99+ */
100+ static int getBaseline(JComponent c, int y, int ascent,
101+ int w, int h) {
102+ View view = (View)c.getClientProperty(BasicHTML.propertyKey);
103+ if (view != null) {
104+ int baseline = getHTMLBaseline(view, w, h);
105+ if (baseline < 0) {
106+ return baseline;
107+ }
108+ return y + baseline;
109+ }
110+ return y + ascent;
111+ }
112+
113+ /**
114+ * Gets the baseline for the specified View.
115+ */
116+ static int getBaseline(View view, int w, int h) {
117+ if (hasParagraph(view)) {
118+ view.setSize(w, h);
119+ return getBaseline(view, new Rectangle(0, 0, w, h));
120+ }
121+ return -1;
122+ }
123+
124+ private static int getBaseline(View view, Shape bounds) {
125+ if (view.getViewCount() == 0) {
126+ return -1;
127+ }
128+ AttributeSet attributes = view.getElement().getAttributes();
129+ Object name = null;
130+ if (attributes != null) {
131+ name = attributes.getAttribute(StyleConstants.NameAttribute);
132+ }
133+ int index = 0;
134+ if (name == HTML.Tag.HTML && view.getViewCount() > 1) {
135+ // For html on widgets the header is not visible, skip it.
136+ index++;
137+ }
138+ bounds = view.getChildAllocation(index, bounds);
139+ if (bounds == null) {
140+ return -1;
141+ }
142+ View child = view.getView(index);
143+ if (view instanceof javax.swing.text.ParagraphView) {
144+ Rectangle rect;
145+ if (bounds instanceof Rectangle) {
146+ rect = (Rectangle)bounds;
147+ }
148+ else {
149+ rect = bounds.getBounds();
150+ }
151+ return rect.y + (int)(rect.height *
152+ child.getAlignment(View.Y_AXIS));
153+ }
154+ return getBaseline(child, bounds);
155+ }
156+
157+ private static boolean hasParagraph(View view) {
158+ if (view instanceof javax.swing.text.ParagraphView) {
159+ return true;
160+ }
161+ if (view.getViewCount() == 0) {
162+ return false;
163+ }
164+ AttributeSet attributes = view.getElement().getAttributes();
165+ Object name = null;
166+ if (attributes != null) {
167+ name = attributes.getAttribute(StyleConstants.NameAttribute);
168+ }
169+ int index = 0;
170+ if (name == HTML.Tag.HTML && view.getViewCount() > 1) {
171+ // For html on widgets the header is not visible, skip it.
172+ index = 1;
173+ }
174+ return hasParagraph(view.getView(index));
175+ }
176+
177+ /**
178+ * Check the given string to see if it should trigger the
179+ * html rendering logic in a non-text component that supports
180+ * html rendering.
181+ */
182+ public static boolean isHTMLString(String s) {
183+ if (s != null) {
184+ if ((s.length() >= 6) && (s.charAt(0) == '<') && (s.charAt(5) == '>')) {
185+ String tag = s.substring(1,5);
186+ return tag.equalsIgnoreCase(propertyKey);
187+ }
188+ }
189+ return false;
190+ }
191+
192+ /**
193+ * Stash the HTML render for the given text into the client
194+ * properties of the given JComponent. If the given text is
195+ * <em>NOT HTML</em> the property will be cleared of any
196+ * renderer.
197+ * <p>
198+ * This method is useful for ComponentUI implementations
199+ * that are static (i.e. shared) and get their state
200+ * entirely from the JComponent.
201+ */
202+ public static void updateRenderer(JComponent c, String text) {
203+ View value = null;
204+ View oldValue = (View)c.getClientProperty(BasicHTML.propertyKey);
205+ Boolean htmlDisabled = (Boolean) c.getClientProperty(htmlDisable);
206+ if (htmlDisabled != Boolean.TRUE && BasicHTML.isHTMLString(text)) {
207+ value = BasicHTML.createHTMLView(c, text);
208+ }
209+ if (value != oldValue && oldValue != null) {
210+ for (int i = 0; i < oldValue.getViewCount(); i++) {
211+ oldValue.getView(i).setParent(null);
212+ }
213+ }
214+ c.putClientProperty(BasicHTML.propertyKey, value);
215+ }
216+
217+ /**
218+ * If this client property of a JComponent is set to Boolean.TRUE
219+ * the component's 'text' property is never treated as HTML.
220+ */
221+ private static final String htmlDisable = "html.disable";
222+
223+ /**
224+ * Key to use for the html renderer when stored as a
225+ * client property of a JComponent.
226+ */
227+ public static final String propertyKey = "html";
228+
229+ /**
230+ * Key stored as a client property to indicate the base that relative
231+ * references are resolved against. For example, lets say you keep
232+ * your images in the directory resources relative to the code path,
233+ * you would use the following the set the base:
234+ * <pre>
235+ * jComponent.putClientProperty(documentBaseKey,
236+ * xxx.class.getResource("resources/"));
237+ * </pre>
238+ */
239+ public static final String documentBaseKey = "html.base";
240+
241+ static BasicEditorKit getFactory() {
242+ if (basicHTMLFactory == null) {
243+ basicHTMLViewFactory = new BasicHTMLViewFactory();
244+ basicHTMLFactory = new BasicEditorKit();
245+ }
246+ return basicHTMLFactory;
247+ }
248+
249+ /**
250+ * The source of the html renderers
251+ */
252+ private static BasicEditorKit basicHTMLFactory;
253+
254+ /**
255+ * Creates the Views that visually represent the model.
256+ */
257+ private static ViewFactory basicHTMLViewFactory;
258+
259+ /**
260+ * Overrides to the default stylesheet. Should consider
261+ * just creating a completely fresh stylesheet.
262+ */
263+ private static final String styleChanges =
264+ "p { margin-top: 0; margin-bottom: 0; margin-left: 0; margin-right: 0 }" +
265+ "body { margin-top: 0; margin-bottom: 0; margin-left: 0; margin-right: 0 }";
266+
267+ /**
268+ * The views produced for the ComponentUI implementations aren't
269+ * going to be edited and don't need full html support. This kit
270+ * alters the HTMLEditorKit to try and trim things down a bit.
271+ * It does the following:
272+ * <ul>
273+ * <li>It doesn't produce Views for things like comments,
274+ * head, title, unknown tags, etc.
275+ * <li>It installs a different set of css settings from the default
276+ * provided by HTMLEditorKit.
277+ * </ul>
278+ */
279+ static class BasicEditorKit extends HTMLEditorKit {
280+ /** Shared base style for all documents created by us use. */
281+ private static StyleSheet defaultStyles;
282+
283+ /**
284+ * Overriden to return our own slimmed down style sheet.
285+ */
286+ public StyleSheet getStyleSheet() {
287+ if (defaultStyles == null) {
288+ defaultStyles = new StyleSheet();
289+ StringReader r = new StringReader(styleChanges);
290+ try {
291+ defaultStyles.loadRules(r, null);
292+ } catch (Throwable e) {
293+ // don't want to die in static initialization...
294+ // just display things wrong.
295+ }
296+ r.close();
297+ defaultStyles.addStyleSheet(super.getStyleSheet());
298+ }
299+ return defaultStyles;
300+ }
301+
302+ /**
303+ * Sets the async policy to flush everything in one chunk, and
304+ * to not display unknown tags.
305+ */
306+ public Document createDefaultDocument(Font defaultFont,
307+ Color foreground) {
308+ StyleSheet styles = getStyleSheet();
309+ StyleSheet ss = new StyleSheet();
310+ ss.addStyleSheet(styles);
311+ BasicDocument doc = new BasicDocument(ss, defaultFont, foreground);
312+ doc.setAsynchronousLoadPriority(Integer.MAX_VALUE);
313+ doc.setPreservesUnknownTags(false);
314+ return doc;
315+ }
316+
317+ /**
318+ * Returns the ViewFactory that is used to make sure the Views don't
319+ * load in the background.
320+ */
321+ public ViewFactory getViewFactory() {
322+ return basicHTMLViewFactory;
323+ }
324+ }
325+
326+
327+ /**
328+ * BasicHTMLViewFactory extends HTMLFactory to force images to be loaded
329+ * synchronously.
330+ */
331+ static class BasicHTMLViewFactory extends HTMLEditorKit.HTMLFactory {
332+ public View create(Element elem) {
333+ View view = super.create(elem);
334+
335+ if (view instanceof ImageView) {
336+ ((ImageView)view).setLoadsSynchronously(true);
337+ }
338+ return view;
339+ }
340+ }
341+
342+
343+ /**
344+ * The subclass of HTMLDocument that is used as the model. getForeground
345+ * is overridden to return the foreground property from the Component this
346+ * was created for.
347+ */
348+ static class BasicDocument extends HTMLDocument {
349+ /** The host, that is where we are rendering. */
350+ // private JComponent host;
351+
352+ BasicDocument(StyleSheet s, Font defaultFont, Color foreground) {
353+ super(s);
354+ setPreservesUnknownTags(false);
355+ setFontAndColor(defaultFont, foreground);
356+ }
357+
358+ /**
359+ * Sets the default font and default color. These are set by
360+ * adding a rule for the body that specifies the font and color.
361+ * This allows the html to override these should it wish to have
362+ * a custom font or color.
363+ */
364+ private void setFontAndColor(Font font, Color fg) {
365+ getStyleSheet().addRule(sun.swing.SwingUtilities2.
366+ displayPropertiesToCSS(font,fg));
367+ }
368+ }
369+
370+
371+ /**
372+ * Root text view that acts as an HTML renderer.
373+ */
374+ static class Renderer extends View {
375+
376+ Renderer(JComponent c, ViewFactory f, View v) {
377+ super(null);
378+ host = c;
379+ factory = f;
380+ view = v;
381+ view.setParent(this);
382+ // initially layout to the preferred size
383+ setSize(view.getPreferredSpan(X_AXIS), view.getPreferredSpan(Y_AXIS));
384+ }
385+
386+ /**
387+ * Fetches the attributes to use when rendering. At the root
388+ * level there are no attributes. If an attribute is resolved
389+ * up the view hierarchy this is the end of the line.
390+ */
391+ public AttributeSet getAttributes() {
392+ return null;
393+ }
394+
395+ /**
396+ * Determines the preferred span for this view along an axis.
397+ *
398+ * @param axis may be either X_AXIS or Y_AXIS
399+ * @return the span the view would like to be rendered into.
400+ * Typically the view is told to render into the span
401+ * that is returned, although there is no guarantee.
402+ * The parent may choose to resize or break the view.
403+ */
404+ public float getPreferredSpan(int axis) {
405+ if (axis == X_AXIS) {
406+ // width currently laid out to
407+ return width;
408+ }
409+ return view.getPreferredSpan(axis);
410+ }
411+
412+ /**
413+ * Determines the minimum span for this view along an axis.
414+ *
415+ * @param axis may be either X_AXIS or Y_AXIS
416+ * @return the span the view would like to be rendered into.
417+ * Typically the view is told to render into the span
418+ * that is returned, although there is no guarantee.
419+ * The parent may choose to resize or break the view.
420+ */
421+ public float getMinimumSpan(int axis) {
422+ return view.getMinimumSpan(axis);
423+ }
424+
425+ /**
426+ * Determines the maximum span for this view along an axis.
427+ *
428+ * @param axis may be either X_AXIS or Y_AXIS
429+ * @return the span the view would like to be rendered into.
430+ * Typically the view is told to render into the span
431+ * that is returned, although there is no guarantee.
432+ * The parent may choose to resize or break the view.
433+ */
434+ public float getMaximumSpan(int axis) {
435+ return Integer.MAX_VALUE;
436+ }
437+
438+ /**
439+ * Specifies that a preference has changed.
440+ * Child views can call this on the parent to indicate that
441+ * the preference has changed. The root view routes this to
442+ * invalidate on the hosting component.
443+ * <p>
444+ * This can be called on a different thread from the
445+ * event dispatching thread and is basically unsafe to
446+ * propagate into the component. To make this safe,
447+ * the operation is transferred over to the event dispatching
448+ * thread for completion. It is a design goal that all view
449+ * methods be safe to call without concern for concurrency,
450+ * and this behavior helps make that true.
451+ *
452+ * @param child the child view
453+ * @param width true if the width preference has changed
454+ * @param height true if the height preference has changed
455+ */
456+ public void preferenceChanged(View child, boolean width, boolean height) {
457+ host.revalidate();
458+ host.repaint();
459+ }
460+
461+ /**
462+ * Determines the desired alignment for this view along an axis.
463+ *
464+ * @param axis may be either X_AXIS or Y_AXIS
465+ * @return the desired alignment, where 0.0 indicates the origin
466+ * and 1.0 the full span away from the origin
467+ */
468+ public float getAlignment(int axis) {
469+ return view.getAlignment(axis);
470+ }
471+
472+ /**
473+ * Renders the view.
474+ *
475+ * @param g the graphics context
476+ * @param allocation the region to render into
477+ */
478+ public void paint(Graphics g, Shape allocation) {
479+ Rectangle alloc = allocation.getBounds();
480+ view.setSize(alloc.width, alloc.height);
481+ view.paint(g, allocation);
482+ }
483+
484+ /**
485+ * Sets the view parent.
486+ *
487+ * @param parent the parent view
488+ */
489+ public void setParent(View parent) {
490+ throw new Error("Can't set parent on root view");
491+ }
492+
493+ /**
494+ * Returns the number of views in this view. Since
495+ * this view simply wraps the root of the view hierarchy
496+ * it has exactly one child.
497+ *
498+ * @return the number of views
499+ * @see #getView
500+ */
501+ public int getViewCount() {
502+ return 1;
503+ }
504+
505+ /**
506+ * Gets the n-th view in this container.
507+ *
508+ * @param n the number of the view to get
509+ * @return the view
510+ */
511+ public View getView(int n) {
512+ return view;
513+ }
514+
515+ /**
516+ * Provides a mapping from the document model coordinate space
517+ * to the coordinate space of the view mapped to it.
518+ *
519+ * @param pos the position to convert
520+ * @param a the allocated region to render into
521+ * @return the bounding box of the given position
522+ */
523+ public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
524+ return view.modelToView(pos, a, b);
525+ }
526+
527+ /**
528+ * Provides a mapping from the document model coordinate space
529+ * to the coordinate space of the view mapped to it.
530+ *
531+ * @param p0 the position to convert >= 0
532+ * @param b0 the bias toward the previous character or the
533+ * next character represented by p0, in case the
534+ * position is a boundary of two views.
535+ * @param p1 the position to convert >= 0
536+ * @param b1 the bias toward the previous character or the
537+ * next character represented by p1, in case the
538+ * position is a boundary of two views.
539+ * @param a the allocated region to render into
540+ * @return the bounding box of the given position is returned
541+ * @exception BadLocationException if the given position does
542+ * not represent a valid location in the associated document
543+ * @exception IllegalArgumentException for an invalid bias argument
544+ * @see View#viewToModel
545+ */
546+ public Shape modelToView(int p0, Position.Bias b0, int p1,
547+ Position.Bias b1, Shape a) throws BadLocationException {
548+ return view.modelToView(p0, b0, p1, b1, a);
549+ }
550+
551+ /**
552+ * Provides a mapping from the view coordinate space to the logical
553+ * coordinate space of the model.
554+ *
555+ * @param x x coordinate of the view location to convert
556+ * @param y y coordinate of the view location to convert
557+ * @param a the allocated region to render into
558+ * @return the location within the model that best represents the
559+ * given point in the view
560+ */
561+ public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
562+ return view.viewToModel(x, y, a, bias);
563+ }
564+
565+ /**
566+ * Returns the document model underlying the view.
567+ *
568+ * @return the model
569+ */
570+ public Document getDocument() {
571+ return view.getDocument();
572+ }
573+
574+ /**
575+ * Returns the starting offset into the model for this view.
576+ *
577+ * @return the starting offset
578+ */
579+ public int getStartOffset() {
580+ return view.getStartOffset();
581+ }
582+
583+ /**
584+ * Returns the ending offset into the model for this view.
585+ *
586+ * @return the ending offset
587+ */
588+ public int getEndOffset() {
589+ return view.getEndOffset();
590+ }
591+
592+ /**
593+ * Gets the element that this view is mapped to.
594+ *
595+ * @return the view
596+ */
597+ public Element getElement() {
598+ return view.getElement();
599+ }
600+
601+ /**
602+ * Sets the view size.
603+ *
604+ * @param width the width
605+ * @param height the height
606+ */
607+ public void setSize(float width, float height) {
608+ this.width = (int) width;
609+ view.setSize(width, height);
610+ }
611+
612+ /**
613+ * Fetches the container hosting the view. This is useful for
614+ * things like scheduling a repaint, finding out the host
615+ * components font, etc. The default implementation
616+ * of this is to forward the query to the parent view.
617+ *
618+ * @return the container
619+ */
620+ public Container getContainer() {
621+ return host;
622+ }
623+
624+ /**
625+ * Fetches the factory to be used for building the
626+ * various view fragments that make up the view that
627+ * represents the model. This is what determines
628+ * how the model will be represented. This is implemented
629+ * to fetch the factory provided by the associated
630+ * EditorKit.
631+ *
632+ * @return the factory
633+ */
634+ public ViewFactory getViewFactory() {
635+ return factory;
636+ }
637+
638+ private int width;
639+ private View view;
640+ private ViewFactory factory;
641+ private JComponent host;
642+
643+ }
644+}