Source for javax.swing.text.html.HTMLEditorKit

   1: /* HTMLEditorKit.java --
   2:    Copyright (C) 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.text.html;
  40: 
  41: 
  42: import java.awt.event.ActionEvent;
  43: import java.awt.event.MouseAdapter;
  44: import java.awt.event.MouseEvent;
  45: import java.awt.event.MouseMotionListener;
  46: import java.awt.Cursor;
  47: import java.awt.Point;
  48: 
  49: import java.io.IOException;
  50: import java.io.InputStream;
  51: import java.io.InputStreamReader;
  52: import java.io.Reader;
  53: import java.io.Serializable;
  54: import java.io.StringReader;
  55: import java.io.Writer;
  56: import java.net.MalformedURLException;
  57: import java.net.URL;
  58: 
  59: import javax.accessibility.Accessible;
  60: import javax.accessibility.AccessibleContext;
  61: 
  62: import javax.swing.Action;
  63: import javax.swing.JEditorPane;
  64: import javax.swing.SwingUtilities;
  65: import javax.swing.event.HyperlinkEvent;
  66: import javax.swing.text.AttributeSet;
  67: import javax.swing.text.BadLocationException;
  68: import javax.swing.text.Document;
  69: import javax.swing.text.EditorKit;
  70: import javax.swing.text.Element;
  71: import javax.swing.text.MutableAttributeSet;
  72: import javax.swing.text.StyleConstants;
  73: import javax.swing.text.StyledDocument;
  74: import javax.swing.text.StyledEditorKit;
  75: import javax.swing.text.TextAction;
  76: import javax.swing.text.View;
  77: import javax.swing.text.ViewFactory;
  78: import javax.swing.text.html.parser.ParserDelegator;
  79: 
  80: /* Move these imports here after javax.swing.text.html to make it compile
  81:    with jikes.  */
  82: import gnu.javax.swing.text.html.parser.GnuParserDelegator;
  83: import gnu.javax.swing.text.html.parser.HTML_401F;
  84: 
  85: /**
  86:  * @author Lillian Angel (langel at redhat dot com)
  87:  */
  88: public class HTMLEditorKit
  89:   extends StyledEditorKit
  90:   implements Serializable, Cloneable, Accessible
  91: {
  92:   
  93:   /**
  94:    * Fires the hyperlink events on the associated component
  95:    * when needed.
  96:    */
  97:   public static class LinkController
  98:     extends MouseAdapter
  99:     implements MouseMotionListener, Serializable
 100:     {
 101: 
 102:       /**
 103:        * The element of the last anchor tag.
 104:        */
 105:       private Element lastAnchorElement;
 106: 
 107:       /**
 108:        * Constructor
 109:        */
 110:       public LinkController() 
 111:       {
 112:         super();
 113:       }
 114:       
 115:       /**
 116:        * Dispatched when the mouse is clicked. If the component
 117:        * is read-only, then the clicked event is used to drive an
 118:        * attempt to follow the reference specified by a link
 119:        * 
 120:        * @param e - the mouse event
 121:        */
 122:       public void mouseClicked(MouseEvent e)
 123:       {
 124:         JEditorPane editor = (JEditorPane) e.getSource();
 125:         if (! editor.isEditable() && SwingUtilities.isLeftMouseButton(e))
 126:           {
 127:             Point loc = e.getPoint();
 128:             int pos = editor.viewToModel(loc);
 129:             if (pos >= 0)
 130:               activateLink(pos, editor, e.getX(), e.getY());
 131:           }
 132:       }
 133:       
 134:       /**
 135:        * Dispatched when the mouse is dragged on a component.
 136:        * 
 137:        * @param e - the mouse event.
 138:        */
 139:       public void mouseDragged(MouseEvent e)
 140:       {
 141:         // Nothing to do here.
 142:       }
 143:       
 144:       /**
 145:        * Dispatched when the mouse cursor has moved into the component.
 146:        * 
 147:        * @param e - the mouse event.
 148:        */
 149:       public void mouseMoved(MouseEvent e)
 150:       {
 151:         JEditorPane editor = (JEditorPane) e.getSource();
 152:         HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit();
 153:         if (! editor.isEditable())
 154:           {
 155:             Document doc = editor.getDocument();
 156:             if (doc instanceof HTMLDocument)
 157:               {
 158:                 Cursor newCursor = kit.getDefaultCursor();
 159:                 HTMLDocument htmlDoc = (HTMLDocument) doc;
 160:                 Point loc = e.getPoint();
 161:                 int pos = editor.viewToModel(loc);
 162:                 Element el = htmlDoc.getCharacterElement(pos);
 163:                 if (pos < el.getStartOffset() || pos >= el.getEndOffset())
 164:                   el = null;
 165:                 if (el != null)
 166:                   {
 167:                     AttributeSet aAtts = (AttributeSet)
 168:                                    el.getAttributes().getAttribute(HTML.Tag.A);
 169:                     if (aAtts != null)
 170:                       {
 171:                         if (el != lastAnchorElement)
 172:                           {
 173:                             if (lastAnchorElement != null)
 174:                               htmlDoc.updateSpecialClass(lastAnchorElement,
 175:                                                   HTML.Attribute.DYNAMIC_CLASS,
 176:                                                   null);
 177:                             lastAnchorElement = el;
 178:                             htmlDoc.updateSpecialClass(el,
 179:                                                   HTML.Attribute.DYNAMIC_CLASS,
 180:                                                   "hover");
 181:                           }
 182:                         newCursor = kit.getLinkCursor();
 183:                       }
 184:                     else
 185:                       {
 186:                         if (lastAnchorElement != null)
 187:                           htmlDoc.updateSpecialClass(lastAnchorElement,
 188:                                               HTML.Attribute.DYNAMIC_CLASS,
 189:                                               null);
 190:                         lastAnchorElement = null;
 191:                       }
 192:                   }
 193:                 else
 194:                   {
 195:                     if (lastAnchorElement != null)
 196:                       htmlDoc.updateSpecialClass(lastAnchorElement,
 197:                                           HTML.Attribute.DYNAMIC_CLASS,
 198:                                           null);
 199:                     lastAnchorElement = null;
 200:                   }
 201:                 if (editor.getCursor() != newCursor)
 202:                   {
 203:                     editor.setCursor(newCursor);
 204:                   }
 205:               }
 206:           }
 207:       }
 208: 
 209:       /**
 210:        * If the given position represents a link, then linkActivated is called
 211:        * on the JEditorPane.
 212:        *
 213:        * @param pos the position
 214:        * @param editor the editor pane
 215:        */
 216:       protected void activateLink(int pos, JEditorPane editor)
 217:       {
 218:         activateLink(pos, editor);
 219:       }
 220: 
 221:       private void activateLink(int pos, JEditorPane editor, int x, int y)
 222:       {
 223:         // TODO: This is here for future extension for mapped links support.
 224:         // For the time beeing we implement simple hyperlinks.
 225:         Document doc = editor.getDocument();
 226:         if (doc instanceof HTMLDocument)
 227:           {
 228:             HTMLDocument htmlDoc = (HTMLDocument) doc;
 229:             Element el = htmlDoc.getCharacterElement(pos);
 230:             AttributeSet atts = el.getAttributes();
 231:             AttributeSet anchorAtts =
 232:               (AttributeSet) atts.getAttribute(HTML.Tag.A);
 233:             String href = null;
 234:             if (anchorAtts != null)
 235:               {
 236:                 href = (String) anchorAtts.getAttribute(HTML.Attribute.HREF);
 237:                 htmlDoc.updateSpecialClass(el, HTML.Attribute.PSEUDO_CLASS,
 238:                                            "visited");
 239:               }
 240:             else
 241:               {
 242:                 // TODO: Implement link maps here.
 243:               }
 244:             HyperlinkEvent event = null;
 245:             if (href != null)
 246:               event = createHyperlinkEvent(editor, htmlDoc, href,
 247:                                            anchorAtts, el);
 248:             if (event != null)
 249:               editor.fireHyperlinkUpdate(event);
 250:           }
 251:         
 252:       }
 253: 
 254:       /**
 255:        * Creates a HyperlinkEvent for the specified href and anchor if
 256:        * possible. If for some reason this won't work, return null.
 257:        *
 258:        * @param editor the editor
 259:        * @param doc the document
 260:        * @param href the href link
 261:        * @param anchor the anchor
 262:        * @param el the element
 263:        *
 264:        * @return the hyperlink event, or <code>null</code> if we couldn't
 265:        *         create one
 266:        */
 267:       private HyperlinkEvent createHyperlinkEvent(JEditorPane editor,
 268:                                                   HTMLDocument doc,
 269:                                                   String href,
 270:                                                   AttributeSet anchor,
 271:                                                   Element el)
 272:       {
 273:         URL url;
 274:         try
 275:           {
 276:             URL base = doc.getBase();
 277:             url = new URL(base, href);
 278:             
 279:           }
 280:         catch (MalformedURLException ex)
 281:           {
 282:             url = null;
 283:           }
 284:         HyperlinkEvent ev;
 285:         if (doc.isFrameDocument())
 286:           {
 287:             String target = null;
 288:             if (anchor != null)
 289:               target = (String) anchor.getAttribute(HTML.Attribute.TARGET);
 290:             if (target == null || target.equals(""))
 291:               target = doc.getBaseTarget();
 292:             if (target == null || target.equals(""))
 293:               target = "_self";
 294:             ev = new HTMLFrameHyperlinkEvent(editor,
 295:                                             HyperlinkEvent.EventType.ACTIVATED,
 296:                                             url, href, el, target);
 297:           }
 298:         else
 299:           {
 300:             ev = new HyperlinkEvent(editor, HyperlinkEvent.EventType.ACTIVATED,
 301:                                     url, href, el);
 302:           }
 303:         return ev;
 304:       }
 305:     }
 306:   
 307:   /**
 308:    * This class is used to insert a string of HTML into an existing
 309:    * document. At least 2 HTML.Tags need to be supplied. The first Tag (parentTag)
 310:    * identifies the parent in the document to add the elements to. The second, (addTag), 
 311:    * identifies that the first tag should be added to the document as seen in the string.
 312:    * The parser will generate all appropriate (opening/closing tags_ even if they are not
 313:    * in the HTML string passed in.
 314:    */
 315:   public static class InsertHTMLTextAction
 316:     extends HTMLTextAction
 317:     {
 318:       
 319:       /**
 320:        * Tag in HTML to start adding tags from.
 321:        */
 322:       protected HTML.Tag addTag;
 323:       
 324:       /**
 325:        * Alternate tag in HTML to start adding tags from if parentTag is
 326:        * not found and alternateParentTag is not found.
 327:        */      
 328:       protected HTML.Tag alternateAddTag;
 329:       
 330:       /**
 331:        * Alternate tag to check if parentTag is not found.
 332:        */
 333:       protected HTML.Tag alternateParentTag;
 334:       
 335:       /**
 336:        * HTML to insert.
 337:        */
 338:       protected String html;
 339:       
 340:       /**
 341:        * Tag to check for in the document.
 342:        */
 343:       protected HTML.Tag parentTag;
 344: 
 345:       /**
 346:        * Initializes all fields.
 347:        * 
 348:        * @param name - the name of the document.
 349:        * @param html - the html to insert
 350:        * @param parentTag - the parent tag to check for
 351:        * @param addTag - the tag to start adding from
 352:        */
 353:       public InsertHTMLTextAction(String name, String html, 
 354:                                   HTML.Tag parentTag, HTML.Tag addTag)
 355:       {
 356:         this(name, html, parentTag, addTag, null, null);
 357:       }
 358:       
 359:       /**
 360:        * Initializes all fields and calls super
 361:        * 
 362:        * @param name - the name of the document.
 363:        * @param html - the html to insert
 364:        * @param parentTag - the parent tag to check for
 365:        * @param addTag - the tag to start adding from
 366:        * @param alternateParentTag - the alternate parent tag
 367:        * @param alternateAddTag - the alternate add tag
 368:        */
 369:       public InsertHTMLTextAction(String name, String html, HTML.Tag parentTag, 
 370:                                   HTML.Tag addTag, HTML.Tag alternateParentTag, 
 371:                                   HTML.Tag alternateAddTag) 
 372:       {
 373:         super(name);
 374:         // Fields are for easy access when the action is applied to an actual
 375:         // document.
 376:         this.html = html;
 377:         this.parentTag = parentTag;
 378:         this.addTag = addTag;
 379:         this.alternateParentTag = alternateParentTag;
 380:         this.alternateAddTag = alternateAddTag;
 381:       }
 382:       
 383:       /**
 384:        * HTMLEditorKit.insertHTML is called. If an exception is
 385:        * thrown, it is wrapped in a RuntimeException and thrown.
 386:        * 
 387:        * @param editor - the editor to use to get the editorkit
 388:        * @param doc -
 389:        *          the Document to insert the HTML into.
 390:        * @param offset -
 391:        *          where to begin inserting the HTML.
 392:        * @param html -
 393:        *          the String to insert
 394:        * @param popDepth -
 395:        *          the number of ElementSpec.EndTagTypes to generate before
 396:        *          inserting
 397:        * @param pushDepth -
 398:        *          the number of ElementSpec.StartTagTypes with a direction of
 399:        *          ElementSpec.JoinNextDirection that should be generated before
 400:        * @param addTag -
 401:        *          the first tag to start inserting into document
 402:        */
 403:       protected void insertHTML(JEditorPane editor, HTMLDocument doc, int offset,
 404:                               String html, int popDepth, int pushDepth,
 405:                               HTML.Tag addTag)
 406:       {
 407:         try
 408:           {
 409:             super.getHTMLEditorKit(editor).insertHTML(doc, offset, html,
 410:                                                       popDepth, pushDepth, addTag);
 411:           }
 412:         catch (IOException e)
 413:           {
 414:             throw (RuntimeException) new RuntimeException("Parser is null.").initCause(e);
 415:           }
 416:         catch (BadLocationException ex)
 417:           {
 418:             throw (RuntimeException) new RuntimeException("BadLocationException: "
 419:                                               + offset).initCause(ex);
 420:           }
 421:       }
 422:       
 423:       /**
 424:        * Invoked when inserting at a boundary. Determines the number of pops,
 425:        * and then the number of pushes that need to be performed. The it calls
 426:        * insertHTML.
 427:        * 
 428:        * @param editor -
 429:        *          the editor to use to get the editorkit
 430:        * @param doc -
 431:        *          the Document to insert the HTML into.
 432:        * @param offset -
 433:        *          where to begin inserting the HTML.
 434:        * @param insertElement -
 435:        *          the element to insert
 436:        * @param html -
 437:        *          the html to insert
 438:        * @param parentTag -
 439:        *          the parent tag
 440:        * @param addTag -
 441:        *          the first tag
 442:        */
 443:       protected void insertAtBoundary(JEditorPane editor,
 444:                                       HTMLDocument doc, int offset,
 445:                                       Element insertElement,
 446:                                       String html, HTML.Tag parentTag,
 447:                                       HTML.Tag addTag)
 448:       {
 449:         insertAtBoundry(editor, doc, offset, insertElement,
 450:                         html, parentTag, addTag);
 451:       }
 452:       
 453:       /**
 454:        * Invoked when inserting at a boundary. Determines the number of pops, 
 455:        * and then the number of pushes that need to be performed. The it calls
 456:        * insertHTML.
 457:        * 
 458:        * @param editor - the editor to use to get the editorkit
 459:        * @param doc -
 460:        *          the Document to insert the HTML into.
 461:        * @param offset -
 462:        *          where to begin inserting the HTML.
 463:        * @param insertElement - the element to insert
 464:        * @param html - the html to insert
 465:        * @param parentTag - the parent tag
 466:        * @param addTag - the first tag
 467:        * 
 468:        * @deprecated as of v1.3, use insertAtBoundary
 469:        */
 470:       protected void insertAtBoundry(JEditorPane editor,
 471:                                      HTMLDocument doc,
 472:                                      int offset, Element insertElement,
 473:                                      String html, HTML.Tag parentTag,
 474:                                      HTML.Tag addTag)
 475:       {
 476:         Element parent = insertElement;
 477:         Element el;
 478:         // Find common parent element.
 479:         if (offset > 0 || insertElement == null)
 480:           {
 481:             el = doc.getDefaultRootElement();
 482:             while (el != null && el.getStartOffset() != offset
 483:                    && ! el.isLeaf())
 484:               el = el.getElement(el.getElementIndex(offset));
 485:             parent = el != null ? el.getParentElement() : null;
 486:           }
 487:         if (parent != null)
 488:           {
 489:             int pops = 0;
 490:             int pushes = 0;
 491:             if (offset == 0 && insertElement != null)
 492:               {
 493:                 el = parent;
 494:                 while (el != null && ! el.isLeaf())
 495:                   {
 496:                     el = el.getElement(el.getElementIndex(offset));
 497:                     pops++;
 498:                   }
 499:               }
 500:             else
 501:               {
 502:                 el = parent;
 503:                 offset--;
 504:                 while (el != null && ! el.isLeaf())
 505:                   {
 506:                     el = el.getElement(el.getElementIndex(offset));
 507:                     pops++;
 508:                   }
 509:                 el = parent;
 510:                 offset++;
 511:                 while (el != null && el != insertElement)
 512:                   {
 513:                     el = el.getElement(el.getElementIndex(offset));
 514:                     pushes++;
 515:                   }
 516:               }
 517:             pops = Math.max(0, pops - 1);
 518:             insertHTML(editor, doc, offset, html, pops, pushes, addTag);
 519:           }
 520:       }
 521:       
 522:       /**
 523:        * Inserts the HTML.
 524:        * 
 525:        * @param ae - the action performed
 526:        */
 527:       public void actionPerformed(ActionEvent ae)
 528:       {
 529:         JEditorPane source = getEditor(ae);
 530:         if (source != null)
 531:           {
 532:             HTMLDocument d = getHTMLDocument(source);
 533:             int offset = source.getSelectionStart();
 534:             int length = d.getLength();
 535:             boolean inserted = true;
 536:             if (! tryInsert(source, d, offset, parentTag, addTag))
 537:               {
 538:                 inserted = tryInsert(source, d, offset, alternateParentTag,
 539:                                      alternateAddTag);
 540:               }
 541:             if (inserted)
 542:               adjustSelection(source, d, offset, length);
 543:           }
 544:       }
 545: 
 546:       /**
 547:        * Tries to insert the html chunk to the specified <code>addTag</code>.
 548:        *
 549:        * @param pane the editor
 550:        * @param doc the document
 551:        * @param offset the offset at which to insert
 552:        * @param tag the tag at which to insert
 553:        * @param addTag the add tag
 554:        *
 555:        * @return <code>true</code> when the html has been inserted successfully,
 556:        *         <code>false</code> otherwise
 557:        */
 558:       private boolean tryInsert(JEditorPane pane, HTMLDocument doc, int offset,
 559:                                 HTML.Tag tag, HTML.Tag addTag)
 560:       {
 561:         boolean inserted = false;
 562:         Element el = findElementMatchingTag(doc, offset, tag);
 563:         if (el != null && el.getStartOffset() == offset)
 564:           {
 565:             insertAtBoundary(pane, doc, offset, el, html, tag, addTag);
 566:             inserted = true;
 567:           }
 568:         else if (offset > 0)
 569:           {
 570:             int depth = elementCountToTag(doc, offset - 1, tag);
 571:             if (depth != -1)
 572:               {
 573:                 insertHTML(pane, doc, offset, html, depth, 0, addTag);
 574:                 inserted = true;
 575:               }
 576:           }
 577:         return inserted;
 578:       }
 579: 
 580:       /**
 581:        * Adjusts the selection after an insertion has been performed.
 582:        *
 583:        * @param pane the editor pane
 584:        * @param doc the document
 585:        * @param offset the insert offset
 586:        * @param oldLen the old document length
 587:        */
 588:       private void adjustSelection(JEditorPane pane, HTMLDocument doc,
 589:                                    int offset, int oldLen)
 590:       {
 591:         int newLen = doc.getLength();
 592:         if (newLen != oldLen && offset < newLen)
 593:           {
 594:             if (offset > 0)
 595:               {
 596:                 String text;
 597:                 try
 598:                   {
 599:                     text = doc.getText(offset - 1, 1);
 600:                   }
 601:                 catch (BadLocationException ex)
 602:                   {
 603:                     text = null;
 604:                   }
 605:                 if (text != null && text.length() > 0
 606:                     && text.charAt(0) == '\n')
 607:                   {
 608:                     pane.select(offset, offset);
 609:                   }
 610:                 else
 611:                   {
 612:                     pane.select(offset + 1, offset + 1);
 613:                   }
 614:               }
 615:             else
 616:               {
 617:                 pane.select(1, 1);
 618:               }
 619:           }
 620:       }
 621:   }
 622:   
 623:   /**
 624:    * Abstract Action class that helps inserting HTML into an existing document.
 625:    */
 626:   public abstract static class HTMLTextAction
 627:     extends StyledEditorKit.StyledTextAction
 628:     {
 629:       
 630:       /**
 631:        * Constructor
 632:        */
 633:       public HTMLTextAction(String name) 
 634:       {
 635:         super(name);
 636:       }
 637:       
 638:       /**
 639:        * Gets the HTMLDocument from the JEditorPane.
 640:        * 
 641:        * @param e - the editor pane
 642:        * @return the html document.
 643:        */
 644:       protected HTMLDocument getHTMLDocument(JEditorPane e)
 645:       {
 646:         Document d = e.getDocument();
 647:         if (d instanceof HTMLDocument)
 648:           return (HTMLDocument) d;
 649:         throw new IllegalArgumentException("Document is not a HTMLDocument.");
 650:       }
 651:       
 652:       /**
 653:        * Gets the HTMLEditorKit
 654:        *  
 655:        * @param e - the JEditorPane to get the HTMLEditorKit from.
 656:        * @return the HTMLEditorKit
 657:        */
 658:       protected HTMLEditorKit getHTMLEditorKit(JEditorPane e) 
 659:       {
 660:         EditorKit d = e.getEditorKit();
 661:         if (d instanceof HTMLEditorKit)
 662:           return (HTMLEditorKit) d;
 663:         throw new IllegalArgumentException("EditorKit is not a HTMLEditorKit.");
 664:       }
 665:       
 666:       /**
 667:        * Returns an array of Elements that contain the offset.
 668:        * The first elements corresponds to the roots of the doc.
 669:        * 
 670:        * @param doc - the document to get the Elements from.
 671:        * @param offset - the offset the Elements must contain
 672:        * @return an array of all the elements containing the offset.
 673:        */
 674:       protected Element[] getElementsAt(HTMLDocument doc,
 675:                                         int offset)
 676:       {
 677:         return getElementsAt(doc.getDefaultRootElement(), offset, 0);
 678:       }
 679:       
 680:       /**
 681:        * Helper function to get all elements using recursion.
 682:        */
 683:       private Element[] getElementsAt(Element root, int offset, int depth)
 684:       {
 685:         Element[] elements = null;
 686:         if (root != null)
 687:           {
 688:             if (root.isLeaf())
 689:               {
 690:                 elements = new Element[depth + 1];
 691:                 elements[depth] = root;
 692:                 return elements;
 693:               }
 694:             elements = getElementsAt(root.getElement(root.getElementIndex(offset)),
 695:                                      offset, depth + 1);
 696:             elements[depth] = root;
 697:           }
 698:         return elements;
 699:       }
 700:       
 701:       /**
 702:        * Returns the number of elements, starting at the deepest point, needed
 703:        * to get an element representing tag. -1 if no elements are found, 0 if
 704:        * the parent of the leaf at offset represents the tag.
 705:        * 
 706:        * @param doc -
 707:        *          the document to search
 708:        * @param offset -
 709:        *          the offset to check
 710:        * @param tag -
 711:        *          the tag to look for
 712:        * @return - the number of elements needed to get an element representing
 713:        *         tag.
 714:        */
 715:       protected int elementCountToTag(HTMLDocument doc,
 716:                                       int offset, HTML.Tag tag)
 717:       {
 718:         Element root = doc.getDefaultRootElement();
 719:         int num = -1;
 720:         Element next = root.getElement(root.getElementIndex(offset));
 721:         
 722:         while (!next.isLeaf())
 723:           {
 724:             num++;
 725:             if (next.getAttributes().
 726:                 getAttribute(StyleConstants.NameAttribute).equals(tag))
 727:               return num;
 728:             next = next.getElement(next.getElementIndex(offset));
 729:           }
 730:         return num;
 731:       }
 732:       
 733:       /**
 734:        * Gets the deepest element at offset with the
 735:        * matching tag.
 736:        * 
 737:        * @param doc - the document to search
 738:        * @param offset - the offset to check for
 739:        * @param tag - the tag to match
 740:        * @return - the element that is found, null if not found.
 741:        */
 742:       protected Element findElementMatchingTag(HTMLDocument doc,
 743:                                                int offset, HTML.Tag tag)
 744:       {
 745:         Element element = doc.getDefaultRootElement();
 746:         Element tagElement = null;
 747:         
 748:         while (element != null)
 749:           {
 750:             Object otag = element.getAttributes().getAttribute(
 751:                                      StyleConstants.NameAttribute);
 752:             if (otag instanceof HTML.Tag && otag.equals(tag))
 753:               tagElement = element;
 754:             element = element.getElement(element.getElementIndex(offset));
 755:           }
 756:         
 757:         return tagElement;
 758:       }
 759:     }
 760:   
 761:   /**
 762:    * A {@link ViewFactory} that is able to create {@link View}s for
 763:    * the <code>Element</code>s that are supported.
 764:    */
 765:   public static class HTMLFactory
 766:     implements ViewFactory
 767:   {
 768:     
 769:     /**
 770:      * Constructor
 771:      */
 772:     public HTMLFactory()
 773:     {
 774:       // Do Nothing here.
 775:     }
 776:     
 777:     /**
 778:      * Creates a {@link View} for the specified <code>Element</code>.
 779:      *
 780:      * @param element the <code>Element</code> to create a <code>View</code>
 781:      *        for
 782:      * @return the <code>View</code> for the specified <code>Element</code>
 783:      *         or <code>null</code> if the type of <code>element</code> is
 784:      *         not supported
 785:      */
 786:     public View create(Element element)
 787:     {
 788:       View view = null;
 789:       Object attr =
 790:         element.getAttributes().getAttribute(StyleConstants.NameAttribute);
 791:       if (attr instanceof HTML.Tag)
 792:         {
 793:           HTML.Tag tag = (HTML.Tag) attr;
 794: 
 795:           if (tag == HTML.Tag.IMPLIED || tag == HTML.Tag.P
 796:               || tag == HTML.Tag.H1 || tag == HTML.Tag.H2
 797:               || tag == HTML.Tag.H3 || tag == HTML.Tag.H4
 798:               || tag == HTML.Tag.H5 || tag == HTML.Tag.H6
 799:               || tag == HTML.Tag.DT)
 800:             view = new ParagraphView(element);
 801:           else if (tag == HTML.Tag.LI || tag == HTML.Tag.DL
 802:                    || tag == HTML.Tag.DD || tag == HTML.Tag.BODY
 803:                    || tag == HTML.Tag.HTML || tag == HTML.Tag.CENTER
 804:                    || tag == HTML.Tag.DIV
 805:                    || tag == HTML.Tag.BLOCKQUOTE
 806:                    || tag == HTML.Tag.PRE
 807:                    || tag == HTML.Tag.FORM
 808:                    // Misplaced TD and TH tags get mapped as vertical block.
 809:                    // Note that correctly placed tags get mapped in TableView.
 810:                    || tag == HTML.Tag.TD || tag == HTML.Tag.TH)
 811:             view = new BlockView(element, View.Y_AXIS);
 812:           else if (tag == HTML.Tag.TR)
 813:             // Misplaced TR tags get mapped as horizontal blocks.
 814:             // Note that correctly placed tags get mapped in TableView.
 815:             view = new BlockView(element, View.X_AXIS);
 816:           else if (tag == HTML.Tag.IMG)
 817:             view = new ImageView(element);
 818:           
 819:           else if (tag == HTML.Tag.CONTENT)
 820:             view = new InlineView(element);
 821:           else if (tag == HTML.Tag.HEAD)
 822:             view = new NullView(element);
 823:           else if (tag == HTML.Tag.TABLE)
 824:             view = new javax.swing.text.html.TableView(element);
 825:           else if (tag == HTML.Tag.HR)
 826:             view = new HRuleView(element);
 827:           else if (tag == HTML.Tag.BR)
 828:             view = new BRView(element);
 829:           else if (tag == HTML.Tag.INPUT || tag == HTML.Tag.SELECT
 830:                    || tag == HTML.Tag.TEXTAREA)
 831:             view = new FormView(element);
 832: 
 833:           else if (tag == HTML.Tag.MENU || tag == HTML.Tag.DIR
 834:                    || tag == HTML.Tag.UL || tag == HTML.Tag.OL)
 835:             view = new ListView(element);
 836:           else if (tag == HTML.Tag.FRAMESET)
 837:             view = new FrameSetView(element);
 838:           else if (tag == HTML.Tag.FRAME)
 839:             view = new FrameView(element);
 840:           else if (tag == HTML.Tag.OBJECT)
 841:             view = new ObjectView(element);
 842:         }
 843:       if (view == null)
 844:         {
 845:           view = new NullView(element);
 846:         }
 847:       return view;
 848:     }
 849:   }
 850:   
 851:   /**
 852:    * The abstract HTML parser declaration.
 853:    */
 854:   public abstract static class Parser
 855:   {
 856:     /**
 857:      * Parse the HTML text, calling various methods of the provided callback
 858:      * in response to the occurence of the corresponding HTML constructions.
 859:      * @param reader The reader to read the source HTML from.
 860:      * @param callback The callback to receive information about the parsed
 861:      * HTML structures
 862:      * @param ignoreCharSet If true, the parser ignores all charset information
 863:      * that may be present in HTML documents.
 864:      * @throws IOException, normally if the reader throws one.
 865:      */
 866:     public abstract void parse(Reader reader, ParserCallback callback,
 867:                                boolean ignoreCharSet) throws IOException;
 868:   }
 869: 
 870:   /**
 871:    * The "hook" that receives all information about the HTML document
 872:    * structure while parsing it. The methods are invoked by parser
 873:    * and should be normally overridden.
 874:    */
 875:   public static class ParserCallback
 876:   {
 877:     /**
 878:      * If the tag does not occurs in the html stream directly, but
 879:      * is supposed by parser, the tag attribute set contains this additional
 880:      * attribute, having value Boolean.True.
 881:      */
 882:     public static final Object IMPLIED = "_implied_";
 883: 
 884:     /**
 885:      * Constructor
 886:      */
 887:     public ParserCallback()
 888:     {
 889:       // Nothing to do here.
 890:     }
 891:     
 892:     /**
 893:      * The parser calls this method after it finishes parsing the document.
 894:      */
 895:     public void flush() throws BadLocationException
 896:     {
 897:       // Nothing to do here.
 898:     }
 899: 
 900:     /**
 901:      * Handle HTML comment, present in the given position.
 902:      * @param comment the comment
 903:      * @position the position of the comment in the text being parsed.
 904:      */
 905:     public void handleComment(char[] comment, int position)
 906:     {
 907:       // Nothing to do here.
 908:     }
 909: 
 910:     /**
 911:      * Notifies about the character sequences, used to separate lines in
 912:      * this document. The parser calls this method after it finishes
 913:      * parsing the document, but before flush().
 914:      * @param end_of_line The "end of line sequence", one of: \r or \n or \r\n.
 915:      */
 916:     public void handleEndOfLineString(String end_of_line)
 917:     {
 918:       // Nothing to do here.
 919:     }
 920: 
 921:     /**
 922:      * The method is called when the HTML closing tag ((like &lt;/table&gt;)
 923:      * is found or if the parser concludes that the one should be present
 924:      * in the current position.
 925:      * @param tag The tag being handled
 926:      * @param position the tag position in the text being parsed.
 927:      */
 928:     public void handleEndTag(HTML.Tag tag, int position)
 929:     {
 930:       // Nothing to do here.
 931:     }
 932: 
 933:     /**
 934:      * Handle the error.
 935:      * @param message The message, explaining the error.
 936:      * @param position The starting position of the fragment that has caused
 937:      * the error in the html document being parsed.
 938:      */
 939:     public void handleError(String message, int position)
 940:     {
 941:       // Nothing to do here.
 942:     }
 943: 
 944:     /**
 945:      * Handle the tag with no content, like &lt;br&gt;. The method is
 946:      * called for the elements that, in accordance with the current DTD,
 947:      * has an empty content.
 948:      * @param tag The tag being handled.
 949:      * @param position The tag position in the text being parsed.
 950:      */
 951:     public void handleSimpleTag(HTML.Tag tag, MutableAttributeSet attributes,
 952:                                 int position)
 953:     {
 954:       // Nothing to do here.
 955:     }
 956: 
 957:     /**
 958:      * The method is called when the HTML opening tag ((like &lt;table&gt;)
 959:      * is found or if the parser concludes that the one should be present
 960:      * in the current position.
 961:      * @param tag The tag being handled
 962:      * @param position The tag position in the text being parsed
 963:      */
 964:     public void handleStartTag(HTML.Tag tag, MutableAttributeSet attributes,
 965:                                int position)
 966:     {
 967:       // Nothing to do here.
 968:     }
 969: 
 970:     /**
 971:      * Handle the text section.
 972:      * @param text A section text.
 973:      * @param position The text position in the HTML document text being parsed.
 974:      */
 975:     public void handleText(char[] text, int position)
 976:     {
 977:       // Nothing to do here.
 978:     }
 979:   }
 980: 
 981:   /**
 982:    * Use serialVersionUID (v1.4) for interoperability.
 983:    */
 984:   private static final long serialVersionUID = 8751997116710384592L;
 985: 
 986:   /**
 987:    * Default cascading stylesheed file ("default.css").
 988:    */
 989:   public static final String DEFAULT_CSS = "default.css";
 990: 
 991:   /**
 992:    * The <b>bold</b> action identifier.
 993:    */
 994:   public static final String BOLD_ACTION = "html-bold-action";
 995: 
 996:   /**
 997:    * The <i>italic</i> action identifier.
 998:    */
 999:   public static final String ITALIC_ACTION = "html-italic-action";
1000: 
1001:   /**
1002:    * The <font color="#FF0000">color</font> action indentifier
1003:    * (passing the color as an argument).
1004:    */
1005:   public static final String COLOR_ACTION = "html-color-action";
1006: 
1007:   /**
1008:    * The <font size="+1">increase</font> font action identifier.
1009:    */
1010:   public static final String FONT_CHANGE_BIGGER = "html-font-bigger";
1011: 
1012:   /**
1013:    * The <font size="-1">decrease</font> font action identifier.
1014:    */
1015:   public static final String FONT_CHANGE_SMALLER = "html-font-smaller";
1016: 
1017:   /**
1018:    * Align images at the bottom.
1019:    */
1020:   public static final String IMG_ALIGN_BOTTOM = "html-image-align-bottom";
1021: 
1022:   /**
1023:    * Align images at the middle.
1024:    */
1025:   public static final String IMG_ALIGN_MIDDLE = "html-image-align-middle";
1026: 
1027:   /**
1028:    * Align images at the top.
1029:    */
1030:   public static final String IMG_ALIGN_TOP = "html-image-align-top";
1031: 
1032:   /**
1033:    * Align images at the border.
1034:    */
1035:   public static final String IMG_BORDER = "html-image-border";
1036: 
1037:   /**
1038:    * The "logical style" action identifier, passing that style as parameter.
1039:    */
1040:   public static final String LOGICAL_STYLE_ACTION = "html-logical-style-action";
1041: 
1042:   /**
1043:    * The "ident paragraph left" action.
1044:    */
1045:   public static final String PARA_INDENT_LEFT = "html-para-indent-left";
1046: 
1047:   /**
1048:    * The "ident paragraph right" action.
1049:    */
1050:   public static final String PARA_INDENT_RIGHT = "html-para-indent-right";
1051:   
1052:   /**
1053:    * Actions for HTML 
1054:    */
1055:   private static final Action[] defaultActions =
1056:   {
1057:     new InsertHTMLTextAction("InsertTable",
1058:                              "<table border=1><tr><td></td></tr></table>",
1059:                              HTML.Tag.BODY, HTML.Tag.TABLE),
1060:     new InsertHTMLTextAction("InsertTableRow",
1061:                              "<table border=1><tr><td></td></tr></table>",
1062:                              HTML.Tag.TABLE, HTML.Tag.TR,
1063:                              HTML.Tag.BODY, HTML.Tag.TABLE),
1064:     new InsertHTMLTextAction("InsertTableCell",
1065:                              "<table border=1><tr><td></td></tr></table>",
1066:                              HTML.Tag.TR, HTML.Tag.TD,
1067:                              HTML.Tag.BODY, HTML.Tag.TABLE),
1068:     new InsertHTMLTextAction("InsertUnorderedList",
1069:                              "<ul><li></li></ul>",
1070:                              HTML.Tag.BODY, HTML.Tag.UL),
1071:     new InsertHTMLTextAction("InsertUnorderedListItem",
1072:                              "<ul><li></li></ul>",
1073:                              HTML.Tag.UL, HTML.Tag.LI,
1074:                              HTML.Tag.BODY, HTML.Tag.UL),
1075:     new InsertHTMLTextAction("InsertOrderedList",
1076:                              "<ol><li></li></ol>",
1077:                              HTML.Tag.BODY, HTML.Tag.OL),
1078:     new InsertHTMLTextAction("InsertOrderedListItem",
1079:                              "<ol><li></li></ol>",
1080:                              HTML.Tag.OL, HTML.Tag.LI,
1081:                              HTML.Tag.BODY, HTML.Tag.OL),
1082:     new InsertHTMLTextAction("InsertPre",
1083:                              "<pre></pre>", HTML.Tag.BODY, HTML.Tag.PRE)
1084:     // TODO: The reference impl has an InsertHRAction too.
1085:   };
1086:   
1087:   /**
1088:    * The current style sheet.
1089:    */
1090:   private StyleSheet styleSheet;
1091:   
1092:   /**
1093:    * The ViewFactory for HTMLFactory.
1094:    */
1095:   HTMLFactory viewFactory;
1096:   
1097:   /**
1098:    * The Cursor for links.
1099:    */
1100:   Cursor linkCursor;
1101:   
1102:   /**
1103:    * The default cursor.
1104:    */
1105:   Cursor defaultCursor;
1106:   
1107:   /**
1108:    * The parser.
1109:    */
1110:   Parser parser;
1111:   
1112:   /**
1113:    * The mouse listener used for links.
1114:    */
1115:   private LinkController linkController;
1116:   
1117:   /** The content type */
1118:   String contentType = "text/html";
1119:   
1120:   /** The input attributes defined by default.css */
1121:   MutableAttributeSet inputAttributes;
1122:   
1123:   /** The editor pane used. */
1124:   JEditorPane editorPane;
1125: 
1126:   /**
1127:    * Whether or not the editor kit handles form submissions.
1128:    *
1129:    * @see #isAutoFormSubmission()
1130:    * @see #setAutoFormSubmission(boolean)
1131:    */
1132:   private boolean autoFormSubmission;
1133: 
1134:   /**
1135:    * Constructs an HTMLEditorKit, creates a StyleContext, and loads the style sheet.
1136:    */
1137:   public HTMLEditorKit()
1138:   {
1139:     linkController = new LinkController();
1140:     autoFormSubmission = true;
1141:   }
1142:   
1143:   /**
1144:    * Gets a factory suitable for producing views of any 
1145:    * models that are produced by this kit.
1146:    * 
1147:    * @return the view factory suitable for producing views.
1148:    */
1149:   public ViewFactory getViewFactory()
1150:   {
1151:     if (viewFactory == null)
1152:       viewFactory = new HTMLFactory();
1153:     return viewFactory;
1154:   }
1155:   
1156:   /**
1157:    * Create a text storage model for this type of editor.
1158:    *
1159:    * @return the model
1160:    */
1161:   public Document createDefaultDocument()
1162:   {
1163:     // Protect the shared stylesheet.
1164:     StyleSheet styleSheet = getStyleSheet();
1165:     StyleSheet ss = new StyleSheet();
1166:     ss.addStyleSheet(styleSheet);
1167: 
1168:     HTMLDocument document = new HTMLDocument(ss);
1169:     document.setParser(getParser());
1170:     document.setAsynchronousLoadPriority(4);
1171:     document.setTokenThreshold(100);
1172:     return document;
1173:   }
1174: 
1175:   /**
1176:    * Get the parser that this editor kit uses for reading HTML streams. This
1177:    * method can be overridden to use the alternative parser.
1178:    * 
1179:    * @return the HTML parser (by default, {@link ParserDelegator}).
1180:    */
1181:   protected Parser getParser()
1182:   {
1183:     if (parser == null)
1184:       {
1185:         parser = new GnuParserDelegator(HTML_401F.getInstance());
1186:       }
1187:     return parser;
1188:   }
1189:   
1190:   /**
1191:    * Inserts HTML into an existing document.
1192:    * 
1193:    * @param doc - the Document to insert the HTML into.
1194:    * @param offset - where to begin inserting the HTML.
1195:    * @param html - the String to insert
1196:    * @param popDepth - the number of ElementSpec.EndTagTypes 
1197:    * to generate before inserting
1198:    * @param pushDepth - the number of ElementSpec.StartTagTypes 
1199:    * with a direction of ElementSpec.JoinNextDirection that 
1200:    * should be generated before
1201:    * @param insertTag - the first tag to start inserting into document
1202:    * @throws IOException - on any I/O error
1203:    * @throws BadLocationException - if pos represents an invalid location
1204:    * within the document
1205:    */
1206:   public void insertHTML(HTMLDocument doc, int offset, String html,
1207:                          int popDepth, int pushDepth, HTML.Tag insertTag)
1208:       throws BadLocationException, IOException
1209:   {
1210:     Parser parser = getParser();
1211:     if (offset < 0 || offset > doc.getLength())
1212:       throw new BadLocationException("Bad location", offset);
1213:     if (parser == null)
1214:       throw new IOException("Parser is null.");
1215: 
1216:     ParserCallback pc = doc.getReader(offset, popDepth, pushDepth, insertTag);
1217: 
1218:     // FIXME: What should ignoreCharSet be set to?
1219:     
1220:     // parser.parse inserts html into the buffer
1221:     parser.parse(new StringReader(html), pc, false);
1222:     pc.flush();
1223:   }
1224:   
1225:   /**
1226:    * Inserts content from the given stream. Inserting HTML into a non-empty 
1227:    * document must be inside the body Element, if you do not insert into 
1228:    * the body an exception will be thrown. When inserting into a non-empty 
1229:    * document all tags outside of the body (head, title) will be dropped.
1230:    * 
1231:    * @param in - the stream to read from
1232:    * @param doc - the destination for the insertion
1233:    * @param pos - the location in the document to place the content
1234:    * @throws IOException - on any I/O error
1235:    * @throws BadLocationException - if pos represents an invalid location
1236:    * within the document
1237:    */
1238:   public void read(Reader in, Document doc, int pos) throws IOException,
1239:       BadLocationException
1240:   {
1241:     if (doc instanceof HTMLDocument)
1242:       {
1243:         Parser parser = getParser();
1244:         if (pos < 0 || pos > doc.getLength())
1245:           throw new BadLocationException("Bad location", pos);
1246:         if (parser == null)
1247:           throw new IOException("Parser is null.");
1248:         
1249:         HTMLDocument hd = ((HTMLDocument) doc);
1250:         if (editorPane != null)
1251:           hd.setBase(editorPane.getPage());
1252:         ParserCallback pc = hd.getReader(pos);
1253:         
1254:         // FIXME: What should ignoreCharSet be set to?
1255:         
1256:         // parser.parse inserts html into the buffer
1257:         parser.parse(in, pc, false);
1258:         pc.flush();
1259:       }
1260:     else
1261:       // read in DefaultEditorKit is called.
1262:       // the string is inserted in the document as usual.
1263:       super.read(in, doc, pos);
1264:   }
1265:   
1266:   /**
1267:    * Writes content from a document to the given stream in 
1268:    * an appropriate format.
1269:    * 
1270:    * @param out - the stream to write to
1271:    * @param doc - the source for the write
1272:    * @param pos - the location in the document to get the content.
1273:    * @param len - the amount to write out
1274:    * @throws IOException - on any I/O error
1275:    * @throws BadLocationException - if pos represents an invalid location
1276:    * within the document
1277:    */
1278:   public void write(Writer out, Document doc, int pos, int len)
1279:       throws IOException, BadLocationException
1280:   {
1281:     if (doc instanceof HTMLDocument)
1282:       {
1283:         HTMLWriter writer = new HTMLWriter(out, (HTMLDocument) doc, pos, len);
1284:         writer.write();
1285:       }
1286:     else if (doc instanceof StyledDocument)
1287:       {
1288:         MinimalHTMLWriter writer = new MinimalHTMLWriter(out,
1289:                                                          (StyledDocument) doc,
1290:                                                          pos, len);
1291:         writer.write();
1292:       }
1293:     else
1294:       super.write(out, doc, pos, len);
1295:   }
1296:   
1297:   /**
1298:    * Gets the content type that the kit supports.
1299:    * This kit supports the type text/html.
1300:    * 
1301:    * @returns the content type supported.
1302:    */
1303:   public String getContentType()
1304:   {
1305:     return contentType;
1306:   } 
1307:   
1308:   /**
1309:    * Creates a copy of the editor kit.
1310:    * 
1311:    * @return a copy of this.
1312:    */
1313:   public Object clone()
1314:   {
1315:     // FIXME: Need to clone all fields
1316:     HTMLEditorKit copy = (HTMLEditorKit) super.clone();
1317:     copy.linkController = new LinkController();
1318:     return copy;
1319:   }
1320:   
1321:   /**
1322:    * Copies the key/values in elements AttributeSet into set. 
1323:    * This does not copy component, icon, or element names attributes.
1324:    * This is called anytime the caret moves over a different location. 
1325:    * 
1326:    * @param element - the element to create the input attributes for.
1327:    * @param set - the set to copy the values into.
1328:    */
1329:   protected void createInputAttributes(Element element,
1330:                                        MutableAttributeSet set)
1331:   {
1332:     set.removeAttributes(set);
1333:     set.addAttributes(element.getAttributes());
1334:     // FIXME: Not fully implemented.
1335:   }
1336:   
1337:   /**
1338:    * Called when this is installed into the JEditorPane.
1339:    * 
1340:    * @param c - the JEditorPane installed into.
1341:    */
1342:   public void install(JEditorPane c)
1343:   {
1344:     super.install(c);
1345:     c.addMouseListener(linkController);
1346:     c.addMouseMotionListener(linkController);
1347:     editorPane = c;
1348:   }
1349:   
1350:   /**
1351:    * Called when the this is removed from the JEditorPane.
1352:    * It unregisters any listeners.
1353:    * 
1354:    * @param c - the JEditorPane being removed from.
1355:    */
1356:   public void deinstall(JEditorPane c)
1357:   {
1358:     super.deinstall(c);
1359:     c.removeMouseListener(linkController);
1360:     c.removeMouseMotionListener(linkController);
1361:     editorPane = null;
1362:   }
1363:   
1364:   /**
1365:    * Gets the AccessibleContext associated with this.
1366:    * 
1367:    * @return the AccessibleContext for this.
1368:    */
1369:   public AccessibleContext getAccessibleContext()
1370:   {
1371:     // FIXME: Should return an instance of 
1372:     // javax.swing.text.html.AccessibleHTML$RootHTMLAccessibleContext
1373:     // Not implemented yet.
1374:     return null;
1375:   }
1376:   
1377:   /**
1378:    * Gets the action list. This list is supported by the superclass
1379:    * augmented by the collection of actions defined locally for style
1380:    * operations.
1381:    * 
1382:    * @return an array of all the actions
1383:    */
1384:   public Action[] getActions()
1385:   {
1386:     return TextAction.augmentList(super.getActions(), defaultActions);
1387:   }
1388:   
1389:   /**
1390:    * Returns the default cursor.
1391:    * 
1392:    * @return the default cursor
1393:    */
1394:   public Cursor getDefaultCursor()
1395:   {
1396:     if (defaultCursor == null)
1397:       defaultCursor = Cursor.getDefaultCursor();
1398:     return defaultCursor;
1399:   }
1400:   
1401:   /**
1402:    * Returns the cursor for links.
1403:    * 
1404:    * @return the cursor for links.
1405:    */
1406:   public Cursor getLinkCursor()
1407:   {
1408:     if (linkCursor == null)
1409:       linkCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
1410:     return linkCursor;
1411:   }
1412:   
1413:   /**
1414:    * Sets the Cursor for links.
1415:    * 
1416:    * @param cursor - the new cursor for links.
1417:    */
1418:   public void setLinkCursor(Cursor cursor)
1419:   {
1420:     linkCursor = cursor;
1421:   }
1422:   
1423:   /**
1424:    * Sets the default cursor.
1425:    * 
1426:    * @param cursor - the new default cursor.
1427:    */
1428:   public void setDefaultCursor(Cursor cursor)
1429:   {
1430:     defaultCursor = cursor;
1431:   }
1432:   
1433:   /**
1434:    * Gets the input attributes used for the styled editing actions.
1435:    * 
1436:    * @return the attribute set
1437:    */
1438:   public MutableAttributeSet getInputAttributes()
1439:   {
1440:     return inputAttributes;
1441:   }
1442:   
1443:   /**
1444:    * Get the set of styles currently being used to render the HTML elements. 
1445:    * By default the resource specified by DEFAULT_CSS gets loaded, and is 
1446:    * shared by all HTMLEditorKit instances.
1447:    * 
1448:    * @return the style sheet.
1449:    */
1450:   public StyleSheet getStyleSheet()
1451:   {
1452:     if (styleSheet == null)
1453:       {
1454:         try
1455:           {
1456:             styleSheet = new StyleSheet();
1457:             Class c = HTMLEditorKit.class;
1458:             InputStream in = c.getResourceAsStream(DEFAULT_CSS);
1459:             InputStreamReader r = new InputStreamReader(in);
1460:             styleSheet.loadRules(r,  null);
1461:             r.close();
1462:           }
1463:         catch (IOException ex)
1464:           {
1465:             // No style available.
1466:           }
1467:       }
1468:     return styleSheet;
1469:   }
1470:   
1471:   /**
1472:    * Set the set of styles to be used to render the various HTML elements. 
1473:    * These styles are specified in terms of CSS specifications. Each document 
1474:    * produced by the kit will have a copy of the sheet which it can add the 
1475:    * document specific styles to. By default, the StyleSheet specified is shared 
1476:    * by all HTMLEditorKit instances. 
1477:    * 
1478:    * @param s - the new style sheet
1479:    */
1480:   public void setStyleSheet(StyleSheet s)
1481:   {
1482:     styleSheet = s;
1483:   }
1484: 
1485:   /**
1486:    * Returns <code>true</code> when forms should be automatically submitted
1487:    * by the editor kit. Set this to <code>false</code> when you want to
1488:    * intercept form submission. In this case you'd want to listen for
1489:    * hyperlink events on the document and handle FormSubmitEvents specially.
1490:    *
1491:    * The default is <code>true</code>.
1492:    *
1493:    * @return <code>true</code> when forms should be automatically submitted
1494:    *         by the editor kit, <code>false</code> otherwise
1495:    *
1496:    * @since 1.5
1497:    *
1498:    * @see #setAutoFormSubmission(boolean)
1499:    * @see FormSubmitEvent
1500:    */
1501:   public boolean isAutoFormSubmission()
1502:   {
1503:     return autoFormSubmission;
1504:   }
1505: 
1506:   /**
1507:    * Sets whether or not the editor kit should automatically submit forms.
1508:    *  
1509:    * @param auto <code>true</code> when the editor kit should handle form
1510:    *        submission, <code>false</code> otherwise
1511:    *
1512:    * @since 1.5
1513:    *
1514:    * @see #isAutoFormSubmission()
1515:    */
1516:   public void setAutoFormSubmission(boolean auto)
1517:   {
1518:     autoFormSubmission = auto;
1519:   }
1520: }