/*
* Copyright (c) 2014 tabletoptool.com team.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* rptools.com team - initial implementation
* tabletoptool.com team - further development
*/
package com.t3.client.ui.commandpanel;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JEditorPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ToolTipManager;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.StyleSheet;
import org.apache.log4j.Logger;
import com.t3.client.AppPreferences;
import com.t3.client.TabletopTool;
import com.t3.client.swing.MessagePanelEditorKit;
import com.t3.macro.MacroEngine;
import com.t3.macro.MacroException;
import com.t3.macro.api.views.MacroButtonView;
import com.t3.macro.api.views.MacroView;
import com.t3.model.chat.Speaker;
import com.t3.model.chat.TextMessage;
import com.t3.util.URLUtil;
//FIXMESOON full of weird stuff that should no longer be required -> use a diffrent script here
public class MessagePanel extends JPanel {
private static final Logger log = Logger.getLogger(MessagePanel.class);
private final JScrollPane scrollPane;
private final HTMLDocument document;
private final JEditorPane textPane;
private Speaker lastSpeaker;
private static final String SND_MESSAGE_RECEIVED = "messageReceived";
/**
* From ImageView
*/
private static final String IMAGE_CACHE_PROPERTY = "imageCache";
public static final Pattern URL_PATTERN = Pattern.compile("([^:]*)://([^/]*)/([^?]*)(?:\\?(.*))?");
public MessagePanel() {
setLayout(new GridLayout());
textPane = new JEditorPane();
textPane.setEditable(false);
textPane.setEditorKit(new MessagePanelEditorKit());
textPane.addComponentListener(new ComponentListener() {
@Override
public void componentHidden(ComponentEvent e) {
}
@Override
public void componentMoved(ComponentEvent e) {
}
@Override
public void componentResized(ComponentEvent e) {
// Jump to the bottom on new text
if (!TabletopTool.getFrame().getCommandPanel().getScrollLockButton().isSelected()) {
Rectangle rowBounds = new Rectangle(0, textPane.getSize().height, 1, 1);
textPane.scrollRectToVisible(rowBounds);
}
}
@Override
public void componentShown(ComponentEvent e) {
}
});
textPane.addHyperlinkListener(new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
if (e.getURL() != null) {
TabletopTool.showDocument(e.getURL().toString());
} else {
Matcher m = URL_PATTERN.matcher(e.getDescription());
if (m.matches()) {
if (m.group(1).equalsIgnoreCase("macro")) {
try {
MacroButtonView.executeLink(e.getDescription());
} catch (MacroException e1) {
//FIXME should use macro error handling
throw new RuntimeException(e1);
}
}
}
}
}
}
});
ToolTipManager.sharedInstance().registerComponent(textPane);
document = (HTMLDocument) textPane.getDocument();
// Initialize and prepare for usage
refreshRenderer();
scrollPane = new JScrollPane(textPane, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setBorder(null);
scrollPane.getViewport().setBorder(null);
scrollPane.getViewport().setBackground(Color.white);
scrollPane.getVerticalScrollBar().addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
boolean lock = (scrollPane.getSize().height + scrollPane.getVerticalScrollBar().getValue()) < scrollPane.getVerticalScrollBar().getMaximum();
// The user has manually scrolled the scrollbar, Scroll lock time baby !
TabletopTool.getFrame().getCommandPanel().getScrollLockButton().setSelected(lock);
}
});
add(scrollPane);
clearMessages();
TabletopTool.getSoundManager().registerSoundEvent(SND_MESSAGE_RECEIVED, TabletopTool.getSoundManager().getRegisteredSound("Clink"));
}
public void refreshRenderer() {
// Create the style
StyleSheet style = document.getStyleSheet();
style.addRule("body { font-family: sans-serif; font-size: " + AppPreferences.getFontSize() + "pt}");
style.addRule("div {margin-bottom: 5px}");
style.addRule("span.roll {background:#efefef}");
setTrustedMacroPrefixColors(AppPreferences.getTrustedPrefixFG(), AppPreferences.getTrustedPrefixBG());
repaint();
}
public void setTrustedMacroPrefixColors(Color foreground, Color background) {
StringBuilder sb = new StringBuilder();
sb.append("span.trustedPrefix {background: #").append(String.format("%06X", (background.getRGB() & 0xFFFFFF)));
sb.append("; color: #").append(String.format("%06X", (foreground.getRGB() & 0xFFFFFF))).append("}");
StyleSheet style = document.getStyleSheet();
style.addRule(sb.toString());
repaint();
}
public String getMessagesText() {
return textPane.getText();
}
public void clearMessages() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
textPane.setText("<html><body id=\"body\"></body></html>");
((MessagePanelEditorKit) textPane.getEditorKit()).flush();
}
});
}
/*
* We use ASCII control characters to mark off the rolls so that there's no limitation on what (printable)
* characters the output can include Rolls look like "\036roll output\036" or "\036tooltip\037roll output\036" or
* "\036\001format info\002roll output\036" or "\036\001format info\002tooltip\037roll output\036"
*/
private static Pattern roll_pattern = Pattern.compile("\036(?:\001([^\002]*)\002)?([^\036\037]*)(?:\037([^\036]*))?\036");
public void addMessage(final TextMessage message) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
String output = message.getMessage();
output=URLUtil.replaceUrlsWithLinks(output);
if (!message.getSource().equals(TabletopTool.getPlayer().getName())) {
// TODO change this so 'macro' is case-insensitive
Matcher m = Pattern.compile("href=([\"'])\\s*(macro://(?:[^/]*)/(?:[^?]*)(?:\\?(?:.*?))?)\\1\\s*", Pattern.CASE_INSENSITIVE).matcher(output);
while (m.find()) {
//FIXMESOON MacroLinkFunction.getInstance().processMacroLink(m.group(2));
}
}
output=output.trim();
// if rolls not being visible to this user result in an empty message, display nothing
if(!output.isEmpty()) {
//this tries to insert the message so that names are not printed a thousand times
try {
Element element = document.getElement("body");
if(lastSpeaker != null && lastSpeaker.equals(message.getSpeaker())) {
Element lastDiv=element.getElement(element.getElementCount()-1);
document.insertBeforeEnd(lastDiv.getElement(0).getElement(0).getElement(1),"<div>"+output+"</div>");
}
else
if(message.getSpeaker()==null)
document.insertBeforeEnd(element, "<div>" + output + "</div>");
else
document.insertBeforeEnd(element,
"<div><table cellpadding=0><tr><td valign=top rowspan=0 style=\"margin-right: 5px\">"+
message.getSpeaker().toHTML()+ "</td><td valign=top align=left><div>" + output + "</div></td></tr></table></div>");
lastSpeaker=message.getSpeaker();
if (!message.getSource().equals(TabletopTool.getPlayer().getName())) {
TabletopTool.playSound(SND_MESSAGE_RECEIVED);
}
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (BadLocationException ble) {
ble.printStackTrace();
}
}
}
});
}
}