package com.niklim.clicktrace.dialog.description; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.io.ByteArrayInputStream; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.KeyStroke; import javax.swing.ScrollPaneConstants; import org.xhtmlrenderer.simple.FSScrollPane; import org.xhtmlrenderer.simple.XHTMLPanel; import com.niklim.clicktrace.props.UserProperties.MarkupSyntax; import com.niklim.clicktrace.service.MarkupParser; /** * Switches between EDIT and PREVIEW mode on its {@link JCheckBox} change. In * PREVIEW mode takes text from {@link JTextArea} and uses {@link MarkupParser} * to render HTML from Markdown. Displays it in {@link JPanel} placeholder. In * EDIT mode gets back to {@link JTextArea} edition. */ public class EditPreviewDescriptionToggle { private static final String XML_DOC_TYPE = "<?xml version=\"1.0\"?><!DOCTYPE some_name [<!ENTITY nbsp \" \">]> "; private JDialog dialog; private JPanel descriptionPlaceholder; private JTextArea textarea; private String migLayoutConstraints; private JCheckBox previewCheckbox; MarkupParser markupParser; public EditPreviewDescriptionToggle(JDialog dialog, JCheckBox previewCheckbox, JPanel descriptionPlaceholder, JTextArea description, String migLayoutConstraints) { this.dialog = dialog; this.descriptionPlaceholder = descriptionPlaceholder; this.textarea = description; this.migLayoutConstraints = migLayoutConstraints; this.previewCheckbox = previewCheckbox; createListeners(); } public static JCheckBox createPreviewCheckbox() { JCheckBox previewCheckbox = new JCheckBox("preview"); previewCheckbox.setToolTipText("show HTML of provided Markdown text"); return previewCheckbox; } private void createListeners() { previewCheckbox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { toggle(); } }); dialog.getRootPane().registerKeyboardAction(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { previewCheckbox.setSelected(!previewCheckbox.isSelected()); toggle(); } }, KeyStroke.getKeyStroke(KeyEvent.VK_P, KeyEvent.CTRL_DOWN_MASK), JComponent.WHEN_IN_FOCUSED_WINDOW); } public void toggle() { if (previewCheckbox.isSelected()) { descriptionPlaceholder.removeAll(); Component htmlComponent = toHtmlPanel(textarea.getText()); descriptionPlaceholder.add(htmlComponent, migLayoutConstraints); } else { descriptionPlaceholder.removeAll(); descriptionPlaceholder.add(new JScrollPane(textarea), migLayoutConstraints); } dialog.pack(); dialog.repaint(); } private Component toHtmlPanel(String markup) { return flyingsaucer(markupParser.toHtml(markup)); } private Component flyingsaucer(String html) { html = XML_DOC_TYPE + wrapWithRootElement(html); // Create a JPanel subclass to render the page XHTMLPanel panel = new XHTMLPanel(); // Set the XHTML document to render. We use the simplest form // of the API call, which uses a File reference. There // are a variety of overloads for setDocument(). try { panel.setDocument(new ByteArrayInputStream(html.getBytes()), null); } catch (Exception e) { e.printStackTrace(); } // Put our panel in a scrolling pane. You can use // a regular JScrollPane here, or our FSScrollPane. // FSScrollPane is already set up to move the correct // amount when scrolling 1 line or 1 page FSScrollPane scroll = new FSScrollPane(panel); scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); return scroll; } private String wrapWithRootElement(String html) { return "<div>" + html + "</div>"; } public void reset(MarkupSyntax syntax) { markupParser = new MarkupParser(syntax); } }