package NET.worlds.console; import NET.worlds.core.IniFile; import NET.worlds.scape.SendURLAction; import NET.worlds.scape.TeleportAction; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.MenuItem; import java.awt.Panel; import java.awt.PopupMenu; import java.awt.Scrollbar; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.awt.event.FocusEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.util.StringTokenizer; import java.util.Vector; public class GammaTextArea extends Panel implements AdjustmentListener, ActionListener { private static final long serialVersionUID = 4042890054976563288L; public static final int SCROLLBARS_BOTH = 1; public static final int SCROLLBARS_VERTICAL_ONLY = 2; public static final int SCROLLBARS_HORIZONTAL_ONLY = 3; public static final int SCROLLBARS_NONE = 4; protected static final int _margin = 2; private int _width; private int _height; private StyledTextCanvas _canvas; private String _string; private Font _font; private int _currentStyle; private String _currentFontName; private int _currentPointSize; private Color _currentColor; private GammaTextScrollbar _vertBar; private GammaTextScrollbar _horzBar; private int _numLines; private int _canvasLines; private int _scrollLine; private Vector _lines; private Vector _pos; private int _lastpos = 0; protected int _curpos = 0; protected boolean _hasFocus; protected int selectionStart = -1; protected int selectionEnd = -1; private static final Color skyblue = new Color(135, 206, 255); private PopupMenu rightMenu; private MenuItem textCopyItem = new MenuItem(Console.message("Copy")); private MenuItem textCopyTextItem = new MenuItem(Console.message("Copy Text")); private MenuItem textCopyAllItem = new MenuItem(Console.message("Copy All")); private MenuItem textCopyAllTextItem = new MenuItem(Console.message("Copy All Text")); private MenuItem textOpenURLItem = new MenuItem(Console.message("Open URL")); static Color bgColor = null; private int _numColumns; private int _numRows; private static int scrollWheelStep = IniFile.gamma().getIniInt("ScrollWheelStep", 3); public static String boldStartTag = ""; public static String boldEndTag = ""; public static String italicStartTag = ""; public static String italicEndTag = ""; public static String colorStartMagentaTag = ""; public static String colorStartBlueTag = ""; public static String colorStartRedTag = ""; public static String colorStartGreenTag = ""; public static String colorEndTag = ""; public static String colorMagenta2Tag = ""; public static String colorBlue2Tag = ""; public static String colorRed2Tag = ""; public static String colorGreen2Tag = ""; public static String colorCyanTag = ""; public static String colorDarkGrayTag = ""; public static String colorGrayTag = ""; public static String colorOrangeTag = ""; public static String colorPinkTag = ""; public static String colorYellowTag = ""; public static String colorWhiteTag = ""; public static String colorLightGrayTag = ""; public static EmoteHandler emotes = new EmoteHandler(); protected static String[] tagList = new String[]{ boldStartTag, boldEndTag, italicStartTag, italicEndTag, colorStartMagentaTag, colorStartRedTag, colorStartGreenTag, colorStartBlueTag, colorEndTag, colorMagenta2Tag, colorBlue2Tag, colorRed2Tag, colorGreen2Tag, colorCyanTag, colorDarkGrayTag, colorGrayTag, colorOrangeTag, colorPinkTag, colorYellowTag, colorWhiteTag, colorLightGrayTag }; private int lastWidth = 0; protected boolean selectionConversion = false; protected int _initialSelection = -1; @Override public Font getFont() { return this._font; } @Override public int getWidth() { return this._width; } @Override public int getHeight() { return this._height; } public synchronized void setWidth(int w) { this._width = w - 4; } public synchronized void setHeight(int h) { this._height = h - 4; } public Vector getLines() { return this._lines; } public int getScrollLine() { return this._scrollLine; } public int getCanvasLines() { return this._canvasLines; } public int getNumLines() { return this._numLines; } public boolean getHasFocus() { return this._hasFocus; } public Scrollbar getVertScrollbar() { return this._vertBar; } static Color getBackgroundColor() { if (bgColor == null) { int bgR = IniFile.override().getIniInt("chatBgR", 255); int bgG = IniFile.override().getIniInt("chatBgG", 255); int bgB = IniFile.override().getIniInt("chatBgB", 203); bgColor = new Color(bgR, bgG, bgB); } return bgColor; } GammaTextArea(String text, int rows, int columns, int scrollbars) { this._string = text; this._numColumns = columns; this._numRows = rows; this._currentFontName = Console.message("GammaTextFont"); this._currentStyle = 0; this._currentPointSize = IniFile.gamma().getIniInt("ChatFontSize", 12); this._currentColor = Color.black; this._canvas = new StyledTextCanvas(); this.setFontSize(this._currentPointSize); this._lines = new Vector(); this._pos = new Vector(); this._lastpos = 0; this._hasFocus = false; this._numLines = this._scrollLine = this._canvasLines = 0; switch (scrollbars) { case 1: this._vertBar = new GammaTextScrollbar(1); this._horzBar = new GammaTextScrollbar(0); break; case 2: this._vertBar = new GammaTextScrollbar(1); this._horzBar = null; break; case 3: this._vertBar = null; this._horzBar = new GammaTextScrollbar(0); break; case 4: this._vertBar = this._horzBar = null; } GridBagLayout gridbag = new GridBagLayout(); this.setLayout(gridbag); GridBagConstraints c = new GridBagConstraints(); c.fill = 1; c.weightx = 1.0; c.weighty = 1.0; gridbag.setConstraints(this._canvas, c); this.add(this._canvas); if (this._vertBar != null) { c = new GridBagConstraints(); c.fill = 3; c.gridwidth = 0; gridbag.setConstraints(this._vertBar, c); this.add(this._vertBar); this._vertBar.addAdjustmentListener(this); } if (this._horzBar != null) { c = new GridBagConstraints(); c.fill = 2; c.gridwidth = 1; this.add(this._horzBar); this._horzBar.addAdjustmentListener(this); } this.enableEvents(131231L); this._canvas.repaint(); } public synchronized void setFontSize(int fontsize) { this._currentPointSize = fontsize; this._font = new Font(this._currentFontName, this._currentStyle, this._currentPointSize); this.recalcSize(); } public synchronized void setRows(int rows) { this._numRows = rows; this.recalcSize(); } public synchronized void recalcSize() { FontMetrics fm = this._canvas.getFontMetrics(this._font); assert fm != null; int w = fm.charWidth('M') * this._numColumns; int h = fm.getHeight() * this._numRows; this.setWidth(w); this.setHeight(h); this._canvas.setSize(w, h); this.setWidth(w); this.setHeight(h); } @Override public void update(Graphics g) { this.paint(g); } @Override protected synchronized void processMouseEvent(MouseEvent e) { if (e.isPopupTrigger()) { if (this.rightMenu != null && this.rightMenu.isEnabled()) { this.hideRightMenu(); } else { this.showRightMenu(e.getX(), e.getY()); } } super.processMouseEvent(e); } @Override protected synchronized void processFocusEvent(FocusEvent e) { if (e.getID() == 1004) { this._hasFocus = true; } else if (e.getID() == 1005) { this._hasFocus = false; } this._canvas.repaint(); super.processFocusEvent(e); } @Override protected synchronized void processKeyEvent(KeyEvent e) { this._canvas.dispatchEvent(e); super.processKeyEvent(e); } @Override protected synchronized void processMouseWheelEvent(MouseWheelEvent e) { if (e.getID() == 507) { int move = 0; if (e.getScrollType() == 0) { move = e.getScrollAmount() * e.getWheelRotation(); } else { move = scrollWheelStep * e.getWheelRotation(); } if (move != 0) { this._vertBar.setValue(this._vertBar.getValue() + move); this.requestFocus(); this._canvas.sendDelayedMouseEvent(e); this.adjustmentValueChanged(null); } } super.processMouseWheelEvent(e); } @Override public synchronized void adjustmentValueChanged(AdjustmentEvent e) { this._scrollLine = this._vertBar.getValue(); this._canvas.repaint(); } public void setEditable(boolean b) { if (b) { System.out.println("Can't set GammaTextArea to be editable."); } } public synchronized String getText() { return this._string; } public synchronized void setText(String s) { this._string = s; this.wordWrapAll(); this._scrollLine = this._numLines - this._canvasLines; if (this._scrollLine < 0) { this._scrollLine = 0; } this.setScrollBounds(); } @Override public synchronized void repaint() { this._canvas.repaint(); super.repaint(); } @Override public synchronized void paint(Graphics g) { this._canvas.paint(g); super.paint(g); } protected synchronized void wordWrapAll() { this._lines.removeAllElements(); this._pos.removeAllElements(); this._numLines = 0; this._lastpos = 0; this.wordWrap(this._string); } protected synchronized boolean isLastLineVisible() { return !this._canvas.mouseActive && (this.selectionStart < 0 || this.selectionEnd <= this.selectionStart) ? this._scrollLine >= this._numLines - this._canvasLines || this._numLines <= this._canvasLines : false; } protected synchronized boolean handleTag(Graphics g, String word, int x, int y) { String lower = word.toLowerCase(); if (word.charAt(0) == '<') { for (int i = 0; i < tagList.length; i++) { if (lower.equals(tagList[i].toLowerCase())) { switch (i) { case 0: this._currentStyle |= 1; break; case 1: this._currentStyle &= -2; break; case 2: this._currentStyle |= 2; break; case 3: this._currentStyle &= -3; break; case 4: this._currentColor = Color.magenta; break; case 5: this._currentColor = Color.red; break; case 6: this._currentColor = Color.green; break; case 7: this._currentColor = Color.blue; break; case 8: this._currentColor = Color.black; break; case 9: this._currentColor = Color.magenta; break; case 10: this._currentColor = Color.blue; break; case 11: this._currentColor = Color.red; break; case 12: this._currentColor = Color.green; break; case 13: this._currentColor = Color.cyan; break; case 14: this._currentColor = Color.darkGray; break; case 15: this._currentColor = Color.gray; break; case 16: this._currentColor = Color.orange; break; case 17: this._currentColor = Color.pink; break; case 18: this._currentColor = Color.yellow; break; case 19: this._currentColor = Color.white; break; case 20: this._currentColor = Color.lightGray; } try { this._font = new Font(this._currentFontName, this._currentStyle, this._currentPointSize); } catch (IllegalArgumentException var8) { } return true; } } } if (lower.startsWith(" 15) { int color = 0; try { int end = word.lastIndexOf(34); if (end < 10 || end > 15) { end = 15; } color = Integer.parseInt(word.substring(9, end).trim(), 16); } catch (Exception var10) { } this._currentColor = new Color(color); try { this._font = new Font(this._currentFontName, this._currentStyle, this._currentPointSize); } catch (IllegalArgumentException var9) { } return true; } else { return this.handleSmiley(g, lower, x, y); } } private synchronized boolean handleSmiley(Graphics g, String lower, int x, int y) { ImageCanvas image = emotes.getImage(lower); if (image != null) { this.drawImage(g, image, x, y); return true; } else { return false; } } private synchronized void drawImage(Graphics g, ImageCanvas image, int x, int y) { if (image != null && image.loaded_) { Dimension size = image.imageSize(); if (g != null) { image.paint(g, x, (int)(y - size.getHeight())); } this.lastWidth = (int)(this.lastWidth - (size.getWidth() + 8.0)); } } protected void ClearTags(Graphics g) { if (this._currentStyle != 0) { this._currentStyle = 0; this._font = new Font(this._currentFontName, this._currentStyle, this._currentPointSize); } if (this._currentColor != Color.black) { this._currentColor = Color.black; } g.setFont(this._font); g.setColor(this._currentColor); } protected synchronized void wordWrap(String newText) { int canvasWidth = this._width; if (canvasWidth > 0) { StringTokenizer rawLines = new StringTokenizer(newText, "\n\r"); while (rawLines.hasMoreTokens()) { String rawLine = rawLines.nextToken(); StringTokenizer words = new StringTokenizer(rawLine, "\n\r\t -", true); int lineWidth = 0; String thisLine = ""; this.lastWidth = 0; FontMetrics fm = this._canvas.getFontMetrics(this._font); assert fm != null; while (words.hasMoreTokens()) { String word = words.nextToken(); if (!word.equals("\n") && !word.equals("\r")) { if (this.handleTag(null, word, 0, 0)) { lineWidth -= this.lastWidth; thisLine = thisLine + word; if (words.hasMoreTokens()) { thisLine = thisLine + words.nextToken(); } this.lastWidth = 0; fm = this._canvas.getFontMetrics(this._font); assert fm != null; } else { this.lastWidth = fm.stringWidth(word); if (this.lastWidth >= canvasWidth) { if (!thisLine.equals("")) { this._pos.addElement(this._lastpos); this._lastpos = this._lastpos + thisLine.length(); this._lines.addElement(thisLine.trim()); this._numLines++; lineWidth = 0; } while (this.lastWidth >= canvasWidth) { word = this.breakWord(word, fm); lineWidth = this.lastWidth = 0; thisLine = ""; if (word != null && word.length() > 0) { this.lastWidth = fm.stringWidth(word); } } } lineWidth += this.lastWidth; if (lineWidth > 0 && word != null && word.length() > 0) { if (lineWidth >= canvasWidth) { this._pos.addElement(this._lastpos); this._lastpos = this._lastpos + thisLine.length(); this._lines.addElement(thisLine.trim()); this._numLines++; lineWidth = this.lastWidth; thisLine = ""; } thisLine = thisLine + word; } } } } if (!thisLine.equals("")) { this._pos.addElement(this._lastpos); this._lastpos = this._lastpos + thisLine.length() + 1; this._numLines++; this._lines.addElement(thisLine.trim()); } } } } protected String breakWord(String longWord, FontMetrics fm) { int lineWidth = 0; String thisLine = ""; for (int c = 0; c < longWord.length(); c++) { char ch = longWord.charAt(c); lineWidth += fm.charWidth(ch); if (lineWidth >= this._width) { this._pos.addElement(this._lastpos); this._lastpos = this._lastpos + thisLine.length(); this._lines.addElement(thisLine); this._numLines++; lineWidth = fm.charWidth(ch); thisLine = ""; } thisLine = thisLine + ch; } if (thisLine.length() > 0) { this._pos.addElement(this._lastpos); this._lastpos = this._lastpos + thisLine.length(); this._lines.addElement(thisLine); this._numLines++; } return ""; } public synchronized void rewrap() { if (this._width > 0 && this._height > 0) { this.wordWrapAll(); this._scrollLine = this._numLines - this._canvasLines; if (this._scrollLine < 0) { this._scrollLine = 0; } this.setScrollBounds(); this._canvas.repaint(); } } public synchronized void setScrollBounds() { FontMetrics fm = this._canvas.getFontMetrics(this._font); assert fm != null; int lineHeight = fm.getHeight(); this._canvasLines = (this._height + fm.getDescent()) / lineHeight; if (this._vertBar != null) { if (this._numLines <= this._canvasLines) { this._vertBar.setEnabled(false); } else { this._vertBar.setEnabled(true); this._vertBar.setValues(this._scrollLine, this._canvasLines, 0, this._numLines); this._vertBar.setBlockIncrement(this._canvasLines); } } } public synchronized void append(String s) { this._string = this._string + s; this.wordWrap(s); this._scrollLine = this._numLines - this._canvasLines; if (this._scrollLine < 0) { this._scrollLine = 0; } this.setScrollBounds(); } public synchronized void replaceRange(String s, int start, int end) { String newString = this._string.substring(0, start) + s + this._string.substring(end); this._string = newString; } public synchronized void drawLine(Graphics g, int lineNum, int y) { if (lineNum < this._pos.size()) { String thisLine = this._lines.elementAt(lineNum); assert thisLine != null; this._curpos = this._pos.elementAt(lineNum); StringTokenizer st = new StringTokenizer(thisLine, " \n\r", true); int x = 0; this.lastWidth = 0; while (st.hasMoreTokens()) { String word = st.nextToken(); if (!this.handleTag(g, word, 2 + x, y + 2)) { this.drawString(g, word, 2 + x, y + 2); this.lastWidth = g.getFontMetrics().stringWidth(word); x += this.lastWidth; } else { x -= this.lastWidth; if (st.hasMoreTokens()) { this._curpos = this._curpos + st.nextToken().length(); } this.lastWidth = 0; g.setFont(this._font); g.setColor(this._currentColor); this._curpos = this._curpos + word.length(); } } this.ClearTags(g); } } private void drawString(Graphics g, String text, int x, int y) { FontMetrics fm = g.getFontMetrics(); if (this.selectionConversion) { int height = fm.getHeight(); int pos = -1; if (y - height < this._canvas.mouseY) { if (y < this._canvas.mouseY) { if (y < this._canvas.mouseY) { if (this.selectionStart >= 0) { pos = this._curpos + text.length(); } } else { this.selectionConversion = false; } } else if (x <= this._canvas.mouseX) { if (this._canvas.mouseX > x + g.getFontMetrics().stringWidth(text)) { if (this.selectionStart >= 0 && this._curpos >= this.selectionStart) { pos = this._curpos + text.length(); } } else { if (this._canvas.mouseDoubleClick) { this._canvas.mouseDoubleClick = false; this.selectionStart = this._initialSelection = this._curpos; this.selectionEnd = this._curpos + text.length(); pos = -1; } else { int i = 0; while (i < text.length() - 1 && x + fm.stringWidth(text.substring(0, i + 1)) < this._canvas.mouseX) { i++; } pos = this._curpos + i; } this.selectionConversion = false; } } else { this.selectionConversion = false; } } else if (this.selectionStart >= 0 && this._curpos >= this.selectionStart) { this.selectionConversion = false; } if (pos >= 0) { if (this.selectionStart < 0) { this.selectionStart = this._initialSelection = pos; this.selectionEnd = this.selectionStart + 1; } else if (pos < this._initialSelection) { this.selectionStart = pos; this.selectionEnd = this._initialSelection + 1; } else { this.selectionStart = this._initialSelection; this.selectionEnd = pos + 1; } } } if (this.selectionStart >= 0 && this.selectionEnd > this.selectionStart) { int startdist = this.selectionStart - this._curpos; if (startdist > 0) { if (startdist < text.length()) { int end = text.length(); if (this._curpos + end >= this.selectionEnd) { end = this.selectionEnd - this._curpos; } this.drawHighlight(g, text.substring(startdist, end), x + fm.stringWidth(text.substring(0, startdist)), y); } } else if (this.selectionEnd > this._curpos) { if (this.selectionEnd - this._curpos < text.length()) { this.drawHighlight(g, text.substring(0, this.selectionEnd - this._curpos), x, y); } else { this.drawHighlight(g, text, x, y); } } } g.drawString(text, x, y); this._curpos = this._curpos + text.length(); } private void drawHighlight(Graphics g, String text, int x, int y) { if (text != null && text.length() > 0) { Color c = g.getColor(); g.setColor(skyblue); int height = g.getFontMetrics().getHeight(); g.fillRect(x, y - (height - 2), g.getFontMetrics().stringWidth(text), height); g.setColor(c); } } public int getSelectionStart() { return this.selectionStart; } public int getSelectionEnd() { return this.selectionEnd; } public void setSelectionStart(int position) { this.selectionStart = position; } public void setSelectionEnd(int position) { this.selectionEnd = position; } public synchronized String getSelectionText() { if (this.selectionStart >= 0 && this.selectionEnd > this.selectionStart) { return this.selectionEnd >= this.getText().length() ? this.getText().substring(this.selectionStart) : this.getText().substring(this.selectionStart, this.selectionEnd); } else { return ""; } } private void initRightMenu() { this.rightMenu = new PopupMenu(); this.rightMenu.add(this.textCopyItem); this.rightMenu.add(this.textCopyTextItem); if (this.selectionStart >= 0 && this.selectionEnd > this.selectionStart) { this.textCopyItem.setEnabled(true); this.textCopyTextItem.setEnabled(true); } else { this.textCopyItem.setEnabled(false); this.textCopyTextItem.setEnabled(false); } this.rightMenu.add(this.textCopyAllItem); this.rightMenu.add(this.textCopyAllTextItem); this.rightMenu.add(this.textOpenURLItem); this.rightMenu.addActionListener(this); this.add(this.rightMenu); } private void showRightMenu(int x, int y) { if (this.rightMenu == null) { this.initRightMenu(); } this.rightMenu.show(this, x, y); } private void hideRightMenu() { this.remove(this.rightMenu); this.rightMenu = null; } @Override public synchronized void actionPerformed(ActionEvent e) { String text = null; boolean doClean = false; if (e.getActionCommand() == this.textCopyItem.getActionCommand()) { text = this.getSelectionText(); this.selectionStart = this.selectionEnd = -1; this.repaint(); } else if (e.getActionCommand() == this.textCopyTextItem.getActionCommand()) { text = this.getSelectionText(); doClean = true; this.selectionStart = this.selectionEnd = -1; this.repaint(); } else if (e.getActionCommand() == this.textCopyAllItem.getActionCommand()) { text = this.getText(); } else if (e.getActionCommand() == this.textCopyAllTextItem.getActionCommand()) { text = this.getText(); doClean = true; } else if (e.getActionCommand() == this.textOpenURLItem.getActionCommand()) { text = this.getSelectionText().trim(); if (text != null && text.length() > 5) { if (!text.startsWith("http://") && !text.startsWith("https://") && !text.startsWith("world:")) { text = "http://" + text; } if (text.startsWith("world:") || text.contains(".world#") || (text.startsWith("http") || text.startsWith("file:")) && text.endsWith(".world")) { if (text.startsWith("world:")) { text = text.substring(6); } TeleportAction.teleport(text, null); } else { new SendURLAction(text).startBrowser(); } this.selectionStart = this.selectionEnd = -1; this.repaint(); } this.hideRightMenu(); return; } if (doClean) { StringTokenizer st = new StringTokenizer(text, " \n\r", true); String newText = ""; while (st.hasMoreTokens()) { String word = st.nextToken(); if (!this.handleTag(null, word, 0, 0)) { newText = newText + word; } else { if (newText.length() > 0 && newText.substring(newText.length() - 1).toCharArray()[0] == ' ') { newText = newText.substring(0, newText.length() - 1); } if (st.hasMoreTokens()) { st.nextToken(); } } } text = newText; } if (text != null && text.length() > 0) { Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); clipboard.setContents(new StringSelection(text), null); } this.hideRightMenu(); } }