// Copyright (c) 2006 - 2008, Markus Strauch. // 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. // // 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. package net.sf.sdedit.ui.components; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Cursor; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.BorderFactory; import javax.swing.JEditorPane; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTree; import javax.swing.JViewport; import javax.swing.border.Border; import javax.swing.border.EtchedBorder; import javax.swing.event.HyperlinkListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeCellRenderer; import javax.swing.tree.TreePath; import net.sf.sdedit.util.Browser; public class AdvancedHelpPanel extends JPanel implements MouseListener, MouseMotionListener, TreeCellRenderer { private static final long serialVersionUID = 421111139685691723L; private JScrollPane navigatorScrollPane; private JScrollPane contentScrollPane; private JEditorPane editorPane; private JTree navigator; private DefaultTreeModel navigatorModel; private DefaultMutableTreeNode navigatorRoot; private static Pattern pattern = Pattern .compile(".*<a name=\"(.+?)\">(.*?)<\\/a.*"); private Map<String, DefaultMutableTreeNode> anchorMap; private String url; private static Font[] fonts; private static Cursor HAND = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); private static Cursor DEFAULT = Cursor .getPredefinedCursor(Cursor.DEFAULT_CURSOR); static { fonts = new Font[3]; String fontName = "Dialog"; fonts[0] = new Font(fontName, Font.BOLD, 13); fonts[1] = new Font(fontName, Font.PLAIN, 13); fonts[2] = new Font(fontName, Font.PLAIN, 12); } public AdvancedHelpPanel(URL url, HyperlinkListener listener) { init(); this.url = url.toString(); anchorMap = new HashMap<String, DefaultMutableTreeNode>(); try { readAnchors(url); editorPane.setPage(url); } catch (RuntimeException re) { throw re; } catch (IOException e) { e.printStackTrace(); } navigatorModel.nodeStructureChanged(navigatorRoot); expandNavigator(); navigator.addMouseListener(this); navigator.addMouseMotionListener(this); editorPane.addHyperlinkListener(Browser.getBrowser()); editorPane.addHyperlinkListener(listener); } private void readAnchors(URL url) throws IOException { InputStream stream = url.openStream(); try { BufferedReader br = new BufferedReader(new InputStreamReader(stream)); String line = br.readLine(); while (line != null) { searchAnchors(line); line = br.readLine(); } } finally { stream.close(); } } private void searchAnchors(String string) { Matcher matcher = pattern.matcher(string); if (matcher.matches()) { String id = matcher.group(1); String content = matcher.group(2); addAnchor(id, content); } } private DefaultMutableTreeNode addAnchor(String id, String content) { int dot = id.lastIndexOf('.'); DefaultMutableTreeNode parent; if (dot == -1) { parent = navigatorRoot; } else { String parentId = id.substring(0, dot); parent = anchorMap.get(parentId); if (parent == null) { parent = addAnchor(parentId, ""); } } UserObject obj = new UserObject(id, content); DefaultMutableTreeNode node = new DefaultMutableTreeNode(obj); parent.add(node); anchorMap.put(id, node); return node; } @SuppressWarnings("serial") private void init() { setLayout(new BorderLayout()); navigatorScrollPane = new JScrollPane(); contentScrollPane = new JScrollPane(); editorPane = new JEditorPane() { protected void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); super.paintComponent(g); } }; editorPane.setContentType("text/html; charset=ISO-8859-1"); editorPane.setEditable(false); JViewport viewPort = new GrabbableViewport(); viewPort.setView(editorPane); contentScrollPane.setViewport(viewPort); Border emptyBorder1 = BorderFactory.createEmptyBorder(5, 5, 0, 10); add(navigatorScrollPane, BorderLayout.WEST); Border border = BorderFactory.createEtchedBorder(EtchedBorder.RAISED); navigatorScrollPane.setBorder(border); add(contentScrollPane, BorderLayout.CENTER); navigatorRoot = new DefaultMutableTreeNode(); navigatorModel = new DefaultTreeModel(navigatorRoot); navigator = new JTree() { protected void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); super.paintComponent(g); } }; navigator.setBorder(emptyBorder1); navigator.setCellRenderer(this); navigator.setModel(navigatorModel); navigator.setRootVisible(false); navigatorScrollPane.setViewportView(navigator); } private void expandNavigator() { int numberOfRows = 0; while (navigator.getRowCount() != numberOfRows) { numberOfRows = navigator.getRowCount(); for (int i = 0; i < numberOfRows; i++) { navigator.expandRow(i); } } } private static class UserObject { private String id; private String content; UserObject(String id, String content) { this.id = id; this.content = content; } public String toString() { return content; } } public void mousePressed(MouseEvent e) { } public void mouseClicked(MouseEvent e) { TreePath path = navigator.getSelectionPath(); if (path != null) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) path .getLastPathComponent(); UserObject obj = (UserObject) node.getUserObject(); String id = obj.id; try { editorPane.setPage(url + "#" + id); } catch (RuntimeException re) { throw re; } catch (Exception ex) { ex.printStackTrace(); } } e.consume(); } public void mouseReleased(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } private static int countDots(String string) { int count = 0; for (int i = 0; i < string.length(); i++) { if (string.charAt(i) == '.') { count++; } } return count; } public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { Object o = ((DefaultMutableTreeNode) value).getUserObject(); JLabel label = new JLabel(); if (!(o instanceof UserObject)) { return label; } UserObject obj = (UserObject) o; int f = Math.min(countDots(obj.id), fonts.length - 1); label.setFont(fonts[f]); label.setText("<html><u>" + obj.content + "</u>"); return label; } public void mouseDragged(MouseEvent e) { } public void mouseMoved(MouseEvent e) { int x = e.getX(); int y = e.getY(); TreePath path = navigator.getPathForLocation(x, y); if (path != null) { navigator.setCursor(HAND); } else { navigator.setCursor(DEFAULT); } } }