| <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
| <html xmlns="http://www.w3.org/1999/xhtml"> |
| <head> |
| <!-- -*- xhtml -*- --> |
| <title>NetBeans Hyperlink Navigation Tutorial</title> |
| <link rel="stylesheet" type="text/css" href="https://netbeans.org/netbeans.css"> |
| <meta name="AUDIENCE" content="NBUSER"> |
| <meta name="TYPE" content="ARTICLE"> |
| <meta name="EXPIRES" content="N"> |
| <meta name="developer" content="tboudreau@netbeans.org"> |
| <meta name="indexed" content="y"> |
| <meta name="description" |
| content="A hyperlink in/to HTML files."> |
| <!-- Copyright (c) 2006 Sun Microsystems, Inc. All rights reserved. --> |
| <!-- Use is subject to license terms.--> |
| </head> |
| <body> |
| <h1>NetBeans Hyperlink Navigation Tutorial</h1> |
| |
| <p>In this tutorial, you learn how to create hyperlinks in HTML files, programmatically. You will |
| do this by implementing the NetBeans API <a href="https://netbeans.org/download/dev/javadoc/org-netbeans-modules-editor-lib/org/netbeans/lib/editor/hyperlink/spi/HyperlinkProvider.html">HyperlinkProvider</a> class. |
| The hyperlink will let the user navigate from an HREF attribute in an HTML link to the referenced |
| HTML file. |
| |
| |
| <p>The new hyperlink will appear when the user holds down the Ctrl |
| key and moves the mouse over the value of the HREF attribute, as shown here: |
| |
| <p><img border="1" src="../images/tutorials/hyperlink/hyperlink.png"/> |
| |
| <p>When the hyperlink is clicked, the referenced file opens and the cursor |
| lands on the first H2 tag, if one exists.</p> |
| |
| <p>This is what the completed project will look like in the Projects window: |
| |
| |
| <p><img border="1" src="../images/tutorials/hyperlink/hyperlink2.png"/> |
| |
| <p>Though the focus of this tutorial is on hyperlinking from one HTML file to another, |
| the principles shown here could equally be applied to other types of files, |
| such as to Java source files, XML files, and JSP files. |
| |
| |
| <!-- ===================================================================================== --> |
| |
| <p><h3 class="tutorial"><a name="installing-software"></a>Installing the Software</h3> |
| |
| |
| <p>Before you begin, you need to install the following software on your |
| computer:</p> |
| <ul> |
| <li>NetBeans IDE 5.x. |
| (<a href="http://www.netbeans.info/downloads/download.php?type=5.5b2">Click here</a> |
| to download NetBeans IDE 5.5 Beta 2.)</li> |
| <li>Java Standard Development Kit (JDK™) version |
| 1.4.2 (<a href="http://java.sun.com/j2se/1.4.2/download.html">download</a>) |
| or 5.0 (<a href="http://java.sun.com/j2se/1.5.0/download.jsp">download</a>)</li> |
| </ul> |
| |
| <br /> |
| |
| |
| <p><h2><a name="gettingtoknowthesample"></a>Getting Started</h2> |
| |
| <p>In this section, we use a wizard to create a module project. |
| We declare dependencies on modules that provide the NetBeans API classes |
| needed by our hyperlink module. |
| |
| <ol> |
| |
| <p><li>Choose File > New Project. In the New Project wizard, |
| choose NetBeans Plug-in Modules under Categories and Module Project |
| under Projects. Click Next. Type <tt>AHrefHyperlink</tt> in Project Name |
| and set Project Location to an appropriate folder on your disk. If they |
| are not selected, select |
| Standalone Module and Set as Main Project. Click Next. |
| |
| <p><li>Type <tt>org.netbeans.modules.ahrefhyperlink</tt> in Code Name Base. |
| Click Finish. |
| |
| |
| <p><li>Right-click the project, choose Properties, click Libraries |
| in the Project Properties dialog box and declare a dependency on the following APIs: |
| |
| <p><ul> |
| <li>Datasystems API |
| <li>Editor |
| <li>Editor Library |
| <li>File System API |
| <li>HTML Editor Library |
| <li>Nodes API |
| <li>Text API |
| <li>Utilities API |
| </ul> |
| |
| |
| </ol> |
| |
| <br /> |
| |
| <!-- ===================================================================================== --> |
| <p><h2><a name="hyperlinkprovider"></a>Implementing the HyperlinkProvider Class</h2> |
| |
| <p>The <a href="https://netbeans.org/download/dev/javadoc/org-netbeans-modules-editor-lib/org/netbeans/lib/editor/hyperlink/spi/HyperlinkProvider.html">HyperlinkProvider</a> class |
| implements three methods, each of which is discussed in detail below, accompanied by a |
| practical example in the context of our module. First we set up the class and then |
| we implement each of the three methods in turn. |
| |
| <br /> |
| <div class="indent"> |
| <p><h3 class="tutorial"><a name="method1"></a>Setting Up the HyperlinkProvider Class</h3> |
| |
| <p>Setting up our class means implementing <a href="https://netbeans.org/download/dev/javadoc/org-netbeans-modules-editor-lib/org/netbeans/lib/editor/hyperlink/spi/HyperlinkProvider.html">HyperlinkProvider</a> |
| and initializing some values that we will need in our implementation. |
| |
| <ol> |
| |
| <p><li>Create a Java class in <tt>org.netbeans.modules.ahrefhyperlink</tt>, |
| and call it AHrefHyperlinkProvider. |
| |
| <p><li>Change the signature so that HyperlinkProvider is implemented. |
| |
| <p><li>Note that the following import statements will be needed: |
| |
| <p><pre class="examplecode">import javax.swing.text.BadLocationException; |
| import javax.swing.text.Document; |
| import javax.swing.text.JTextComponent; |
| import org.netbeans.editor.BaseDocument; |
| import org.netbeans.editor.TokenItem; |
| import org.netbeans.editor.ext.html.HTMLSyntaxSupport; |
| import org.netbeans.editor.ext.html.HTMLTokenContext; |
| import org.netbeans.lib.editor.hyperlink.spi.HyperlinkProvider; |
| import org.openide.util.RequestProcessor;</pre> |
| |
| <p><li>Add the following initial values at the top of the class: |
| |
| <p><pre class="examplecode">private static String AHREF_IDENTIFIER = "href"; |
| private Document lastDocument; |
| private int startOffset; |
| private int endOffset; |
| private String identifier;</pre> |
| |
| <p><li>Define the Constructor as follows: |
| |
| <p><pre class="examplecode">public AHrefHyperlinkProvider() { |
| |
| lastDocument = null; |
| |
| }</pre> |
| |
| </ol> |
| |
| |
| </div> |
| <br /> |
| |
| <br /> |
| <div class="indent"> |
| <p><h3 class="tutorial"><a name="method1"></a>isHyperlinkPoint(Document doc, int offset)</h3> |
| |
| <p><a href="https://netbeans.org/download/dev/javadoc/org-netbeans-modules-editor-lib/org/netbeans/lib/editor/hyperlink/spi/HyperlinkProvider.html#isHyperlinkPoint(javax.swing.text.Document,%20int)">isHyperlinkPoint(Document doc, int offset)</a> |
| determines whether there should be a hyperlink at the given offset within the given document. The inline |
| comments in the method below, as well as in the code in the remainder of this tutorial, |
| serve to explain the purpose of the code. |
| |
| <p><pre class="examplecode">public boolean isHyperlinkPoint(Document doc, int offset) { |
| |
| <b>// We want to work only with org.netbeans.editor.BaseDocument:</b> |
| if (!(doc instanceof BaseDocument)) { |
| return false; |
| } |
| |
| BaseDocument bdoc = (BaseDocument) doc; |
| JTextComponent target = org.netbeans.editor.Utilities.getFocusedComponent(); |
| |
| <b>// We want to work only with the open editor and |
| // the editor has to be the active component:</b> |
| if ((target == null) || (target.getDocument() != bdoc)) { |
| return false; |
| } |
| |
| <b>// We want to work only with HTMLSyntaxSupport:</b> |
| if (!(bdoc.getSyntaxSupport() instanceof HTMLSyntaxSupport)) { |
| return false; |
| } |
| |
| HTMLSyntaxSupport sup = (HTMLSyntaxSupport) bdoc.getSyntaxSupport(); |
| |
| try { |
| <b>// Get the token on the offset:</b> |
| TokenItem token = sup.getTokenChain(offset, offset + 1); |
| |
| <b>// We are interested only in the value of a tag:</b> |
| if ((token != null) && (token.getTokenID().getNumericID() |
| == HTMLTokenContext.VALUE_ID)) { |
| |
| TokenItem firstToken = token; |
| |
| <b>// We want to find the nearest argument:</b> |
| while ((token != null) && |
| (token.getTokenID().getNumericID() |
| != HTMLTokenContext.ARGUMENT_ID)) |
| |
| token = token.getPrevious(); |
| |
| <b>// If the token is an argument and |
| // it is an HREF attribute identifier...</b> |
| if ((token != null) |
| && (token.getTokenID().getNumericID() |
| == HTMLTokenContext.ARGUMENT_ID) |
| && token.getImage().equals(AHREF_IDENTIFIER)) { |
| |
| <b>//...then remember the identifier:</b> |
| identifier = firstToken.getImage(); |
| |
| <b>// Here we exclude certain types of HREF links, |
| // those that start with 'nbdocs' and those that |
| // are internal links, marked by the '#' symbol:</b> |
| if ((identifier.startsWith("nbdocs",1) == false) && |
| (identifier.startsWith("#",1) == false)) { |
| |
| <b>// Remove " or ' from the hyperlink:</b> |
| identifier = identifier.substring(0, |
| identifier.length() - 1).substring(1); |
| |
| <b>// Set the real start and end of the hyperlink, |
| // which is after char " or ':</b> |
| startOffset = firstToken.getOffset() + 1; |
| endOffset = (firstToken.getOffset() + |
| firstToken.getImage().length()) - 2; |
| |
| <b>// Remember this document:</b> |
| lastDocument = bdoc; |
| |
| } else { |
| return false; |
| } |
| |
| return true; |
| } |
| } |
| } catch (BadLocationException ex) { |
| ex.printStackTrace(); |
| } |
| |
| return false; |
| |
| }</pre> |
| |
| |
| </div> |
| <br /> |
| |
| <div class="indent"> |
| <p><h3 class="tutorial"><a name="method2"></a>getHyperlinkSpan(Document doc, int offset)</h3> |
| |
| <p><tt><a href="https://netbeans.org/download/dev/javadoc/org-netbeans-modules-editor-lib/org/netbeans/lib/editor/hyperlink/spi/HyperlinkProvider.html#getHyperlinkSpan(javax.swing.text.Document,%20int)">getHyperlinkSpan(Document doc, int offset)</a></tt> determines the length of the hyperlink. |
| |
| |
| <p><pre class="examplecode">public int[] getHyperlinkSpan(Document doc, int offset) { |
| |
| <b>// First check that we are working with BaseDocument:</b> |
| if (!(doc instanceof BaseDocument)) { |
| return null; |
| } |
| |
| BaseDocument bdoc = (BaseDocument) doc; |
| JTextComponent target = org.netbeans.editor.Utilities.getFocusedComponent(); |
| |
| <b>// We want to work only with the open editor |
| // and the editor has to be the active component and |
| // the document has to be the same as was used in the isHyperlinkPoint method:</b> |
| if ((target == null) || (lastDocument != bdoc)) { |
| return null; |
| } |
| |
| <b>// Return the position that we defined in the isHyperlinkPoint method:</b> |
| return new int[] { startOffset, endOffset }; |
| |
| }</pre> |
| |
| |
| </div> |
| <br /> |
| |
| |
| <div class="indent"> |
| <h3 class="tutorial"><a name="method3"></a>performClickAction(Document doc, int offset)</h3> |
| |
| |
| <p><a href="https://netbeans.org/download/dev/javadoc/org-netbeans-modules-editor-lib/org/netbeans/lib/editor/hyperlink/spi/HyperlinkProvider.html#performClickAction(javax.swing.text.Document,%20int)">performClickAction(Document doc, int offset)</a> |
| determines what happens when the hyperlink is clicked. In general, a document |
| should open, the cursor should move to a certain place in a document, or both. |
| |
| <p><pre class="examplecode">public void performClickAction(Document doc, int offset) { |
| |
| <b>// First check that we are working with BaseDocument:</b> |
| if (!(doc instanceof BaseDocument)) { |
| return; |
| } |
| |
| BaseDocument bdoc = (BaseDocument) doc; |
| JTextComponent target = org.netbeans.editor.Utilities.getFocusedComponent(); |
| |
| <b>// We want to work only with the open editor and |
| // the editor has to be active component and |
| // the document has to be the same as was used in the isHyperlinkPoint method:</b> |
| if ((target == null) || (lastDocument != bdoc)) { |
| return; |
| } |
| |
| <b>//Start a new thread for opening the HTML document:</b> |
| OpenHTMLThread run = new OpenHTMLThread(bdoc,identifier); |
| RequestProcessor.getDefault().post(run); |
| |
| }</pre> |
| |
| |
| </div> |
| <br /> |
| |
| |
| |
| |
| <!-- ===================================================================================== --> |
| <p><h2><a name="gettingtoknowthesample"></a>Opening the Referenced HTML File</h2> |
| |
| <p>Next, you need to create a class that opens an HTML file in a separate thread. |
| Here, the class is called <tt>OpenHTMLThread</tt>. It makes use of the |
| following import statements: |
| |
| <p><pre class="examplecode">import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.io.File; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import javax.swing.JEditorPane; |
| import javax.swing.text.BadLocationException; |
| import org.netbeans.editor.BaseDocument; |
| import org.netbeans.modules.editor.NbEditorUtilities; |
| import org.openide.cookies.EditorCookie; |
| import org.openide.filesystems.FileObject; |
| import org.openide.filesystems.URLMapper; |
| import org.openide.loaders.DataObject; |
| import org.openide.loaders.DataObjectNotFoundException;</pre> |
| |
| <p>The token identified in the <tt>isHyperlinkPoint</tt> method is |
| received by this class. Then the token is analyzed to see whether |
| it contains a slash, which indicates that it is a relative link. In |
| that case, the file object is extrapolated from the URL to the file. |
| Otherwise, the file object is created from the token itself. Next, the |
| document with the name of the file object is opened and the cursor |
| is positioned at the H2 tag, if found. |
| |
| <pre class="examplecode">public class OpenHTMLThread implements Runnable { |
| |
| private String fqn; |
| private BaseDocument doc; |
| private String identifier; |
| |
| public OpenHTMLThread(BaseDocument doc, String identifier) { |
| |
| super(); |
| this.doc = doc; |
| this.identifier = identifier; |
| |
| } |
| |
| public void run() { |
| |
| FileObject fo = NbEditorUtilities.getFileObject(doc); |
| FileObject foHtml = null; |
| |
| <b>// Here we're working out whether we're dealing with a relative link or not:</b> |
| if (identifier.contains("/")){ |
| String fullPath = fo.getPath(); |
| try { |
| URL f = new File(fullPath).toURI().resolve(identifier).toURL(); |
| foHtml = URLMapper.findFileObject(f); |
| } catch (MalformedURLException ex) { |
| ex.printStackTrace(); |
| } |
| } else { |
| foHtml = fo.getParent().getFileObject(identifier); |
| } |
| |
| <b>//Here we're finding our HTML file:</b> |
| DataObject dObject; |
| try { |
| dObject = DataObject.find(foHtml); |
| |
| final EditorCookie.Observable ec = (EditorCookie.Observable) |
| dObject.getCookie(EditorCookie.Observable.class); |
| |
| if (ec != null) { |
| org.netbeans.editor.Utilities.runInEventDispatchThread(new Runnable() { |
| public void run() { |
| final JEditorPane[] panes = ec.getOpenedPanes(); |
| |
| <b>//Here we're positioning the cursor, |
| //if the document isn't open, we need to open it first:</b> |
| if ((panes != null) && (panes.length > 0)) { |
| setPosition(panes[0],identifier); |
| } else { |
| ec.addPropertyChangeListener(new PropertyChangeListener() { |
| public void propertyChange(PropertyChangeEvent evt) { |
| if (EditorCookie.Observable. |
| PROP_OPENED_PANES.equals(evt.getPropertyName())) { |
| final JEditorPane[] panes = ec.getOpenedPanes(); |
| if ((panes != null) && (panes.length > 0)) { |
| setPosition(panes[0],identifier); |
| } |
| ec.removePropertyChangeListener(this); |
| } |
| } |
| }); |
| } |
| |
| ec.open(); |
| } |
| }); |
| } |
| } catch (DataObjectNotFoundException ex) { |
| ex.printStackTrace(); |
| } |
| } |
| |
| <b>//Here we specify where the cursor will land:</b> |
| private void setPosition(JEditorPane pane, String identifier) { |
| |
| try { |
| <b>//The whole text:</b> |
| String text = pane.getDocument().getText(0, |
| pane.getDocument().getLength() - 1); |
| <b>//The place where we want the cursor to be:</b> |
| int index = text.indexOf("<h2>"); |
| <b>//If we can find it, we place the cursor there:</b> |
| if (index > 0) { |
| pane.setCaretPosition(index); |
| } |
| } catch (BadLocationException ex) { |
| ex.printStackTrace(); |
| } |
| |
| } |
| |
| }</pre> |
| |
| |
| </div> |
| <br /> |
| |
| |
| |
| |
| <!-- ===================================================================================== --> |
| <p><h2><a name="gettingtoknowthesample"></a>Registering the HyperlinkProvider Implementation Class</h2> |
| |
| <p>Finally, you need to register the hyperlink provider implementation class |
| in the module's <tt>layer.xml</tt> file. Do this as follows, while making sure |
| that the line in bold below is the fully qualified class name of the class that |
| implements HyperlinkProvider: |
| |
| <pre class="examplecode"><folder name="Editors"> |
| <folder name="text"> |
| <folder name="html"> |
| <folder name="HyperlinkProviders"> |
| |
| <file name="AHrefHyperlinkProvider.instance"> |
| <attr name="instanceClass" |
| stringvalue="<b>org.netbeans.modules. |
| ahrefhyperlink.AHrefHyperlinkProvider</b>"/> |
| <attr name="instanceOf" |
| stringvalue="org.netbeans.lib.editor. |
| hyperlink.spi.HyperlinkProvider"/> |
| </file> |
| |
| </folder> |
| </folder> |
| </folder> |
| </folder></pre> |
| |
| <p>If you create a hyperlink for a different MIME type, you need to change the |
| <tt>text/html</tt> folders above to the appropriate MIME type.</p> |
| |
| <p>Now that the HyperlinkProvider is registered, you can install the module and |
| try out your new hyperlinks. Hold down the Ctrl key, move the mouse over an HREF |
| attribute as shown at the start of this tutorial: |
| |
| <p><img border="1" src="../images/tutorials/hyperlink/hyperlink.png"/> |
| |
| |
| <p>When the hyperlink appears, |
| you can click it and let the IDE navigate to the referenced HTML file. |
| |
| <br /> |
| |
| <br> |
| <div class="feedback-box"><a href="https://netbeans.org/about/contact_form.html?to=3&subject=Feedback:%20Hyperlink%20Module%20Tutorial">Send Us Your Feedback</a></div> |
| <br style="clear:both;" /> |
| |
| <!-- ===================================================================================== --> |
| <p><h2><a name="gettingtoknowthesample"></a>Next Steps</h2> |
| |
| <ul> |
| |
| <li>Utility method for finding and opening Java source files. |
| <li>Working with JSP and XML documents. (Same principle as above.) |
| <li>Need to provide for the situation where the referenced HTML file doesn't exist. |
| <li>Show hyperlink within same document. |
| <li>Implement external links, i.e., http links should go to external browser. |
| <li>Provide links to NetBeans sources, such as StrutsHyperlinkProvider, etc. |
| </ul> |
| |
| <hr><p> |
| |
| </body> |
| </html> |