package com.laytonsmith.PureUtilities.UI;
import com.laytonsmith.PureUtilities.Common.ReflectionUtils;
import com.laytonsmith.PureUtilities.Common.UIUtils;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
/**
* A TextDialog is a JDialog that supports basic HTML text formatting. It comes
* with two main UI components, a JEditorPane that has been configured to support
* html text, and an "Ok" button which closes the dialog. The text supports some
* features that the JEditorPane doesn't normally support, namely link handling.
* URLs to an external site will work as expected, opening the user's web browser.
* Internal links are also supported, for scrolling to sections with a specific id.
* Those links should look like <code><a href="#section1">Link</a></code>,
* and some element elsewhere should have the id "section1".
*/
public class TextDialog extends javax.swing.JDialog {
/**
* Creates new TextDialog. The dialog box provides a window to show simple
* stylized text.
* @param parent The parent window
* @param modal Whether or not this is modal
* @param text The text to show in the box. This should be html text.
*/
public TextDialog(java.awt.Frame parent, boolean modal, String text) {
super(parent, modal);
initComponents();
okButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
TextDialog.this.setVisible(false);
TextDialog.this.dispose();
}
});
inputDialog.setContentType("text/html");
inputDialog.setEditable(false);
HTMLDocument doc = (HTMLDocument)inputDialog.getDocument();
HTMLEditorKit editorKit = (HTMLEditorKit) inputDialog.getEditorKit();
try {
editorKit.insertHTML(doc, doc.getLength(), "<html>" + text + "</html>", 0, 0, null);
} catch (BadLocationException | IOException ex) {
Logger.getLogger(TextDialog.class.getName()).log(Level.SEVERE, null, ex);
}
inputDialog.setCaretPosition(0);
inputDialog.addHyperlinkListener(new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
if(e.getEventType() == HyperlinkEvent.EventType.ACTIVATED){
URL url = e.getURL();
if(url == null){
// This is an internal link. The url will be null if the url
// is "invalid", which also includes anchor links. In order
// to work around this, we have to get messy and use some
// reflection stuff to grab what we actually need.
HTMLDocument doc = (HTMLDocument)inputDialog.getDocument();
Element clicked = e.getSourceElement();
Enumeration enu = clicked.getAttributes().getAttributeNames();
Object[] attr = (Object[]) ReflectionUtils.get(enu.getClass(), enu, "attr");
String link = null;
for(Object item : attr){
if(item instanceof SimpleAttributeSet){
SimpleAttributeSet tag = (SimpleAttributeSet)item;
@SuppressWarnings("UseOfObsoleteCollectionType")
Hashtable table = (Hashtable) ReflectionUtils.get(tag.getClass(), tag, "table");
for(Object key : table.keySet()){
if(key instanceof HTML.Attribute){
if("href".equals(((HTML.Attribute)key).toString())){
link = (String) table.get(key);
break;
}
}
}
break;
}
}
if(link != null){
String id = link.substring(1);
Element elem = doc.getElement(id);
if(elem != null){
inputDialog.setCaretPosition(elem.getStartOffset());
inputDialog.scrollToReference(id);
}
}
} else {
try {
// It's an external link
UIUtils.openWebpage(url);
} catch (IOException | URISyntaxException ex) {
Logger.getLogger(TextDialog.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
});
}
/**
* Sets the OK button text.
* @param text
*/
public void setOKButtonText(String text){
okButton.setText(text);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jPanel1 = new javax.swing.JPanel();
okButton = new javax.swing.JButton();
jScrollPane2 = new javax.swing.JScrollPane();
inputDialog = new javax.swing.JEditorPane();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
okButton.setText("Ok");
okButton.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
okButton.setSelected(true);
jScrollPane2.setAutoscrolls(true);
inputDialog.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED));
inputDialog.setMinimumSize(new java.awt.Dimension(20, 10));
jScrollPane2.setViewportView(inputDialog);
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 376, Short.MAX_VALUE)
.addContainerGap())
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(okButton)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 246, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(okButton)
.addContainerGap())
);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
pack();
}// </editor-fold>//GEN-END:initComponents
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
TextDialog td = new TextDialog(null, false, "<font color=\"red\">Hi</font>");
UIUtils.centerWindow(td);
td.setVisible(true);
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JEditorPane inputDialog;
private javax.swing.JPanel jPanel1;
private javax.swing.JScrollPane jScrollPane2;
private javax.swing.JButton okButton;
// End of variables declaration//GEN-END:variables
}