Source Code Cross Referenced for HTMLEditorKit.java in  » 6.0-JDK-Core » swing » javax » swing » text » html » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » swing » javax.swing.text.html 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001        /*
0002         * Copyright 1997-2007 Sun Microsystems, Inc.  All Rights Reserved.
0003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004         *
0005         * This code is free software; you can redistribute it and/or modify it
0006         * under the terms of the GNU General Public License version 2 only, as
0007         * published by the Free Software Foundation.  Sun designates this
0008         * particular file as subject to the "Classpath" exception as provided
0009         * by Sun in the LICENSE file that accompanied this code.
0010         *
0011         * This code is distributed in the hope that it will be useful, but WITHOUT
0012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
0014         * version 2 for more details (a copy is included in the LICENSE file that
0015         * accompanied this code).
0016         *
0017         * You should have received a copy of the GNU General Public License version
0018         * 2 along with this work; if not, write to the Free Software Foundation,
0019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020         *
0021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022         * CA 95054 USA or visit www.sun.com if you need additional information or
0023         * have any questions.
0024         */
0025        package javax.swing.text.html;
0026
0027        import java.lang.reflect.Method;
0028        import java.awt.*;
0029        import java.awt.event.*;
0030        import java.io.*;
0031        import java.net.MalformedURLException;
0032        import java.net.URL;
0033        import javax.swing.text.*;
0034        import javax.swing.*;
0035        import javax.swing.border.*;
0036        import javax.swing.event.*;
0037        import javax.swing.plaf.TextUI;
0038        import java.util.*;
0039        import javax.accessibility.*;
0040        import java.lang.ref.*;
0041
0042        /**
0043         * The Swing JEditorPane text component supports different kinds
0044         * of content via a plug-in mechanism called an EditorKit.  Because
0045         * HTML is a very popular format of content, some support is provided
0046         * by default.  The default support is provided by this class, which
0047         * supports HTML version 3.2 (with some extensions), and is migrating 
0048         * toward version 4.0.
0049         * The <applet> tag is not supported, but some support is provided
0050         * for the <object> tag.
0051         * <p>
0052         * There are several goals of the HTML EditorKit provided, that have
0053         * an effect upon the way that HTML is modeled.  These
0054         * have influenced its design in a substantial way.  
0055         * <dl>
0056         * <p>
0057         * <dt>
0058         * Support editing
0059         * <dd>
0060         * It might seem fairly obvious that a plug-in for JEditorPane
0061         * should provide editing support, but that fact has several
0062         * design considerations.  There are a substantial number of HTML
0063         * documents that don't properly conform to an HTML specification.
0064         * These must be normalized somewhat into a correct form if one
0065         * is to edit them.  Additionally, users don't like to be presented
0066         * with an excessive amount of structure editing, so using traditional
0067         * text editing gestures is preferred over using the HTML structure 
0068         * exactly as defined in the HTML document.
0069         * <p>
0070         * The modeling of HTML is provided by the class <code>HTMLDocument</code>.
0071         * Its documention describes the details of how the HTML is modeled.
0072         * The editing support leverages heavily off of the text package.
0073         * <p>
0074         * <dt>
0075         * Extendable/Scalable
0076         * <dd>
0077         * To maximize the usefulness of this kit, a great deal of effort
0078         * has gone into making it extendable.  These are some of the
0079         * features.
0080         * <ol>
0081         *   <li>
0082         *   The parser is replacable.  The default parser is the Hot Java
0083         *   parser which is DTD based.  A different DTD can be used, or an
0084         *   entirely different parser can be used.  To change the parser,
0085         *   reimplement the getParser method.  The default parser is 
0086         *   dynamically loaded when first asked for, so the class files
0087         *   will never be loaded if an alternative parser is used.  The
0088         *   default parser is in a separate package called parser below
0089         *   this package.
0090         *   <li>
0091         *   The parser drives the ParserCallback, which is provided by
0092         *   HTMLDocument.  To change the callback, subclass HTMLDocument
0093         *   and reimplement the createDefaultDocument method to return
0094         *   document that produces a different reader.  The reader controls
0095         *   how the document is structured.  Although the Document provides
0096         *   HTML support by default, there is nothing preventing support of
0097         *   non-HTML tags that result in alternative element structures.
0098         *   <li>
0099         *   The default view of the models are provided as a hierarchy of
0100         *   View implementations, so one can easily customize how a particular
0101         *   element is displayed or add capabilities for new kinds of elements
0102         *   by providing new View implementations.  The default set of views
0103         *   are provided by the <code>HTMLFactory</code> class.  This can
0104         *   be easily changed by subclassing or replacing the HTMLFactory 
0105         *   and reimplementing the getViewFactory method to return the alternative
0106         *   factory.
0107         *   <li>
0108         *   The View implementations work primarily off of CSS attributes, 
0109         *   which are kept in the views.  This makes it possible to have
0110         *   multiple views mapped over the same model that appear substantially
0111         *   different.  This can be especially useful for printing.  For
0112         *   most HTML attributes, the HTML attributes are converted to CSS
0113         *   attributes for display.  This helps make the View implementations
0114         *   more general purpose
0115         * </ol>
0116         * <p>
0117         * <dt>
0118         * Asynchronous Loading
0119         * <dd>
0120         * Larger documents involve a lot of parsing and take some time
0121         * to load.  By default, this kit produces documents that will be
0122         * loaded asynchronously if loaded using <code>JEditorPane.setPage</code>.
0123         * This is controlled by a property on the document.  The method
0124         * <a href="#createDefaultDocument">createDefaultDocument</a> can
0125         * be overriden to change this.  The batching of work is done
0126         * by the <code>HTMLDocument.HTMLReader</code> class.  The actual
0127         * work is done by the <code>DefaultStyledDocument</code> and
0128         * <code>AbstractDocument</code> classes in the text package.
0129         * <p>
0130         * <dt>
0131         * Customization from current LAF
0132         * <dd>
0133         * HTML provides a well known set of features without exactly
0134         * specifying the display characteristics.  Swing has a theme
0135         * mechanism for its look-and-feel implementations.  It is desirable
0136         * for the look-and-feel to feed display characteristics into the
0137         * HTML views.  An user with poor vision for example would want
0138         * high contrast and larger than typical fonts.
0139         * <p>
0140         * The support for this is provided by the <code>StyleSheet</code>
0141         * class.  The presentation of the HTML can be heavily influenced
0142         * by the setting of the StyleSheet property on the EditorKit.
0143         * <p>
0144         * <dt>
0145         * Not lossy
0146         * <dd>
0147         * An EditorKit has the ability to be read and save documents.
0148         * It is generally the most pleasing to users if there is no loss
0149         * of data between the two operation.  The policy of the HTMLEditorKit
0150         * will be to store things not recognized or not necessarily visible
0151         * so they can be subsequently written out.  The model of the HTML document
0152         * should therefore contain all information discovered while reading the
0153         * document.  This is constrained in some ways by the need to support 
0154         * editing (i.e. incorrect documents sometimes must be normalized).
0155         * The guiding principle is that information shouldn't be lost, but
0156         * some might be synthesized to produce a more correct model or it might
0157         * be rearranged.
0158         * </dl>
0159         *
0160         * @author  Timothy Prinzing
0161         * @version 1.142 05/05/07
0162         */
0163        public class HTMLEditorKit extends StyledEditorKit implements 
0164                Accessible {
0165
0166            private JEditorPane theEditor;
0167
0168            /**
0169             * Constructs an HTMLEditorKit, creates a StyleContext,
0170             * and loads the style sheet.
0171             */
0172            public HTMLEditorKit() {
0173
0174            }
0175
0176            /**
0177             * Get the MIME type of the data that this
0178             * kit represents support for.  This kit supports
0179             * the type <code>text/html</code>.
0180             *
0181             * @return the type
0182             */
0183            public String getContentType() {
0184                return "text/html";
0185            }
0186
0187            /**
0188             * Fetch a factory that is suitable for producing 
0189             * views of any models that are produced by this
0190             * kit.  
0191             *
0192             * @return the factory
0193             */
0194            public ViewFactory getViewFactory() {
0195                return defaultFactory;
0196            }
0197
0198            /**
0199             * Create an uninitialized text storage model
0200             * that is appropriate for this type of editor.
0201             *
0202             * @return the model
0203             */
0204            public Document createDefaultDocument() {
0205                StyleSheet styles = getStyleSheet();
0206                StyleSheet ss = new StyleSheet();
0207
0208                ss.addStyleSheet(styles);
0209
0210                HTMLDocument doc = new HTMLDocument(ss);
0211                doc.setParser(getParser());
0212                doc.setAsynchronousLoadPriority(4);
0213                doc.setTokenThreshold(100);
0214                return doc;
0215            }
0216
0217            /**
0218             * Try to get an HTML parser from the document.  If no parser is set for
0219             * the document, return the editor kit's default parser.  It is an error
0220             * if no parser could be obtained from the editor kit.
0221             */
0222            private Parser ensureParser(HTMLDocument doc) throws IOException {
0223                Parser p = doc.getParser();
0224                if (p == null) {
0225                    p = getParser();
0226                }
0227                if (p == null) {
0228                    throw new IOException("Can't load parser");
0229                }
0230                return p;
0231            }
0232
0233            /**
0234             * Inserts content from the given stream. If <code>doc</code> is
0235             * an instance of HTMLDocument, this will read
0236             * HTML 3.2 text. Inserting HTML into a non-empty document must be inside
0237             * the body Element, if you do not insert into the body an exception will
0238             * be thrown. When inserting into a non-empty document all tags outside
0239             * of the body (head, title) will be dropped.
0240             * 
0241             * @param in  the stream to read from
0242             * @param doc the destination for the insertion
0243             * @param pos the location in the document to place the
0244             *   content
0245             * @exception IOException on any I/O error
0246             * @exception BadLocationException if pos represents an invalid
0247             *   location within the document
0248             * @exception RuntimeException (will eventually be a BadLocationException)
0249             *            if pos is invalid
0250             */
0251            public void read(Reader in, Document doc, int pos)
0252                    throws IOException, BadLocationException {
0253
0254                if (doc instanceof  HTMLDocument) {
0255                    HTMLDocument hdoc = (HTMLDocument) doc;
0256                    if (pos > doc.getLength()) {
0257                        throw new BadLocationException("Invalid location", pos);
0258                    }
0259
0260                    Parser p = ensureParser(hdoc);
0261                    ParserCallback receiver = hdoc.getReader(pos);
0262                    Boolean ignoreCharset = (Boolean) doc
0263                            .getProperty("IgnoreCharsetDirective");
0264                    p.parse(in, receiver, (ignoreCharset == null) ? false
0265                            : ignoreCharset.booleanValue());
0266                    receiver.flush();
0267                } else {
0268                    super .read(in, doc, pos);
0269                }
0270            }
0271
0272            /**
0273             * Inserts HTML into an existing document.
0274             *
0275             * @param doc       the document to insert into
0276             * @param offset    the offset to insert HTML at
0277             * @param popDepth  the number of ElementSpec.EndTagTypes to generate before
0278             *        inserting
0279             * @param pushDepth the number of ElementSpec.StartTagTypes with a direction
0280             *        of ElementSpec.JoinNextDirection that should be generated
0281             *        before inserting, but after the end tags have been generated
0282             * @param insertTag the first tag to start inserting into document
0283             * @exception RuntimeException (will eventually be a BadLocationException)
0284             *            if pos is invalid
0285             */
0286            public void insertHTML(HTMLDocument doc, int offset, String html,
0287                    int popDepth, int pushDepth, HTML.Tag insertTag)
0288                    throws BadLocationException, IOException {
0289                if (offset > doc.getLength()) {
0290                    throw new BadLocationException("Invalid location", offset);
0291                }
0292
0293                Parser p = ensureParser(doc);
0294                ParserCallback receiver = doc.getReader(offset, popDepth,
0295                        pushDepth, insertTag);
0296                Boolean ignoreCharset = (Boolean) doc
0297                        .getProperty("IgnoreCharsetDirective");
0298                p.parse(new StringReader(html), receiver,
0299                        (ignoreCharset == null) ? false : ignoreCharset
0300                                .booleanValue());
0301                receiver.flush();
0302            }
0303
0304            /**
0305             * Write content from a document to the given stream
0306             * in a format appropriate for this kind of content handler.
0307             * 
0308             * @param out  the stream to write to
0309             * @param doc  the source for the write
0310             * @param pos  the location in the document to fetch the
0311             *   content
0312             * @param len  the amount to write out
0313             * @exception IOException on any I/O error
0314             * @exception BadLocationException if pos represents an invalid
0315             *   location within the document
0316             */
0317            public void write(Writer out, Document doc, int pos, int len)
0318                    throws IOException, BadLocationException {
0319
0320                if (doc instanceof  HTMLDocument) {
0321                    HTMLWriter w = new HTMLWriter(out, (HTMLDocument) doc, pos,
0322                            len);
0323                    w.write();
0324                } else if (doc instanceof  StyledDocument) {
0325                    MinimalHTMLWriter w = new MinimalHTMLWriter(out,
0326                            (StyledDocument) doc, pos, len);
0327                    w.write();
0328                } else {
0329                    super .write(out, doc, pos, len);
0330                }
0331            }
0332
0333            /**
0334             * Called when the kit is being installed into the
0335             * a JEditorPane. 
0336             *
0337             * @param c the JEditorPane
0338             */
0339            public void install(JEditorPane c) {
0340                c.addMouseListener(linkHandler);
0341                c.addMouseMotionListener(linkHandler);
0342                c.addCaretListener(nextLinkAction);
0343                super .install(c);
0344                theEditor = c;
0345            }
0346
0347            /**
0348             * Called when the kit is being removed from the
0349             * JEditorPane.  This is used to unregister any 
0350             * listeners that were attached.
0351             *
0352             * @param c the JEditorPane
0353             */
0354            public void deinstall(JEditorPane c) {
0355                c.removeMouseListener(linkHandler);
0356                c.removeMouseMotionListener(linkHandler);
0357                c.removeCaretListener(nextLinkAction);
0358                super .deinstall(c);
0359                theEditor = null;
0360            }
0361
0362            /**
0363             * Default Cascading Style Sheet file that sets
0364             * up the tag views.
0365             */
0366            public static final String DEFAULT_CSS = "default.css";
0367
0368            /**
0369             * Set the set of styles to be used to render the various
0370             * HTML elements.  These styles are specified in terms of
0371             * CSS specifications.  Each document produced by the kit
0372             * will have a copy of the sheet which it can add the 
0373             * document specific styles to.  By default, the StyleSheet
0374             * specified is shared by all HTMLEditorKit instances.
0375             * This should be reimplemented to provide a finer granularity
0376             * if desired.
0377             */
0378            public void setStyleSheet(StyleSheet s) {
0379                defaultStyles = s;
0380            }
0381
0382            /**
0383             * Get the set of styles currently being used to render the
0384             * HTML elements.  By default the resource specified by
0385             * DEFAULT_CSS gets loaded, and is shared by all HTMLEditorKit 
0386             * instances.
0387             */
0388            public StyleSheet getStyleSheet() {
0389                if (defaultStyles == null) {
0390                    defaultStyles = new StyleSheet();
0391                    try {
0392                        InputStream is = HTMLEditorKit
0393                                .getResourceAsStream(DEFAULT_CSS);
0394                        Reader r = new BufferedReader(new InputStreamReader(is,
0395                                "ISO-8859-1"));
0396                        defaultStyles.loadRules(r, null);
0397                        r.close();
0398                    } catch (Throwable e) {
0399                        // on error we simply have no styles... the html
0400                        // will look mighty wrong but still function.
0401                    }
0402                }
0403                return defaultStyles;
0404            }
0405
0406            /**
0407             * Fetch a resource relative to the HTMLEditorKit classfile.
0408             * If this is called on 1.2 the loading will occur under the
0409             * protection of a doPrivileged call to allow the HTMLEditorKit
0410             * to function when used in an applet.
0411             *
0412             * @param name the name of the resource, relative to the
0413             *  HTMLEditorKit class
0414             * @return a stream representing the resource
0415             */
0416            static InputStream getResourceAsStream(String name) {
0417                try {
0418                    return ResourceLoader.getResourceAsStream(name);
0419                } catch (Throwable e) {
0420                    // If the class doesn't exist or we have some other 
0421                    // problem we just try to call getResourceAsStream directly.
0422                    return HTMLEditorKit.class.getResourceAsStream(name);
0423                }
0424            }
0425
0426            /**
0427             * Fetches the command list for the editor.  This is
0428             * the list of commands supported by the superclass
0429             * augmented by the collection of commands defined
0430             * locally for style operations.
0431             *
0432             * @return the command list
0433             */
0434            public Action[] getActions() {
0435                return TextAction.augmentList(super .getActions(),
0436                        this .defaultActions);
0437            }
0438
0439            /**
0440             * Copies the key/values in <code>element</code>s AttributeSet into
0441             * <code>set</code>. This does not copy component, icon, or element
0442             * names attributes. Subclasses may wish to refine what is and what
0443             * isn't copied here. But be sure to first remove all the attributes that
0444             * are in <code>set</code>.<p>
0445             * This is called anytime the caret moves over a different location.
0446             *
0447             */
0448            protected void createInputAttributes(Element element,
0449                    MutableAttributeSet set) {
0450                set.removeAttributes(set);
0451                set.addAttributes(element.getAttributes());
0452                set.removeAttribute(StyleConstants.ComposedTextAttribute);
0453
0454                Object o = set.getAttribute(StyleConstants.NameAttribute);
0455                if (o instanceof  HTML.Tag) {
0456                    HTML.Tag tag = (HTML.Tag) o;
0457                    // PENDING: we need a better way to express what shouldn't be
0458                    // copied when editing...
0459                    if (tag == HTML.Tag.IMG) {
0460                        // Remove the related image attributes, src, width, height
0461                        set.removeAttribute(HTML.Attribute.SRC);
0462                        set.removeAttribute(HTML.Attribute.HEIGHT);
0463                        set.removeAttribute(HTML.Attribute.WIDTH);
0464                        set.addAttribute(StyleConstants.NameAttribute,
0465                                HTML.Tag.CONTENT);
0466                    } else if (tag == HTML.Tag.HR || tag == HTML.Tag.BR) {
0467                        // Don't copy HRs or BRs either.
0468                        set.addAttribute(StyleConstants.NameAttribute,
0469                                HTML.Tag.CONTENT);
0470                    } else if (tag == HTML.Tag.COMMENT) {
0471                        // Don't copy COMMENTs either
0472                        set.addAttribute(StyleConstants.NameAttribute,
0473                                HTML.Tag.CONTENT);
0474                        set.removeAttribute(HTML.Attribute.COMMENT);
0475                    } else if (tag == HTML.Tag.INPUT) {
0476                        // or INPUT either
0477                        set.addAttribute(StyleConstants.NameAttribute,
0478                                HTML.Tag.CONTENT);
0479                        set.removeAttribute(HTML.Tag.INPUT);
0480                    } else if (tag instanceof  HTML.UnknownTag) {
0481                        // Don't copy unknowns either:(
0482                        set.addAttribute(StyleConstants.NameAttribute,
0483                                HTML.Tag.CONTENT);
0484                        set.removeAttribute(HTML.Attribute.ENDTAG);
0485                    }
0486                }
0487            }
0488
0489            /**
0490             * Gets the input attributes used for the styled 
0491             * editing actions.
0492             *
0493             * @return the attribute set
0494             */
0495            public MutableAttributeSet getInputAttributes() {
0496                if (input == null) {
0497                    input = getStyleSheet().addStyle(null, null);
0498                }
0499                return input;
0500            }
0501
0502            /**
0503             * Sets the default cursor.
0504             *
0505             * @since 1.3
0506             */
0507            public void setDefaultCursor(Cursor cursor) {
0508                defaultCursor = cursor;
0509            }
0510
0511            /**
0512             * Returns the default cursor.
0513             *
0514             * @since 1.3
0515             */
0516            public Cursor getDefaultCursor() {
0517                return defaultCursor;
0518            }
0519
0520            /**
0521             * Sets the cursor to use over links.
0522             *
0523             * @since 1.3
0524             */
0525            public void setLinkCursor(Cursor cursor) {
0526                linkCursor = cursor;
0527            }
0528
0529            /**
0530             * Returns the cursor to use over hyper links.
0531             * @since 1.3
0532             */
0533            public Cursor getLinkCursor() {
0534                return linkCursor;
0535            }
0536
0537            /**
0538             * Indicates whether an html form submission is processed automatically 
0539             * or only <code>FormSubmitEvent</code> is fired.
0540             *
0541             * @return true  if html form submission is processed automatically,
0542             *         false otherwise.
0543             *
0544             * @see #setAutoFormSubmission
0545             * @since 1.5
0546             */
0547            public boolean isAutoFormSubmission() {
0548                return isAutoFormSubmission;
0549            }
0550
0551            /**
0552             * Specifies if an html form submission is processed
0553             * automatically or only <code>FormSubmitEvent</code> is fired.
0554             * By default it is set to true.
0555             *
0556             * @see #isAutoFormSubmission
0557             * @see FormSubmitEvent
0558             * @since 1.5
0559             */
0560            public void setAutoFormSubmission(boolean isAuto) {
0561                isAutoFormSubmission = isAuto;
0562            }
0563
0564            /**
0565             * Creates a copy of the editor kit.
0566             *
0567             * @return the copy
0568             */
0569            public Object clone() {
0570                HTMLEditorKit o = (HTMLEditorKit) super .clone();
0571                if (o != null) {
0572                    o.input = null;
0573                    o.linkHandler = new LinkController();
0574                }
0575                return o;
0576            }
0577
0578            /**
0579             * Fetch the parser to use for reading HTML streams.
0580             * This can be reimplemented to provide a different
0581             * parser.  The default implementation is loaded dynamically
0582             * to avoid the overhead of loading the default parser if
0583             * it's not used.  The default parser is the HotJava parser
0584             * using an HTML 3.2 DTD.
0585             */
0586            protected Parser getParser() {
0587                if (defaultParser == null) {
0588                    try {
0589                        Class c = Class
0590                                .forName("javax.swing.text.html.parser.ParserDelegator");
0591                        defaultParser = (Parser) c.newInstance();
0592                    } catch (Throwable e) {
0593                    }
0594                }
0595                return defaultParser;
0596            }
0597
0598            // ----- Accessibility support -----
0599            private AccessibleContext accessibleContext;
0600
0601            /**
0602             * returns the AccessibleContext associated with this editor kit
0603             *
0604             * @return the AccessibleContext associated with this editor kit
0605             * @since 1.4
0606             */
0607            public AccessibleContext getAccessibleContext() {
0608                if (theEditor == null) {
0609                    return null;
0610                }
0611                if (accessibleContext == null) {
0612                    AccessibleHTML a = new AccessibleHTML(theEditor);
0613                    accessibleContext = a.getAccessibleContext();
0614                }
0615                return accessibleContext;
0616            }
0617
0618            // --- variables ------------------------------------------
0619
0620            private static final Cursor MoveCursor = Cursor
0621                    .getPredefinedCursor(Cursor.HAND_CURSOR);
0622            private static final Cursor DefaultCursor = Cursor
0623                    .getPredefinedCursor(Cursor.DEFAULT_CURSOR);
0624
0625            /** Shared factory for creating HTML Views. */
0626            private static final ViewFactory defaultFactory = new HTMLFactory();
0627
0628            MutableAttributeSet input;
0629            private static StyleSheet defaultStyles = null;
0630            private LinkController linkHandler = new LinkController();
0631            private static Parser defaultParser = null;
0632            private Cursor defaultCursor = DefaultCursor;
0633            private Cursor linkCursor = MoveCursor;
0634            private boolean isAutoFormSubmission = true;
0635
0636            /**
0637             * Class to watch the associated component and fire
0638             * hyperlink events on it when appropriate.
0639             */
0640            public static class LinkController extends MouseAdapter implements 
0641                    MouseMotionListener, Serializable {
0642                private Element curElem = null;
0643                /**
0644                 * If true, the current element (curElem) represents an image.
0645                 */
0646                private boolean curElemImage = false;
0647                private String href = null;
0648                /** This is used by viewToModel to avoid allocing a new array each
0649                 * time. */
0650                private transient Position.Bias[] bias = new Position.Bias[1];
0651                /**
0652                 * Current offset.
0653                 */
0654                private int curOffset;
0655
0656                /**
0657                 * Called for a mouse click event.
0658                 * If the component is read-only (ie a browser) then 
0659                 * the clicked event is used to drive an attempt to
0660                 * follow the reference specified by a link.
0661                 *
0662                 * @param e the mouse event
0663                 * @see MouseListener#mouseClicked
0664                 */
0665                public void mouseClicked(MouseEvent e) {
0666                    JEditorPane editor = (JEditorPane) e.getSource();
0667
0668                    if (!editor.isEditable() && editor.isEnabled()
0669                            && SwingUtilities.isLeftMouseButton(e)) {
0670                        Point pt = new Point(e.getX(), e.getY());
0671                        int pos = editor.viewToModel(pt);
0672                        if (pos >= 0) {
0673                            activateLink(pos, editor, e.getX(), e.getY());
0674                        }
0675                    }
0676                }
0677
0678                // ignore the drags
0679                public void mouseDragged(MouseEvent e) {
0680                }
0681
0682                // track the moving of the mouse.
0683                public void mouseMoved(MouseEvent e) {
0684                    JEditorPane editor = (JEditorPane) e.getSource();
0685                    if (!editor.isEnabled()) {
0686                        return;
0687                    }
0688
0689                    HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit();
0690                    boolean adjustCursor = true;
0691                    Cursor newCursor = kit.getDefaultCursor();
0692                    if (!editor.isEditable()) {
0693                        Point pt = new Point(e.getX(), e.getY());
0694                        int pos = editor.getUI().viewToModel(editor, pt, bias);
0695                        if (bias[0] == Position.Bias.Backward && pos > 0) {
0696                            pos--;
0697                        }
0698                        if (pos >= 0
0699                                && (editor.getDocument() instanceof  HTMLDocument)) {
0700                            HTMLDocument hdoc = (HTMLDocument) editor
0701                                    .getDocument();
0702                            Element elem = hdoc.getCharacterElement(pos);
0703                            if (!doesElementContainLocation(editor, elem, pos,
0704                                    e.getX(), e.getY())) {
0705                                elem = null;
0706                            }
0707                            if (curElem != elem || curElemImage) {
0708                                Element lastElem = curElem;
0709                                curElem = elem;
0710                                String href = null;
0711                                curElemImage = false;
0712                                if (elem != null) {
0713                                    AttributeSet a = elem.getAttributes();
0714                                    AttributeSet anchor = (AttributeSet) a
0715                                            .getAttribute(HTML.Tag.A);
0716                                    if (anchor == null) {
0717                                        curElemImage = (a
0718                                                .getAttribute(StyleConstants.NameAttribute) == HTML.Tag.IMG);
0719                                        if (curElemImage) {
0720                                            href = getMapHREF(editor, hdoc,
0721                                                    elem, a, pos, e.getX(), e
0722                                                            .getY());
0723                                        }
0724                                    } else {
0725                                        href = (String) anchor
0726                                                .getAttribute(HTML.Attribute.HREF);
0727                                    }
0728                                }
0729
0730                                if (href != this .href) {
0731                                    // reference changed, fire event(s)
0732                                    fireEvents(editor, hdoc, href, lastElem);
0733                                    this .href = href;
0734                                    if (href != null) {
0735                                        newCursor = kit.getLinkCursor();
0736                                    }
0737                                } else {
0738                                    adjustCursor = false;
0739                                }
0740                            } else {
0741                                adjustCursor = false;
0742                            }
0743                            curOffset = pos;
0744                        }
0745                    }
0746                    if (adjustCursor && editor.getCursor() != newCursor) {
0747                        editor.setCursor(newCursor);
0748                    }
0749                }
0750
0751                /**
0752                 * Returns a string anchor if the passed in element has a
0753                 * USEMAP that contains the passed in location.
0754                 */
0755                private String getMapHREF(JEditorPane html, HTMLDocument hdoc,
0756                        Element elem, AttributeSet attr, int offset, int x,
0757                        int y) {
0758                    Object useMap = attr.getAttribute(HTML.Attribute.USEMAP);
0759                    if (useMap != null && (useMap instanceof  String)) {
0760                        Map m = hdoc.getMap((String) useMap);
0761                        if (m != null && offset < hdoc.getLength()) {
0762                            Rectangle bounds;
0763                            TextUI ui = html.getUI();
0764                            try {
0765                                Shape lBounds = ui.modelToView(html, offset,
0766                                        Position.Bias.Forward);
0767                                Shape rBounds = ui.modelToView(html,
0768                                        offset + 1, Position.Bias.Backward);
0769                                bounds = lBounds.getBounds();
0770                                bounds
0771                                        .add((rBounds instanceof  Rectangle) ? (Rectangle) rBounds
0772                                                : rBounds.getBounds());
0773                            } catch (BadLocationException ble) {
0774                                bounds = null;
0775                            }
0776                            if (bounds != null) {
0777                                AttributeSet area = m
0778                                        .getArea(x - bounds.x, y - bounds.y,
0779                                                bounds.width, bounds.height);
0780                                if (area != null) {
0781                                    return (String) area
0782                                            .getAttribute(HTML.Attribute.HREF);
0783                                }
0784                            }
0785                        }
0786                    }
0787                    return null;
0788                }
0789
0790                /**
0791                 * Returns true if the View representing <code>e</code> contains
0792                 * the location <code>x</code>, <code>y</code>. <code>offset</code>
0793                 * gives the offset into the Document to check for.
0794                 */
0795                private boolean doesElementContainLocation(JEditorPane editor,
0796                        Element e, int offset, int x, int y) {
0797                    if (e != null && offset > 0 && e.getStartOffset() == offset) {
0798                        try {
0799                            TextUI ui = editor.getUI();
0800                            Shape s1 = ui.modelToView(editor, offset,
0801                                    Position.Bias.Forward);
0802                            if (s1 == null) {
0803                                return false;
0804                            }
0805                            Rectangle r1 = (s1 instanceof  Rectangle) ? (Rectangle) s1
0806                                    : s1.getBounds();
0807                            Shape s2 = ui.modelToView(editor, e.getEndOffset(),
0808                                    Position.Bias.Backward);
0809                            if (s2 != null) {
0810                                Rectangle r2 = (s2 instanceof  Rectangle) ? (Rectangle) s2
0811                                        : s2.getBounds();
0812                                r1.add(r2);
0813                            }
0814                            return r1.contains(x, y);
0815                        } catch (BadLocationException ble) {
0816                        }
0817                    }
0818                    return true;
0819                }
0820
0821                /**
0822                 * Calls linkActivated on the associated JEditorPane
0823                 * if the given position represents a link.<p>This is implemented
0824                 * to forward to the method with the same name, but with the following
0825                 * args both == -1.
0826                 *
0827                 * @param pos the position
0828                 * @param editor the editor pane
0829                 */
0830                protected void activateLink(int pos, JEditorPane editor) {
0831                    activateLink(pos, editor, -1, -1);
0832                }
0833
0834                /**
0835                 * Calls linkActivated on the associated JEditorPane
0836                 * if the given position represents a link. If this was the result
0837                 * of a mouse click, <code>x</code> and
0838                 * <code>y</code> will give the location of the mouse, otherwise
0839                 * they will be < 0.
0840                 *
0841                 * @param pos the position
0842                 * @param html the editor pane
0843                 */
0844                void activateLink(int pos, JEditorPane html, int x, int y) {
0845                    Document doc = html.getDocument();
0846                    if (doc instanceof  HTMLDocument) {
0847                        HTMLDocument hdoc = (HTMLDocument) doc;
0848                        Element e = hdoc.getCharacterElement(pos);
0849                        AttributeSet a = e.getAttributes();
0850                        AttributeSet anchor = (AttributeSet) a
0851                                .getAttribute(HTML.Tag.A);
0852                        HyperlinkEvent linkEvent = null;
0853                        String description;
0854
0855                        if (anchor == null) {
0856                            href = getMapHREF(html, hdoc, e, a, pos, x, y);
0857                        } else {
0858                            href = (String) anchor
0859                                    .getAttribute(HTML.Attribute.HREF);
0860                        }
0861
0862                        if (href != null) {
0863                            linkEvent = createHyperlinkEvent(html, hdoc, href,
0864                                    anchor, e);
0865                        }
0866                        if (linkEvent != null) {
0867                            html.fireHyperlinkUpdate(linkEvent);
0868                        }
0869                    }
0870                }
0871
0872                /**
0873                 * Creates and returns a new instance of HyperlinkEvent. If
0874                 * <code>hdoc</code> is a frame document a HTMLFrameHyperlinkEvent
0875                 * will be created.
0876                 */
0877                HyperlinkEvent createHyperlinkEvent(JEditorPane html,
0878                        HTMLDocument hdoc, String href, AttributeSet anchor,
0879                        Element element) {
0880                    URL u;
0881                    try {
0882                        URL base = hdoc.getBase();
0883                        u = new URL(base, href);
0884                        // Following is a workaround for 1.2, in which
0885                        // new URL("file://...", "#...") causes the filename to
0886                        // be lost.
0887                        if (href != null && "file".equals(u.getProtocol())
0888                                && href.startsWith("#")) {
0889                            String baseFile = base.getFile();
0890                            String newFile = u.getFile();
0891                            if (baseFile != null && newFile != null
0892                                    && !newFile.startsWith(baseFile)) {
0893                                u = new URL(base, baseFile + href);
0894                            }
0895                        }
0896                    } catch (MalformedURLException m) {
0897                        u = null;
0898                    }
0899                    HyperlinkEvent linkEvent = null;
0900
0901                    if (!hdoc.isFrameDocument()) {
0902                        linkEvent = new HyperlinkEvent(html,
0903                                HyperlinkEvent.EventType.ACTIVATED, u, href,
0904                                element);
0905                    } else {
0906                        String target = (anchor != null) ? (String) anchor
0907                                .getAttribute(HTML.Attribute.TARGET) : null;
0908                        if ((target == null) || (target.equals(""))) {
0909                            target = hdoc.getBaseTarget();
0910                        }
0911                        if ((target == null) || (target.equals(""))) {
0912                            target = "_self";
0913                        }
0914                        linkEvent = new HTMLFrameHyperlinkEvent(html,
0915                                HyperlinkEvent.EventType.ACTIVATED, u, href,
0916                                element, target);
0917                    }
0918                    return linkEvent;
0919                }
0920
0921                void fireEvents(JEditorPane editor, HTMLDocument doc,
0922                        String href, Element lastElem) {
0923                    if (this .href != null) {
0924                        // fire an exited event on the old link
0925                        URL u;
0926                        try {
0927                            u = new URL(doc.getBase(), this .href);
0928                        } catch (MalformedURLException m) {
0929                            u = null;
0930                        }
0931                        HyperlinkEvent exit = new HyperlinkEvent(editor,
0932                                HyperlinkEvent.EventType.EXITED, u, this .href,
0933                                lastElem);
0934                        editor.fireHyperlinkUpdate(exit);
0935                    }
0936                    if (href != null) {
0937                        // fire an entered event on the new link
0938                        URL u;
0939                        try {
0940                            u = new URL(doc.getBase(), href);
0941                        } catch (MalformedURLException m) {
0942                            u = null;
0943                        }
0944                        HyperlinkEvent entered = new HyperlinkEvent(editor,
0945                                HyperlinkEvent.EventType.ENTERED, u, href,
0946                                curElem);
0947                        editor.fireHyperlinkUpdate(entered);
0948                    }
0949                }
0950            }
0951
0952            /**
0953             * Interface to be supported by the parser.  This enables
0954             * providing a different parser while reusing some of the
0955             * implementation provided by this editor kit.
0956             */
0957            public static abstract class Parser {
0958                /**
0959                 * Parse the given stream and drive the given callback 
0960                 * with the results of the parse.  This method should
0961                 * be implemented to be thread-safe.
0962                 */
0963                public abstract void parse(Reader r, ParserCallback cb,
0964                        boolean ignoreCharSet) throws IOException;
0965
0966            }
0967
0968            /**
0969             * The result of parsing drives these callback methods.
0970             * The open and close actions should be balanced.  The
0971             * <code>flush</code> method will be the last method
0972             * called, to give the receiver a chance to flush any
0973             * pending data into the document.
0974             * <p>Refer to DocumentParser, the default parser used, for further
0975             * information on the contents of the AttributeSets, the positions, and
0976             * other info.
0977             *
0978             * @see javax.swing.text.html.parser.DocumentParser
0979             */
0980            public static class ParserCallback {
0981                /**
0982                 * This is passed as an attribute in the attributeset to indicate
0983                 * the element is implied eg, the string '&lt;&gt;foo&lt;\t&gt;' 
0984                 * contains an implied html element and an implied body element.
0985                 *
0986                 * @since 1.3
0987                 */
0988                public static final Object IMPLIED = "_implied_";
0989
0990                public void flush() throws BadLocationException {
0991                }
0992
0993                public void handleText(char[] data, int pos) {
0994                }
0995
0996                public void handleComment(char[] data, int pos) {
0997                }
0998
0999                public void handleStartTag(HTML.Tag t, MutableAttributeSet a,
1000                        int pos) {
1001                }
1002
1003                public void handleEndTag(HTML.Tag t, int pos) {
1004                }
1005
1006                public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a,
1007                        int pos) {
1008                }
1009
1010                public void handleError(String errorMsg, int pos) {
1011                }
1012
1013                /**
1014                 * This is invoked after the stream has been parsed, but before
1015                 * <code>flush</code>. <code>eol</code> will be one of \n, \r
1016                 * or \r\n, which ever is encountered the most in parsing the
1017                 * stream.
1018                 *
1019                 * @since 1.3
1020                 */
1021                public void handleEndOfLineString(String eol) {
1022                }
1023            }
1024
1025            /**
1026             * A factory to build views for HTML.  The following 
1027             * table describes what this factory will build by
1028             * default.
1029             *
1030             * <table summary="Describes the tag and view created by this factory by default">
1031             * <tr>
1032             * <th align=left>Tag<th align=left>View created
1033             * </tr><tr>
1034             * <td>HTML.Tag.CONTENT<td>InlineView
1035             * </tr><tr>
1036             * <td>HTML.Tag.IMPLIED<td>javax.swing.text.html.ParagraphView
1037             * </tr><tr>
1038             * <td>HTML.Tag.P<td>javax.swing.text.html.ParagraphView
1039             * </tr><tr>
1040             * <td>HTML.Tag.H1<td>javax.swing.text.html.ParagraphView
1041             * </tr><tr>
1042             * <td>HTML.Tag.H2<td>javax.swing.text.html.ParagraphView
1043             * </tr><tr>
1044             * <td>HTML.Tag.H3<td>javax.swing.text.html.ParagraphView
1045             * </tr><tr>
1046             * <td>HTML.Tag.H4<td>javax.swing.text.html.ParagraphView
1047             * </tr><tr>
1048             * <td>HTML.Tag.H5<td>javax.swing.text.html.ParagraphView
1049             * </tr><tr>
1050             * <td>HTML.Tag.H6<td>javax.swing.text.html.ParagraphView
1051             * </tr><tr>
1052             * <td>HTML.Tag.DT<td>javax.swing.text.html.ParagraphView
1053             * </tr><tr>
1054             * <td>HTML.Tag.MENU<td>ListView
1055             * </tr><tr>
1056             * <td>HTML.Tag.DIR<td>ListView
1057             * </tr><tr>
1058             * <td>HTML.Tag.UL<td>ListView
1059             * </tr><tr>
1060             * <td>HTML.Tag.OL<td>ListView
1061             * </tr><tr>
1062             * <td>HTML.Tag.LI<td>BlockView
1063             * </tr><tr>
1064             * <td>HTML.Tag.DL<td>BlockView
1065             * </tr><tr>
1066             * <td>HTML.Tag.DD<td>BlockView
1067             * </tr><tr>
1068             * <td>HTML.Tag.BODY<td>BlockView
1069             * </tr><tr>
1070             * <td>HTML.Tag.HTML<td>BlockView
1071             * </tr><tr>
1072             * <td>HTML.Tag.CENTER<td>BlockView
1073             * </tr><tr>
1074             * <td>HTML.Tag.DIV<td>BlockView
1075             * </tr><tr>
1076             * <td>HTML.Tag.BLOCKQUOTE<td>BlockView
1077             * </tr><tr>
1078             * <td>HTML.Tag.PRE<td>BlockView
1079             * </tr><tr>
1080             * <td>HTML.Tag.BLOCKQUOTE<td>BlockView
1081             * </tr><tr>
1082             * <td>HTML.Tag.PRE<td>BlockView
1083             * </tr><tr>
1084             * <td>HTML.Tag.IMG<td>ImageView
1085             * </tr><tr>
1086             * <td>HTML.Tag.HR<td>HRuleView
1087             * </tr><tr>
1088             * <td>HTML.Tag.BR<td>BRView
1089             * </tr><tr>
1090             * <td>HTML.Tag.TABLE<td>javax.swing.text.html.TableView
1091             * </tr><tr>
1092             * <td>HTML.Tag.INPUT<td>FormView
1093             * </tr><tr>
1094             * <td>HTML.Tag.SELECT<td>FormView
1095             * </tr><tr>
1096             * <td>HTML.Tag.TEXTAREA<td>FormView
1097             * </tr><tr>
1098             * <td>HTML.Tag.OBJECT<td>ObjectView
1099             * </tr><tr>
1100             * <td>HTML.Tag.FRAMESET<td>FrameSetView
1101             * </tr><tr>
1102             * <td>HTML.Tag.FRAME<td>FrameView
1103             * </tr>
1104             * </table>
1105             */
1106            public static class HTMLFactory implements  ViewFactory {
1107
1108                /**
1109                 * Creates a view from an element.
1110                 *
1111                 * @param elem the element
1112                 * @return the view
1113                 */
1114                public View create(Element elem) {
1115                    AttributeSet attrs = elem.getAttributes();
1116                    Object elementName = attrs
1117                            .getAttribute(AbstractDocument.ElementNameAttribute);
1118                    Object o = (elementName != null) ? null : attrs
1119                            .getAttribute(StyleConstants.NameAttribute);
1120                    if (o instanceof  HTML.Tag) {
1121                        HTML.Tag kind = (HTML.Tag) o;
1122                        if (kind == HTML.Tag.CONTENT) {
1123                            return new InlineView(elem);
1124                        } else if (kind == HTML.Tag.IMPLIED) {
1125                            String ws = (String) elem.getAttributes()
1126                                    .getAttribute(CSS.Attribute.WHITE_SPACE);
1127                            if ((ws != null) && ws.equals("pre")) {
1128                                return new LineView(elem);
1129                            }
1130                            return new javax.swing.text.html.ParagraphView(elem);
1131                        } else if ((kind == HTML.Tag.P)
1132                                || (kind == HTML.Tag.H1)
1133                                || (kind == HTML.Tag.H2)
1134                                || (kind == HTML.Tag.H3)
1135                                || (kind == HTML.Tag.H4)
1136                                || (kind == HTML.Tag.H5)
1137                                || (kind == HTML.Tag.H6)
1138                                || (kind == HTML.Tag.DT)) {
1139                            // paragraph
1140                            return new javax.swing.text.html.ParagraphView(elem);
1141                        } else if ((kind == HTML.Tag.MENU)
1142                                || (kind == HTML.Tag.DIR)
1143                                || (kind == HTML.Tag.UL)
1144                                || (kind == HTML.Tag.OL)) {
1145                            return new ListView(elem);
1146                        } else if (kind == HTML.Tag.BODY) {
1147                            return new BodyBlockView(elem);
1148                        } else if (kind == HTML.Tag.HTML) {
1149                            return new BlockView(elem, View.Y_AXIS);
1150                        } else if ((kind == HTML.Tag.LI)
1151                                || (kind == HTML.Tag.CENTER)
1152                                || (kind == HTML.Tag.DL)
1153                                || (kind == HTML.Tag.DD)
1154                                || (kind == HTML.Tag.DIV)
1155                                || (kind == HTML.Tag.BLOCKQUOTE)
1156                                || (kind == HTML.Tag.PRE)
1157                                || (kind == HTML.Tag.FORM)) {
1158                            // vertical box
1159                            return new BlockView(elem, View.Y_AXIS);
1160                        } else if (kind == HTML.Tag.NOFRAMES) {
1161                            return new NoFramesView(elem, View.Y_AXIS);
1162                        } else if (kind == HTML.Tag.IMG) {
1163                            return new ImageView(elem);
1164                        } else if (kind == HTML.Tag.ISINDEX) {
1165                            return new IsindexView(elem);
1166                        } else if (kind == HTML.Tag.HR) {
1167                            return new HRuleView(elem);
1168                        } else if (kind == HTML.Tag.BR) {
1169                            return new BRView(elem);
1170                        } else if (kind == HTML.Tag.TABLE) {
1171                            return new javax.swing.text.html.TableView(elem);
1172                        } else if ((kind == HTML.Tag.INPUT)
1173                                || (kind == HTML.Tag.SELECT)
1174                                || (kind == HTML.Tag.TEXTAREA)) {
1175                            return new FormView(elem);
1176                        } else if (kind == HTML.Tag.OBJECT) {
1177                            return new ObjectView(elem);
1178                        } else if (kind == HTML.Tag.FRAMESET) {
1179                            if (elem.getAttributes().isDefined(
1180                                    HTML.Attribute.ROWS)) {
1181                                return new FrameSetView(elem, View.Y_AXIS);
1182                            } else if (elem.getAttributes().isDefined(
1183                                    HTML.Attribute.COLS)) {
1184                                return new FrameSetView(elem, View.X_AXIS);
1185                            }
1186                            throw new RuntimeException("Can't build a" + kind
1187                                    + ", " + elem + ":"
1188                                    + "no ROWS or COLS defined.");
1189                        } else if (kind == HTML.Tag.FRAME) {
1190                            return new FrameView(elem);
1191                        } else if (kind instanceof  HTML.UnknownTag) {
1192                            return new HiddenTagView(elem);
1193                        } else if (kind == HTML.Tag.COMMENT) {
1194                            return new CommentView(elem);
1195                        } else if (kind == HTML.Tag.HEAD) {
1196                            // Make the head never visible, and never load its
1197                            // children. For Cursor positioning,
1198                            // getNextVisualPositionFrom is overriden to always return
1199                            // the end offset of the element.
1200                            return new BlockView(elem, View.X_AXIS) {
1201                                public float getPreferredSpan(int axis) {
1202                                    return 0;
1203                                }
1204
1205                                public float getMinimumSpan(int axis) {
1206                                    return 0;
1207                                }
1208
1209                                public float getMaximumSpan(int axis) {
1210                                    return 0;
1211                                }
1212
1213                                protected void loadChildren(ViewFactory f) {
1214                                }
1215
1216                                public Shape modelToView(int pos, Shape a,
1217                                        Position.Bias b)
1218                                        throws BadLocationException {
1219                                    return a;
1220                                }
1221
1222                                public int getNextVisualPositionFrom(int pos,
1223                                        Position.Bias b, Shape a,
1224                                        int direction, Position.Bias[] biasRet) {
1225                                    return getElement().getEndOffset();
1226                                }
1227                            };
1228                        } else if ((kind == HTML.Tag.TITLE)
1229                                || (kind == HTML.Tag.META)
1230                                || (kind == HTML.Tag.LINK)
1231                                || (kind == HTML.Tag.STYLE)
1232                                || (kind == HTML.Tag.SCRIPT)
1233                                || (kind == HTML.Tag.AREA)
1234                                || (kind == HTML.Tag.MAP)
1235                                || (kind == HTML.Tag.PARAM)
1236                                || (kind == HTML.Tag.APPLET)) {
1237                            return new HiddenTagView(elem);
1238                        }
1239                    }
1240                    // If we get here, it's either an element we don't know about
1241                    // or something from StyledDocument that doesn't have a mapping to HTML.
1242                    String nm = (elementName != null) ? (String) elementName
1243                            : elem.getName();
1244                    if (nm != null) {
1245                        if (nm.equals(AbstractDocument.ContentElementName)) {
1246                            return new LabelView(elem);
1247                        } else if (nm
1248                                .equals(AbstractDocument.ParagraphElementName)) {
1249                            return new ParagraphView(elem);
1250                        } else if (nm
1251                                .equals(AbstractDocument.SectionElementName)) {
1252                            return new BoxView(elem, View.Y_AXIS);
1253                        } else if (nm
1254                                .equals(StyleConstants.ComponentElementName)) {
1255                            return new ComponentView(elem);
1256                        } else if (nm.equals(StyleConstants.IconElementName)) {
1257                            return new IconView(elem);
1258                        }
1259                    }
1260
1261                    // default to text display
1262                    return new LabelView(elem);
1263                }
1264
1265                static class BodyBlockView extends BlockView implements 
1266                        ComponentListener {
1267                    public BodyBlockView(Element elem) {
1268                        super (elem, View.Y_AXIS);
1269                    }
1270
1271                    // reimplement major axis requirements to indicate that the
1272                    // block is flexible for the body element... so that it can
1273                    // be stretched to fill the background properly.
1274                    protected SizeRequirements calculateMajorAxisRequirements(
1275                            int axis, SizeRequirements r) {
1276                        r = super .calculateMajorAxisRequirements(axis, r);
1277                        r.maximum = Integer.MAX_VALUE;
1278                        return r;
1279                    }
1280
1281                    protected void layoutMinorAxis(int targetSpan, int axis,
1282                            int[] offsets, int[] spans) {
1283                        Container container = getContainer();
1284                        Container parentContainer;
1285                        if (container != null
1286                                && (container instanceof  javax.swing.JEditorPane)
1287                                && (parentContainer = container.getParent()) != null
1288                                && (parentContainer instanceof  javax.swing.JViewport)) {
1289                            JViewport viewPort = (JViewport) parentContainer;
1290                            Object cachedObject;
1291                            if (cachedViewPort != null) {
1292                                if ((cachedObject = cachedViewPort.get()) != null) {
1293                                    if (cachedObject != viewPort) {
1294                                        ((JComponent) cachedObject)
1295                                                .removeComponentListener(this );
1296                                    }
1297                                } else {
1298                                    cachedViewPort = null;
1299                                }
1300                            }
1301                            if (cachedViewPort == null) {
1302                                viewPort.addComponentListener(this );
1303                                cachedViewPort = new WeakReference(viewPort);
1304                            }
1305
1306                            componentVisibleWidth = viewPort.getExtentSize().width;
1307                            if (componentVisibleWidth > 0) {
1308                                Insets insets = container.getInsets();
1309                                viewVisibleWidth = componentVisibleWidth
1310                                        - insets.left - getLeftInset();
1311                                //try to use viewVisibleWidth if it is smaller than targetSpan
1312                                targetSpan = Math.min(targetSpan,
1313                                        viewVisibleWidth);
1314                            }
1315                        } else {
1316                            if (cachedViewPort != null) {
1317                                Object cachedObject;
1318                                if ((cachedObject = cachedViewPort.get()) != null) {
1319                                    ((JComponent) cachedObject)
1320                                            .removeComponentListener(this );
1321                                }
1322                                cachedViewPort = null;
1323                            }
1324                        }
1325                        super .layoutMinorAxis(targetSpan, axis, offsets, spans);
1326                    }
1327
1328                    public void setParent(View parent) {
1329                        //if parent == null unregister component listener
1330                        if (parent == null) {
1331                            if (cachedViewPort != null) {
1332                                Object cachedObject;
1333                                if ((cachedObject = cachedViewPort.get()) != null) {
1334                                    ((JComponent) cachedObject)
1335                                            .removeComponentListener(this );
1336                                }
1337                                cachedViewPort = null;
1338                            }
1339                        }
1340                        super .setParent(parent);
1341                    }
1342
1343                    public void componentResized(ComponentEvent e) {
1344                        if (!(e.getSource() instanceof  JViewport)) {
1345                            return;
1346                        }
1347                        JViewport viewPort = (JViewport) e.getSource();
1348                        if (componentVisibleWidth != viewPort.getExtentSize().width) {
1349                            Document doc = getDocument();
1350                            if (doc instanceof  AbstractDocument) {
1351                                AbstractDocument document = (AbstractDocument) getDocument();
1352                                document.readLock();
1353                                try {
1354                                    layoutChanged(X_AXIS);
1355                                    preferenceChanged(null, true, true);
1356                                } finally {
1357                                    document.readUnlock();
1358                                }
1359
1360                            }
1361                        }
1362                    }
1363
1364                    public void componentHidden(ComponentEvent e) {
1365                    }
1366
1367                    public void componentMoved(ComponentEvent e) {
1368                    }
1369
1370                    public void componentShown(ComponentEvent e) {
1371                    }
1372
1373                    /*
1374                     * we keep weak reference to viewPort if and only if BodyBoxView is listening for ComponentEvents
1375                     * only in that case cachedViewPort is not equal to null.
1376                     * we need to keep this reference in order to remove BodyBoxView from viewPort listeners.
1377                     * 
1378                     */
1379                    private Reference cachedViewPort = null;
1380                    private boolean isListening = false;
1381                    private int viewVisibleWidth = Integer.MAX_VALUE;
1382                    private int componentVisibleWidth = Integer.MAX_VALUE;
1383                }
1384
1385            }
1386
1387            // --- Action implementations ------------------------------
1388
1389            /** The bold action identifier
1390             */
1391            public static final String BOLD_ACTION = "html-bold-action";
1392            /** The italic action identifier
1393             */
1394            public static final String ITALIC_ACTION = "html-italic-action";
1395            /** The paragraph left indent action identifier
1396             */
1397            public static final String PARA_INDENT_LEFT = "html-para-indent-left";
1398            /** The paragraph right indent action identifier
1399             */
1400            public static final String PARA_INDENT_RIGHT = "html-para-indent-right";
1401            /** The  font size increase to next value action identifier
1402             */
1403            public static final String FONT_CHANGE_BIGGER = "html-font-bigger";
1404            /** The font size decrease to next value action identifier
1405             */
1406            public static final String FONT_CHANGE_SMALLER = "html-font-smaller";
1407            /** The Color choice action identifier
1408             The color is passed as an argument
1409             */
1410            public static final String COLOR_ACTION = "html-color-action";
1411            /** The logical style choice action identifier
1412             The logical style is passed in as an argument
1413             */
1414            public static final String LOGICAL_STYLE_ACTION = "html-logical-style-action";
1415            /**
1416             * Align images at the top.
1417             */
1418            public static final String IMG_ALIGN_TOP = "html-image-align-top";
1419
1420            /**
1421             * Align images in the middle.
1422             */
1423            public static final String IMG_ALIGN_MIDDLE = "html-image-align-middle";
1424
1425            /**
1426             * Align images at the bottom.
1427             */
1428            public static final String IMG_ALIGN_BOTTOM = "html-image-align-bottom";
1429
1430            /**
1431             * Align images at the border.
1432             */
1433            public static final String IMG_BORDER = "html-image-border";
1434
1435            /** HTML used when inserting tables. */
1436            private static final String INSERT_TABLE_HTML = "<table border=1><tr><td></td></tr></table>";
1437
1438            /** HTML used when inserting unordered lists. */
1439            private static final String INSERT_UL_HTML = "<ul><li></li></ul>";
1440
1441            /** HTML used when inserting ordered lists. */
1442            private static final String INSERT_OL_HTML = "<ol><li></li></ol>";
1443
1444            /** HTML used when inserting hr. */
1445            private static final String INSERT_HR_HTML = "<hr>";
1446
1447            /** HTML used when inserting pre. */
1448            private static final String INSERT_PRE_HTML = "<pre></pre>";
1449
1450            private static NavigateLinkAction nextLinkAction = new NavigateLinkAction(
1451                    "next-link-action");
1452
1453            private static NavigateLinkAction previousLinkAction = new NavigateLinkAction(
1454                    "previous-link-action");
1455
1456            private static ActivateLinkAction activateLinkAction = new ActivateLinkAction(
1457                    "activate-link-action");
1458
1459            private static final Action[] defaultActions = {
1460                    new InsertHTMLTextAction("InsertTable", INSERT_TABLE_HTML,
1461                            HTML.Tag.BODY, HTML.Tag.TABLE),
1462                    new InsertHTMLTextAction("InsertTableRow",
1463                            INSERT_TABLE_HTML, HTML.Tag.TABLE, HTML.Tag.TR,
1464                            HTML.Tag.BODY, HTML.Tag.TABLE),
1465                    new InsertHTMLTextAction("InsertTableDataCell",
1466                            INSERT_TABLE_HTML, HTML.Tag.TR, HTML.Tag.TD,
1467                            HTML.Tag.BODY, HTML.Tag.TABLE),
1468                    new InsertHTMLTextAction("InsertUnorderedList",
1469                            INSERT_UL_HTML, HTML.Tag.BODY, HTML.Tag.UL),
1470                    new InsertHTMLTextAction("InsertUnorderedListItem",
1471                            INSERT_UL_HTML, HTML.Tag.UL, HTML.Tag.LI,
1472                            HTML.Tag.BODY, HTML.Tag.UL),
1473                    new InsertHTMLTextAction("InsertOrderedList",
1474                            INSERT_OL_HTML, HTML.Tag.BODY, HTML.Tag.OL),
1475                    new InsertHTMLTextAction("InsertOrderedListItem",
1476                            INSERT_OL_HTML, HTML.Tag.OL, HTML.Tag.LI,
1477                            HTML.Tag.BODY, HTML.Tag.OL),
1478                    new InsertHRAction(),
1479                    new InsertHTMLTextAction("InsertPre", INSERT_PRE_HTML,
1480                            HTML.Tag.BODY, HTML.Tag.PRE), nextLinkAction,
1481                    previousLinkAction, activateLinkAction,
1482
1483                    new BeginAction(beginAction, false),
1484                    new BeginAction(selectionBeginAction, true) };
1485
1486            /**
1487             * An abstract Action providing some convenience methods that may
1488             * be useful in inserting HTML into an existing document.
1489             * <p>NOTE: None of the convenience methods obtain a lock on the
1490             * document. If you have another thread modifying the text these
1491             * methods may have inconsistent behavior, or return the wrong thing.
1492             */
1493            public static abstract class HTMLTextAction extends
1494                    StyledTextAction {
1495                public HTMLTextAction(String name) {
1496                    super (name);
1497                }
1498
1499                /**
1500                 * @return HTMLDocument of <code>e</code>.
1501                 */
1502                protected HTMLDocument getHTMLDocument(JEditorPane e) {
1503                    Document d = e.getDocument();
1504                    if (d instanceof  HTMLDocument) {
1505                        return (HTMLDocument) d;
1506                    }
1507                    throw new IllegalArgumentException(
1508                            "document must be HTMLDocument");
1509                }
1510
1511                /**
1512                 * @return HTMLEditorKit for <code>e</code>.
1513                 */
1514                protected HTMLEditorKit getHTMLEditorKit(JEditorPane e) {
1515                    EditorKit k = e.getEditorKit();
1516                    if (k instanceof  HTMLEditorKit) {
1517                        return (HTMLEditorKit) k;
1518                    }
1519                    throw new IllegalArgumentException(
1520                            "EditorKit must be HTMLEditorKit");
1521                }
1522
1523                /**
1524                 * Returns an array of the Elements that contain <code>offset</code>.
1525                 * The first elements corresponds to the root.
1526                 */
1527                protected Element[] getElementsAt(HTMLDocument doc, int offset) {
1528                    return getElementsAt(doc.getDefaultRootElement(), offset, 0);
1529                }
1530
1531                /**
1532                 * Recursive method used by getElementsAt.
1533                 */
1534                private Element[] getElementsAt(Element parent, int offset,
1535                        int depth) {
1536                    if (parent.isLeaf()) {
1537                        Element[] retValue = new Element[depth + 1];
1538                        retValue[depth] = parent;
1539                        return retValue;
1540                    }
1541                    Element[] retValue = getElementsAt(parent.getElement(parent
1542                            .getElementIndex(offset)), offset, depth + 1);
1543                    retValue[depth] = parent;
1544                    return retValue;
1545                }
1546
1547                /**
1548                 * Returns number of elements, starting at the deepest leaf, needed
1549                 * to get to an element representing <code>tag</code>. This will
1550                 * return -1 if no elements is found representing <code>tag</code>,
1551                 * or 0 if the parent of the leaf at <code>offset</code> represents
1552                 * <code>tag</code>.
1553                 */
1554                protected int elementCountToTag(HTMLDocument doc, int offset,
1555                        HTML.Tag tag) {
1556                    int depth = -1;
1557                    Element e = doc.getCharacterElement(offset);
1558                    while (e != null
1559                            && e.getAttributes().getAttribute(
1560                                    StyleConstants.NameAttribute) != tag) {
1561                        e = e.getParentElement();
1562                        depth++;
1563                    }
1564                    if (e == null) {
1565                        return -1;
1566                    }
1567                    return depth;
1568                }
1569
1570                /**
1571                 * Returns the deepest element at <code>offset</code> matching
1572                 * <code>tag</code>.
1573                 */
1574                protected Element findElementMatchingTag(HTMLDocument doc,
1575                        int offset, HTML.Tag tag) {
1576                    Element e = doc.getDefaultRootElement();
1577                    Element lastMatch = null;
1578                    while (e != null) {
1579                        if (e.getAttributes().getAttribute(
1580                                StyleConstants.NameAttribute) == tag) {
1581                            lastMatch = e;
1582                        }
1583                        e = e.getElement(e.getElementIndex(offset));
1584                    }
1585                    return lastMatch;
1586                }
1587            }
1588
1589            /**
1590             * InsertHTMLTextAction can be used to insert an arbitrary string of HTML
1591             * into an existing HTML document. At least two HTML.Tags need to be
1592             * supplied. The first Tag, parentTag, identifies the parent in
1593             * the document to add the elements to. The second tag, addTag,
1594             * identifies the first tag that should be added to the document as
1595             * seen in the HTML string. One important thing to remember, is that
1596             * the parser is going to generate all the appropriate tags, even if
1597             * they aren't in the HTML string passed in.<p>
1598             * For example, lets say you wanted to create an action to insert
1599             * a table into the body. The parentTag would be HTML.Tag.BODY,
1600             * addTag would be HTML.Tag.TABLE, and the string could be something
1601             * like &lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;.
1602             * <p>There is also an option to supply an alternate parentTag and
1603             * addTag. These will be checked for if there is no parentTag at
1604             * offset.
1605             */
1606            public static class InsertHTMLTextAction extends HTMLTextAction {
1607                public InsertHTMLTextAction(String name, String html,
1608                        HTML.Tag parentTag, HTML.Tag addTag) {
1609                    this (name, html, parentTag, addTag, null, null);
1610                }
1611
1612                public InsertHTMLTextAction(String name, String html,
1613                        HTML.Tag parentTag, HTML.Tag addTag,
1614                        HTML.Tag alternateParentTag, HTML.Tag alternateAddTag) {
1615                    this (name, html, parentTag, addTag, alternateParentTag,
1616                            alternateAddTag, true);
1617                }
1618
1619                /* public */
1620                InsertHTMLTextAction(String name, String html,
1621                        HTML.Tag parentTag, HTML.Tag addTag,
1622                        HTML.Tag alternateParentTag, HTML.Tag alternateAddTag,
1623                        boolean adjustSelection) {
1624                    super (name);
1625                    this .html = html;
1626                    this .parentTag = parentTag;
1627                    this .addTag = addTag;
1628                    this .alternateParentTag = alternateParentTag;
1629                    this .alternateAddTag = alternateAddTag;
1630                    this .adjustSelection = adjustSelection;
1631                }
1632
1633                /**
1634                 * A cover for HTMLEditorKit.insertHTML. If an exception it
1635                 * thrown it is wrapped in a RuntimeException and thrown.
1636                 */
1637                protected void insertHTML(JEditorPane editor, HTMLDocument doc,
1638                        int offset, String html, int popDepth, int pushDepth,
1639                        HTML.Tag addTag) {
1640                    try {
1641                        getHTMLEditorKit(editor).insertHTML(doc, offset, html,
1642                                popDepth, pushDepth, addTag);
1643                    } catch (IOException ioe) {
1644                        throw new RuntimeException("Unable to insert: " + ioe);
1645                    } catch (BadLocationException ble) {
1646                        throw new RuntimeException("Unable to insert: " + ble);
1647                    }
1648                }
1649
1650                /**
1651                 * This is invoked when inserting at a boundary. It determines
1652                 * the number of pops, and then the number of pushes that need
1653                 * to be performed, and then invokes insertHTML.
1654                 * @since 1.3
1655                 */
1656                protected void insertAtBoundary(JEditorPane editor,
1657                        HTMLDocument doc, int offset, Element insertElement,
1658                        String html, HTML.Tag parentTag, HTML.Tag addTag) {
1659                    insertAtBoundry(editor, doc, offset, insertElement, html,
1660                            parentTag, addTag);
1661                }
1662
1663                /**
1664                 * This is invoked when inserting at a boundary. It determines
1665                 * the number of pops, and then the number of pushes that need
1666                 * to be performed, and then invokes insertHTML.
1667                 * @deprecated As of Java 2 platform v1.3, use insertAtBoundary
1668                 */
1669                @Deprecated
1670                protected void insertAtBoundry(JEditorPane editor,
1671                        HTMLDocument doc, int offset, Element insertElement,
1672                        String html, HTML.Tag parentTag, HTML.Tag addTag) {
1673                    // Find the common parent.
1674                    Element e;
1675                    Element commonParent;
1676                    boolean isFirst = (offset == 0);
1677
1678                    if (offset > 0 || insertElement == null) {
1679                        e = doc.getDefaultRootElement();
1680                        while (e != null && e.getStartOffset() != offset
1681                                && !e.isLeaf()) {
1682                            e = e.getElement(e.getElementIndex(offset));
1683                        }
1684                        commonParent = (e != null) ? e.getParentElement()
1685                                : null;
1686                    } else {
1687                        // If inserting at the origin, the common parent is the
1688                        // insertElement.
1689                        commonParent = insertElement;
1690                    }
1691                    if (commonParent != null) {
1692                        // Determine how many pops to do.
1693                        int pops = 0;
1694                        int pushes = 0;
1695                        if (isFirst && insertElement != null) {
1696                            e = commonParent;
1697                            while (e != null && !e.isLeaf()) {
1698                                e = e.getElement(e.getElementIndex(offset));
1699                                pops++;
1700                            }
1701                        } else {
1702                            e = commonParent;
1703                            offset--;
1704                            while (e != null && !e.isLeaf()) {
1705                                e = e.getElement(e.getElementIndex(offset));
1706                                pops++;
1707                            }
1708
1709                            // And how many pushes
1710                            e = commonParent;
1711                            offset++;
1712                            while (e != null && e != insertElement) {
1713                                e = e.getElement(e.getElementIndex(offset));
1714                                pushes++;
1715                            }
1716                        }
1717                        pops = Math.max(0, pops - 1);
1718
1719                        // And insert!
1720                        insertHTML(editor, doc, offset, html, pops, pushes,
1721                                addTag);
1722                    }
1723                }
1724
1725                /**
1726                 * If there is an Element with name <code>tag</code> at
1727                 * <code>offset</code>, this will invoke either insertAtBoundary
1728                 * or <code>insertHTML</code>. This returns true if there is
1729                 * a match, and one of the inserts is invoked.
1730                 */
1731                /*protected*/
1732                boolean insertIntoTag(JEditorPane editor, HTMLDocument doc,
1733                        int offset, HTML.Tag tag, HTML.Tag addTag) {
1734                    Element e = findElementMatchingTag(doc, offset, tag);
1735                    if (e != null && e.getStartOffset() == offset) {
1736                        insertAtBoundary(editor, doc, offset, e, html, tag,
1737                                addTag);
1738                        return true;
1739                    } else if (offset > 0) {
1740                        int depth = elementCountToTag(doc, offset - 1, tag);
1741                        if (depth != -1) {
1742                            insertHTML(editor, doc, offset, html, depth, 0,
1743                                    addTag);
1744                            return true;
1745                        }
1746                    }
1747                    return false;
1748                }
1749
1750                /**
1751                 * Called after an insertion to adjust the selection.
1752                 */
1753                /* protected */
1754                void adjustSelection(JEditorPane pane, HTMLDocument doc,
1755                        int startOffset, int oldLength) {
1756                    int newLength = doc.getLength();
1757                    if (newLength != oldLength && startOffset < newLength) {
1758                        if (startOffset > 0) {
1759                            String text;
1760                            try {
1761                                text = doc.getText(startOffset - 1, 1);
1762                            } catch (BadLocationException ble) {
1763                                text = null;
1764                            }
1765                            if (text != null && text.length() > 0
1766                                    && text.charAt(0) == '\n') {
1767                                pane.select(startOffset, startOffset);
1768                            } else {
1769                                pane.select(startOffset + 1, startOffset + 1);
1770                            }
1771                        } else {
1772                            pane.select(1, 1);
1773                        }
1774                    }
1775                }
1776
1777                /**
1778                 * Inserts the HTML into the document.
1779                 *
1780                 * @param ae the event
1781                 */
1782                public void actionPerformed(ActionEvent ae) {
1783                    JEditorPane editor = getEditor(ae);
1784                    if (editor != null) {
1785                        HTMLDocument doc = getHTMLDocument(editor);
1786                        int offset = editor.getSelectionStart();
1787                        int length = doc.getLength();
1788                        boolean inserted;
1789                        // Try first choice
1790                        if (!insertIntoTag(editor, doc, offset, parentTag,
1791                                addTag)
1792                                && alternateParentTag != null) {
1793                            // Then alternate.
1794                            inserted = insertIntoTag(editor, doc, offset,
1795                                    alternateParentTag, alternateAddTag);
1796                        } else {
1797                            inserted = true;
1798                        }
1799                        if (adjustSelection && inserted) {
1800                            adjustSelection(editor, doc, offset, length);
1801                        }
1802                    }
1803                }
1804
1805                /** HTML to insert. */
1806                protected String html;
1807                /** Tag to check for in the document. */
1808                protected HTML.Tag parentTag;
1809                /** Tag in HTML to start adding tags from. */
1810                protected HTML.Tag addTag;
1811                /** Alternate Tag to check for in the document if parentTag is
1812                 * not found. */
1813                protected HTML.Tag alternateParentTag;
1814                /** Alternate tag in HTML to start adding tags from if parentTag
1815                 * is not found and alternateParentTag is found. */
1816                protected HTML.Tag alternateAddTag;
1817                /** True indicates the selection should be adjusted after an insert. */
1818                boolean adjustSelection;
1819            }
1820
1821            /**
1822             * InsertHRAction is special, at actionPerformed time it will determine
1823             * the parent HTML.Tag based on the paragraph element at the selection
1824             * start.
1825             */
1826            static class InsertHRAction extends InsertHTMLTextAction {
1827                InsertHRAction() {
1828                    super ("InsertHR", "<hr>", null, HTML.Tag.IMPLIED, null,
1829                            null, false);
1830                }
1831
1832                /**
1833                 * Inserts the HTML into the document.
1834                 *
1835                 * @param ae the event
1836                 */
1837                public void actionPerformed(ActionEvent ae) {
1838                    JEditorPane editor = getEditor(ae);
1839                    if (editor != null) {
1840                        HTMLDocument doc = getHTMLDocument(editor);
1841                        int offset = editor.getSelectionStart();
1842                        Element paragraph = doc.getParagraphElement(offset);
1843                        if (paragraph.getParentElement() != null) {
1844                            parentTag = (HTML.Tag) paragraph.getParentElement()
1845                                    .getAttributes().getAttribute(
1846                                            StyleConstants.NameAttribute);
1847                            super .actionPerformed(ae);
1848                        }
1849                    }
1850                }
1851
1852            }
1853
1854            /*
1855             * Returns the object in an AttributeSet matching a key
1856             */
1857            static private Object getAttrValue(AttributeSet attr,
1858                    HTML.Attribute key) {
1859                Enumeration names = attr.getAttributeNames();
1860                while (names.hasMoreElements()) {
1861                    Object nextKey = names.nextElement();
1862                    Object nextVal = attr.getAttribute(nextKey);
1863                    if (nextVal instanceof  AttributeSet) {
1864                        Object value = getAttrValue((AttributeSet) nextVal, key);
1865                        if (value != null) {
1866                            return value;
1867                        }
1868                    } else if (nextKey == key) {
1869                        return nextVal;
1870                    }
1871                }
1872                return null;
1873            }
1874
1875            /*
1876             * Action to move the focus on the next or previous hypertext link 
1877             * or object. TODO: This method relies on support from the 
1878             * javax.accessibility package.  The text package should support
1879             * keyboard navigation of text elements directly.
1880             */
1881            static class NavigateLinkAction extends TextAction implements 
1882                    CaretListener {
1883
1884                private static int prevHypertextOffset = -1;
1885                private static boolean foundLink = false;
1886                private static FocusHighlightPainter focusPainter = new FocusHighlightPainter(
1887                        null);
1888                private Object selectionTag;
1889                private boolean focusBack = false;
1890
1891                /*
1892                 * Create this action with the appropriate identifier. 
1893                 */
1894                public NavigateLinkAction(String actionName) {
1895                    super (actionName);
1896                    if ("previous-link-action".equals(actionName)) {
1897                        focusBack = true;
1898                    }
1899                }
1900
1901                /**
1902                 * Called when the caret position is updated.
1903                 *
1904                 * @param e the caret event
1905                 */
1906                public void caretUpdate(CaretEvent e) {
1907                    if (foundLink) {
1908                        foundLink = false;
1909                        // TODO: The AccessibleContext for the editor should register
1910                        // as a listener for CaretEvents and forward the events to
1911                        // assistive technologies listening for such events.
1912                        Object src = e.getSource();
1913                        if (src instanceof  JTextComponent) {
1914                            ((JTextComponent) src)
1915                                    .getAccessibleContext()
1916                                    .firePropertyChange(
1917                                            AccessibleContext.ACCESSIBLE_HYPERTEXT_OFFSET,
1918                                            new Integer(prevHypertextOffset),
1919                                            new Integer(e.getDot()));
1920                        }
1921                    }
1922                }
1923
1924                /*
1925                 * The operation to perform when this action is triggered. 
1926                 */
1927                public void actionPerformed(ActionEvent e) {
1928                    JTextComponent comp = getTextComponent(e);
1929                    if (comp == null || comp.isEditable()) {
1930                        return;
1931                    }
1932                    Document doc = comp.getDocument();
1933                    if (doc == null) {
1934                        return;
1935                    }
1936                    // TODO: Should start successive iterations from the
1937                    // current caret position.
1938                    ElementIterator ei = new ElementIterator(doc);
1939
1940                    int currentOffset = comp.getCaretPosition();
1941                    int prevStartOffset = -1;
1942                    int prevEndOffset = -1;
1943
1944                    // highlight the next link or object after the current caret position
1945                    Element nextElement = null;
1946                    while ((nextElement = ei.next()) != null) {
1947                        String name = nextElement.getName();
1948                        AttributeSet attr = nextElement.getAttributes();
1949
1950                        Object href = getAttrValue(attr, HTML.Attribute.HREF);
1951                        if (!(name.equals(HTML.Tag.OBJECT.toString()))
1952                                && href == null) {
1953                            continue;
1954                        }
1955
1956                        int elementOffset = nextElement.getStartOffset();
1957                        if (focusBack) {
1958                            if (elementOffset >= currentOffset
1959                                    && prevStartOffset >= 0) {
1960
1961                                foundLink = true;
1962                                comp.setCaretPosition(prevStartOffset);
1963                                moveCaretPosition(comp, prevStartOffset,
1964                                        prevEndOffset);
1965                                prevHypertextOffset = prevStartOffset;
1966                                return;
1967                            }
1968                        } else { // focus forward
1969                            if (elementOffset > currentOffset) {
1970
1971                                foundLink = true;
1972                                comp.setCaretPosition(elementOffset);
1973                                moveCaretPosition(comp, elementOffset,
1974                                        nextElement.getEndOffset());
1975                                prevHypertextOffset = elementOffset;
1976                                return;
1977                            }
1978                        }
1979                        prevStartOffset = nextElement.getStartOffset();
1980                        prevEndOffset = nextElement.getEndOffset();
1981                    }
1982                    if (focusBack && prevStartOffset >= 0) {
1983                        foundLink = true;
1984                        comp.setCaretPosition(prevStartOffset);
1985                        moveCaretPosition(comp, prevStartOffset, prevEndOffset);
1986                        prevHypertextOffset = prevStartOffset;
1987                        return;
1988                    }
1989                }
1990
1991                /*
1992                 * Moves the caret from mark to dot
1993                 */
1994                private void moveCaretPosition(JTextComponent comp, int mark,
1995                        int dot) {
1996                    Highlighter h = comp.getHighlighter();
1997                    if (h != null) {
1998                        int p0 = Math.min(dot, mark);
1999                        int p1 = Math.max(dot, mark);
2000                        try {
2001                            if (selectionTag != null) {
2002                                h.changeHighlight(selectionTag, p0, p1);
2003                            } else {
2004                                Highlighter.HighlightPainter p = focusPainter;
2005                                selectionTag = h.addHighlight(p0, p1, p);
2006                            }
2007                        } catch (BadLocationException e) {
2008                        }
2009                    }
2010                }
2011
2012                /**
2013                 * A highlight painter that draws a one-pixel border around
2014                 * the highlighted area.
2015                 */
2016                static class FocusHighlightPainter extends
2017                        DefaultHighlighter.DefaultHighlightPainter {
2018
2019                    FocusHighlightPainter(Color color) {
2020                        super (color);
2021                    }
2022
2023                    /**
2024                     * Paints a portion of a highlight.
2025                     *
2026                     * @param g the graphics context
2027                     * @param offs0 the starting model offset >= 0
2028                     * @param offs1 the ending model offset >= offs1
2029                     * @param bounds the bounding box of the view, which is not
2030                     *        necessarily the region to paint.
2031                     * @param c the editor
2032                     * @param view View painting for
2033                     * @return region in which drawing occurred
2034                     */
2035                    public Shape paintLayer(Graphics g, int offs0, int offs1,
2036                            Shape bounds, JTextComponent c, View view) {
2037
2038                        Color color = getColor();
2039
2040                        if (color == null) {
2041                            g.setColor(c.getSelectionColor());
2042                        } else {
2043                            g.setColor(color);
2044                        }
2045                        if (offs0 == view.getStartOffset()
2046                                && offs1 == view.getEndOffset()) {
2047                            // Contained in view, can just use bounds.
2048                            Rectangle alloc;
2049                            if (bounds instanceof  Rectangle) {
2050                                alloc = (Rectangle) bounds;
2051                            } else {
2052                                alloc = bounds.getBounds();
2053                            }
2054                            g.drawRect(alloc.x, alloc.y, alloc.width - 1,
2055                                    alloc.height);
2056                            return alloc;
2057                        } else {
2058                            // Should only render part of View.
2059                            try {
2060                                // --- determine locations ---
2061                                Shape shape = view.modelToView(offs0,
2062                                        Position.Bias.Forward, offs1,
2063                                        Position.Bias.Backward, bounds);
2064                                Rectangle r = (shape instanceof  Rectangle) ? (Rectangle) shape
2065                                        : shape.getBounds();
2066                                g.drawRect(r.x, r.y, r.width - 1, r.height);
2067                                return r;
2068                            } catch (BadLocationException e) {
2069                                // can't render
2070                            }
2071                        }
2072                        // Only if exception
2073                        return null;
2074                    }
2075                }
2076            }
2077
2078            /*
2079             * Action to activate the hypertext link that has focus. 
2080             * TODO: This method relies on support from the 
2081             * javax.accessibility package.  The text package should support
2082             * keyboard navigation of text elements directly.
2083             */
2084            static class ActivateLinkAction extends TextAction {
2085
2086                /** 
2087                 * Create this action with the appropriate identifier. 
2088                 */
2089                public ActivateLinkAction(String actionName) {
2090                    super (actionName);
2091                }
2092
2093                /*
2094                 * activates the hyperlink at offset
2095                 */
2096                private void activateLink(String href, HTMLDocument doc,
2097                        JEditorPane editor, int offset) {
2098                    try {
2099                        URL page = (URL) doc
2100                                .getProperty(Document.StreamDescriptionProperty);
2101                        URL url = new URL(page, href);
2102                        HyperlinkEvent linkEvent = new HyperlinkEvent(editor,
2103                                HyperlinkEvent.EventType.ACTIVATED, url, url
2104                                        .toExternalForm(), doc
2105                                        .getCharacterElement(offset));
2106                        editor.fireHyperlinkUpdate(linkEvent);
2107                    } catch (MalformedURLException m) {
2108                    }
2109                }
2110
2111                /*
2112                 * Invokes default action on the object in an element
2113                 */
2114                private void doObjectAction(JEditorPane editor, Element elem) {
2115                    View view = getView(editor, elem);
2116                    if (view != null && view instanceof  ObjectView) {
2117                        Component comp = ((ObjectView) view).getComponent();
2118                        if (comp != null && comp instanceof  Accessible) {
2119                            AccessibleContext ac = ((Accessible) comp)
2120                                    .getAccessibleContext();
2121                            if (ac != null) {
2122                                AccessibleAction aa = ac.getAccessibleAction();
2123                                if (aa != null) {
2124                                    aa.doAccessibleAction(0);
2125                                }
2126                            }
2127                        }
2128                    }
2129                }
2130
2131                /*
2132                 * Returns the root view for a document
2133                 */
2134                private View getRootView(JEditorPane editor) {
2135                    return editor.getUI().getRootView(editor);
2136                }
2137
2138                /*
2139                 * Returns a view associated with an element
2140                 */
2141                private View getView(JEditorPane editor, Element elem) {
2142                    Object lock = lock(editor);
2143                    try {
2144                        View rootView = getRootView(editor);
2145                        int start = elem.getStartOffset();
2146                        if (rootView != null) {
2147                            return getView(rootView, elem, start);
2148                        }
2149                        return null;
2150                    } finally {
2151                        unlock(lock);
2152                    }
2153                }
2154
2155                private View getView(View parent, Element elem, int start) {
2156                    if (parent.getElement() == elem) {
2157                        return parent;
2158                    }
2159                    int index = parent.getViewIndex(start,
2160                            Position.Bias.Forward);
2161
2162                    if (index != -1 && index < parent.getViewCount()) {
2163                        return getView(parent.getView(index), elem, start);
2164                    }
2165                    return null;
2166                }
2167
2168                /*
2169                 * If possible acquires a lock on the Document.  If a lock has been
2170                 * obtained a key will be retured that should be passed to
2171                 * <code>unlock</code>.
2172                 */
2173                private Object lock(JEditorPane editor) {
2174                    Document document = editor.getDocument();
2175
2176                    if (document instanceof  AbstractDocument) {
2177                        ((AbstractDocument) document).readLock();
2178                        return document;
2179                    }
2180                    return null;
2181                }
2182
2183                /*
2184                 * Releases a lock previously obtained via <code>lock</code>.
2185                 */
2186                private void unlock(Object key) {
2187                    if (key != null) {
2188                        ((AbstractDocument) key).readUnlock();
2189                    }
2190                }
2191
2192                /*
2193                 * The operation to perform when this action is triggered. 
2194                 */
2195                public void actionPerformed(ActionEvent e) {
2196
2197                    JTextComponent c = getTextComponent(e);
2198                    if (c.isEditable() || !(c instanceof  JEditorPane)) {
2199                        return;
2200                    }
2201                    JEditorPane editor = (JEditorPane) c;
2202
2203                    Document d = editor.getDocument();
2204                    if (d == null || !(d instanceof  HTMLDocument)) {
2205                        return;
2206                    }
2207                    HTMLDocument doc = (HTMLDocument) d;
2208
2209                    ElementIterator ei = new ElementIterator(doc);
2210                    int currentOffset = editor.getCaretPosition();
2211
2212                    // invoke the next link or object action
2213                    String urlString = null;
2214                    String objString = null;
2215                    Element currentElement = null;
2216                    while ((currentElement = ei.next()) != null) {
2217                        String name = currentElement.getName();
2218                        AttributeSet attr = currentElement.getAttributes();
2219
2220                        Object href = getAttrValue(attr, HTML.Attribute.HREF);
2221                        if (href != null) {
2222                            if (currentOffset >= currentElement
2223                                    .getStartOffset()
2224                                    && currentOffset <= currentElement
2225                                            .getEndOffset()) {
2226
2227                                activateLink((String) href, doc, editor,
2228                                        currentOffset);
2229                                return;
2230                            }
2231                        } else if (name.equals(HTML.Tag.OBJECT.toString())) {
2232                            Object obj = getAttrValue(attr,
2233                                    HTML.Attribute.CLASSID);
2234                            if (obj != null) {
2235                                if (currentOffset >= currentElement
2236                                        .getStartOffset()
2237                                        && currentOffset <= currentElement
2238                                                .getEndOffset()) {
2239
2240                                    doObjectAction(editor, currentElement);
2241                                    return;
2242                                }
2243                            }
2244                        }
2245                    }
2246                }
2247            }
2248
2249            private static int getBodyElementStart(JTextComponent comp) {
2250                Element rootElement = comp.getDocument().getRootElements()[0];
2251                for (int i = 0; i < rootElement.getElementCount(); i++) {
2252                    Element currElement = rootElement.getElement(i);
2253                    if ("body".equals(currElement.getName())) {
2254                        return currElement.getStartOffset();
2255                    }
2256                }
2257                return 0;
2258            }
2259
2260            /*
2261             * Move the caret to the beginning of the document.
2262             * @see DefaultEditorKit#beginAction
2263             * @see HTMLEditorKit#getActions
2264             */
2265
2266            static class BeginAction extends TextAction {
2267
2268                /* Create this object with the appropriate identifier. */
2269                BeginAction(String nm, boolean select) {
2270                    super (nm);
2271                    this .select = select;
2272                }
2273
2274                /** The operation to perform when this action is triggered. */
2275                public void actionPerformed(ActionEvent e) {
2276                    JTextComponent target = getTextComponent(e);
2277                    int bodyStart = getBodyElementStart(target);
2278
2279                    if (target != null) {
2280                        if (select) {
2281                            target.moveCaretPosition(bodyStart);
2282                        } else {
2283                            target.setCaretPosition(bodyStart);
2284                        }
2285                    }
2286                }
2287
2288                private boolean select;
2289            }
2290        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.