package ru.denull.wire;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.font.TextAttribute;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.text.*;
import ru.denull.wire.Utils.EmojiIcon;
public class EmojiLabel extends JComponent {
private int lineHeight = 18;
private String text;
private String author;
private Color authorColor;
private String intro;
private Color introColor;
public boolean center = true;
public ArrayList<Link> links = new ArrayList<Link>();
// TODO: write own regexp
//public static final Pattern linkRegexp = Pattern.compile("((([A-Za-z]{3,9}:(?:\\/\\/)?)(?:[-;:&=\\+\\$,\\w]+@)?[A-Za-z0-9.-]+|(?:www\\.|[-;:&=\\+\\$,\\w]+@)[A-Za-z0-9.-]+)((?:\\/[\\+~%\\/.\\w-_]*)?\\??(?:[-\\+=&;%@.\\w_]*)#?(?:[\\w]*))?)");
//public static final Pattern linkRegexp = Pattern.compile("(https:[/][/]|http:[/][/]|www.)[a-zA-Z0-9\\-\\.]+\\.[a-zA-Z]{2,3}(:[a-zA-Z0-9]*)?/?([a-zA-Z0-9\\-._?,'/\\\\+&%$#\\=~])*$");
public class Link {
int st, en;
Rectangle bounds = null;
public Link(int st, int en) {
this.st = st;
this.en = en;
}
}
public EmojiLabel(String text) {
super();
this.text = text;
}
public EmojiLabel(String text, String author, Color authorColor) {
super();
this.text = text;
this.author = author;
this.authorColor = authorColor;
parseLinks();
}
public EmojiLabel(String text, String author, Color authorColor, String intro, Color introColor) {
super();
this.text = text;
this.author = author;
this.authorColor = authorColor;
this.intro = intro;
this.introColor = introColor;
parseLinks();
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
parseLinks();
revalidate();
repaint();
}
private void parseLinks() {
/*links.clear();
text = text.trim() + "\n";
Matcher m = linkRegexp.matcher(text);
while (m.find()) {
links.add(new Link(m.start(), m.end() - 1));
}*/
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
revalidate();
repaint();
}
public Color getAuthorColor() {
return authorColor;
}
public void setAuthorColor(Color authorColor) {
this.authorColor = authorColor;
revalidate();
repaint();
}
public String getIntro() {
return intro;
}
public void setIntro(String intro) {
this.intro = intro;
revalidate();
repaint();
}
public Color getIntroColor() {
return introColor;
}
public void setIntroColor(Color introColor) {
this.introColor = introColor;
revalidate();
repaint();
}
public int getLineHeight() {
return lineHeight;
}
public void setLineHeight(int lineHeight) {
this.lineHeight = lineHeight;
}
protected void paintComponent(Graphics g) {
if (isOpaque()) {
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
}
int offs = center ? (getHeight() - getHeightForWidth(getWidth())) / 2 : 0;
Point pos = new Point(0, offs + g.getFontMetrics().getAscent() + 1);
int maxWidth = getWidth();
if (author != null) {
g.setColor(authorColor);
pos = drawText(g, author, pos.x, pos.y, maxWidth, false);
}
if (intro != null) {
g.setColor(introColor);
pos = drawText(g, intro, pos.x, pos.y, maxWidth, false);
}
if (text != null) {
g.setColor(getForeground());
pos = drawText(g, text, pos.x, pos.y, maxWidth, false);
}
}
private Point drawText(Graphics g, String text, int x, int y, int maxWidth, boolean fake) {
if (g == null || text == null) {
return new Point(x, y);
}
y += 1;
int max = 0;
Font font = getFont();
Color fcolor = g.getColor();
Color lcolor = Color.decode("0x006fc8");
Map<TextAttribute, Object> map =
new Hashtable<TextAttribute, Object>();
map.put(TextAttribute.UNDERLINE,
TextAttribute.UNDERLINE_ON);
Font lfont = font.deriveFont(map);
FontRenderContext frc = ((Graphics2D) g).getFontRenderContext();
((Graphics2D) g).setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
((Graphics2D) g).setRenderingHint(
RenderingHints.KEY_TEXT_LCD_CONTRAST,
100);
text = text.trim() + "\n";
char[] ch = text.toCharArray();
int len = ch.length;
int lastWord = 0;
int linkIndex = 0;
boolean wasLink = false;
for (int i = 0; i < len; i++) {
long code = ch[i];
while (linkIndex < links.size() && links.get(linkIndex).en < i) { // Move pointer forward
linkIndex++;
}
boolean withinLink = (linkIndex < links.size() && links.get(linkIndex).st <= i && links.get(linkIndex).en >= i);
if (i == 0) wasLink = withinLink;
//boolean nextLink = (linkIndex < links.size() && links.get(linkIndex).st == i + 1);
Icon icon = null;
int skip = 0;
if (i < len - 1 && (code == 0xD83D || code == 0xD83C || code == 35 || (code >= 48 && code <= 57))) { // 4-byte emoji
code = (code << 16) | text.charAt(i + 1);
if (i < len - 3 &&
code == 0xD83CDDEF || code == 0xD83CDDF0 || code == 0xD83CDDE9 || code == 0xD83CDDE8 ||
code == 0xD83CDDFA || code == 0xD83CDDEB || code == 0xD83CDDEA || code == 0xD83CDDEE ||
code == 0xD83CDDF7 || code == 0xD83CDDEC) { // 8-byte emoji
code = /*(code << 32) |*/ (text.charAt(i + 2) << 16) | text.charAt(i + 3);
icon = Utils.getEmojiIcon(code);
skip = 3;
} else {
icon = Utils.getEmojiIcon(code);
skip = 1;
}
} else
if (code == 0x00A9 || code == 0x00AE || code > 0x2100) { // 2-byte emoji
icon = Utils.getEmojiIcon(code);
//System.out.println("next:" + (int)ch[i+1]);
skip = (i + 1 < len && ch[i + 1] == 65039) ? 1 : 0;
}
boolean nonLetter = Character.isLetterOrDigit(ch[i]) || withinLink;
if (icon != null || !nonLetter || wasLink != withinLink) { // end of word
boolean whitespace = (icon == null) && Character.isWhitespace(ch[i]);
boolean newLine = (icon == null) && (ch[i] == '\n');
int width = (int) font.getStringBounds(ch, lastWord, i + (icon == null ? 1 : 0), frc).getWidth();
boolean wrap = false;
if (i > lastWord && x > 0) {
wrap = (x + width > maxWidth);
if (!wrap && !whitespace && (icon == null)) {
width = (int) font.getStringBounds(ch, lastWord, i + 1, frc).getWidth();
if (x + width > maxWidth) {
wrap = true;
}
}
}
if (wrap) {
x = 0;
y += lineHeight;
}
if (!fake && (icon == null || i > lastWord)) {
if (wasLink) {
g.setColor(lcolor);
//g.setFont(lfont);
g.drawLine(x, y + 1, x + (int) font.getStringBounds(ch, lastWord, i, frc).getWidth(), y + 1);
} else {
g.setColor(fcolor);
//g.setFont(font);
}
g.drawChars(ch, lastWord, i - lastWord + (icon == null ? 1 : 0), x, y);
}
x += width;
lastWord = i + skip + 1;
max = Math.max(max, x);
if (icon != null) {
if (x + EmojiIcon.ICON_SIZE > maxWidth) {
x = 0;
y += lineHeight;
}
if (!fake) {
icon.paintIcon(this, g, x, y - 15);
}
x += EmojiIcon.ICON_SIZE;
}
if (newLine) {
x = 0;
y += lineHeight;
}
i += skip;
}
wasLink = withinLink;
}
return new Point(maxWidth == Integer.MAX_VALUE ? max : x, y);
}
public int getHeightForWidth(int width) {
Graphics g = getGraphics();
return drawText(g, author, 0, 0, width, true).y + drawText(g, intro, 0, 0, width, true).y + drawText(g, text, 0, 0, width, true).y;
}
public int getMaximalWidth() {
Graphics g = getGraphics();
return Math.max(drawText(g, author, 0, 0, Integer.MAX_VALUE, true).x, Math.max(drawText(g, intro, 0, 0, Integer.MAX_VALUE, true).x, drawText(g, text, 0, 0, Integer.MAX_VALUE, true).x));
}
public Dimension getMinimumSize() {
return new Dimension(center ? 4 : Integer.MAX_VALUE, center ? 28 : 38);
}
public Dimension getPreferredSize() {
return new Dimension(getMaximalWidth(), 38/* Math.max(28, getHeightForWidth(getWidth()))*/);
}
}