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