001/* 002 * Gridarta MMORPG map editor for Crossfire, Daimonin and similar games. 003 * Copyright (C) 2000-2011 The Gridarta Developers. 004 * 005 * This program is free software; you can redistribute it and/or modify 006 * it under the terms of the GNU General Public License as published by 007 * the Free Software Foundation; either version 2 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU General Public License for more details. 014 * 015 * You should have received a copy of the GNU General Public License along 016 * with this program; if not, write to the Free Software Foundation, Inc., 017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 018 */ 019 020package net.sf.gridarta.gui.dialog.help; 021 022import java.awt.Component; 023import java.awt.Cursor; 024import java.io.File; 025import java.io.IOException; 026import java.net.MalformedURLException; 027import java.net.URL; 028import java.util.logging.Level; 029import java.util.logging.Logger; 030import javax.swing.JEditorPane; 031import javax.swing.JScrollPane; 032import javax.swing.JViewport; 033import javax.swing.SwingUtilities; 034import javax.swing.event.HyperlinkEvent; 035import javax.swing.event.HyperlinkListener; 036import javax.swing.text.Document; 037import net.sf.gridarta.utils.CommonConstants; 038import org.jetbrains.annotations.NotNull; 039import org.jetbrains.annotations.Nullable; 040 041/** 042 * Pane for displaying HTML. 043 * @author <a href="mailto:cher@riedquat.de">Christian Hujer</a> 044 */ 045class HtmlPane extends JScrollPane implements HyperlinkListener { 046 047 /** 048 * The Logger for printing log messages. 049 */ 050 @NotNull 051 private static final Logger log = Logger.getLogger("HtmlPane.class"); 052 053 /** 054 * Serial Version UID. 055 */ 056 private static final long serialVersionUID = 1L; 057 058 /** 059 * The JEditorPane that displays the html page. 060 */ 061 @NotNull 062 private final JEditorPane html; 063 064 /** 065 * Constructor to load the html-file <fileName> and display it's 066 * contents in this HtmlPane. 067 * @param fileName the name of the HTML-file 068 */ 069 HtmlPane(@NotNull final String fileName) { 070 try { 071 // first looking for the html file in extracted form 072 final File file = new File(CommonConstants.HELP_DIR, fileName); 073 if (file.exists()) { 074 // file exists in expected directory 075 final String s = "file:" + file.getAbsolutePath(); 076 html = new JEditorPane(s); 077 } else { 078 // file missing, so let's look if we can get it from the jar 079 final URL url1 = ClassLoader.getSystemResource(CommonConstants.HELP_DIR.replace('\\', '/') + '/' + fileName); 080 081 if (url1 != null) { 082 html = new JEditorPane(url1); 083 } else { 084 // let's try it again without first directory 085 log.info("trying: HelpFiles/" + fileName); 086 final URL url2 = ClassLoader.getSystemResource("HelpFiles/" + fileName); 087 if (url2 != null) { 088 html = new JEditorPane(url2); 089 } else { 090 log.info("Failed to open help file '" + fileName + "'!"); 091 throw new RuntimeException(); // FIXME 092 } 093 } 094 } 095 096 html.setEditable(false); 097 html.addHyperlinkListener(this); 098 final JViewport vp = getViewport(); 099 100 // under windows, the content of the panel get destroyed after scrolling 101 // this will avoid this problem! 102 // but it will make the scrolling slower 103 // so test this! 104 // FIXME: This problem should be fixed in the latest versions of the JRE. 105 getViewport().putClientProperty("EnableWindowBlit", Boolean.TRUE); 106 vp.setScrollMode(JViewport.SIMPLE_SCROLL_MODE); 107 108 vp.add(html); 109 setFocusable(true); 110 requestFocus(); 111 setAutoscrolls(true); 112 } catch (final NullPointerException e) { 113 // failed to open the html file 114 throw new RuntimeException(e); // FIXME 115 } catch (final MalformedURLException e) { 116 log.log(Level.WARNING, "Malformed URL: %s", e); 117 throw new RuntimeException(e); // FIXME 118 } catch (final IOException e) { 119 log.log(Level.WARNING, "IOException: %s", e); 120 throw new RuntimeException(e); // FIXME 121 } 122 } 123 124 /** 125 * Constructor to load the html-file <fileName> and display it's 126 * contents in this HtmlPane. 127 * @param type mime-type of the given text (e.g. "text/html") 128 * @param text text to display (can be html-text for example) 129 */ 130 HtmlPane(@NotNull final String type, @NotNull final String text) { 131 // open new JEditorPane 132 html = new JEditorPane(type, text); 133 html.setEditable(false); 134 html.addHyperlinkListener(this); 135 final JViewport vp = getViewport(); 136 137 // under windows, the content of the panel get destroyed after scrolling 138 // this will avoid this problem! 139 // but it will make the scrolling slower 140 // so test this! 141 // XXX The main issue probably is extending JScrollPane (Cher) 142 getViewport().putClientProperty("EnableWindowBlit", Boolean.TRUE); 143 vp.setScrollMode(JViewport.SIMPLE_SCROLL_MODE); 144 145 vp.add(html); 146 //vp.setView(html); 147 //vp.setViewPosition(new Point(0, 0)); 148 149 setAutoscrolls(true); 150 } 151 152 /** 153 * Notification of a change relative to a hyperlink. 154 * @param e occurred <code>HyperlinkEvent</code> 155 */ 156 @Override 157 public void hyperlinkUpdate(@NotNull final HyperlinkEvent e) { 158 if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) { 159 linkActivated(e.getURL()); 160 } 161 } 162 163 /** 164 * Follows the reference in an link. The given url is the requested 165 * reference. By default this calls <a href="#setPage">setPage</a>, and if 166 * an exception is thrown the original previous document is restored and a 167 * beep sounded. If an attempt was made to follow a link, but it 168 * represented a malformed url, this method will be called with a null 169 * argument. 170 * @param u the URL to follow 171 */ 172 private void linkActivated(@NotNull final URL u) { 173 final Cursor cursor = html.getCursor(); 174 final Cursor waitCursor = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR); 175 html.setCursor(waitCursor); 176 SwingUtilities.invokeLater(new PageLoader(u, cursor)); 177 } 178 179 /** 180 * Synchronous page loader, loads a page and handles the cursor. 181 */ 182 private class PageLoader implements Runnable { 183 184 /** 185 * URL to load. 186 */ 187 @Nullable 188 private URL url; 189 190 /** 191 * Original cursor that should be restored once the document is loaded. 192 */ 193 @NotNull 194 private final Cursor cursor; 195 196 /** 197 * Create a PageLoader. 198 * @param url the URL to load 199 * @param cursor the cursor to restore once the document is loaded 200 */ 201 private PageLoader(@NotNull final URL url, @NotNull final Cursor cursor) { 202 this.url = url; 203 this.cursor = cursor; 204 } 205 206 @Override 207 public void run() { 208 if (url == null) { 209 // restore the original cursor 210 html.setCursor(cursor); 211 212 // PENDING(prinz) remove this hack when 213 // automatic validation is activated. 214 final Component parent = html.getParent(); 215 parent.repaint(); 216 } else { 217 final Document doc = html.getDocument(); 218 try { 219 html.setPage(url); 220 } catch (final IOException ioe) { 221 html.setDocument(doc); 222 getToolkit().beep(); 223 // TODO not just beep but display an error message as well. 224 } finally { 225 // schedule the cursor to revert after 226 // the paint has happened. 227 url = null; 228 SwingUtilities.invokeLater(this); 229 } 230 } 231 } 232 233 } 234 235}