/*
* SK's Minecraft Launcher
* Copyright (C) 2010-2014 Albert Pham <http://www.sk89q.com> and contributors
* Please see LICENSE.txt for license information.
*/
package com.skcraft.launcher.swing;
import com.skcraft.launcher.LauncherUtils;
import lombok.extern.java.Log;
import javax.swing.*;
import javax.swing.border.CompoundBorder;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.html.HTMLDocument;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.logging.Level;
import static com.skcraft.launcher.LauncherUtils.checkInterrupted;
@Log
public final class WebpagePanel extends JPanel {
private final WebpagePanel self = this;
private URL url;
private boolean activated;
private JEditorPane documentView;
private JProgressBar progressBar;
private Thread thread;
public static WebpagePanel forURL(URL url, boolean lazy) {
return new WebpagePanel(url, lazy);
}
public static WebpagePanel forHTML(String html) {
return new WebpagePanel(html);
}
private WebpagePanel(URL url, boolean lazy) {
this.url = url;
setLayout(new BorderLayout());
if (lazy) {
setPlaceholder();
} else {
setDocument();
fetchAndDisplay(url);
}
}
private WebpagePanel(String text) {
this.url = null;
setLayout(new BorderLayout());
setDocument();
setDisplay(text, null);
}
public WebpagePanel(boolean lazy) {
this.url = null;
setLayout(new BorderLayout());
if (lazy) {
setPlaceholder();
} else {
setDocument();
}
}
private void setDocument() {
activated = true;
JLayeredPane panel = new JLayeredPane();
panel.setLayout(new WebpageLayoutManager());
documentView = new JEditorPane();
documentView.setBorder(null);
documentView.setEditable(false);
documentView.addHyperlinkListener(new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
if (e.getURL() != null) {
SwingHelper.openURL(e.getURL(), self);
}
}
}
});
JScrollPane scrollPane = new JScrollPane(documentView);
panel.add(scrollPane, new Integer(1));
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
panel.add(progressBar, new Integer(2));
add(panel, BorderLayout.CENTER);
}
private void setPlaceholder() {
activated = false;
JLayeredPane panel = new JLayeredPane();
panel.setBorder(new CompoundBorder(
BorderFactory.createEtchedBorder(), BorderFactory
.createEmptyBorder(4, 4, 4, 4)));
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
final JButton showButton = new JButton("Load page");
showButton.setAlignmentX(Component.CENTER_ALIGNMENT);
showButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
showButton.setVisible(false);
setDocument();
fetchAndDisplay(url);
}
});
// Center the button vertically.
panel.add(new Box.Filler(
new Dimension(0, 0),
new Dimension(0, 0),
new Dimension(1000, 1000)));
panel.add(showButton);
panel.add(new Box.Filler(
new Dimension(0, 0),
new Dimension(0, 0),
new Dimension(1000, 1000)));
add(panel, BorderLayout.CENTER);
}
/**
* Browse to a URL.
*
* @param url the URL
* @param onlyChanged true to only browse if the last URL was different
* @return true if only the URL was changed
*/
public boolean browse(URL url, boolean onlyChanged) {
if (onlyChanged && this.url != null && this.url.equals(url)) {
return false;
}
this.url = url;
if (activated) {
fetchAndDisplay(url);
}
return true;
}
/**
* Update the page. This has to be run in the Swing event thread.
*
* @param url the URL
*/
private synchronized void fetchAndDisplay(URL url) {
if (thread != null) {
thread.interrupt();
}
progressBar.setVisible(true);
thread = new Thread(new FetchWebpage(url));
thread.setDaemon(true);
thread.start();
}
private void setDisplay(String text, URL baseUrl) {
progressBar.setVisible(false);
documentView.setContentType("text/html");
HTMLDocument document = (HTMLDocument) documentView.getDocument();
// Clear existing styles
Enumeration<?> e = document.getStyleNames();
while (e.hasMoreElements()) {
Object o = e.nextElement();
document.removeStyle((String) o);
}
document.setBase(baseUrl);
documentView.setText(text);
documentView.setCaretPosition(0);
}
private void setError(String text) {
progressBar.setVisible(false);
documentView.setContentType("text/plain");
documentView.setText(text);
documentView.setCaretPosition(0);
}
private class FetchWebpage implements Runnable {
private URL url;
public FetchWebpage(URL url) {
this.url = url;
}
@Override
public void run() {
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setUseCaches(false);
conn.setDoInput(true);
conn.setDoOutput(false);
conn.setReadTimeout(5000);
conn.connect();
checkInterrupted();
if (conn.getResponseCode() != 200) {
throw new IOException(
"Did not get expected 200 code, got "
+ conn.getResponseCode());
}
BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream(),
"UTF-8"));
StringBuilder s = new StringBuilder();
char[] buf = new char[1024];
int len = 0;
while ((len = reader.read(buf)) != -1) {
s.append(buf, 0, len);
}
String result = s.toString();
checkInterrupted();
setDisplay(result, LauncherUtils.concat(url, ""));
} catch (IOException e) {
if (Thread.interrupted()) {
return;
}
log.log(Level.WARNING, "Failed to fetch page", e);
setError("Failed to fetch page: " + e.getMessage());
} catch (InterruptedException e) {
} finally {
if (conn != null)
conn.disconnect();
conn = null;
}
}
}
}