Source for javax.swing.JTextPane

   1: /* JTextPane.java -- A powerful text widget supporting styled text
   2:    Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing;
  40: 
  41: import java.awt.Component;
  42: 
  43: import javax.swing.text.AttributeSet;
  44: import javax.swing.text.BadLocationException;
  45: import javax.swing.text.Caret;
  46: import javax.swing.text.Document;
  47: import javax.swing.text.EditorKit;
  48: import javax.swing.text.Element;
  49: import javax.swing.text.MutableAttributeSet;
  50: import javax.swing.text.SimpleAttributeSet;
  51: import javax.swing.text.Style;
  52: import javax.swing.text.StyleConstants;
  53: import javax.swing.text.StyledDocument;
  54: import javax.swing.text.StyledEditorKit;
  55: 
  56: /**
  57:  * A powerful text component that supports styled content as well as
  58:  * embedding images and components. It is entirely based on a
  59:  * {@link StyledDocument} content model and a {@link StyledEditorKit}.
  60:  *
  61:  * @author Roman Kennke (roman@kennke.org)
  62:  * @author Andrew Selkirk
  63:  */
  64: public class JTextPane
  65:   extends JEditorPane
  66: {
  67:   /**
  68:    * Creates a new <code>JTextPane</code> with a <code>null</code> document.
  69:    */
  70:   public JTextPane()
  71:   {
  72:     super();
  73:   }
  74: 
  75:   /**
  76:    * Creates a new <code>JTextPane</code> and sets the specified
  77:    * <code>document</code>.
  78:    *
  79:    * @param document the content model to use
  80:    */
  81:   public JTextPane(StyledDocument document)
  82:   {
  83:     this();
  84:     setStyledDocument(document);
  85:   }
  86: 
  87:   /**
  88:    * Returns the UI class ID. This is <code>TextPaneUI</code>.
  89:    *
  90:    * @return <code>TextPaneUI</code>
  91:    */
  92:   public String getUIClassID()
  93:   {
  94:     return "TextPaneUI";
  95:   }
  96: 
  97:   /**
  98:    * Sets the content model for this <code>JTextPane</code>.
  99:    * <code>JTextPane</code> can only be used with {@link StyledDocument}s,
 100:    * if you try to set a different type of <code>Document</code>, an
 101:    * <code>IllegalArgumentException</code> is thrown.
 102:    *
 103:    * @param document the content model to set
 104:    *
 105:    * @throws IllegalArgumentException if <code>document</code> is not an
 106:    *         instance of <code>StyledDocument</code>
 107:    *
 108:    * @see #setStyledDocument
 109:    */
 110:   public void setDocument(Document document)
 111:   {
 112:     if (document != null && !(document instanceof StyledDocument))
 113:       throw new IllegalArgumentException
 114:         ("JTextPane can only handle StyledDocuments");
 115: 
 116:     setStyledDocument((StyledDocument) document);
 117:   }
 118: 
 119:   /**
 120:    * Returns the {@link StyledDocument} that is the content model for
 121:    * this <code>JTextPane</code>. This is a typed wrapper for
 122:    * {@link #getDocument()}.
 123:    *
 124:    * @return the content model of this <code>JTextPane</code>
 125:    */
 126:   public StyledDocument getStyledDocument()
 127:   {
 128:     return (StyledDocument) super.getDocument();
 129:   }
 130: 
 131:   /**
 132:    * Sets the content model for this <code>JTextPane</code>.
 133:    *
 134:    * @param document the content model to set
 135:    */
 136:   public void setStyledDocument(StyledDocument document)
 137:   {
 138:     super.setDocument(document);
 139:   }
 140: 
 141:   /**
 142:    * Replaces the currently selected text with the specified
 143:    * <code>content</code>. If there is no selected text, this results
 144:    * in a simple insertion at the current caret position. If there is
 145:    * no <code>content</code> specified, this results in the selection
 146:    * beeing deleted.
 147:    *
 148:    * @param content the text with which the selection is replaced
 149:    */
 150:   public void replaceSelection(String content)
 151:   {
 152:     Caret caret = getCaret();
 153:     StyledDocument doc = getStyledDocument();
 154: 
 155:     int dot = caret.getDot();
 156:     int mark = caret.getMark();
 157: 
 158:     // If content is empty delete selection.
 159:     if (content == null)
 160:       {
 161:     caret.setDot(dot);
 162:     return;
 163:       }
 164: 
 165:     try
 166:       {
 167:     int start = getSelectionStart();
 168:     int end = getSelectionEnd();
 169:     int contentLength = content.length();
 170: 
 171:     // Remove selected text.
 172:     if (dot != mark)
 173:       doc.remove(start, end - start);
 174: 
 175:     // Insert new text.
 176:     doc.insertString(start, content, null);
 177:     // Set attributes for inserted text
 178:     doc.setCharacterAttributes(start, contentLength, getInputAttributes(),
 179:                    true);
 180: 
 181:       }
 182:     catch (BadLocationException e)
 183:       {
 184:     throw new AssertionError
 185:       ("No BadLocationException should be thrown here");
 186:       }
 187:   }
 188: 
 189:   /**
 190:    * Inserts an AWT or Swing component into the text at the current caret
 191:    * position.
 192:    *
 193:    * @param component the component to be inserted
 194:    */
 195:   public void insertComponent(Component component)
 196:   {
 197:     SimpleAttributeSet atts = new SimpleAttributeSet();
 198:     atts.addAttribute(StyleConstants.ComponentAttribute, component);
 199:     atts.addAttribute(StyleConstants.NameAttribute,
 200:                       StyleConstants.ComponentElementName);
 201:     try
 202:       {
 203:         getDocument().insertString(getCaret().getDot(), " ", atts);
 204:       }
 205:     catch (BadLocationException ex)
 206:       {
 207:         AssertionError err = new AssertionError("Unexpected bad location");
 208:         err.initCause(ex);
 209:         throw err;
 210:       }
 211:   }
 212: 
 213:   /**
 214:    * Inserts an <code>Icon</code> into the text at the current caret position.
 215:    *
 216:    * @param icon the <code>Icon</code> to be inserted
 217:    */
 218:   public void insertIcon(Icon icon)
 219:   {
 220:     SimpleAttributeSet atts = new SimpleAttributeSet();
 221:     atts.addAttribute(StyleConstants.IconAttribute, icon);
 222:     atts.addAttribute(StyleConstants.NameAttribute,
 223:                       StyleConstants.IconElementName);
 224:     try
 225:       {
 226:         getDocument().insertString(getCaret().getDot(), " ", atts);
 227:       }
 228:     catch (BadLocationException ex)
 229:       {
 230:         AssertionError err = new AssertionError("Unexpected bad location");
 231:         err.initCause(ex);
 232:         throw err;
 233:       }
 234:   }
 235: 
 236:   /**
 237:    * Adds a style into the style hierarchy. Unspecified style attributes
 238:    * can be resolved in the <code>parent</code> style, if one is specified.
 239:    *
 240:    * While it is legal to add nameless styles (<code>nm == null</code),
 241:    * you must be aware that the client application is then responsible
 242:    * for managing the style hierarchy, since unnamed styles cannot be
 243:    * looked up by their name.
 244:    *
 245:    * @param nm the name of the style or <code>null</code> if the style should
 246:    *           be unnamed
 247:    * @param parent the parent in which unspecified style attributes are
 248:    *           resolved, or <code>null</code> if that is not necessary
 249:    *
 250:    * @return the newly created <code>Style</code>
 251:    */
 252:   public Style addStyle(String nm, Style parent)
 253:   {
 254:     return getStyledDocument().addStyle(nm, parent);
 255:   }
 256: 
 257:   /**
 258:    * Removes a named <code>Style</code> from the style hierarchy.
 259:    *
 260:    * @param nm the name of the <code>Style</code> to be removed
 261:    */
 262:   public void removeStyle(String nm)
 263:   {
 264:     getStyledDocument().removeStyle(nm);
 265:   }
 266: 
 267:   /**
 268:    * Looks up and returns a named <code>Style</code>.
 269:    *
 270:    * @param nm the name of the <code>Style</code>
 271:    *
 272:    * @return the found <code>Style</code> of <code>null</code> if no such
 273:    *         <code>Style</code> exists
 274:    */
 275:   public Style getStyle(String nm)
 276:   {
 277:     return getStyledDocument().getStyle(nm);
 278:   }
 279: 
 280:   /**
 281:    * Returns the logical style of the paragraph at the current caret position.
 282:    *
 283:    * @return the logical style of the paragraph at the current caret position
 284:    */
 285:   public Style getLogicalStyle()
 286:   {
 287:     return getStyledDocument().getLogicalStyle(getCaretPosition());
 288:   }
 289: 
 290:   /**
 291:    * Sets the logical style for the paragraph at the current caret position.
 292:    *
 293:    * @param style the style to set for the current paragraph
 294:    */
 295:   public void setLogicalStyle(Style style)
 296:   {
 297:     getStyledDocument().setLogicalStyle(getCaretPosition(), style);
 298:   }
 299: 
 300:   /**
 301:    * Returns the text attributes for the character at the current caret
 302:    * position.
 303:    *
 304:    * @return the text attributes for the character at the current caret
 305:    *         position
 306:    */
 307:   public AttributeSet getCharacterAttributes()
 308:   {
 309:     StyledDocument doc = getStyledDocument();
 310:     Element el = doc.getCharacterElement(getCaretPosition());
 311:     return el.getAttributes();
 312:   }
 313: 
 314:   /**
 315:    * Sets text attributes for the current selection. If there is no selection
 316:    * the text attributes are applied to newly inserted text
 317:    *
 318:    * @param attribute the text attributes to set
 319:    * @param replace if <code>true</code>, the attributes of the current
 320:    *     selection are overridden, otherwise they are merged
 321:    *
 322:    * @see #getInputAttributes
 323:    */
 324:   public void setCharacterAttributes(AttributeSet attribute,
 325:                                      boolean replace)
 326:   {
 327:     int dot = getCaret().getDot();
 328:     int start = getSelectionStart();
 329:     int end = getSelectionEnd();
 330:     if (start == dot && end == dot)
 331:       // There is no selection, update insertAttributes instead
 332:       {
 333:     MutableAttributeSet inputAttributes =
 334:       getStyledEditorKit().getInputAttributes();
 335:     inputAttributes.addAttributes(attribute);
 336:       }
 337:     else
 338:       getStyledDocument().setCharacterAttributes(start, end - start, attribute,
 339:                          replace);
 340:   }
 341: 
 342:   /**
 343:    * Returns the text attributes of the paragraph at the current caret
 344:    * position.
 345:    *
 346:    * @return the attributes of the paragraph at the current caret position
 347:    */
 348:   public AttributeSet getParagraphAttributes()
 349:   {
 350:     StyledDocument doc = getStyledDocument();
 351:     Element el = doc.getParagraphElement(getCaretPosition());
 352:     return el.getAttributes();
 353:   }
 354: 
 355:   /**
 356:    * Sets text attributes for the paragraph at the current selection.
 357:    * If there is no selection the text attributes are applied to
 358:    * the paragraph at the current caret position.
 359:    *
 360:    * @param attribute the text attributes to set
 361:    * @param replace if <code>true</code>, the attributes of the current
 362:    *     selection are overridden, otherwise they are merged
 363:    */
 364:   public void setParagraphAttributes(AttributeSet attribute,
 365:                                      boolean replace)
 366:   {
 367:     // TODO
 368:   }
 369: 
 370:   /**
 371:    * Returns the attributes that are applied to newly inserted text.
 372:    * This is a {@link MutableAttributeSet}, so you can easily modify these
 373:    * attributes.
 374:    *
 375:    * @return the attributes that are applied to newly inserted text
 376:    */
 377:   public MutableAttributeSet getInputAttributes()
 378:   {
 379:     return getStyledEditorKit().getInputAttributes();
 380:   }
 381: 
 382:   /**
 383:    * Returns the {@link StyledEditorKit} that is currently used by this
 384:    * <code>JTextPane</code>.
 385:    *
 386:    * @return the current <code>StyledEditorKit</code> of this
 387:    *         <code>JTextPane</code>
 388:    */
 389:   protected final StyledEditorKit getStyledEditorKit()
 390:   {
 391:     return (StyledEditorKit) getEditorKit();
 392:   }
 393: 
 394:   /**
 395:    * Creates the default {@link EditorKit} that is used in
 396:    * <code>JTextPane</code>s. This is an instance of {@link StyledEditorKit}.
 397:    *
 398:    * @return the default {@link EditorKit} that is used in
 399:    *         <code>JTextPane</code>s
 400:    */
 401:   protected EditorKit createDefaultEditorKit()
 402:   {
 403:     return new StyledEditorKit();
 404:   }
 405: 
 406:   /**
 407:    * Sets the {@link EditorKit} to use for this <code>JTextPane</code>.
 408:    * <code>JTextPane</code>s can only handle {@link StyledEditorKit}s,
 409:    * if client programs try to set a different type of <code>EditorKit</code>
 410:    * then an IllegalArgumentException is thrown
 411:    *
 412:    * @param editor the <code>EditorKit</code> to set
 413:    *
 414:    * @throws IllegalArgumentException if <code>editor</code> is no
 415:    *         <code>StyledEditorKit</code>
 416:    */
 417:   public final void setEditorKit(EditorKit editor)
 418:   {
 419:     if (!(editor instanceof StyledEditorKit))
 420:       throw new IllegalArgumentException
 421:         ("JTextPanes can only handle StyledEditorKits");
 422:     super.setEditorKit(editor);
 423:   }
 424: 
 425:   /**
 426:    * Returns a param string that can be used for debugging.
 427:    *
 428:    * @return a param string that can be used for debugging.
 429:    */
 430:   protected String paramString()
 431:   {
 432:     return super.paramString(); // TODO
 433:   }
 434: }