package org.chartsy.chatsy.chat.ui; import java.awt.Color; import java.awt.Cursor; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import javax.swing.AbstractAction; import javax.swing.JComponent; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JTextPane; import javax.swing.KeyStroke; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.Element; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.StyledDocument; import org.chartsy.chatsy.chat.ChatsyManager; import org.chartsy.chatsy.chat.plugin.ContextMenuListener; import org.chartsy.chatsy.chat.util.BrowserLauncher; import org.chartsy.chatsy.chat.util.ModelUtil; public class ChatArea extends JTextPane implements MouseListener, MouseMotionListener, ActionListener { public final SimpleAttributeSet styles = new SimpleAttributeSet(); public static final Cursor HAND_CURSOR = new Cursor(Cursor.HAND_CURSOR); public static final Cursor DEFAULT_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR); private int fontSize = 12; private List<ContextMenuListener> contextMenuListener = new ArrayList<ContextMenuListener>(); private JPopupMenu popup; private JMenuItem cutMenu; private JMenuItem copyMenu; private JMenuItem pasteMenu; private JMenuItem selectAll; private List<LinkInterceptor> interceptors = new ArrayList<LinkInterceptor>(); protected boolean forceEmoticons = false; public ChatArea() { setFontSize(fontSize); setFont(new Font("Dialog", Font.PLAIN, fontSize)); cutMenu = new JMenuItem("Cut"); cutMenu.addActionListener((ActionListener)this); getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("Ctrl x"), "cut"); getActionMap().put("cut", new AbstractAction("cut") { public void actionPerformed(ActionEvent evt) { cutAction(); } }); copyMenu = new JMenuItem("Copy"); copyMenu.addActionListener((ActionListener)this); getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("Ctrl c"), "copy"); getActionMap().put("copy", new AbstractAction("copy") { public void actionPerformed(ActionEvent evt) { ChatsyManager.setClipboard(getSelectedText()); } }); pasteMenu = new JMenuItem("Paste"); pasteMenu.addActionListener((ActionListener)this); getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("Ctrl v"), "paste"); getActionMap().put("paste", new AbstractAction("paste") { public void actionPerformed(ActionEvent evt) { pasteAction(); } }); selectAll = new JMenuItem("Select All"); selectAll.addActionListener((ActionListener)this); } public void setText(String message) { setCursor(HAND_CURSOR); if (ModelUtil.hasLength(message)) { try { insert(message); } catch (BadLocationException e) { } } } public void clear() { super.setText(""); StyleConstants.setUnderline(styles, false); StyleConstants.setBold(styles, false); StyleConstants.setItalic(styles, false); setCharacterAttributes(styles, false); } public void insert(String text) throws BadLocationException { boolean bold = false; boolean italic = false; boolean underlined = false; final StringTokenizer tokenizer = new StringTokenizer(text, " \n \t", true); while (tokenizer.hasMoreTokens()) { String textFound = tokenizer.nextToken(); if ((textFound.startsWith("http://") || textFound.startsWith("ftp://") || textFound.startsWith("https://") || textFound.startsWith("www.")) && textFound.indexOf(".") > 1) insertLink(textFound); else if (textFound.startsWith("\\\\") || (textFound.indexOf("://") > 0 && textFound.indexOf(".") < 1)) insertAddress(textFound); else insertText(textFound); } StyleConstants.setBold(styles, bold); StyleConstants.setItalic(styles, italic); StyleConstants.setUnderline(styles, underlined); } public void insertText(String text) throws BadLocationException { final Document doc = getDocument(); styles.removeAttribute("link"); doc.insertString(doc.getLength(), text, styles); } public void insertText(String text, Color color) throws BadLocationException { final Document doc = getDocument(); StyleConstants.setForeground(styles, color); doc.insertString(doc.getLength(), text, styles); } public void insertLink(String link) throws BadLocationException { final Document doc = getDocument(); styles.addAttribute("link", link); StyleConstants.setForeground(styles, Color.decode("0x336699")); StyleConstants.setUnderline(styles, true); doc.insertString(doc.getLength(), link, styles); StyleConstants.setUnderline(styles, false); StyleConstants.setForeground(styles, Color.decode("0x111111")); styles.removeAttribute("link"); setCharacterAttributes(styles, false); } public void insertAddress(String address) throws BadLocationException { final Document doc = getDocument(); styles.addAttribute("link", address); StyleConstants.setForeground(styles, Color.decode("0x009900")); StyleConstants.setUnderline(styles, true); doc.insertString(doc.getLength(), address, styles); StyleConstants.setUnderline(styles, false); StyleConstants.setForeground(styles, Color.decode("0x111111")); styles.removeAttribute("link"); setCharacterAttributes(styles, false); } public boolean insertImage(String imageKey) { return false; } public void insertHorizontalLine() { try { insertText("\n"); } catch (BadLocationException e) { } } public void setBold() { final Element element = getStyledDocument().getCharacterElement(getCaretPosition() - 1); if (element != null) { AttributeSet as = element.getAttributes(); boolean isBold = StyleConstants.isBold(as); StyleConstants.setBold(styles, !isBold); try { setCharacterAttributes(styles, true); } catch (Exception ex) { } } } public void setItalics() { final Element element = getStyledDocument().getCharacterElement(getCaretPosition() - 1); if (element != null) { AttributeSet as = element.getAttributes(); boolean isItalic = StyleConstants.isItalic(as); StyleConstants.setItalic(styles, !isItalic); try { setCharacterAttributes(styles, true); } catch (Exception fontException) { } } } public void setUnderlined() { final Element element = getStyledDocument().getCharacterElement(getCaretPosition() - 1); if (element != null) { AttributeSet as = element.getAttributes(); boolean isUnderlined = StyleConstants.isUnderline(as); StyleConstants.setUnderline(styles, !isUnderlined); try { setCharacterAttributes(styles, true); } catch (Exception underlineException) { } } } public void setFont(String font) { StyleConstants.setFontFamily(styles, font); try { setCharacterAttributes(styles, false); } catch (Exception fontException) { } } public void setFontSize(int size) { StyleConstants.setFontSize(styles, size); try { setCharacterAttributes(styles, false); } catch (Exception fontException) { } fontSize = size; } public void mouseClicked(MouseEvent e) { try { final int pos = viewToModel(e.getPoint()); final Element element = getStyledDocument().getCharacterElement(pos); if (element != null) { final AttributeSet as = element.getAttributes(); final Object o = as.getAttribute("link"); if (o != null) { try { final String url = (String)o; boolean handled = fireLinkInterceptors(e, url); if (!handled) BrowserLauncher.openURL(url); } catch (Exception ioe) { } } } } catch (Exception ex) { } } public void mousePressed(MouseEvent e) { if (e.isPopupTrigger()) handlePopup(e); } public void mouseReleased(MouseEvent e) { if (e.isPopupTrigger()) handlePopup(e); } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mouseDragged(MouseEvent e) { } public void mouseMoved(MouseEvent e) { checkForLink(e); } private void checkForLink(MouseEvent e) { try { final int pos = viewToModel(e.getPoint()); final Element element = getStyledDocument().getCharacterElement(pos); if (element != null) { final AttributeSet as = element.getAttributes(); final Object o = as.getAttribute("link"); if (o != null) setCursor(HAND_CURSOR); else setCursor(DEFAULT_CURSOR); } } catch (Exception ex) { } } public String getMarkup() { final StringBuilder buf = new StringBuilder(); final String text = getText(); final StyledDocument doc = getStyledDocument(); final Element rootElem = doc.getDefaultRootElement(); if (text.trim().length() <= 0) return null; boolean endsInNewline = text.charAt(text.length() - 1) == '\n'; for (int j = 0; j < rootElem.getElementCount(); j++) { final Element pElem = rootElem.getElement(j); for (int i = 0; i < pElem.getElementCount(); i++) { final Element e = pElem.getElement(i); final AttributeSet as = e.getAttributes(); final boolean bold = StyleConstants.isBold(as); final boolean italic = StyleConstants.isItalic(as); final boolean underline = StyleConstants.isUnderline(as); int end = e.getEndOffset(); if (end > text.length()) end = text.length(); if (endsInNewline && end >= text.length() - 1) end--; if (j == rootElem.getElementCount() - 1 && i == pElem.getElementCount() - 1) end = text.length(); final String current = text.substring(e.getStartOffset(), end); if (bold) buf.append("[b]"); if (italic) buf.append("[i]"); if (underline) buf.append("[u]"); final StringTokenizer tkn = new StringTokenizer(current, " ", true); while (tkn.hasMoreTokens()) { final String token = tkn.nextToken(); if (token.startsWith("http://") || token.startsWith("ftp://") || token.startsWith("https://")) buf.append("[url]").append(token).append("[/url]"); else if (token.startsWith("www")) { buf.append("[url "); buf.append("http://").append(token); buf.append("]"); buf.append(token); buf.append("[/url]"); } else buf.append(token); } if (underline) buf.append("[/u]"); if (italic) buf.append("[/i]"); if (bold) buf.append("[/b]"); } } return buf.toString(); } private void handlePopup(MouseEvent e) { popup = new JPopupMenu(); popup.add(cutMenu); popup.add(copyMenu); popup.add(pasteMenu); fireContextMenuListeners(); popup.addSeparator(); popup.add(selectAll); boolean textSelected = ModelUtil.hasLength(getSelectedText()); String clipboard = ChatsyManager.getClipboard(); cutMenu.setEnabled(textSelected && isEditable()); copyMenu.setEnabled(textSelected); pasteMenu.setEnabled(ModelUtil.hasLength(clipboard) && isEditable()); popup.show(this, e.getX(), e.getY()); } public void addContextMenuListener(ContextMenuListener listener) { contextMenuListener.add(listener); } public void removeContextMenuListener(ContextMenuListener listener) { contextMenuListener.remove(listener); } private void fireContextMenuListeners() { for (ContextMenuListener listener : new ArrayList<ContextMenuListener>(contextMenuListener)) listener.poppingUp(this, popup); } public void addLinkInterceptor(LinkInterceptor interceptor) { interceptors.add(interceptor); } public void removeLinkInterceptor(LinkInterceptor interceptor) { interceptors.remove(interceptor); } public boolean fireLinkInterceptors(MouseEvent event, String link) { for (LinkInterceptor linkInterceptor : new ArrayList<LinkInterceptor>(interceptors)) { boolean handled = linkInterceptor.handleLink(event, link); if (handled) return true; } return false; } public void actionPerformed(ActionEvent e) { if (e.getSource() == cutMenu) cutAction(); else if (e.getSource() == copyMenu) ChatsyManager.setClipboard(getSelectedText()); else if (e.getSource() == pasteMenu) pasteAction(); else if (e.getSource() == selectAll) { requestFocus(); selectAll(); } } private void cutAction() { String selectedText = getSelectedText(); replaceSelection(""); ChatsyManager.setClipboard(selectedText); } private void pasteAction() { String text = ChatsyManager.getClipboard(); if (text != null) replaceSelection(text); } protected void releaseResources() { getActionMap().remove("copy"); getActionMap().remove("cut"); getActionMap().remove("paste"); } public Boolean getForceEmoticons() { return forceEmoticons; } public void setForceEmoticons(Boolean forceEmoticons) { this.forceEmoticons = forceEmoticons; } }