/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * NavViewerPane.java * Creation date: Jul 28, 2003 * By: Frank Worsley */ package org.openquark.gems.client.navigator; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.print.PageFormat; import java.awt.print.Printable; import java.util.ArrayList; import java.util.List; import javax.swing.JEditorPane; import javax.swing.RepaintManager; import javax.swing.event.DocumentEvent; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import javax.swing.text.Document; import javax.swing.text.html.HTMLDocument; import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.StyleSheet; /** * The metadata HTML viewer component. This class takes care of displaying metadata in * HTML form. It handles listening to hyperlinks and displaying the correct metadata * if a hyperlink is clicked. On top of that it provides a page history and printing support. * * Use a document change listener to find out whenever the displayed page changes. A new event * is fired if a new page is loaded or if the user clicks on an anchored link and the page * location changes as a result of that. * * @author Frank Worsley */ public class NavViewerPane extends JEditorPane implements Printable { private static final long serialVersionUID = -9104792276977818071L; /** * This listener listens for links being clicked on and performs the correct * action for the given address of the link. * @author Frank Worsley */ private class NavHyperlinkListener implements HyperlinkListener { /** * @see javax.swing.event.HyperlinkListener#hyperlinkUpdate(javax.swing.event.HyperlinkEvent) */ public void hyperlinkUpdate(HyperlinkEvent e) { // We only react to clicked events, we don't want to do anything if // the mouse simply hovers over a link. if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED) { return; } // If the hyperlink refers to a standard URL (e.g. http, ftp), then e.getURL() will // return a non-null value. if (e.getURL() != null) { // Since we cannot handle such a URL, for the time being we simply ignore it. } else { // We have to use the String form of the url since navigator urls are not // really valid urls which means that e.getURL() will return null. String url = e.getDescription(); if (url.startsWith("#")) { String anchor = url.substring(1); displayMetadata(currentAddress.withAnchor(anchor)); } else { displayMetadata(NavAddress.getAddress(url)); } } } } /** The maximum size of the history lists. */ private static final int MAX_HISTORY_SIZE = 50; /** The history list of URLs we can go back to by pressing the back button. */ private final List<NavAddress> backHistory = new ArrayList<NavAddress>(); /** The history list of URLs we can go forward to by pressing the forward button. */ private final List<NavAddress> forwardHistory = new ArrayList<NavAddress>(); /** The address of the current page being viewed. */ private NavAddress currentAddress = null; /** The owner that is using this editor pane. */ private final NavFrameOwner owner; /** * Constructs a new editor pane with the given navigator owner. * @param owner the navigator owner using this editor pane */ public NavViewerPane(NavFrameOwner owner) { this.owner = owner; NavEditorKit editorKit = new NavEditorKit(); NavHtmlDocument doc = (NavHtmlDocument) editorKit.createDefaultDocument(); setEditorKit(editorKit); setDocument(doc); setEditable(false); addHyperlinkListener(new NavHyperlinkListener()); } /** * Display the metadata for the given url. * @param url the url to display metadata for */ public void displayMetadata(NavAddress url) { // update the history if we are going to a new location if (!url.equals(currentAddress) && currentAddress != null) { updateHistory(backHistory, currentAddress); } displayMetadata(url, true); } /** * Display the metadata for the given CAL url. * @param url the url to display metadata for * @param clearForwardHistory if true the forward history will be cleared */ private void displayMetadata(NavAddress url, boolean clearForwardHistory) { if (clearForwardHistory) { forwardHistory.clear(); } // update the current url NavAddress oldUrl = currentAddress; currentAddress = url; if (oldUrl == null || !oldUrl.withAnchor(null).equals(currentAddress.withAnchor(null))) { // only reload the url if we really have to since it might be costly // for example, reloading search results if we just clicked on an anchor would be bad setText(NavHtmlFactory.getPage(owner, url)); } else { // if we don't have to reload the page it must mean we the user clicked on // an anchor and we moved to a different page position NavHtmlDocument doc = (NavHtmlDocument) getDocument(); doc.firePagePositionChanged(); } // scroll to anchor if there is one, otherwise scroll to top if (url.getAnchor() != null) { scrollToReference(url.getAnchor()); } else { setCaretPosition(0); scrollRectToVisible(new Rectangle(0, 0, 1, 1)); } } /** * Refreshes the displayed metadata to the latest information from the metadata. */ public void refresh() { setText(NavHtmlFactory.getPage(owner, currentAddress)); if (currentAddress.getAnchor() != null) { scrollToReference(currentAddress.getAnchor()); } else { setCaretPosition(0); scrollRectToVisible(new Rectangle(0, 0, 1, 1)); } } /** * @return the url of the currently displayed page. */ public NavAddress getCurrentAddress() { return currentAddress; } /** * Display the page that is next in the forward history. */ public void goForward() { if (forwardHistory.size() == 0) { return; } updateHistory(backHistory, currentAddress); NavAddress url = forwardHistory.remove(0); displayMetadata(url, false); } /** * Display the page that is next in the backward history. */ public void goBack() { if (backHistory.size() == 0) { return; } updateHistory(forwardHistory, currentAddress); NavAddress url = backHistory.remove(0); displayMetadata(url, false); } /** * Updates the given history list with the given url. * @param history the history list to update * @param url the url to add to the list */ private void updateHistory(List<NavAddress> history, NavAddress url) { if (url != null) { history.add(0, url); if (history.size() > MAX_HISTORY_SIZE) { history.remove(MAX_HISTORY_SIZE); } } } /** * @return the size of the forward history list */ public int getForwardHistorySize() { return forwardHistory.size(); } /** * @return the size of the backward history list */ public int getBackHistorySize() { return backHistory.size(); } /** * @see java.awt.print.Printable#print(java.awt.Graphics, java.awt.print.PageFormat, int) */ public int print(Graphics g, PageFormat pf, int pageIndex) { Graphics2D g2 = (Graphics2D) g; //set default foreground color to black g2.setColor(Color.BLACK); // turn of double-buffering since we are printing and not drawing on the screen RepaintManager.currentManager(this).setDoubleBufferingEnabled(false); // get document size Dimension d = this.getSize(); double panelWidth = d.width; double panelHeight = d.height; // get size of the printer page double pageHeight = pf.getImageableHeight(); double pageWidth = pf.getImageableWidth(); // figure out the total number of pages double scale = pageWidth/panelWidth; int totalNumPages = (int)Math.ceil(scale * panelHeight / pageHeight); // stop printing once we reach the last page if(pageIndex >= totalNumPages) { return Printable.NO_SUCH_PAGE; } // shift Graphics to line up with beginning of the printable region g2.translate(pf.getImageableX(), pf.getImageableY()); // shift Graphics to line up with beginning of the page to print g2.translate(0f, -pageIndex * pageHeight); // scale the page so it fits the width of the printer page g2.scale(scale, scale); // paint the page using the printer's graphics paint(g2); // turn double-buffering back on RepaintManager.currentManager(this).setDoubleBufferingEnabled(true); return Printable.PAGE_EXISTS; } } /** * Subclasses the HTMLEditorKit class to return a NavHtmlDocument as the default document. * * @author Frank Worsley */ class NavEditorKit extends HTMLEditorKit { private static final long serialVersionUID = -2979397588969833890L; /** * @see javax.swing.text.EditorKit#createDefaultDocument() */ @Override public Document createDefaultDocument() { StyleSheet styles = getStyleSheet(); NavHtmlDocument doc = new NavHtmlDocument(styles); doc.setParser(getParser()); doc.setAsynchronousLoadPriority(4); doc.setTokenThreshold(100); return doc; } } /** * Subclasses the HTMLDocument class to allow us to fire dummy document changed event, * if the position of the page changes after the user clicks on an anchor link. * This is needed because the NavFrame class uses a document change listener to update * it's UI state if the displayed page changes. * * @author Frank Worsley */ class NavHtmlDocument extends HTMLDocument { private static final long serialVersionUID = -1661993424567523051L; /** * Construct a new document with the given styles. * @param styles the styles to use */ public NavHtmlDocument(StyleSheet styles) { super(styles); } /** * Fire a dummy document change event to trigger document listeners that depends * on document change events to update their UI. */ public void firePagePositionChanged() { fireChangedUpdate(new DefaultDocumentEvent(0, 0, DocumentEvent.EventType.CHANGE)); } }