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</code> 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 }