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:     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: }