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; 26 27 import java.awt.*; 28 import java.awt.event.ActionEvent; 29 30 import java.io.ObjectOutputStream; 31 import java.io.ObjectInputStream; 32 import java.io.IOException; 33 34 import javax.swing.text.*; 35 import javax.swing.event.*; 36 import javax.swing.plaf.*; 37 38 /** 39 * A text component that can be marked up with attributes that are 40 * represented graphically. 41 * You can find how-to information and examples of using text panes in 42 * <a href="http://docs.oracle.com/javase/tutorial/uiswing/components/text.html">Using Text Components</a>, 43 * a section in <em>The Java Tutorial.</em> 44 * 45 * <p> 46 * This component models paragraphs 47 * that are composed of runs of character level attributes. Each 48 * paragraph may have a logical style attached to it which contains 49 * the default attributes to use if not overridden by attributes set 50 * on the paragraph or character run. Components and images may 51 * be embedded in the flow of text. 52 * 53 * <dl> 54 * <dt><b>Newlines</b> 55 * <dd> 56 * For a discussion on how newlines are handled, see 57 * <a href="text/DefaultEditorKit.html">DefaultEditorKit</a>. 58 * </dl> 59 * 60 * <p> 61 * <strong>Warning:</strong> Swing is not thread safe. For more 62 * information see <a 63 * href="package-summary.html#threading">Swing's Threading 64 * Policy</a>. 65 * <p> 66 * <strong>Warning:</strong> 67 * Serialized objects of this class will not be compatible with 68 * future Swing releases. The current serialization support is 69 * appropriate for short term storage or RMI between applications running 70 * the same version of Swing. As of 1.4, support for long term storage 71 * of all JavaBeans™ 72 * has been added to the <code>java.beans</code> package. 73 * Please see {@link java.beans.XMLEncoder}. 74 * 75 * @beaninfo 76 * attribute: isContainer true 77 * description: A text component that can be marked up with attributes that are graphically represented. 78 * 79 * @author Timothy Prinzing 80 * @see javax.swing.text.StyledEditorKit 81 * @since 1.2 82 */ 83 @SuppressWarnings("serial") // Same-version serialization only 84 public class JTextPane extends JEditorPane { 85 86 /** 87 * Creates a new <code>JTextPane</code>. A new instance of 88 * <code>StyledEditorKit</code> is 89 * created and set, and the document model set to <code>null</code>. 90 */ 91 public JTextPane() { 92 super(); 93 EditorKit editorKit = createDefaultEditorKit(); 94 String contentType = editorKit.getContentType(); 95 if (contentType != null 96 && getEditorKitClassNameForContentType(contentType) == 97 defaultEditorKitMap.get(contentType)) { 98 setEditorKitForContentType(contentType, editorKit); 99 } 100 setEditorKit(editorKit); 101 } 102 103 /** 104 * Creates a new <code>JTextPane</code>, with a specified document model. 105 * A new instance of <code>javax.swing.text.StyledEditorKit</code> 106 * is created and set. 107 * 108 * @param doc the document model 109 */ 110 public JTextPane(StyledDocument doc) { 111 this(); 112 setStyledDocument(doc); 113 } 114 115 /** 116 * Returns the class ID for the UI. 117 * 118 * @return the string "TextPaneUI" 119 * 120 * @see JComponent#getUIClassID 121 * @see UIDefaults#getUI 122 */ 123 public String getUIClassID() { 124 return uiClassID; 125 } 126 127 /** 128 * Associates the editor with a text document. This 129 * must be a <code>StyledDocument</code>. 130 * 131 * @param doc the document to display/edit 132 * @exception IllegalArgumentException if <code>doc</code> can't 133 * be narrowed to a <code>StyledDocument</code> which is the 134 * required type of model for this text component 135 */ 136 public void setDocument(Document doc) { 137 if (doc instanceof StyledDocument) { 138 super.setDocument(doc); 139 } else { 140 throw new IllegalArgumentException("Model must be StyledDocument"); 141 } 142 } 143 144 /** 145 * Associates the editor with a text document. 146 * The currently registered factory is used to build a view for 147 * the document, which gets displayed by the editor. 148 * 149 * @param doc the document to display/edit 150 */ 151 public void setStyledDocument(StyledDocument doc) { 152 super.setDocument(doc); 153 } 154 155 /** 156 * Fetches the model associated with the editor. 157 * 158 * @return the model 159 */ 160 public StyledDocument getStyledDocument() { 161 return (StyledDocument) getDocument(); 162 } 163 164 /** 165 * Replaces the currently selected content with new content 166 * represented by the given string. If there is no selection 167 * this amounts to an insert of the given text. If there 168 * is no replacement text this amounts to a removal of the 169 * current selection. The replacement text will have the 170 * attributes currently defined for input at the point of 171 * insertion. If the document is not editable, beep and return. 172 * 173 * @param content the content to replace the selection with 174 */ 175 @Override 176 public void replaceSelection(String content) { 177 replaceSelection(content, true); 178 } 179 180 private void replaceSelection(String content, boolean checkEditable) { 181 if (checkEditable && !isEditable()) { 182 UIManager.getLookAndFeel().provideErrorFeedback(JTextPane.this); 183 return; 184 } 185 Document doc = getStyledDocument(); 186 if (doc != null) { 187 try { 188 Caret caret = getCaret(); 189 boolean composedTextSaved = saveComposedText(caret.getDot()); 190 int p0 = Math.min(caret.getDot(), caret.getMark()); 191 int p1 = Math.max(caret.getDot(), caret.getMark()); 192 AttributeSet attr = getInputAttributes().copyAttributes(); 193 if (doc instanceof AbstractDocument) { 194 ((AbstractDocument)doc).replace(p0, p1 - p0, content,attr); 195 } 196 else { 197 if (p0 != p1) { 198 doc.remove(p0, p1 - p0); 199 } 200 if (content != null && content.length() > 0) { 201 doc.insertString(p0, content, attr); 202 } 203 } 204 if (composedTextSaved) { 205 restoreComposedText(); 206 } 207 } catch (BadLocationException e) { 208 UIManager.getLookAndFeel().provideErrorFeedback(JTextPane.this); 209 } 210 } 211 } 212 213 /** 214 * Inserts a component into the document as a replacement 215 * for the currently selected content. If there is no 216 * selection the component is effectively inserted at the 217 * current position of the caret. This is represented in 218 * the associated document as an attribute of one character 219 * of content. 220 * <p> 221 * The component given is the actual component used by the 222 * JTextPane. Since components cannot be a child of more than 223 * one container, this method should not be used in situations 224 * where the model is shared by text components. 225 * <p> 226 * The component is placed relative to the text baseline 227 * according to the value returned by 228 * <code>Component.getAlignmentY</code>. For Swing components 229 * this value can be conveniently set using the method 230 * <code>JComponent.setAlignmentY</code>. For example, setting 231 * a value of <code>0.75</code> will cause 75 percent of the 232 * component to be above the baseline, and 25 percent of the 233 * component to be below the baseline. 234 * 235 * @param c the component to insert 236 */ 237 public void insertComponent(Component c) { 238 MutableAttributeSet inputAttributes = getInputAttributes(); 239 inputAttributes.removeAttributes(inputAttributes); 240 StyleConstants.setComponent(inputAttributes, c); 241 replaceSelection(" ", false); 242 inputAttributes.removeAttributes(inputAttributes); 243 } 244 245 /** 246 * Inserts an icon into the document as a replacement 247 * for the currently selected content. If there is no 248 * selection the icon is effectively inserted at the 249 * current position of the caret. This is represented in 250 * the associated document as an attribute of one character 251 * of content. 252 * 253 * @param g the icon to insert 254 * @see Icon 255 */ 256 public void insertIcon(Icon g) { 257 MutableAttributeSet inputAttributes = getInputAttributes(); 258 inputAttributes.removeAttributes(inputAttributes); 259 StyleConstants.setIcon(inputAttributes, g); 260 replaceSelection(" ", false); 261 inputAttributes.removeAttributes(inputAttributes); 262 } 263 264 /** 265 * Adds a new style into the logical style hierarchy. Style attributes 266 * resolve from bottom up so an attribute specified in a child 267 * will override an attribute specified in the parent. 268 * 269 * @param nm the name of the style (must be unique within the 270 * collection of named styles). The name may be <code>null</code> 271 * if the style is unnamed, but the caller is responsible 272 * for managing the reference returned as an unnamed style can't 273 * be fetched by name. An unnamed style may be useful for things 274 * like character attribute overrides such as found in a style 275 * run. 276 * @param parent the parent style. This may be <code>null</code> 277 * if unspecified 278 * attributes need not be resolved in some other style. 279 * @return the new <code>Style</code> 280 */ 281 public Style addStyle(String nm, Style parent) { 282 StyledDocument doc = getStyledDocument(); 283 return doc.addStyle(nm, parent); 284 } 285 286 /** 287 * Removes a named non-<code>null</code> style previously added to 288 * the document. 289 * 290 * @param nm the name of the style to remove 291 */ 292 public void removeStyle(String nm) { 293 StyledDocument doc = getStyledDocument(); 294 doc.removeStyle(nm); 295 } 296 297 /** 298 * Fetches a named non-<code>null</code> style previously added. 299 * 300 * @param nm the name of the style 301 * @return the <code>Style</code> 302 */ 303 public Style getStyle(String nm) { 304 StyledDocument doc = getStyledDocument(); 305 return doc.getStyle(nm); 306 } 307 308 /** 309 * Sets the logical style to use for the paragraph at the 310 * current caret position. If attributes aren't explicitly set 311 * for character and paragraph attributes they will resolve 312 * through the logical style assigned to the paragraph, which 313 * in term may resolve through some hierarchy completely 314 * independent of the element hierarchy in the document. 315 * 316 * @param s the logical style to assign to the paragraph, 317 * or <code>null</code> for no style 318 */ 319 public void setLogicalStyle(Style s) { 320 StyledDocument doc = getStyledDocument(); 321 doc.setLogicalStyle(getCaretPosition(), s); 322 } 323 324 /** 325 * Fetches the logical style assigned to the paragraph represented 326 * by the current position of the caret, or <code>null</code>. 327 * 328 * @return the <code>Style</code> 329 */ 330 public Style getLogicalStyle() { 331 StyledDocument doc = getStyledDocument(); 332 return doc.getLogicalStyle(getCaretPosition()); 333 } 334 335 /** 336 * Fetches the character attributes in effect at the 337 * current location of the caret, or <code>null</code>. 338 * 339 * @return the attributes, or <code>null</code> 340 */ 341 public AttributeSet getCharacterAttributes() { 342 StyledDocument doc = getStyledDocument(); 343 Element run = doc.getCharacterElement(getCaretPosition()); 344 if (run != null) { 345 return run.getAttributes(); 346 } 347 return null; 348 } 349 350 /** 351 * Applies the given attributes to character 352 * content. If there is a selection, the attributes 353 * are applied to the selection range. If there 354 * is no selection, the attributes are applied to 355 * the input attribute set which defines the attributes 356 * for any new text that gets inserted. 357 * 358 * @param attr the attributes 359 * @param replace if true, then replace the existing attributes first 360 */ 361 public void setCharacterAttributes(AttributeSet attr, boolean replace) { 362 int p0 = getSelectionStart(); 363 int p1 = getSelectionEnd(); 364 if (p0 != p1) { 365 StyledDocument doc = getStyledDocument(); 366 doc.setCharacterAttributes(p0, p1 - p0, attr, replace); 367 } else { 368 MutableAttributeSet inputAttributes = getInputAttributes(); 369 if (replace) { 370 inputAttributes.removeAttributes(inputAttributes); 371 } 372 inputAttributes.addAttributes(attr); 373 } 374 } 375 376 /** 377 * Fetches the current paragraph attributes in effect 378 * at the location of the caret, or <code>null</code> if none. 379 * 380 * @return the attributes 381 */ 382 public AttributeSet getParagraphAttributes() { 383 StyledDocument doc = getStyledDocument(); 384 Element paragraph = doc.getParagraphElement(getCaretPosition()); 385 if (paragraph != null) { 386 return paragraph.getAttributes(); 387 } 388 return null; 389 } 390 391 /** 392 * Applies the given attributes to paragraphs. If 393 * there is a selection, the attributes are applied 394 * to the paragraphs that intersect the selection. 395 * If there is no selection, the attributes are applied 396 * to the paragraph at the current caret position. 397 * 398 * @param attr the non-<code>null</code> attributes 399 * @param replace if true, replace the existing attributes first 400 */ 401 public void setParagraphAttributes(AttributeSet attr, boolean replace) { 402 int p0 = getSelectionStart(); 403 int p1 = getSelectionEnd(); 404 StyledDocument doc = getStyledDocument(); 405 doc.setParagraphAttributes(p0, p1 - p0, attr, replace); 406 } 407 408 /** 409 * Gets the input attributes for the pane. 410 * 411 * @return the attributes 412 */ 413 public MutableAttributeSet getInputAttributes() { 414 return getStyledEditorKit().getInputAttributes(); 415 } 416 417 /** 418 * Gets the editor kit. 419 * 420 * @return the editor kit 421 */ 422 protected final StyledEditorKit getStyledEditorKit() { 423 return (StyledEditorKit) getEditorKit(); 424 } 425 426 /** 427 * @see #getUIClassID 428 * @see #readObject 429 */ 430 private static final String uiClassID = "TextPaneUI"; 431 432 433 /** 434 * See <code>readObject</code> and <code>writeObject</code> in 435 * <code>JComponent</code> for more 436 * information about serialization in Swing. 437 * 438 * @param s the output stream 439 */ 440 private void writeObject(ObjectOutputStream s) throws IOException { 441 s.defaultWriteObject(); 442 if (getUIClassID().equals(uiClassID)) { 443 byte count = JComponent.getWriteObjCounter(this); 444 JComponent.setWriteObjCounter(this, --count); 445 if (count == 0 && ui != null) { 446 ui.installUI(this); 447 } 448 } 449 } 450 451 452 // --- JEditorPane ------------------------------------ 453 454 /** 455 * Creates the <code>EditorKit</code> to use by default. This 456 * is implemented to return <code>javax.swing.text.StyledEditorKit</code>. 457 * 458 * @return the editor kit 459 */ 460 protected EditorKit createDefaultEditorKit() { 461 return new StyledEditorKit(); 462 } 463 464 /** 465 * Sets the currently installed kit for handling 466 * content. This is the bound property that 467 * establishes the content type of the editor. 468 * 469 * @param kit the desired editor behavior 470 * @exception IllegalArgumentException if kit is not a 471 * <code>StyledEditorKit</code> 472 */ 473 public final void setEditorKit(EditorKit kit) { 474 if (kit instanceof StyledEditorKit) { 475 super.setEditorKit(kit); 476 } else { 477 throw new IllegalArgumentException("Must be StyledEditorKit"); 478 } 479 } 480 481 /** 482 * Returns a string representation of this <code>JTextPane</code>. 483 * This method 484 * is intended to be used only for debugging purposes, and the 485 * content and format of the returned string may vary between 486 * implementations. The returned string may be empty but may not 487 * be <code>null</code>. 488 * 489 * @return a string representation of this <code>JTextPane</code> 490 */ 491 protected String paramString() { 492 return super.paramString(); 493 } 494 495 }