curator/src/main/java/zander/ui/docs/DocumentationViewer.java

296 lines
11 KiB
Java

package zander.ui.docs;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Desktop;
import java.awt.Dialog;
import java.awt.Window;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.NoSuchElementException;
import javax.swing.JCheckBox;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTree;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.HyperlinkEvent;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import zander.ui.ErrorDialog;
import zander.ui.UIUtils;
public class DocumentationViewer extends JFrame {
private static final long serialVersionUID = -129157128310235L;
private static final Logger LOGGER = LoggerFactory.getLogger(DocumentationViewer.class);
private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
private static final DocumentBuilder DOCUMENT_BUILDER;
static {
try {
DOCUMENT_BUILDER = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new Error("Could not create XML parser!", e);
}
}
private static class DocumentationEntry {
public String name;
public URL url;
public DocumentationEntry(String name, URL url) {
this.name = name;
this.url = url;
}
@Override
public String toString() {
return name;
}
}
private final DefaultMutableTreeNode treeRoot;
private final DefaultMutableTreeNode aboutNode;
private final CuratorAboutPanel aboutPanel;
private final DefaultTreeModel treeModel;
private final JTree tree;
private final JScrollPane treeScroll;
private final JCheckBox onTopCheck;
private final JPanel treePanel;
private final JEditorPane viewer;
private final JScrollPane viewerScroll;
private final JSplitPane content;
public DocumentationViewer(String structureResource) {
super("Curator Help");
treeRoot = getDocumentFromString(structureResource);
aboutNode = new DefaultMutableTreeNode("About");
treeRoot.add(aboutNode);
aboutPanel = new CuratorAboutPanel();
treeModel = new DefaultTreeModel(treeRoot);
tree = new JTree(treeModel);
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.setRootVisible(false);
tree.setExpandsSelectedPaths(true);
tree.addTreeSelectionListener((e) -> {
if (e.getNewLeadSelectionPath() != null) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.getNewLeadSelectionPath().getLastPathComponent();
if (node == aboutNode) {
openAbout();
} else {
openHelp((DefaultMutableTreeNode) e.getNewLeadSelectionPath().getLastPathComponent());
}
}
});
treeScroll = new JScrollPane(tree);
onTopCheck = new JCheckBox("Stay above other windows");
onTopCheck.setHorizontalAlignment(SwingConstants.CENTER);
onTopCheck.setMnemonic(KeyEvent.VK_S);
onTopCheck.addActionListener((e) -> {
setAlwaysOnTop(onTopCheck.isSelected());
});
treePanel = new JPanel(new BorderLayout());
treePanel.add(treeScroll, BorderLayout.CENTER);
treePanel.add(onTopCheck, BorderLayout.PAGE_END);
viewer = new JEditorPane();
viewer.setContentType("text/html");
viewer.setEditable(false);
viewer.addHyperlinkListener((e) -> {
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
String tu = e.getDescription();
if (tu.startsWith("help:")) {
if (tu.length() <= 6) {
return;
}
String path = tu.substring(5);
openHelp(path);
} else if (tu.startsWith("http://") || tu.startsWith("https://")) {
URL u = e.getURL();
try {
Desktop.getDesktop().browse(u.toURI());
} catch (URISyntaxException | IOException ex) {
LOGGER.error("Could not open url: {}", u.toExternalForm(), ex);
ErrorDialog ed = new ErrorDialog("Link Error", "Could not open link: " +
u.toExternalForm(), ex);
ed.setVisible(true);
}
}
}
});
viewerScroll = new JScrollPane(viewer);
content = new JSplitPane();
content.setOneTouchExpandable(true);
content.setLeftComponent(treePanel);
content.setRightComponent(viewerScroll);
setContentPane(content);
setModalExclusionType(Dialog.ModalExclusionType.APPLICATION_EXCLUDE);
setResizable(true);
setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
setSize((2 * UIUtils.SCREEN_SIZE.width) / 3, (2 * UIUtils.SCREEN_SIZE.height) / 3);
expandFirstLevelNodes();
}
public void openHelp(String page) {
String[] path = page.split("/");
if (path.length != 0) {
openHelp(path, treeRoot);
}
setVisible(true);
}
private void openHelp(String[] path, DefaultMutableTreeNode node) {
int ns = node.getChildCount();
for (int i = 0; i < ns; ++i) {
DefaultMutableTreeNode c = (DefaultMutableTreeNode) node.getChildAt(i);
if (path[0].equals(String.valueOf(c.getUserObject()))) {
openHelp(Arrays.copyOfRange(path, 1, path.length), c);
return;
}
}
tree.setSelectionPath(new TreePath(node.getPath()));
openHelp(node);
}
private void openHelp(DefaultMutableTreeNode node) {
setTitle("Curator Help");
Object uo = node.getUserObject();
if (uo instanceof DocumentationEntry) {
DocumentationEntry e = (DocumentationEntry) uo;
try {
if (content.getRightComponent() != viewerScroll) {
int div = content.getDividerLocation();
content.setRightComponent(viewerScroll);
content.setDividerLocation(div);
revalidate();
}
viewer.setPage(e.url);
return;
} catch (IOException ex) {
// ignore
}
}
}
private void openAbout() {
tree.setSelectionPath(new TreePath(aboutNode.getPath()));
if (content.getRightComponent() != aboutPanel) {
int div = content.getDividerLocation();
content.setRightComponent(aboutPanel);
content.setDividerLocation(div);
revalidate();
}
setTitle("About Curator");
LOGGER.info("Oppened about");
setVisible(true);
}
private DefaultMutableTreeNode getDocumentFromString(String resource) {
try {
try (InputStream in = ClassLoader.getSystemResourceAsStream(resource)) {
Document doc = DOCUMENT_BUILDER.parse(in);
Element root = doc.getDocumentElement();
return getDocumentNode(root);
}
} catch (Throwable e) {
LOGGER.error("Error parsing json while initializing DocumentationFrame", e);
System.exit(1);
return null;
}
}
private DefaultMutableTreeNode getDocumentNode(Node rn) {
if (rn.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) rn;
String name = element.getAttribute("name");
DefaultMutableTreeNode tn = new DefaultMutableTreeNode();
if (element.getTagName().equalsIgnoreCase("dir")) {
tn.setUserObject(name);
NodeList nl = element.getChildNodes();
for (int i = 0; i < nl.getLength(); ++i) {
DefaultMutableTreeNode child = getDocumentNode(nl.item(i));
if (child != null) {
tn.add(child);
}
}
} else if (element.getTagName().equalsIgnoreCase("file")) {
String value = element.getTextContent();
URL url = ClassLoader.getSystemResource(value);
DocumentationEntry entry = new DocumentationEntry(name, url);
tn.setUserObject(entry);
}
return tn;
}
return null;
}
private void expandFirstLevelNodes() {
try {
DefaultMutableTreeNode fn = (DefaultMutableTreeNode) treeRoot.getFirstChild();
tree.setSelectionPath(new TreePath(fn.getPath()));
} catch (NoSuchElementException e) {
// do nothing
}
}
private static final String DOCS_PATH = "docs.xml";
private static DocumentationViewer FRAME;
static {
if (!SwingUtilities.isEventDispatchThread()) {
try {
SwingUtilities.invokeAndWait(() -> FRAME = new DocumentationViewer(DOCS_PATH));
} catch (Throwable e) {
LOGGER.error("Could not create documentation viewer!", e);
System.exit(1);
}
} else {
FRAME = new DocumentationViewer(DOCS_PATH);
}
}
public static void show(String path) {
show((Window) null, path);
}
public static void show(Component comp, String path) {
show(SwingUtilities.getWindowAncestor(comp), path);
}
public static void show(Window parent, String path) {
if (!FRAME.isVisible()) {
UIUtils.centerWindow(FRAME, parent);
}
FRAME.openHelp(path);
}
public static void showAbout() {
FRAME.openAbout();
}
public static void showAbout(Window parent) {
if (!FRAME.isVisible()) {
UIUtils.centerWindow(FRAME, parent);
}
showAbout();
}
}