001/* JTextPane.java -- A powerful text widget supporting styled text 002 Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package javax.swing; 040 041import java.awt.Component; 042 043import javax.swing.text.AbstractDocument; 044import javax.swing.text.AttributeSet; 045import javax.swing.text.BadLocationException; 046import javax.swing.text.Caret; 047import javax.swing.text.Document; 048import javax.swing.text.EditorKit; 049import javax.swing.text.Element; 050import javax.swing.text.MutableAttributeSet; 051import javax.swing.text.SimpleAttributeSet; 052import javax.swing.text.Style; 053import javax.swing.text.StyleConstants; 054import javax.swing.text.StyledDocument; 055import javax.swing.text.StyledEditorKit; 056 057/** 058 * A powerful text component that supports styled content as well as 059 * embedding images and components. It is entirely based on a 060 * {@link StyledDocument} content model and a {@link StyledEditorKit}. 061 * 062 * @author Roman Kennke (roman@kennke.org) 063 * @author Andrew Selkirk 064 */ 065public class JTextPane 066 extends JEditorPane 067{ 068 /** 069 * Creates a new <code>JTextPane</code> with a <code>null</code> document. 070 */ 071 public JTextPane() 072 { 073 super(); 074 } 075 076 /** 077 * Creates a new <code>JTextPane</code> and sets the specified 078 * <code>document</code>. 079 * 080 * @param document the content model to use 081 */ 082 public JTextPane(StyledDocument document) 083 { 084 this(); 085 setStyledDocument(document); 086 } 087 088 /** 089 * Returns the UI class ID. This is <code>TextPaneUI</code>. 090 * 091 * @return <code>TextPaneUI</code> 092 */ 093 public String getUIClassID() 094 { 095 return "TextPaneUI"; 096 } 097 098 /** 099 * Sets the content model for this <code>JTextPane</code>. 100 * <code>JTextPane</code> can only be used with {@link StyledDocument}s, 101 * if you try to set a different type of <code>Document</code>, an 102 * <code>IllegalArgumentException</code> is thrown. 103 * 104 * @param document the content model to set 105 * 106 * @throws IllegalArgumentException if <code>document</code> is not an 107 * instance of <code>StyledDocument</code> 108 * 109 * @see #setStyledDocument 110 */ 111 public void setDocument(Document document) 112 { 113 if (document != null && !(document instanceof StyledDocument)) 114 throw new IllegalArgumentException 115 ("JTextPane can only handle StyledDocuments"); 116 117 setStyledDocument((StyledDocument) document); 118 } 119 120 /** 121 * Returns the {@link StyledDocument} that is the content model for 122 * this <code>JTextPane</code>. This is a typed wrapper for 123 * {@link #getDocument()}. 124 * 125 * @return the content model of this <code>JTextPane</code> 126 */ 127 public StyledDocument getStyledDocument() 128 { 129 return (StyledDocument) super.getDocument(); 130 } 131 132 /** 133 * Sets the content model for this <code>JTextPane</code>. 134 * 135 * @param document the content model to set 136 */ 137 public void setStyledDocument(StyledDocument document) 138 { 139 super.setDocument(document); 140 } 141 142 /** 143 * Replaces the currently selected text with the specified 144 * <code>content</code>. If there is no selected text, this results 145 * in a simple insertion at the current caret position. If there is 146 * no <code>content</code> specified, this results in the selection 147 * beeing deleted. 148 * 149 * @param content the text with which the selection is replaced 150 */ 151 public void replaceSelection(String content) 152 { 153 Caret caret = getCaret(); 154 StyledDocument doc = getStyledDocument(); 155 AttributeSet a = getInputAttributes().copyAttributes(); 156 if (doc == null) 157 return; 158 159 int dot = caret.getDot(); 160 int mark = caret.getMark(); 161 162 int p0 = Math.min (dot, mark); 163 int p1 = Math.max (dot, mark); 164 165 try 166 { 167 if (doc instanceof AbstractDocument) 168 ((AbstractDocument)doc).replace(p0, p1 - p0, content, a); 169 else 170 { 171 // Remove selected text. 172 if (dot != mark) 173 doc.remove(p0, p1 - p0); 174 // Insert new text. 175 if (content != null && content.length() > 0) 176 doc.insertString(p0, content, a); 177 } 178 } 179 catch (BadLocationException e) 180 { 181 throw new AssertionError 182 ("No BadLocationException should be thrown here"); 183 } 184 } 185 186 /** 187 * Inserts an AWT or Swing component into the text at the current caret 188 * position. 189 * 190 * @param component the component to be inserted 191 */ 192 public void insertComponent(Component component) 193 { 194 SimpleAttributeSet atts = new SimpleAttributeSet(); 195 atts.addAttribute(StyleConstants.ComponentAttribute, component); 196 atts.addAttribute(StyleConstants.NameAttribute, 197 StyleConstants.ComponentElementName); 198 try 199 { 200 getDocument().insertString(getCaret().getDot(), " ", atts); 201 } 202 catch (BadLocationException ex) 203 { 204 AssertionError err = new AssertionError("Unexpected bad location"); 205 err.initCause(ex); 206 throw err; 207 } 208 } 209 210 /** 211 * Inserts an <code>Icon</code> into the text at the current caret position. 212 * 213 * @param icon the <code>Icon</code> to be inserted 214 */ 215 public void insertIcon(Icon icon) 216 { 217 MutableAttributeSet inputAtts = getInputAttributes(); 218 inputAtts.removeAttributes(inputAtts); 219 StyleConstants.setIcon(inputAtts, icon); 220 replaceSelection(" "); 221 inputAtts.removeAttributes(inputAtts); 222 } 223 224 /** 225 * Adds a style into the style hierarchy. Unspecified style attributes 226 * can be resolved in the <code>parent</code> style, if one is specified. 227 * 228 * While it is legal to add nameless styles (<code>nm == null</code), 229 * you must be aware that the client application is then responsible 230 * for managing the style hierarchy, since unnamed styles cannot be 231 * looked up by their name. 232 * 233 * @param nm the name of the style or <code>null</code> if the style should 234 * be unnamed 235 * @param parent the parent in which unspecified style attributes are 236 * resolved, or <code>null</code> if that is not necessary 237 * 238 * @return the newly created <code>Style</code> 239 */ 240 public Style addStyle(String nm, Style parent) 241 { 242 return getStyledDocument().addStyle(nm, parent); 243 } 244 245 /** 246 * Removes a named <code>Style</code> from the style hierarchy. 247 * 248 * @param nm the name of the <code>Style</code> to be removed 249 */ 250 public void removeStyle(String nm) 251 { 252 getStyledDocument().removeStyle(nm); 253 } 254 255 /** 256 * Looks up and returns a named <code>Style</code>. 257 * 258 * @param nm the name of the <code>Style</code> 259 * 260 * @return the found <code>Style</code> of <code>null</code> if no such 261 * <code>Style</code> exists 262 */ 263 public Style getStyle(String nm) 264 { 265 return getStyledDocument().getStyle(nm); 266 } 267 268 /** 269 * Returns the logical style of the paragraph at the current caret position. 270 * 271 * @return the logical style of the paragraph at the current caret position 272 */ 273 public Style getLogicalStyle() 274 { 275 return getStyledDocument().getLogicalStyle(getCaretPosition()); 276 } 277 278 /** 279 * Sets the logical style for the paragraph at the current caret position. 280 * 281 * @param style the style to set for the current paragraph 282 */ 283 public void setLogicalStyle(Style style) 284 { 285 getStyledDocument().setLogicalStyle(getCaretPosition(), style); 286 } 287 288 /** 289 * Returns the text attributes for the character at the current caret 290 * position. 291 * 292 * @return the text attributes for the character at the current caret 293 * position 294 */ 295 public AttributeSet getCharacterAttributes() 296 { 297 StyledDocument doc = getStyledDocument(); 298 Element el = doc.getCharacterElement(getCaretPosition()); 299 return el.getAttributes(); 300 } 301 302 /** 303 * Sets text attributes for the current selection. If there is no selection 304 * the text attributes are applied to newly inserted text 305 * 306 * @param attribute the text attributes to set 307 * @param replace if <code>true</code>, the attributes of the current 308 * selection are overridden, otherwise they are merged 309 * 310 * @see #getInputAttributes 311 */ 312 public void setCharacterAttributes(AttributeSet attribute, 313 boolean replace) 314 { 315 int dot = getCaret().getDot(); 316 int start = getSelectionStart(); 317 int end = getSelectionEnd(); 318 if (start == dot && end == dot) 319 // There is no selection, update insertAttributes instead 320 { 321 MutableAttributeSet inputAttributes = 322 getStyledEditorKit().getInputAttributes(); 323 if (replace) 324 inputAttributes.removeAttributes(inputAttributes); 325 inputAttributes.addAttributes(attribute); 326 } 327 else 328 getStyledDocument().setCharacterAttributes(start, end - start, attribute, 329 replace); 330 } 331 332 /** 333 * Returns the text attributes of the paragraph at the current caret 334 * position. 335 * 336 * @return the attributes of the paragraph at the current caret position 337 */ 338 public AttributeSet getParagraphAttributes() 339 { 340 StyledDocument doc = getStyledDocument(); 341 Element el = doc.getParagraphElement(getCaretPosition()); 342 return el.getAttributes(); 343 } 344 345 /** 346 * Sets text attributes for the paragraph at the current selection. 347 * If there is no selection the text attributes are applied to 348 * the paragraph at the current caret position. 349 * 350 * @param attribute the text attributes to set 351 * @param replace if <code>true</code>, the attributes of the current 352 * selection are overridden, otherwise they are merged 353 */ 354 public void setParagraphAttributes(AttributeSet attribute, 355 boolean replace) 356 { 357 // TODO 358 } 359 360 /** 361 * Returns the attributes that are applied to newly inserted text. 362 * This is a {@link MutableAttributeSet}, so you can easily modify these 363 * attributes. 364 * 365 * @return the attributes that are applied to newly inserted text 366 */ 367 public MutableAttributeSet getInputAttributes() 368 { 369 return getStyledEditorKit().getInputAttributes(); 370 } 371 372 /** 373 * Returns the {@link StyledEditorKit} that is currently used by this 374 * <code>JTextPane</code>. 375 * 376 * @return the current <code>StyledEditorKit</code> of this 377 * <code>JTextPane</code> 378 */ 379 protected final StyledEditorKit getStyledEditorKit() 380 { 381 return (StyledEditorKit) getEditorKit(); 382 } 383 384 /** 385 * Creates the default {@link EditorKit} that is used in 386 * <code>JTextPane</code>s. This is an instance of {@link StyledEditorKit}. 387 * 388 * @return the default {@link EditorKit} that is used in 389 * <code>JTextPane</code>s 390 */ 391 protected EditorKit createDefaultEditorKit() 392 { 393 return new StyledEditorKit(); 394 } 395 396 /** 397 * Sets the {@link EditorKit} to use for this <code>JTextPane</code>. 398 * <code>JTextPane</code>s can only handle {@link StyledEditorKit}s, 399 * if client programs try to set a different type of <code>EditorKit</code> 400 * then an IllegalArgumentException is thrown 401 * 402 * @param editor the <code>EditorKit</code> to set 403 * 404 * @throws IllegalArgumentException if <code>editor</code> is no 405 * <code>StyledEditorKit</code> 406 */ 407 public final void setEditorKit(EditorKit editor) 408 { 409 if (!(editor instanceof StyledEditorKit)) 410 throw new IllegalArgumentException 411 ("JTextPanes can only handle StyledEditorKits"); 412 super.setEditorKit(editor); 413 } 414 415 /** 416 * Returns a param string that can be used for debugging. 417 * 418 * @return a param string that can be used for debugging. 419 */ 420 protected String paramString() 421 { 422 return super.paramString(); // TODO 423 } 424}