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.AbstractDocument;
  44: import javax.swing.text.AttributeSet;
  45: import javax.swing.text.BadLocationException;
  46: import javax.swing.text.Caret;
  47: import javax.swing.text.Document;
  48: import javax.swing.text.EditorKit;
  49: import javax.swing.text.Element;
  50: import javax.swing.text.MutableAttributeSet;
  51: import javax.swing.text.SimpleAttributeSet;
  52: import javax.swing.text.Style;
  53: import javax.swing.text.StyleConstants;
  54: import javax.swing.text.StyledDocument;
  55: import javax.swing.text.StyledEditorKit;
  56: 
  57: /**
  58:  * A powerful text component that supports styled content as well as
  59:  * embedding images and components. It is entirely based on a
  60:  * {@link StyledDocument} content model and a {@link StyledEditorKit}.
  61:  *
  62:  * @author Roman Kennke (roman@kennke.org)
  63:  * @author Andrew Selkirk
  64:  */
  65: public class JTextPane
  66:   extends JEditorPane
  67: {
  68:   /**
  69:    * Creates a new <code>JTextPane</code> with a <code>null</code> document.
  70:    */
  71:   public JTextPane()
  72:   {
  73:     super();
  74:   }
  75: 
  76:   /**
  77:    * Creates a new <code>JTextPane</code> and sets the specified
  78:    * <code>document</code>.
  79:    *
  80:    * @param document the content model to use
  81:    */
  82:   public JTextPane(StyledDocument document)
  83:   {
  84:     this();
  85:     setStyledDocument(document);
  86:   }
  87: 
  88:   /**
  89:    * Returns the UI class ID. This is <code>TextPaneUI</code>.
  90:    *
  91:    * @return <code>TextPaneUI</code>
  92:    */
  93:   public String getUIClassID()
  94:   {
  95:     return "TextPaneUI";
  96:   }
  97: 
  98:   /**
  99:    * 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:     SimpleAttributeSet atts = new SimpleAttributeSet();
 218:     atts.addAttribute(StyleConstants.IconAttribute, icon);
 219:     atts.addAttribute(StyleConstants.NameAttribute,
 220:                       StyleConstants.IconElementName);
 221:     try
 222:       {
 223:         getDocument().insertString(getCaret().getDot(), " ", atts);
 224:       }
 225:     catch (BadLocationException ex)
 226:       {
 227:         AssertionError err = new AssertionError("Unexpected bad location");
 228:         err.initCause(ex);
 229:         throw err;
 230:       }
 231:   }
 232: 
 233:   /**
 234:    * Adds a style into the style hierarchy. Unspecified style attributes
 235:    * can be resolved in the <code>parent</code> style, if one is specified.
 236:    *
 237:    * While it is legal to add nameless styles (<code>nm == null</code),
 238:    * you must be aware that the client application is then responsible
 239:    * for managing the style hierarchy, since unnamed styles cannot be
 240:    * looked up by their name.
 241:    *
 242:    * @param nm the name of the style or <code>null</code> if the style should
 243:    *           be unnamed
 244:    * @param parent the parent in which unspecified style attributes are
 245:    *           resolved, or <code>null</code> if that is not necessary
 246:    *
 247:    * @return the newly created <code>Style</code>
 248:    */
 249:   public Style addStyle(String nm, Style parent)
 250:   {
 251:     return getStyledDocument().addStyle(nm, parent);
 252:   }
 253: 
 254:   /**
 255:    * Removes a named <code>Style</code> from the style hierarchy.
 256:    *
 257:    * @param nm the name of the <code>Style</code> to be removed
 258:    */
 259:   public void removeStyle(String nm)
 260:   {
 261:     getStyledDocument().removeStyle(nm);
 262:   }
 263: 
 264:   /**
 265:    * Looks up and returns a named <code>Style</code>.
 266:    *
 267:    * @param nm the name of the <code>Style</code>
 268:    *
 269:    * @return the found <code>Style</code> of <code>null</code> if no such
 270:    *         <code>Style</code> exists
 271:    */
 272:   public Style getStyle(String nm)
 273:   {
 274:     return getStyledDocument().getStyle(nm);
 275:   }
 276: 
 277:   /**
 278:    * Returns the logical style of the paragraph at the current caret position.
 279:    *
 280:    * @return the logical style of the paragraph at the current caret position
 281:    */
 282:   public Style getLogicalStyle()
 283:   {
 284:     return getStyledDocument().getLogicalStyle(getCaretPosition());
 285:   }
 286: 
 287:   /**
 288:    * Sets the logical style for the paragraph at the current caret position.
 289:    *
 290:    * @param style the style to set for the current paragraph
 291:    */
 292:   public void setLogicalStyle(Style style)
 293:   {
 294:     getStyledDocument().setLogicalStyle(getCaretPosition(), style);
 295:   }
 296: 
 297:   /**
 298:    * Returns the text attributes for the character at the current caret
 299:    * position.
 300:    *
 301:    * @return the text attributes for the character at the current caret
 302:    *         position
 303:    */
 304:   public AttributeSet getCharacterAttributes()
 305:   {
 306:     StyledDocument doc = getStyledDocument();
 307:     Element el = doc.getCharacterElement(getCaretPosition());
 308:     return el.getAttributes();
 309:   }
 310: 
 311:   /**
 312:    * Sets text attributes for the current selection. If there is no selection
 313:    * the text attributes are applied to newly inserted text
 314:    *
 315:    * @param attribute the text attributes to set
 316:    * @param replace if <code>true</code>, the attributes of the current
 317:    *     selection are overridden, otherwise they are merged
 318:    *
 319:    * @see #getInputAttributes
 320:    */
 321:   public void setCharacterAttributes(AttributeSet attribute,
 322:                                      boolean replace)
 323:   {
 324:     int dot = getCaret().getDot();
 325:     int start = getSelectionStart();
 326:     int end = getSelectionEnd();
 327:     if (start == dot && end == dot)
 328:       // There is no selection, update insertAttributes instead
 329:       {
 330:     MutableAttributeSet inputAttributes =
 331:       getStyledEditorKit().getInputAttributes();
 332:     inputAttributes.addAttributes(attribute);
 333:       }
 334:     else
 335:       getStyledDocument().setCharacterAttributes(start, end - start, attribute,
 336:                          replace);
 337:   }
 338: 
 339:   /**
 340:    * Returns the text attributes of the paragraph at the current caret
 341:    * position.
 342:    *
 343:    * @return the attributes of the paragraph at the current caret position
 344:    */
 345:   public AttributeSet getParagraphAttributes()
 346:   {
 347:     StyledDocument doc = getStyledDocument();
 348:     Element el = doc.getParagraphElement(getCaretPosition());
 349:     return el.getAttributes();
 350:   }
 351: 
 352:   /**
 353:    * Sets text attributes for the paragraph at the current selection.
 354:    * If there is no selection the text attributes are applied to
 355:    * the paragraph at the current caret position.
 356:    *
 357:    * @param attribute the text attributes to set
 358:    * @param replace if <code>true</code>, the attributes of the current
 359:    *     selection are overridden, otherwise they are merged
 360:    */
 361:   public void setParagraphAttributes(AttributeSet attribute,
 362:                                      boolean replace)
 363:   {
 364:     // TODO
 365:   }
 366: 
 367:   /**
 368:    * Returns the attributes that are applied to newly inserted text.
 369:    * This is a {@link MutableAttributeSet}, so you can easily modify these
 370:    * attributes.
 371:    *
 372:    * @return the attributes that are applied to newly inserted text
 373:    */
 374:   public MutableAttributeSet getInputAttributes()
 375:   {
 376:     return getStyledEditorKit().getInputAttributes();
 377:   }
 378: 
 379:   /**
 380:    * Returns the {@link StyledEditorKit} that is currently used by this
 381:    * <code>JTextPane</code>.
 382:    *
 383:    * @return the current <code>StyledEditorKit</code> of this
 384:    *         <code>JTextPane</code>
 385:    */
 386:   protected final StyledEditorKit getStyledEditorKit()
 387:   {
 388:     return (StyledEditorKit) getEditorKit();
 389:   }
 390: 
 391:   /**
 392:    * Creates the default {@link EditorKit} that is used in
 393:    * <code>JTextPane</code>s. This is an instance of {@link StyledEditorKit}.
 394:    *
 395:    * @return the default {@link EditorKit} that is used in
 396:    *         <code>JTextPane</code>s
 397:    */
 398:   protected EditorKit createDefaultEditorKit()
 399:   {
 400:     return new StyledEditorKit();
 401:   }
 402: 
 403:   /**
 404:    * Sets the {@link EditorKit} to use for this <code>JTextPane</code>.
 405:    * <code>JTextPane</code>s can only handle {@link StyledEditorKit}s,
 406:    * if client programs try to set a different type of <code>EditorKit</code>
 407:    * then an IllegalArgumentException is thrown
 408:    *
 409:    * @param editor the <code>EditorKit</code> to set
 410:    *
 411:    * @throws IllegalArgumentException if <code>editor</code> is no
 412:    *         <code>StyledEditorKit</code>
 413:    */
 414:   public final void setEditorKit(EditorKit editor)
 415:   {
 416:     if (!(editor instanceof StyledEditorKit))
 417:       throw new IllegalArgumentException
 418:         ("JTextPanes can only handle StyledEditorKits");
 419:     super.setEditorKit(editor);
 420:   }
 421: 
 422:   /**
 423:    * Returns a param string that can be used for debugging.
 424:    *
 425:    * @return a param string that can be used for debugging.
 426:    */
 427:   protected String paramString()
 428:   {
 429:     return super.paramString(); // TODO
 430:   }
 431: }