296 lines
11 KiB
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();
|
|
}
|
|
}
|