/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun * Microsystems, Inc. All Rights Reserved. */ package org.openide.awt; import java.io.FilterInputStream; import java.io.InputStream; import java.io.IOException; import java.net.URL; import java.net.URLConnection; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import javax.swing.event.HyperlinkListener; import javax.swing.event.HyperlinkEvent; import javax.swing.text.html.*; import javax.swing.JEditorPane; import javax.swing.text.Document; import javax.swing.text.AbstractDocument; import java.beans.PropertyChangeEvent; import java.util.Vector; import org.openide.ErrorManager; /** * Implementation of BrowserImpl in Swing. */ final class SwingBrowserImpl extends HtmlBrowser.Impl { /** state of history management */ private static final int NO_NAVIGATION = 1; private static final int NAVIGATION_BACK = 2; private static final int NAVIGATION_FWD = 3; /** Current URL. */ private URL url; /** URL loaded by JEditorPane */ private URL loadingURL; private PropertyChangeSupport pcs; private String statusMessage = ""; // NOI18N private SwingBrowser swingBrowser; /** list of accessed URLs for back/fwd navigation */ private Vector historyList; /** current position in history */ private int historyIndex; /** navigation indication */ private int historyNavigating = NO_NAVIGATION; SwingBrowserImpl () { pcs = new PropertyChangeSupport (this); swingBrowser = new SwingBrowser (); historyList = new Vector (5, 3); historyIndex = -1; swingBrowser.addPropertyChangeListener ( "page", // NOI18N new PropertyChangeListener () { public void propertyChange (PropertyChangeEvent evt) { if (evt.getNewValue () instanceof URL) { URL old = SwingBrowserImpl.this.url; SwingBrowserImpl.this.url = (URL)evt.getNewValue (); SwingBrowserImpl.this.pcs.firePropertyChange (PROP_URL, old, url); if (((URL)evt.getNewValue ()).equals (loadingURL)) loadingURL = null; // update history if (historyNavigating == NAVIGATION_BACK) { int idx = historyList.lastIndexOf (evt.getNewValue (), historyIndex-1); if (idx != -1) historyIndex = idx; } else if (historyNavigating == NAVIGATION_FWD) { int idx = historyList.indexOf (evt.getNewValue (), historyIndex+1); if (idx != -1) historyIndex = idx; } else { while (historyList.size () > historyIndex+1) historyList.remove (historyList.size ()-1); historyList.add (evt.getNewValue ()); historyIndex = historyList.size ()-1; } historyNavigating = NO_NAVIGATION; pcs.firePropertyChange (PROP_BACKWARD, null, null); pcs.firePropertyChange (PROP_FORWARD, null, null); } } } ); } /** * Returns visual component of html browser. * * @return visual component of html browser. */ public java.awt.Component getComponent () { return swingBrowser; } /** * Reloads current html page. */ public synchronized void reloadDocument () { try { if (url == null || loadingURL != null) return; Document doc = swingBrowser.getDocument (); if (doc != null) { doc.putProperty(Document.StreamDescriptionProperty, null); } loadingURL = url; if (doc instanceof AbstractDocument) { String protocol = url.getProtocol(); if ("ftp".equalsIgnoreCase(protocol) // NOI18N || "http".equalsIgnoreCase(protocol) // NOI18N ) { ((AbstractDocument)doc).setAsynchronousLoadPriority( Thread.NORM_PRIORITY); } else { ((AbstractDocument)doc).setAsynchronousLoadPriority(-1); } } swingBrowser.setPage (url); } catch (Exception e) { ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); pcs.firePropertyChange (PROP_STATUS_MESSAGE, null, statusMessage = "" + e); // NOI18N } } /** * Stops loading of current html page. */ public void stopLoading () { } /** * Sets current URL. * * @param url URL to show in the browser. */ public synchronized void setURL (URL url) { try { if (url == null) return; loadingURL = url; if (this.url != null && this.url.sameFile(url)) { boolean sameHosts; if ("nbfs".equals(url.getProtocol())) { // NOI18N sameHosts = true; } else { sameHosts = (url.getHost () != null) && (this.url != null) && (url.getHost ().equals (this.url.getHost ())); } Document doc = swingBrowser.getDocument (); if (doc != null) { doc.putProperty(Document.StreamDescriptionProperty, null); } } swingBrowser.setPage (url); } catch (Exception e) { ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); pcs.firePropertyChange (PROP_STATUS_MESSAGE, null, statusMessage = "" + e); // NOI18N } } /** * Returns current URL. * * @return current URL. */ public URL getURL () { return url; } /** * Returns status message representing status of html browser. * * @return status message. */ public String getStatusMessage () { return statusMessage; } /** Returns title of the displayed page. * @return title */ public String getTitle () { return ""; // NOI18N } /** Is forward button enabled? * @return true if it is */ public boolean isForward () { return historyIndex >=0 && historyIndex < historyList.size ()-1 && historyNavigating == NO_NAVIGATION; } /** Moves the browser forward. Failure is ignored. */ public void forward () { if (isForward ()) { historyNavigating = NAVIGATION_FWD; setURL ((URL)historyList.elementAt (historyIndex+1)); } } /** Is backward button enabled? * @return true if it is */ public boolean isBackward () { return historyIndex > 0 && historyIndex < historyList.size () && historyNavigating == NO_NAVIGATION; } /** Moves the browser forward. Failure is ignored. */ public void backward () { if (isBackward ()) { historyNavigating = NAVIGATION_BACK; setURL ((URL)historyList.elementAt (historyIndex-1)); } } /** Is history button enabled? * @return true if it is */ public boolean isHistory () { return false; } /** Invoked when the history button is pressed. */ public void showHistory () { } /** * Adds PropertyChangeListener to this browser. * * @param l Listener to add. */ public void addPropertyChangeListener (PropertyChangeListener l) { pcs.addPropertyChangeListener (l); } /** * Removes PropertyChangeListener from this browser. * * @param l Listener to remove. */ public void removePropertyChangeListener (PropertyChangeListener l) { pcs.removePropertyChangeListener (l); } // innerclasses .............................................................. private class SwingBrowser extends JEditorPane { private SwingBrowser () { setEditable (false); setEditorKitForContentType ("text/html", new HTMLEditorKit ()); // NOI18N addHyperlinkListener (new HyperlinkListener () { public void hyperlinkUpdate (HyperlinkEvent e) { if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { if (e instanceof HTMLFrameHyperlinkEvent) { HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent) e; HTMLDocument doc = (HTMLDocument) getDocument (); URL old = getURL (); doc.processHTMLFrameHyperlinkEvent (evt); pcs.firePropertyChange (PROP_URL, old, e.getURL ()); } else { try { SwingBrowserImpl.this.setURL (e.getURL ()); } catch (Exception ex) { ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); } } } } }); } /** * Fetches a stream for the given URL, which is about to * be loaded by the <code>setPage</code> method. * This method is expected to have the the side effect of * establishing the content type, and therefore setting the * appropriate <code>EditorKit</code> to use for loading the stream. * <p> * If debugger is not running returns super implementation. * <p> * If debugger runs it will set content type to text/html. * Forwarding is not supported is that case. * <p>Control using sysprop org.openide.awt.SwingBrowserImpl.do-not-block-awt=true. * * @param page the URL of the page */ protected InputStream getStream (URL page) throws IOException { // XXX debugger ought to set this temporarily if (Boolean.getBoolean("org.openide.awt.SwingBrowserImpl.do-not-block-awt")) { // try to set contentType quickly and return (don't block AWT Thread) setContentType ("text/html"); // NOI18N return new FilteredInputStream (page.openConnection()); } else { return super.getStream (page); } } } /** * FilterInputStream that delays opening of stream. * The purpose is not to initialize the stream when it is created in getStream() * but to do it later when the content is asynchronously loaded in separate thread. */ private static class FilteredInputStream extends FilterInputStream { private URLConnection conn; FilteredInputStream (URLConnection conn) { super ((FilterInputStream)null); this.conn = conn; } private void openStream () throws IOException { if (in == null) { synchronized (this) { if (in == null) in = conn.getInputStream (); } } } public int available () throws IOException { openStream (); return super.available (); } public long skip (long n) throws IOException { openStream (); return super.skip (n); } public void reset () throws IOException { openStream (); super.reset (); } public void close () throws IOException { openStream (); super.close (); } public int read (byte[] b) throws IOException { openStream (); return super.read (b); } public int read (byte[] b, int off, int len) throws IOException { openStream (); return super.read (b, off, len); } public int read () throws IOException { openStream (); return super.read (); } } }