| GNU Classpath (0.95) | |
| Frames | No Frames |
1: /* JEditorPane.java -- 2: Copyright (C) 2002, 2004, 2005, 2006, 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.Container; 42: import java.awt.Dimension; 43: import java.io.BufferedInputStream; 44: import java.io.FilterInputStream; 45: import java.io.IOException; 46: import java.io.InputStream; 47: import java.io.InputStreamReader; 48: import java.io.Reader; 49: import java.io.StringReader; 50: import java.net.MalformedURLException; 51: import java.net.URL; 52: import java.net.URLConnection; 53: import java.util.HashMap; 54: 55: import javax.accessibility.AccessibleContext; 56: import javax.accessibility.AccessibleHyperlink; 57: import javax.accessibility.AccessibleHypertext; 58: import javax.accessibility.AccessibleStateSet; 59: import javax.accessibility.AccessibleText; 60: import javax.swing.event.HyperlinkEvent; 61: import javax.swing.event.HyperlinkListener; 62: import javax.swing.plaf.TextUI; 63: import javax.swing.text.AbstractDocument; 64: import javax.swing.text.BadLocationException; 65: import javax.swing.text.DefaultEditorKit; 66: import javax.swing.text.Document; 67: import javax.swing.text.EditorKit; 68: import javax.swing.text.Element; 69: import javax.swing.text.JTextComponent; 70: import javax.swing.text.View; 71: import javax.swing.text.ViewFactory; 72: import javax.swing.text.WrappedPlainView; 73: import javax.swing.text.html.HTML; 74: import javax.swing.text.html.HTMLDocument; 75: import javax.swing.text.html.HTMLEditorKit; 76: 77: /** 78: * A powerful text editor component that can handle different types of 79: * content. 80: * 81: * The JEditorPane text component is driven by an instance of 82: * {@link EditorKit}. The editor kit is responsible for providing 83: * a default {@link Document} implementation, a mechanism for loading 84: * and saving documents of its supported content type and providing 85: * a set of {@link Action}s for manipulating the content. 86: * 87: * By default the following content types are supported: 88: * <ul> 89: * <li><code>text/plain</code>: Plain text, handled by 90: * {@link javax.swing.text.DefaultEditorKit}.</li> 91: * <li><code>text/html</code>: HTML 4.0 styled text, handled by 92: * {@link javax.swing.text.html.HTMLEditorKit}.</li> 93: * <li><code>text/rtf</code>: RTF text, handled by 94: * {@link javax.swing.text.rtf.RTFEditorKit}.</li> 95: * </ul> 96: * 97: * @author original author unknown 98: * @author Roman Kennke (roman@kennke.org) 99: * @author Anthony Balkissoon abalkiss at redhat dot com 100: */ 101: public class JEditorPane extends JTextComponent 102: { 103: /** 104: * Provides accessibility support for <code>JEditorPane</code>. 105: * 106: * @author Roman Kennke (kennke@aicas.com) 107: */ 108: protected class AccessibleJEditorPane extends AccessibleJTextComponent 109: { 110: 111: /** 112: * Creates a new <code>AccessibleJEditorPane</code> object. 113: */ 114: protected AccessibleJEditorPane() 115: { 116: super(); 117: } 118: 119: /** 120: * Returns a description of this <code>AccessibleJEditorPane</code>. If 121: * this property is not set, then this returns the content-type of the 122: * editor pane. 123: * 124: * @return a description of this AccessibleJEditorPane 125: */ 126: public String getAccessibleDescription() 127: { 128: String descr = super.getAccessibleDescription(); 129: if (descr == null) 130: return getContentType(); 131: else 132: return descr; 133: } 134: 135: /** 136: * Returns the accessible state of this <code>AccessibleJEditorPane</code>. 137: * 138: * @return the accessible state of this <code>AccessibleJEditorPane</code> 139: */ 140: public AccessibleStateSet getAccessibleStateSet() 141: { 142: AccessibleStateSet state = super.getAccessibleStateSet(); 143: // TODO: Figure out what state must be added here to the super's state. 144: return state; 145: } 146: } 147: 148: /** 149: * Provides accessibility support for <code>JEditorPane</code>s, when the 150: * editor kit is an instance of {@link HTMLEditorKit}. 151: * 152: * @author Roman Kennke (kennke@aicas.com) 153: */ 154: protected class AccessibleJEditorPaneHTML extends AccessibleJEditorPane 155: { 156: /** 157: * Returns the accessible text of the <code>JEditorPane</code>. This will 158: * be an instance of 159: * {@link JEditorPaneAccessibleHypertextSupport}. 160: * 161: * @return the accessible text of the <code>JEditorPane</code> 162: */ 163: public AccessibleText getAccessibleText() 164: { 165: return new JEditorPaneAccessibleHypertextSupport(); 166: } 167: } 168: 169: /** 170: * This is the accessible text that is returned by 171: * {@link AccessibleJEditorPaneHTML#getAccessibleText()}. 172: * 173: * @author Roman Kennke (kennke@aicas.com) 174: */ 175: protected class JEditorPaneAccessibleHypertextSupport 176: extends AccessibleJEditorPane implements AccessibleHypertext 177: { 178: 179: /** 180: * Creates a new JEditorPaneAccessibleHypertextSupport object. 181: */ 182: public JEditorPaneAccessibleHypertextSupport() 183: { 184: super(); 185: } 186: 187: /** 188: * The accessible representation of a HTML link. 189: * 190: * @author Roman Kennke (kennke@aicas.com) 191: */ 192: public class HTMLLink extends AccessibleHyperlink 193: { 194: 195: /** 196: * The element in the document that represents the link. 197: */ 198: Element element; 199: 200: /** 201: * Creates a new <code>HTMLLink</code>. 202: * 203: * @param el the link element 204: */ 205: public HTMLLink(Element el) 206: { 207: this.element = el; 208: } 209: 210: /** 211: * Returns <code>true</code> if this <code>HTMLLink</code> is still 212: * valid. A <code>HTMLLink</code> can become invalid when the document 213: * changes. 214: * 215: * @return <code>true</code> if this <code>HTMLLink</code> is still 216: * valid 217: */ 218: public boolean isValid() 219: { 220: // I test here if the element at our element's start offset is the 221: // same as the element in the document at this offset. If this is true, 222: // I consider the link valid, if not, then this link no longer 223: // represented by this HTMLLink and therefor invalid. 224: HTMLDocument doc = (HTMLDocument) getDocument(); 225: return doc.getCharacterElement(element.getStartOffset()) == element; 226: } 227: 228: /** 229: * Returns the number of AccessibleActions in this link object. In 230: * general, link have 1 AccessibleAction associated with them. There are 231: * special cases where links can have multiple actions associated, like 232: * in image maps. 233: * 234: * @return the number of AccessibleActions in this link object 235: */ 236: public int getAccessibleActionCount() 237: { 238: // TODO: Implement the special cases. 239: return 1; 240: } 241: 242: /** 243: * Performs the specified action on the link object. This ususally means 244: * activating the link. 245: * 246: * @return <code>true</code> if the action has been performed 247: * successfully, <code>false</code> otherwise 248: */ 249: public boolean doAccessibleAction(int i) 250: { 251: String href = (String) element.getAttributes().getAttribute("href"); 252: HTMLDocument doc = (HTMLDocument) getDocument(); 253: try 254: { 255: URL url = new URL(doc.getBase(), href); 256: setPage(url); 257: String desc = doc.getText(element.getStartOffset(), 258: element.getEndOffset() - element.getStartOffset()); 259: HyperlinkEvent ev = 260: new HyperlinkEvent(JEditorPane.this, 261: HyperlinkEvent.EventType.ACTIVATED, url, desc, 262: element); 263: fireHyperlinkUpdate(ev); 264: return true; 265: } 266: catch (Exception ex) 267: { 268: return false; 269: } 270: } 271: 272: /** 273: * Returns the description of the action at action index <code>i</code>. 274: * This method returns the text within the element associated with this 275: * link. 276: * 277: * @param i the action index 278: * 279: * @return the description of the action at action index <code>i</code> 280: */ 281: public String getAccessibleActionDescription(int i) 282: { 283: HTMLDocument doc = (HTMLDocument) getDocument(); 284: try 285: { 286: return doc.getText(element.getStartOffset(), 287: element.getEndOffset() - element.getStartOffset()); 288: } 289: catch (BadLocationException ex) 290: { 291: throw (AssertionError) 292: new AssertionError("BadLocationException must not be thrown " 293: + "here.") 294: .initCause(ex); 295: } 296: } 297: 298: /** 299: * Returns an {@link URL} object, that represents the action at action 300: * index <code>i</code>. 301: * 302: * @param i the action index 303: * 304: * @return an {@link URL} object, that represents the action at action 305: * index <code>i</code> 306: */ 307: public Object getAccessibleActionObject(int i) 308: { 309: String href = (String) element.getAttributes().getAttribute("href"); 310: HTMLDocument doc = (HTMLDocument) getDocument(); 311: try 312: { 313: URL url = new URL(doc.getBase(), href); 314: return url; 315: } 316: catch (MalformedURLException ex) 317: { 318: return null; 319: } 320: } 321: 322: /** 323: * Returns an object that represents the link anchor. For examples, if 324: * the link encloses a string, then a <code>String</code> object is 325: * returned, if the link encloses an <img> tag, then an 326: * <code>ImageIcon</code> object is returned. 327: * 328: * @return an object that represents the link anchor 329: */ 330: public Object getAccessibleActionAnchor(int i) 331: { 332: // TODO: This is only the String case. Implement all cases. 333: return getAccessibleActionDescription(i); 334: } 335: 336: /** 337: * Returns the start index of the hyperlink element. 338: * 339: * @return the start index of the hyperlink element 340: */ 341: public int getStartIndex() 342: { 343: return element.getStartOffset(); 344: } 345: 346: /** 347: * Returns the end index of the hyperlink element. 348: * 349: * @return the end index of the hyperlink element 350: */ 351: public int getEndIndex() 352: { 353: return element.getEndOffset(); 354: } 355: 356: } 357: 358: /** 359: * Returns the number of hyperlinks in the document. 360: * 361: * @return the number of hyperlinks in the document 362: */ 363: public int getLinkCount() 364: { 365: HTMLDocument doc = (HTMLDocument) getDocument(); 366: HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A); 367: int count = 0; 368: while (linkIter.isValid()) 369: { 370: count++; 371: linkIter.next(); 372: } 373: return count; 374: } 375: 376: /** 377: * Returns the <code>i</code>-th hyperlink in the document or 378: * <code>null</code> if there is no hyperlink with the specified index. 379: * 380: * @param i the index of the hyperlink to return 381: * 382: * @return the <code>i</code>-th hyperlink in the document or 383: * <code>null</code> if there is no hyperlink with the specified 384: * index 385: */ 386: public AccessibleHyperlink getLink(int i) 387: { 388: HTMLDocument doc = (HTMLDocument) getDocument(); 389: HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A); 390: int count = 0; 391: while (linkIter.isValid()) 392: { 393: count++; 394: if (count == i) 395: break; 396: linkIter.next(); 397: } 398: if (linkIter.isValid()) 399: { 400: int offset = linkIter.getStartOffset(); 401: // TODO: I fetch the element for the link via getCharacterElement(). 402: // I am not sure that this is correct, maybe we must use 403: // getParagraphElement()? 404: Element el = doc.getCharacterElement(offset); 405: HTMLLink link = new HTMLLink(el); 406: return link; 407: } 408: else 409: return null; 410: } 411: 412: /** 413: * Returns the index of the link element at the character position 414: * <code>c</code> within the document, or <code>-1</code> if there is no 415: * link at the specified position. 416: * 417: * @param c the character index from which to fetch the link index 418: * 419: * @return the index of the link element at the character position 420: * <code>c</code> within the document, or <code>-1</code> if there 421: * is no link at the specified position 422: */ 423: public int getLinkIndex(int c) 424: { 425: HTMLDocument doc = (HTMLDocument) getDocument(); 426: HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A); 427: int count = 0; 428: while (linkIter.isValid()) 429: { 430: if (linkIter.getStartOffset() <= c && linkIter.getEndOffset() > c) 431: break; 432: count++; 433: linkIter.next(); 434: } 435: if (linkIter.isValid()) 436: return count; 437: else 438: return -1; 439: } 440: 441: /** 442: * Returns the link text of the link at index <code>i</code>, or 443: * <code>null</code>, if there is no link at the specified position. 444: * 445: * @param i the index of the link 446: * 447: * @return the link text of the link at index <code>i</code>, or 448: * <code>null</code>, if there is no link at the specified 449: * position 450: */ 451: public String getLinkText(int i) 452: { 453: HTMLDocument doc = (HTMLDocument) getDocument(); 454: HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A); 455: int count = 0; 456: while (linkIter.isValid()) 457: { 458: count++; 459: if (count == i) 460: break; 461: linkIter.next(); 462: } 463: if (linkIter.isValid()) 464: { 465: int offset = linkIter.getStartOffset(); 466: // TODO: I fetch the element for the link via getCharacterElement(). 467: // I am not sure that this is correct, maybe we must use 468: // getParagraphElement()? 469: Element el = doc.getCharacterElement(offset); 470: try 471: { 472: String text = doc.getText(el.getStartOffset(), 473: el.getEndOffset() - el.getStartOffset()); 474: return text; 475: } 476: catch (BadLocationException ex) 477: { 478: throw (AssertionError) 479: new AssertionError("BadLocationException must not be thrown " 480: + "here.") 481: .initCause(ex); 482: } 483: } 484: else 485: return null; 486: } 487: } 488: 489: /** 490: * Used to store a mapping for content-type to editor kit class. 491: */ 492: private static class EditorKitMapping 493: { 494: /** 495: * The classname of the editor kit. 496: */ 497: String className; 498: 499: /** 500: * The classloader with which the kit is to be loaded. 501: */ 502: ClassLoader classLoader; 503: 504: /** 505: * Creates a new EditorKitMapping object. 506: * 507: * @param cn the classname 508: * @param cl the classloader 509: */ 510: EditorKitMapping(String cn, ClassLoader cl) 511: { 512: className = cn; 513: classLoader = cl; 514: } 515: } 516: 517: /** 518: * An EditorKit used for plain text. This is the default editor kit for 519: * JEditorPanes. 520: * 521: * @author Roman Kennke (kennke@aicas.com) 522: */ 523: private static class PlainEditorKit extends DefaultEditorKit 524: { 525: 526: /** 527: * Returns a ViewFactory that supplies WrappedPlainViews. 528: */ 529: public ViewFactory getViewFactory() 530: { 531: return new ViewFactory() 532: { 533: public View create(Element el) 534: { 535: return new WrappedPlainView(el); 536: } 537: }; 538: } 539: } 540: 541: /** 542: * A special stream that can be cancelled. 543: */ 544: private class PageStream 545: extends FilterInputStream 546: { 547: /** 548: * True when the stream has been cancelled, false otherwise. 549: */ 550: private boolean cancelled; 551: 552: protected PageStream(InputStream in) 553: { 554: super(in); 555: cancelled = false; 556: } 557: 558: private void checkCancelled() 559: throws IOException 560: { 561: if (cancelled) 562: throw new IOException("Stream has been cancelled"); 563: } 564: 565: void cancel() 566: { 567: cancelled = true; 568: } 569: 570: public int read() 571: throws IOException 572: { 573: checkCancelled(); 574: return super.read(); 575: } 576: 577: public int read(byte[] b, int off, int len) 578: throws IOException 579: { 580: checkCancelled(); 581: return super.read(b, off, len); 582: } 583: 584: public long skip(long n) 585: throws IOException 586: { 587: checkCancelled(); 588: return super.skip(n); 589: } 590: 591: public int available() 592: throws IOException 593: { 594: checkCancelled(); 595: return super.available(); 596: } 597: 598: public void reset() 599: throws IOException 600: { 601: checkCancelled(); 602: super.reset(); 603: } 604: } 605: 606: /** 607: * The thread that loads documents asynchronously. 608: */ 609: private class PageLoader 610: implements Runnable 611: { 612: private Document doc; 613: private PageStream in; 614: private URL old; 615: URL page; 616: PageLoader(Document doc, InputStream in, URL old, URL page) 617: { 618: this.doc = doc; 619: this.in = new PageStream(in); 620: this.old = old; 621: this.page = page; 622: } 623: 624: public void run() 625: { 626: try 627: { 628: read(in, doc); 629: } 630: catch (IOException ex) 631: { 632: UIManager.getLookAndFeel().provideErrorFeedback(JEditorPane.this); 633: } 634: finally 635: { 636: if (SwingUtilities.isEventDispatchThread()) 637: firePropertyChange("page", old, page); 638: else 639: { 640: SwingUtilities.invokeLater(new Runnable() 641: { 642: public void run() 643: { 644: firePropertyChange("page", old, page); 645: } 646: }); 647: } 648: } 649: } 650: 651: void cancel() 652: { 653: in.cancel(); 654: } 655: } 656: 657: private static final long serialVersionUID = 3140472492599046285L; 658: 659: private EditorKit editorKit; 660: 661: boolean focus_root; 662: 663: /** 664: * Maps content-types to editor kit instances. 665: */ 666: static HashMap editorKits; 667: 668: // A mapping between content types and registered EditorKit types 669: static HashMap registerMap; 670: 671: static 672: { 673: registerMap = new HashMap(); 674: editorKits = new HashMap(); 675: registerEditorKitForContentType("application/rtf", 676: "javax.swing.text.rtf.RTFEditorKit"); 677: registerEditorKitForContentType("text/plain", 678: "javax.swing.JEditorPane$PlainEditorKit"); 679: registerEditorKitForContentType("text/html", 680: "javax.swing.text.html.HTMLEditorKit"); 681: registerEditorKitForContentType("text/rtf", 682: "javax.swing.text.rtf.RTFEditorKit"); 683: 684: } 685: 686: // A mapping between content types and used EditorKits 687: HashMap editorMap; 688: 689: /** 690: * The currently loading stream, if any. 691: */ 692: private PageLoader loader; 693: 694: public JEditorPane() 695: { 696: init(); 697: setEditorKit(createDefaultEditorKit()); 698: } 699: 700: public JEditorPane(String url) throws IOException 701: { 702: this(new URL(url)); 703: } 704: 705: public JEditorPane(String type, String text) 706: { 707: init(); 708: setEditorKit(createEditorKitForContentType(type)); 709: setText(text); 710: } 711: 712: public JEditorPane(URL url) throws IOException 713: { 714: init(); 715: setEditorKit(createEditorKitForContentType("text/html")); 716: setPage(url); 717: } 718: 719: /** 720: * Called by the constructors to set up the default bindings for content 721: * types and EditorKits. 722: */ 723: void init() 724: { 725: editorMap = new HashMap(); 726: } 727: 728: protected EditorKit createDefaultEditorKit() 729: { 730: return new PlainEditorKit(); 731: } 732: 733: /** 734: * Creates and returns an EditorKit that is appropriate for the given 735: * content type. This is created using the default recognized types 736: * plus any EditorKit types that have been registered. 737: * 738: * @see #registerEditorKitForContentType(String, String) 739: * @see #registerEditorKitForContentType(String, String, ClassLoader) 740: * @param type the content type 741: * @return an EditorKit for use with the given content type 742: */ 743: public static EditorKit createEditorKitForContentType(String type) 744: { 745: // Try cached instance. 746: EditorKit e = (EditorKit) editorKits.get(type); 747: if (e == null) 748: { 749: EditorKitMapping m = (EditorKitMapping) registerMap.get(type); 750: if (m != null) 751: { 752: String className = m.className; 753: ClassLoader loader = m.classLoader; 754: try 755: { 756: e = (EditorKit) loader.loadClass(className).newInstance(); 757: } 758: catch (Exception e2) 759: { 760: // The reference implementation returns null when class is not 761: // loadable or instantiatable. 762: } 763: } 764: // Cache this for later retrieval. 765: if (e != null) 766: editorKits.put(type, e); 767: } 768: return e; 769: } 770: 771: /** 772: * Sends a given <code>HyperlinkEvent</code> to all registered listeners. 773: * 774: * @param event the event to send 775: */ 776: public void fireHyperlinkUpdate(HyperlinkEvent event) 777: { 778: HyperlinkListener[] listeners = getHyperlinkListeners(); 779: 780: for (int index = 0; index < listeners.length; ++index) 781: listeners[index].hyperlinkUpdate(event); 782: } 783: 784: /** 785: * Returns the accessible context associated with this editor pane. 786: * 787: * @return the accessible context associated with this editor pane 788: */ 789: public AccessibleContext getAccessibleContext() 790: { 791: if (accessibleContext == null) 792: { 793: if (getEditorKit() instanceof HTMLEditorKit) 794: accessibleContext = new AccessibleJEditorPaneHTML(); 795: else 796: accessibleContext = new AccessibleJEditorPane(); 797: } 798: return accessibleContext; 799: } 800: 801: public final String getContentType() 802: { 803: return getEditorKit().getContentType(); 804: } 805: 806: /** 807: * Returns the EditorKit. If there is no EditorKit set this method 808: * calls createDefaultEditorKit() and setEditorKit() first. 809: */ 810: public EditorKit getEditorKit() 811: { 812: if (editorKit == null) 813: setEditorKit(createDefaultEditorKit()); 814: return editorKit; 815: } 816: 817: /** 818: * Returns the class name of the EditorKit associated with the given 819: * content type. 820: * 821: * @since 1.3 822: * @param type the content type 823: * @return the class name of the EditorKit associated with this content type 824: */ 825: public static String getEditorKitClassNameForContentType(String type) 826: { 827: EditorKitMapping m = (EditorKitMapping) registerMap.get(type); 828: String kitName = m != null ? m.className : null; 829: return kitName; 830: } 831: 832: /** 833: * Returns the EditorKit to use for the given content type. If an 834: * EditorKit has been explicitly set via 835: * <code>setEditorKitForContentType</code> 836: * then it will be returned. Otherwise an attempt will be made to create 837: * an EditorKit from the default recognzied content types or any 838: * EditorKits that have been registered. If none can be created, a 839: * PlainEditorKit is created. 840: * 841: * @see #registerEditorKitForContentType(String, String) 842: * @see #registerEditorKitForContentType(String, String, ClassLoader) 843: * @param type the content type 844: * @return an appropriate EditorKit for the given content type 845: */ 846: public EditorKit getEditorKitForContentType(String type) 847: { 848: // First check if an EditorKit has been explicitly set. 849: EditorKit e = (EditorKit) editorMap.get(type); 850: // Then check to see if we can create one. 851: if (e == null) 852: { 853: e = createEditorKitForContentType(type); 854: if (e != null) 855: setEditorKitForContentType(type, e); 856: } 857: // Otherwise default to PlainEditorKit. 858: if (e == null) 859: e = createDefaultEditorKit(); 860: return e; 861: } 862: 863: /** 864: * Returns the preferred size for the JEditorPane. This is implemented to 865: * return the super's preferred size, unless one of 866: * {@link #getScrollableTracksViewportHeight()} or 867: * {@link #getScrollableTracksViewportWidth()} returns <code>true</code>, 868: * in which case the preferred width and/or height is replaced by the UI's 869: * minimum size. 870: * 871: * @return the preferred size for the JEditorPane 872: */ 873: public Dimension getPreferredSize() 874: { 875: Dimension pref = super.getPreferredSize(); 876: Container parent = getParent(); 877: if (parent instanceof JViewport) 878: { 879: JViewport vp = (JViewport) getParent(); 880: TextUI ui = getUI(); 881: Dimension min = null; 882: if (! getScrollableTracksViewportWidth()) 883: { 884: min = ui.getMinimumSize(this); 885: int vpWidth = vp.getWidth(); 886: if (vpWidth != 0 && vpWidth < min.width) 887: pref.width = min.width; 888: } 889: if (! getScrollableTracksViewportHeight()) 890: { 891: if (min == null) 892: min = ui.getMinimumSize(this); 893: int vpHeight = vp.getHeight(); 894: if (vpHeight != 0 && vpHeight < min.height) 895: pref.height = min.height; 896: } 897: } 898: return pref; 899: } 900: 901: /** 902: * Returns <code>true</code> when a Viewport should force the height of 903: * this component to match the viewport height. This is implemented to return 904: * <code>true</code> when the parent is an instance of JViewport and 905: * the viewport height > the UI's minimum height. 906: * 907: * @return <code>true</code> when a Viewport should force the height of 908: * this component to match the viewport height 909: */ 910: public boolean getScrollableTracksViewportHeight() 911: { 912: // Tests show that this returns true when the parent is a JViewport 913: // and has a height > minimum UI height. 914: Container parent = getParent(); 915: int height = parent.getHeight(); 916: TextUI ui = getUI(); 917: return parent instanceof JViewport 918: && height >= ui.getMinimumSize(this).height 919: && height <= ui.getMaximumSize(this).height; 920: } 921: 922: /** 923: * Returns <code>true</code> when a Viewport should force the width of 924: * this component to match the viewport width. This is implemented to return 925: * <code>true</code> when the parent