Source for javax.swing.JEditorPane

   1: /* JEditorPane.java --
   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.Dimension;
  42: import java.io.IOException;
  43: import java.io.InputStream;
  44: import java.io.InputStreamReader;
  45: import java.io.Reader;
  46: import java.net.MalformedURLException;
  47: import java.net.URL;
  48: 
  49: import javax.accessibility.AccessibleContext;
  50: import javax.accessibility.AccessibleHyperlink;
  51: import javax.accessibility.AccessibleHypertext;
  52: import javax.accessibility.AccessibleStateSet;
  53: import javax.accessibility.AccessibleText;
  54: import javax.swing.event.HyperlinkEvent;
  55: import javax.swing.event.HyperlinkListener;
  56: import javax.swing.text.BadLocationException;
  57: import javax.swing.text.DefaultEditorKit;
  58: import javax.swing.text.Document;
  59: import javax.swing.text.EditorKit;
  60: import javax.swing.text.Element;
  61: import javax.swing.text.JTextComponent;
  62: import javax.swing.text.html.HTML;
  63: import javax.swing.text.html.HTMLDocument;
  64: import javax.swing.text.html.HTMLEditorKit;
  65: 
  66: /**
  67:  * A powerful text editor component that can handle different types of
  68:  * content.
  69:  *
  70:  * The JEditorPane text component is driven by an instance of
  71:  * {@link EditorKit}. The editor kit is responsible for providing
  72:  * a default {@link Document} implementation, a mechanism for loading
  73:  * and saving documents of its supported content type and providing
  74:  * a set of {@link Action}s for manipulating the content.
  75:  *
  76:  * By default the following content types are supported:
  77:  * <ul>
  78:  * <li><code>text/plain</code>: Plain text, handled by
  79:  *   {@link javax.swing.text.DefaultEditorKit}.</li>
  80:  * <li><code>text/html</code>: HTML 4.0 styled text, handled by
  81:  *   {@link javax.swing.text.html.HTMLEditorKit}.</li>
  82:  * <li><code>text/rtf</code>: RTF text, handled by
  83:  *   {@link javax.swing.text.rtf.RTFEditorKit}.</li>
  84:  * </ul>
  85:  *
  86:  * @author original author unknown
  87:  * @author Roman Kennke (roman@kennke.org)
  88:  */
  89: public class JEditorPane extends JTextComponent
  90: {
  91:   /**
  92:    * Provides accessibility support for <code>JEditorPane</code>.
  93:    *
  94:    * @author Roman Kennke (kennke@aicas.com)
  95:    */
  96:   protected class AccessibleJEditorPane extends AccessibleJTextComponent
  97:   {
  98: 
  99:     /**
 100:      * Creates a new <code>AccessibleJEditorPane</code> object.
 101:      */
 102:     protected AccessibleJEditorPane()
 103:     {
 104:       super();
 105:     }
 106: 
 107:     /**
 108:      * Returns a description of this <code>AccessibleJEditorPane</code>. If
 109:      * this property is not set, then this returns the content-type of the
 110:      * editor pane.
 111:      *
 112:      * @return a description of this AccessibleJEditorPane
 113:      */
 114:     public String getAccessibleDescription()
 115:     {
 116:       String descr = super.getAccessibleDescription(); 
 117:       if (descr == null)
 118:         return getContentType();
 119:       else
 120:         return descr;
 121:     }
 122: 
 123:     /**
 124:      * Returns the accessible state of this <code>AccessibleJEditorPane</code>.
 125:      *
 126:      * @return  the accessible state of this <code>AccessibleJEditorPane</code>
 127:      */
 128:     public AccessibleStateSet getAccessibleStateSet()
 129:     {
 130:       AccessibleStateSet state = super.getAccessibleStateSet();
 131:       // TODO: Figure out what state must be added here to the super's state.
 132:       return state;
 133:     }
 134:   }
 135: 
 136:   /**
 137:    * Provides accessibility support for <code>JEditorPane</code>s, when the
 138:    * editor kit is an instance of {@link HTMLEditorKit}.
 139:    *
 140:    * @author Roman Kennke (kennke@aicas.com)
 141:    */
 142:   protected class AccessibleJEditorPaneHTML extends AccessibleJEditorPane
 143:   {
 144:     /**
 145:      * Returns the accessible text of the <code>JEditorPane</code>. This will
 146:      * be an instance of
 147:      * {@link JEditorPaneAccessibleHypertextSupport}.
 148:      *
 149:      * @return the accessible text of the <code>JEditorPane</code>
 150:      */
 151:     public AccessibleText getAccessibleText()
 152:     {
 153:       return new JEditorPaneAccessibleHypertextSupport();
 154:     }
 155:   }
 156: 
 157:   /**
 158:    * This is the accessible text that is returned by
 159:    * {@link AccessibleJEditorPaneHTML#getAccessibleText()}.
 160:    *
 161:    * @author Roman Kennke (kennke@aicas.com)
 162:    */
 163:   protected class JEditorPaneAccessibleHypertextSupport
 164:     extends AccessibleJEditorPane implements AccessibleHypertext
 165:   {
 166: 
 167:     /**
 168:      * The accessible representation of a HTML link. 
 169:      *
 170:      * @author Roman Kennke (kennke@aicas.com)
 171:      */
 172:     public class HTMLLink extends AccessibleHyperlink
 173:     {
 174: 
 175:       /**
 176:        * The element in the document that represents the link.
 177:        */
 178:       Element element;
 179: 
 180:       /**
 181:        * Creates a new <code>HTMLLink</code>.
 182:        *
 183:        * @param el the link element
 184:        */
 185:       public HTMLLink(Element el)
 186:       {
 187:         this.element = el;
 188:       }
 189: 
 190:       /**
 191:        * Returns <code>true</code> if this <code>HTMLLink</code> is still
 192:        * valid. A <code>HTMLLink</code> can become invalid when the document
 193:        * changes.
 194:        *
 195:        * @return <code>true</code> if this <code>HTMLLink</code> is still
 196:        *         valid
 197:        */
 198:       public boolean isValid()
 199:       {
 200:         // I test here if the element at our element's start offset is the
 201:         // same as the element in the document at this offset. If this is true,
 202:         // I consider the link valid, if not, then this link no longer
 203:         // represented by this HTMLLink and therefor invalid.
 204:         HTMLDocument doc = (HTMLDocument) getDocument();
 205:         return doc.getCharacterElement(element.getStartOffset()) == element;
 206:       }
 207: 
 208:       /**
 209:        * Returns the number of AccessibleActions in this link object. In
 210:        * general, link have 1 AccessibleAction associated with them. There are
 211:        * special cases where links can have multiple actions associated, like
 212:        * in image maps.
 213:        * 
 214:        * @return the number of AccessibleActions in this link object
 215:        */
 216:       public int getAccessibleActionCount()
 217:       {
 218:         // TODO: Implement the special cases.
 219:         return 1;
 220:       }
 221: 
 222:       /**
 223:        * Performs the specified action on the link object. This ususally means
 224:        * activating the link.
 225:        *
 226:        * @return <code>true</code> if the action has been performed
 227:        *         successfully, <code>false</code> otherwise
 228:        */
 229:       public boolean doAccessibleAction(int i)
 230:       {
 231:         String href = (String) element.getAttributes().getAttribute("href");
 232:         HTMLDocument doc = (HTMLDocument) getDocument();
 233:         try
 234:           {
 235:             URL url = new URL(doc.getBase(), href);
 236:             setPage(url);
 237:             String desc = doc.getText(element.getStartOffset(),
 238:                             element.getEndOffset() - element.getStartOffset());
 239:             HyperlinkEvent ev =
 240:               new HyperlinkEvent(JEditorPane.this,
 241:                                  HyperlinkEvent.EventType.ACTIVATED, url, desc,
 242:                                  element);
 243:             fireHyperlinkUpdate(ev);
 244:             return true;
 245:           }
 246:         catch (Exception ex)
 247:           {
 248:             return false;
 249:           }
 250:       }
 251: 
 252:       /**
 253:        * Returns the description of the action at action index <code>i</code>.
 254:        * This method returns the text within the element associated with this
 255:        * link.
 256:        *
 257:        * @param i the action index
 258:        *
 259:        * @return the description of the action at action index <code>i</code>
 260:        */
 261:       public String getAccessibleActionDescription(int i)
 262:       {
 263:         HTMLDocument doc = (HTMLDocument) getDocument();
 264:         try
 265:           {
 266:             return doc.getText(element.getStartOffset(),
 267:                             element.getEndOffset() - element.getStartOffset());
 268:           }
 269:         catch (BadLocationException ex)
 270:           {
 271:             throw (AssertionError)
 272:             new AssertionError("BadLocationException must not be thrown "
 273:                                + "here.")
 274:               .initCause(ex);
 275:           }
 276:       }
 277: 
 278:       /**
 279:        * Returns an {@link URL} object, that represents the action at action
 280:        * index <code>i</code>.
 281:        *
 282:        * @param i the action index
 283:        *
 284:        * @return an {@link URL} object, that represents the action at action
 285:        *         index <code>i</code>
 286:        */
 287:       public Object getAccessibleActionObject(int i)
 288:       {
 289:         String href = (String) element.getAttributes().getAttribute("href");
 290:         HTMLDocument doc = (HTMLDocument) getDocument();
 291:         try
 292:           {
 293:             URL url = new URL(doc.getBase(), href);
 294:             return url;
 295:           }
 296:         catch (MalformedURLException ex)
 297:           {
 298:             return null;
 299:           }
 300:       }
 301: 
 302:       /**
 303:        * Returns an object that represents the link anchor. For examples, if
 304:        * the link encloses a string, then a <code>String</code> object is
 305:        * returned, if the link encloses an &lt;img&gt; tag, then an
 306:        * <code>ImageIcon</code> object is returned.
 307:        *
 308:        * @return an object that represents the link anchor
 309:        */
 310:       public Object getAccessibleActionAnchor(int i)
 311:       {
 312:         // TODO: This is only the String case. Implement all cases.
 313:         return getAccessibleActionDescription(i);
 314:       }
 315: 
 316:       /**
 317:        * Returns the start index of the hyperlink element.
 318:        *
 319:        * @return the start index of the hyperlink element
 320:        */
 321:       public int getStartIndex()
 322:       {
 323:         return element.getStartOffset();
 324:       }
 325: 
 326:       /**
 327:        * Returns the end index of the hyperlink element.
 328:        *
 329:        * @return the end index of the hyperlink element
 330:        */
 331:       public int getEndIndex()
 332:       {
 333:         return element.getEndOffset();
 334:       }
 335:       
 336:     }
 337: 
 338:     /**
 339:      * Returns the number of hyperlinks in the document.
 340:      *
 341:      * @return the number of hyperlinks in the document
 342:      */
 343:     public int getLinkCount()
 344:     {
 345:       HTMLDocument doc = (HTMLDocument) getDocument();
 346:       HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A);
 347:       int count = 0;
 348:       while (linkIter.isValid())
 349:         {
 350:           count++;
 351:           linkIter.next();
 352:         }
 353:       return count;
 354:     }
 355: 
 356:     /**
 357:      * Returns the <code>i</code>-th hyperlink in the document or
 358:      * <code>null</code> if there is no hyperlink with the specified index.
 359:      *
 360:      * @param i the index of the hyperlink to return
 361:      *
 362:      * @return the <code>i</code>-th hyperlink in the document or
 363:      *         <code>null</code> if there is no hyperlink with the specified
 364:      *         index
 365:      */
 366:     public AccessibleHyperlink getLink(int i)
 367:     {
 368:       HTMLDocument doc = (HTMLDocument) getDocument();
 369:       HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A);
 370:       int count = 0;
 371:       while (linkIter.isValid())
 372:         {
 373:           count++;
 374:           if (count == i)
 375:             break;
 376:           linkIter.next();
 377:         }
 378:       if (linkIter.isValid())
 379:         {
 380:           int offset = linkIter.getStartOffset();
 381:           // TODO: I fetch the element for the link via getCharacterElement().
 382:           // I am not sure that this is correct, maybe we must use
 383:           // getParagraphElement()?
 384:           Element el = doc.getCharacterElement(offset);
 385:           HTMLLink link = new HTMLLink(el);
 386:           return link;
 387:         }
 388:       else
 389:         return null;
 390:     }
 391: 
 392:     /**
 393:      * Returns the index of the link element at the character position
 394:      * <code>c</code> within the document, or <code>-1</code> if there is no
 395:      * link at the specified position.
 396:      *
 397:      * @param c the character index from which to fetch the link index
 398:      *
 399:      * @return the index of the link element at the character position
 400:      *         <code>c</code> within the document, or <code>-1</code> if there
 401:      *         is no link at the specified position
 402:      */
 403:     public int getLinkIndex(int c)
 404:     {
 405:       HTMLDocument doc = (HTMLDocument) getDocument();
 406:       HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A);
 407:       int count = 0;
 408:       while (linkIter.isValid())
 409:         {
 410:           if (linkIter.getStartOffset() <= c && linkIter.getEndOffset() > c)
 411:             break;
 412:           count++;
 413:           linkIter.next();
 414:         }
 415:       if (linkIter.isValid())
 416:         return count;
 417:       else
 418:         return -1;
 419:     }
 420: 
 421:     /**
 422:      * Returns the link text of the link at index <code>i</code>, or
 423:      * <code>null</code>, if there is no link at the specified position.
 424:      *
 425:      * @param i the index of the link
 426:      *
 427:      * @return  the link text of the link at index <code>i</code>, or
 428:      *          <code>null</code>, if there is no link at the specified
 429:      *          position
 430:      */
 431:     public String getLinkText(int i)
 432:     {
 433:       HTMLDocument doc = (HTMLDocument) getDocument();
 434:       HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A);
 435:       int count = 0;
 436:       while (linkIter.isValid())
 437:         {
 438:           count++;
 439:           if (count == i)
 440:             break;
 441:           linkIter.next();
 442:         }
 443:       if (linkIter.isValid())
 444:         {
 445:           int offset = linkIter.getStartOffset();
 446:           // TODO: I fetch the element for the link via getCharacterElement().
 447:           // I am not sure that this is correct, maybe we must use
 448:           // getParagraphElement()?
 449:           Element el = doc.getCharacterElement(offset);
 450:           try
 451:             {
 452:               String text = doc.getText(el.getStartOffset(),
 453:                                       el.getEndOffset() - el.getStartOffset());
 454:               return text;
 455:             }
 456:           catch (BadLocationException ex)
 457:             {
 458:               throw (AssertionError)
 459:                 new AssertionError("BadLocationException must not be thrown "
 460:                                    + "here.")
 461:                   .initCause(ex);
 462:             }
 463:         }
 464:       else
 465:         return null;
 466:     }
 467:   }
 468: 
 469:   private static final long serialVersionUID = 3140472492599046285L;
 470:   
 471:   private URL page;
 472:   private EditorKit editorKit;
 473:   
 474:   boolean focus_root;
 475: 
 476:   public JEditorPane()
 477:   {
 478:     setEditorKit(createDefaultEditorKit());
 479:   }
 480: 
 481:   public JEditorPane(String url) throws IOException
 482:   {
 483:     this(new URL(url));
 484:   }
 485: 
 486:   public JEditorPane(String type, String text)
 487:   {
 488:     setEditorKit(createEditorKitForContentType(type));
 489:     setText(text);
 490:   }
 491: 
 492:   public JEditorPane(URL url) throws IOException
 493:   {
 494:     this();
 495:     setPage(url);
 496:   }
 497: 
 498:   protected EditorKit createDefaultEditorKit()
 499:   {
 500:     return new DefaultEditorKit();
 501:   }
 502: 
 503:   public static EditorKit createEditorKitForContentType(String type)
 504:   {
 505:     return new DefaultEditorKit();
 506:   }
 507: 
 508:   /**
 509:    * Sends a given <code>HyperlinkEvent</code> to all registered listeners.
 510:    *
 511:    * @param event the event to send
 512:    */
 513:   public void fireHyperlinkUpdate(HyperlinkEvent event)
 514:   {
 515:     HyperlinkListener[] listeners = getHyperlinkListeners();
 516: 
 517:     for (int index = 0; index < listeners.length; ++index)
 518:        listeners[index].hyperlinkUpdate(event);
 519:   }
 520: 
 521:   /**
 522:    * Returns the accessible context associated with this editor pane.
 523:    *
 524:    * @return the accessible context associated with this editor pane
 525:    */
 526:   public AccessibleContext getAccessibleContext()
 527:   {
 528:     if (accessibleContext == null)
 529:       {
 530:         if (getEditorKit() instanceof HTMLEditorKit)
 531:           accessibleContext = new AccessibleJEditorPaneHTML();
 532:         else
 533:           accessibleContext = new AccessibleJEditorPane();
 534:       }
 535:     return accessibleContext;
 536:   }
 537: 
 538:   public final String getContentType()
 539:   {
 540:     return getEditorKit().getContentType();
 541:   }
 542: 
 543:   /**
 544:    * Returns the EditorKit. If there is no EditorKit set this method
 545:    * calls createDefaultEditorKit() and setEditorKit() first.
 546:    */
 547:   public EditorKit getEditorKit()
 548:   {
 549:     if (editorKit == null)
 550:       setEditorKit(createDefaultEditorKit());
 551:     return editorKit;
 552:   }
 553: 
 554:   public static String getEditorKitClassNameForContentType(String type)
 555:   {
 556:     return "text/plain";
 557:   }
 558: 
 559:   public EditorKit getEditorKitForContentType(String type)
 560:   {
 561:     return editorKit;
 562:   }
 563: 
 564:   /**
 565:    * Returns the preferred size for the JEditorPane.  
 566:    */
 567:   public Dimension getPreferredSize()
 568:   {
 569:     return super.getPreferredSize();
 570:   }
 571: 
 572:   public boolean getScrollableTracksViewportHeight()
 573:   {
 574:   /*  Container parent = getParent();
 575:     return (parent instanceof JViewport &&
 576:         parent.isValid());*/
 577:     return isValid();
 578:   }
 579: 
 580:   public boolean getScrollableTracksViewportWidth()
 581:   {
 582:     /*Container parent = getParent();
 583:     return (parent instanceof JViewport &&
 584:         parent.isValid());*/
 585:     return isValid();
 586:   }
 587: 
 588:   public URL getPage()
 589:   {
 590:     return page;
 591:   }
 592: 
 593:   protected InputStream getStream(URL page)
 594:     throws IOException
 595:   {
 596:     return page.openStream();
 597:   }
 598: 
 599:   public String getText()
 600:   {
 601:     return super.getText();
 602:   }
 603: 
 604:   public String getUIClassID()
 605:   {
 606:     return "EditorPaneUI";
 607:   }
 608: 
 609:   public boolean isFocusCycleRoot()
 610:   {
 611:     return focus_root;
 612:   }
 613: 
 614:   protected String paramString()
 615:   {
 616:     return "JEditorPane";
 617:   }
 618: 
 619:   /**
 620:    * This method initializes from a stream. 
 621:    */
 622:   public void read(InputStream in, Object desc) throws IOException
 623:   {
 624:     EditorKit kit = getEditorKit();
 625:     if (kit instanceof HTMLEditorKit && desc instanceof HTMLDocument)
 626:       {
 627:         Document doc = (Document) desc;
 628:         try
 629:           {
 630:             kit.read(in, doc, 0);
 631:           }
 632:         catch (BadLocationException ex)
 633:           {
 634:             assert false : "BadLocationException must not be thrown here.";
 635:           }
 636:       }
 637:     else
 638:       {
 639:         Reader inRead = new InputStreamReader(in);
 640:         super.read(inRead, desc);
 641:       }
 642:   }
 643: 
 644:   /**
 645:    * Establishes the default bindings of type to classname. 
 646:    */
 647:   public static void registerEditorKitForContentType(String type,
 648:                                                      String classname)
 649:   {
 650:     // TODO: Implement this properly.
 651:   }
 652: 
 653:   /**
 654:    * Establishes the default bindings of type to classname.
 655:    */
 656:   public static void registerEditorKitForContentType(String type,
 657:                                                      String classname,
 658:                                                      ClassLoader loader)
 659:   {
 660:     // TODO: Implement this properly.
 661:   }
 662: 
 663:   /**
 664:    * Replaces the currently selected content with new content represented
 665:    * by the given string.
 666:    */
 667:   public void replaceSelection(String content)
 668:   {
 669:     // TODO: Implement this properly.
 670:   }
 671: 
 672:   /**
 673:    * Scrolls the view to the given reference location (that is, the value
 674:    * returned by the UL.getRef method for the URL being displayed).
 675:    */
 676:   public void scrollToReference(String reference)
 677:   {
 678:     // TODO: Implement this properly.
 679:   }
 680: 
 681:   public final void setContentType(String type)
 682:   {
 683:     if (editorKit != null
 684:     && editorKit.getContentType().equals(type))
 685:       return;
 686:               
 687:     EditorKit kit = getEditorKitForContentType(type);
 688:             
 689:     if (kit != null)
 690:       setEditorKit(kit);
 691:   }
 692: 
 693:   public void setEditorKit(EditorKit newValue)
 694:   {
 695:     if (editorKit == newValue)
 696:       return;
 697:         
 698:     if (editorKit != null)
 699:       editorKit.deinstall(this);
 700:                 
 701:     EditorKit oldValue = editorKit;
 702:     editorKit = newValue;
 703:                     
 704:     if (editorKit != null)
 705:       {
 706:     editorKit.install(this);
 707:     setDocument(editorKit.createDefaultDocument());
 708:       }
 709:                             
 710:     firePropertyChange("editorKit", oldValue, newValue);
 711:     invalidate();
 712:     repaint();
 713:     // Reset the accessibleContext since this depends on the editorKit.
 714:     accessibleContext = null;
 715:   }
 716: 
 717:   public void setEditorKitForContentType(String type, EditorKit k)
 718:   {
 719:     // FIXME: editorKitCache.put(type, kit);
 720:   }
 721: 
 722:   /**
 723:    * Sets the current URL being displayed.  
 724:    */
 725:   public void setPage(String url) throws IOException
 726:   {
 727:     setPage(new URL(url));
 728:   }
 729: 
 730:   /**
 731:    * Sets the current URL being displayed.  
 732:    */
 733:   public void setPage(URL page) throws IOException
 734:   {
 735:     if (page == null)
 736:       throw new IOException("invalid url");
 737: 
 738:     try
 739:       {
 740:     this.page = page;
 741:     getEditorKit().read(page.openStream(), getDocument(), 0);
 742:       }
 743:     catch (BadLocationException e)
 744:       {
 745:     // Ignored. '0' is always a valid offset.
 746:       }
 747:   }
 748: 
 749:   public void setText(String t)
 750:   {
 751:     super.setText(t);
 752:   }
 753: 
 754:   /**
 755:    * Add a <code>HyperlinkListener</code> object to this editor pane.
 756:    *
 757:    * @param listener the listener to add
 758:    */
 759:   public void addHyperlinkListener(HyperlinkListener listener)
 760:   {
 761:     listenerList.add(HyperlinkListener.class, listener);
 762:   }
 763: 
 764:   /**
 765:    * Removes a <code>HyperlinkListener</code> object to this editor pane.
 766:    *
 767:    * @param listener the listener to remove
 768:    */
 769:   public void removeHyperlinkListener(HyperlinkListener listener)
 770:   {
 771:     listenerList.remove(HyperlinkListener.class, listener);
 772:   }
 773: 
 774:   /**
 775:    * Returns all added <code>HyperlinkListener</code> objects.
 776:    *
 777:    * @return array of listeners
 778:    *
 779:    * @since 1.4
 780:    */
 781:   public HyperlinkListener[] getHyperlinkListeners()
 782:   {
 783:     return (HyperlinkListener[]) getListeners(HyperlinkListener.class);
 784:   }
 785: }