blob: b5d76af1e482cd25704c7276a143e5b39eaeb2b8 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25package javax.swing.plaf.basic;
26
27import java.awt.*;
28import java.awt.event.*;
29import java.beans.*;
30import java.net.URL;
31import java.net.MalformedURLException;
32import javax.swing.*;
33import javax.swing.text.*;
34import javax.swing.text.html.*;
35import javax.swing.plaf.*;
36import javax.swing.border.*;
37
38
39/**
40 * Provides the look and feel for a JEditorPane.
41 * <p>
42 * <strong>Warning:</strong>
43 * Serialized objects of this class will not be compatible with
44 * future Swing releases. The current serialization support is
45 * appropriate for short term storage or RMI between applications running
46 * the same version of Swing. As of 1.4, support for long term storage
47 * of all JavaBeans<sup><font size="-2">TM</font></sup>
48 * has been added to the <code>java.beans</code> package.
49 * Please see {@link java.beans.XMLEncoder}.
50 *
51 * @author Timothy Prinzing
52 */
53public class BasicEditorPaneUI extends BasicTextUI {
54
55 /**
56 * Creates a UI for the JTextPane.
57 *
58 * @param c the JTextPane component
59 * @return the UI
60 */
61 public static ComponentUI createUI(JComponent c) {
62 return new BasicEditorPaneUI();
63 }
64
65 /**
66 * Creates a new BasicEditorPaneUI.
67 */
68 public BasicEditorPaneUI() {
69 super();
70 }
71
72 /**
73 * Fetches the name used as a key to lookup properties through the
74 * UIManager. This is used as a prefix to all the standard
75 * text properties.
76 *
77 * @return the name ("EditorPane")
78 */
79 protected String getPropertyPrefix() {
80 return "EditorPane";
81 }
82
83 /**
84 *{@inheritDoc}
85 *
86 * @since 1.5
87 */
88 public void installUI(JComponent c) {
89 super.installUI(c);
90 updateDisplayProperties(c.getFont(),
91 c.getForeground());
92 }
93
94 /**
95 *{@inheritDoc}
96 *
97 * @since 1.5
98 */
99 public void uninstallUI(JComponent c) {
100 cleanDisplayProperties();
101 super.uninstallUI(c);
102 }
103
104 /**
105 * Fetches the EditorKit for the UI. This is whatever is
106 * currently set in the associated JEditorPane.
107 *
108 * @return the editor capabilities
109 * @see TextUI#getEditorKit
110 */
111 public EditorKit getEditorKit(JTextComponent tc) {
112 JEditorPane pane = (JEditorPane) getComponent();
113 return pane.getEditorKit();
114 }
115
116 /**
117 * Fetch an action map to use. The map for a JEditorPane
118 * is not shared because it changes with the EditorKit.
119 */
120 ActionMap getActionMap() {
121 ActionMap am = new ActionMapUIResource();
122 am.put("requestFocus", new FocusAction());
123 EditorKit editorKit = getEditorKit(getComponent());
124 if (editorKit != null) {
125 Action[] actions = editorKit.getActions();
126 if (actions != null) {
127 addActions(am, actions);
128 }
129 }
130 am.put(TransferHandler.getCutAction().getValue(Action.NAME),
131 TransferHandler.getCutAction());
132 am.put(TransferHandler.getCopyAction().getValue(Action.NAME),
133 TransferHandler.getCopyAction());
134 am.put(TransferHandler.getPasteAction().getValue(Action.NAME),
135 TransferHandler.getPasteAction());
136 return am;
137 }
138
139 /**
140 * This method gets called when a bound property is changed
141 * on the associated JTextComponent. This is a hook
142 * which UI implementations may change to reflect how the
143 * UI displays bound properties of JTextComponent subclasses.
144 * This is implemented to rebuild the ActionMap based upon an
145 * EditorKit change.
146 *
147 * @param evt the property change event
148 */
149 protected void propertyChange(PropertyChangeEvent evt) {
150 super.propertyChange(evt);
151 String name = evt.getPropertyName();
152 if ("editorKit".equals(name)) {
153 ActionMap map = SwingUtilities.getUIActionMap(getComponent());
154 if (map != null) {
155 Object oldValue = evt.getOldValue();
156 if (oldValue instanceof EditorKit) {
157 Action[] actions = ((EditorKit)oldValue).getActions();
158 if (actions != null) {
159 removeActions(map, actions);
160 }
161 }
162 Object newValue = evt.getNewValue();
163 if (newValue instanceof EditorKit) {
164 Action[] actions = ((EditorKit)newValue).getActions();
165 if (actions != null) {
166 addActions(map, actions);
167 }
168 }
169 }
170 updateFocusTraversalKeys();
171 } else if ("editable".equals(name)) {
172 updateFocusTraversalKeys();
173 } else if ("foreground".equals(name)
174 || "font".equals(name)
175 || "document".equals(name)
176 || JEditorPane.W3C_LENGTH_UNITS.equals(name)
177 || JEditorPane.HONOR_DISPLAY_PROPERTIES.equals(name)
178 ) {
179 JComponent c = getComponent();
180 updateDisplayProperties(c.getFont(), c.getForeground());
181 if ( JEditorPane.W3C_LENGTH_UNITS.equals(name)
182 || JEditorPane.HONOR_DISPLAY_PROPERTIES.equals(name) ) {
183 modelChanged();
184 }
185 if ("foreground".equals(name)) {
186 Object honorDisplayPropertiesObject = c.
187 getClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES);
188 boolean honorDisplayProperties = false;
189 if (honorDisplayPropertiesObject instanceof Boolean) {
190 honorDisplayProperties =
191 ((Boolean)honorDisplayPropertiesObject).booleanValue();
192 }
193 if (honorDisplayProperties) {
194 modelChanged();
195 }
196 }
197
198
199 }
200 }
201
202 void removeActions(ActionMap map, Action[] actions) {
203 int n = actions.length;
204 for (int i = 0; i < n; i++) {
205 Action a = actions[i];
206 map.remove(a.getValue(Action.NAME));
207 }
208 }
209
210 void addActions(ActionMap map, Action[] actions) {
211 int n = actions.length;
212 for (int i = 0; i < n; i++) {
213 Action a = actions[i];
214 map.put(a.getValue(Action.NAME), a);
215 }
216 }
217
218 void updateDisplayProperties(Font font, Color fg) {
219 JComponent c = getComponent();
220 Object honorDisplayPropertiesObject = c.
221 getClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES);
222 boolean honorDisplayProperties = false;
223 Object w3cLengthUnitsObject = c.getClientProperty(JEditorPane.
224 W3C_LENGTH_UNITS);
225 boolean w3cLengthUnits = false;
226 if (honorDisplayPropertiesObject instanceof Boolean) {
227 honorDisplayProperties =
228 ((Boolean)honorDisplayPropertiesObject).booleanValue();
229 }
230 if (w3cLengthUnitsObject instanceof Boolean) {
231 w3cLengthUnits = ((Boolean)w3cLengthUnitsObject).booleanValue();
232 }
233 if (this instanceof BasicTextPaneUI
234 || honorDisplayProperties) {
235 //using equals because can not use UIResource for Boolean
236 Document doc = getComponent().getDocument();
237 if (doc instanceof StyledDocument) {
238 if (doc instanceof HTMLDocument
239 && honorDisplayProperties) {
240 updateCSS(font, fg);
241 } else {
242 updateStyle(font, fg);
243 }
244 }
245 } else {
246 cleanDisplayProperties();
247 }
248 if ( w3cLengthUnits ) {
249 Document doc = getComponent().getDocument();
250 if (doc instanceof HTMLDocument) {
251 StyleSheet documentStyleSheet =
252 ((HTMLDocument)doc).getStyleSheet();
253 documentStyleSheet.addRule("W3C_LENGTH_UNITS_ENABLE");
254 }
255 } else {
256 Document doc = getComponent().getDocument();
257 if (doc instanceof HTMLDocument) {
258 StyleSheet documentStyleSheet =
259 ((HTMLDocument)doc).getStyleSheet();
260 documentStyleSheet.addRule("W3C_LENGTH_UNITS_DISABLE");
261 }
262
263 }
264 }
265
266 /**
267 * Attribute key to reference the default font.
268 * used in javax.swing.text.StyleContext.getFont
269 * to resolve the default font.
270 */
271 private static final String FONT_ATTRIBUTE_KEY = "FONT_ATTRIBUTE_KEY";
272
273 void cleanDisplayProperties() {
274 Document document = getComponent().getDocument();
275 if (document instanceof HTMLDocument) {
276 StyleSheet documentStyleSheet =
277 ((HTMLDocument)document).getStyleSheet();
278 StyleSheet[] styleSheets = documentStyleSheet.getStyleSheets();
279 if (styleSheets != null) {
280 for (StyleSheet s : styleSheets) {
281 if (s instanceof StyleSheetUIResource) {
282 documentStyleSheet.removeStyleSheet(s);
283 documentStyleSheet.addRule("BASE_SIZE_DISABLE");
284 break;
285 }
286 }
287 }
288 Style style = ((StyledDocument) document).getStyle(StyleContext.DEFAULT_STYLE);
289 if (style.getAttribute(FONT_ATTRIBUTE_KEY) != null) {
290 style.removeAttribute(FONT_ATTRIBUTE_KEY);
291 }
292 }
293 }
294
295 static class StyleSheetUIResource extends StyleSheet implements UIResource {
296 }
297
298 private void updateCSS(Font font, Color fg) {
299 JTextComponent component = getComponent();
300 Document document = component.getDocument();
301 if (document instanceof HTMLDocument) {
302 StyleSheet styleSheet = new StyleSheetUIResource();
303 StyleSheet documentStyleSheet =
304 ((HTMLDocument)document).getStyleSheet();
305 StyleSheet[] styleSheets = documentStyleSheet.getStyleSheets();
306 if (styleSheets != null) {
307 for (StyleSheet s : styleSheets) {
308 if (s instanceof StyleSheetUIResource) {
309 documentStyleSheet.removeStyleSheet(s);
310 }
311 }
312 }
313 String cssRule = sun.swing.
314 SwingUtilities2.displayPropertiesToCSS(font,
315 fg);
316 styleSheet.addRule(cssRule);
317 documentStyleSheet.addStyleSheet(styleSheet);
318 documentStyleSheet.addRule("BASE_SIZE " +
319 component.getFont().getSize());
320 Style style = ((StyledDocument) document).getStyle(StyleContext.DEFAULT_STYLE);
321 if (! font.equals(style.getAttribute(FONT_ATTRIBUTE_KEY))) {
322 style.addAttribute(FONT_ATTRIBUTE_KEY, font);
323 }
324 }
325 }
326
327 private void updateStyle(Font font, Color fg) {
328 updateFont(font);
329 updateForeground(fg);
330 }
331
332 /**
333 * Update the color in the default style of the document.
334 *
335 * @param color the new color to use or null to remove the color attribute
336 * from the document's style
337 */
338 private void updateForeground(Color color) {
339 StyledDocument doc = (StyledDocument)getComponent().getDocument();
340 Style style = doc.getStyle(StyleContext.DEFAULT_STYLE);
341
342 if (style == null) {
343 return;
344 }
345
346 if (color == null) {
347 if (style.getAttribute(StyleConstants.Foreground) != null) {
348 style.removeAttribute(StyleConstants.Foreground);
349 }
350 } else {
351 if (! color.equals(StyleConstants.getForeground(style))) {
352 StyleConstants.setForeground(style, color);
353 }
354 }
355 }
356
357 /**
358 * Update the font in the default style of the document.
359 *
360 * @param font the new font to use or null to remove the font attribute
361 * from the document's style
362 */
363 private void updateFont(Font font) {
364 StyledDocument doc = (StyledDocument)getComponent().getDocument();
365 Style style = doc.getStyle(StyleContext.DEFAULT_STYLE);
366
367 if (style == null) {
368 return;
369 }
370
371 String fontFamily = (String) style.getAttribute(StyleConstants.FontFamily);
372 Integer fontSize = (Integer) style.getAttribute(StyleConstants.FontSize);
373 Boolean isBold = (Boolean) style.getAttribute(StyleConstants.Bold);
374 Boolean isItalic = (Boolean) style.getAttribute(StyleConstants.Italic);
375 Font fontAttribute = (Font) style.getAttribute(FONT_ATTRIBUTE_KEY);
376 if (font == null) {
377 if (fontFamily != null) {
378 style.removeAttribute(StyleConstants.FontFamily);
379 }
380 if (fontSize != null) {
381 style.removeAttribute(StyleConstants.FontSize);
382 }
383 if (isBold != null) {
384 style.removeAttribute(StyleConstants.Bold);
385 }
386 if (isItalic != null) {
387 style.removeAttribute(StyleConstants.Italic);
388 }
389 if (fontAttribute != null) {
390 style.removeAttribute(FONT_ATTRIBUTE_KEY);
391 }
392 } else {
393 if (! font.getName().equals(fontFamily)) {
394 StyleConstants.setFontFamily(style, font.getName());
395 }
396 if (fontSize == null
397 || fontSize.intValue() != font.getSize()) {
398 StyleConstants.setFontSize(style, font.getSize());
399 }
400 if (isBold == null
401 || isBold.booleanValue() != font.isBold()) {
402 StyleConstants.setBold(style, font.isBold());
403 }
404 if (isItalic == null
405 || isItalic.booleanValue() != font.isItalic()) {
406 StyleConstants.setItalic(style, font.isItalic());
407 }
408 if (! font.equals(fontAttribute)) {
409 style.addAttribute(FONT_ATTRIBUTE_KEY, font);
410 }
411 }
412 }
413}