package thaw.plugins.miniFrost; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JPanel; import javax.swing.JLabel; import javax.swing.JTextPane; import javax.swing.BorderFactory; import java.awt.BorderLayout; import java.awt.Rectangle; import java.awt.Dimension; import java.awt.Color; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import javax.swing.text.DefaultStyledDocument; import javax.swing.text.StyledDocument; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.AttributeSet; import javax.swing.text.Element; import thaw.plugins.miniFrost.interfaces.Author; import thaw.plugins.miniFrost.interfaces.SubMessage; import javax.swing.JComponent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.event.MouseInputAdapter; import java.awt.Cursor; import java.util.Vector; import thaw.gui.IconBox; import thaw.plugins.signatures.Identity; import thaw.core.I18n; import thaw.core.Logger; public class SubMessagePanel extends JPanel implements ActionListener { /** * */ private static final long serialVersionUID = 6169263783555144522L; private JButton upDownButton; private boolean retracted; private SubMessage msg; private JComponent area; private MessagePanel messagePanel; public SubMessagePanel(MessagePanel messagePanel, SubMessage msg, boolean retracted) { super(new BorderLayout(5,5)); this.messagePanel = messagePanel; this.retracted=retracted; this.msg = msg; setBorder(BorderFactory.createLineBorder(Color.BLACK)); /* header */ JPanel headPanel = new JPanel(new BorderLayout(10, 10)); JLabel dateLabel = new JLabel(java.text.DateFormat.getDateTimeInstance().format(msg.getDate())); AuthorPanel authorLabel = new AuthorPanel(msg.getAuthor()); //authorLabel.setPreferredSize(new java.awt.Dimension(400, 15)); upDownButton = new JButton("", (retracted ? IconBox.minDown : IconBox.minUp)); upDownButton.addActionListener(this); JPanel rightPanel = new JPanel(new BorderLayout(5, 5)); rightPanel.add(dateLabel, BorderLayout.CENTER); rightPanel.add(upDownButton,BorderLayout.EAST); headPanel.add(authorLabel, BorderLayout.CENTER); headPanel.add(rightPanel, BorderLayout.EAST); this.add(headPanel, BorderLayout.NORTH); /* text */ if (!retracted) { area = getEditorPane(msg.getMessage().trim()); this.add(area, BorderLayout.CENTER); } } protected class AuthorPanel extends JPanel implements ActionListener { /** * */ private static final long serialVersionUID = -2716998589145595737L; private JComboBox box = null; private JLabel nick; private Author author; public AuthorPanel(Author author) { super(new BorderLayout(5, 5)); this.author = author; nick = new JLabel(" "+author.toString(false)); add(nick, BorderLayout.CENTER); if (author.getIdentity() != null && author.getIdentity().getPrivateKey() == null) { if (author.getIdentity().getTrustLevel() == Identity.trustLevelInt[0]) /* if dev */ box = new JComboBox(Identity.trustLevelStr); else box = new JComboBox(Identity.trustLevelUserStr); nick.setForeground(author.getIdentity().getTrustLevelColor()); box.setSelectedItem(author.getIdentity().getTrustLevelStr()); box.setForeground(author.getIdentity().getTrustLevelColor()); box.addActionListener(this); add(box, BorderLayout.EAST); } else if (author.getIdentity() != null) { JLabel status = new JLabel(I18n.getMessage("thaw.plugin.signature.trustLevel.me")); status.setForeground(author.getIdentity().getTrustLevelColor()); add(status, BorderLayout.EAST); } } public void actionPerformed(ActionEvent e) { author.getIdentity().setTrustLevel((String)box.getSelectedItem()); box.setForeground(author.getIdentity().getTrustLevelColor()); nick.setForeground(author.getIdentity().getTrustLevelColor()); /* we just refresh, because if now the trust level is below what must be * displayed ... */ messagePanel.getMiniFrostPanel().getMessageTreeTable().refresh(); } } private class TextPanel extends JTextPane { /** * */ private static final long serialVersionUID = 560297574100382846L; public TextPanel() { super(); } private Rectangle rect(javax.swing.text.Position p) throws javax.swing.text.BadLocationException { int off = p.getOffset(); Rectangle r = modelToView(off>0 ? off-1 : off); return r; } public Dimension getPreferredSize() { try { Rectangle start = rect(getDocument().getStartPosition()); Rectangle end = rect(getDocument().getEndPosition()); if (start==null || end==null) { return super.getPreferredSize(); } int height = end.y + end.height - start.y + 4; return new Dimension(messagePanel.getScrollPane().getWidth()-30, height); } catch (javax.swing.text.BadLocationException e) { return super.getPreferredSize(); } } } public final static String[][] RECOGNIZED_KEYS = { { "KSK@", " " /* or eol */ }, { "CHK@", null /* eol */ }, { "USK@", ".frdx" }, }; public final static String KEY_ATTRIBUTE = "key"; private Vector keys = null; public Vector getKeys() { return keys; } /** * inspired by frost code */ private StyledDocument parseText(String txt) { keys = new Vector(); DefaultStyledDocument doc = new DefaultStyledDocument(); String[] split = txt.trim().split("\n"); SimpleAttributeSet noAttrs = new SimpleAttributeSet(); SimpleAttributeSet keyAttrs = new SimpleAttributeSet(); //keyAttrs.addAttribute(LinkEditorKit.LINK, s); keyAttrs.addAttribute(StyleConstants.Underline, Boolean.TRUE); keyAttrs.addAttribute(StyleConstants.Foreground, Color.BLUE); for (int i = 0 ; i < split.length ; i++) { /* foreach line */ String startStr = null; /* before the key */ String keyStr = null; /* the key */ String endStr = null; /* after the key */ for (int j = 0 ; j < RECOGNIZED_KEYS.length ; j++) { /* foreach key type */ int startKeyInt = split[i].indexOf(RECOGNIZED_KEYS[j][0]); if (startKeyInt >= 0) { startStr = split[i].substring(0, startKeyInt); String keyAndEndStr = split[i].substring(startKeyInt); int keyEndInt = -1; if (RECOGNIZED_KEYS[j][1] != null) { keyEndInt = keyAndEndStr.indexOf(RECOGNIZED_KEYS[j][1]); if (keyEndInt >= 0) { keyEndInt += RECOGNIZED_KEYS[j][1].length(); } } if (keyEndInt < 0 && RECOGNIZED_KEYS[j][1] != null && !" ".equals(RECOGNIZED_KEYS[j][1])) { startStr = null; continue; /* will try to find another key */ } if (keyEndInt >= 0) { keyStr = keyAndEndStr.substring(0, keyEndInt); endStr = keyAndEndStr.substring(keyEndInt); } else { keyStr = keyAndEndStr; endStr = null; } break; /* we got a key */ } } try { /* insertString() will generate Elements and insert them in the document */ if (startStr == null) { doc.insertString(doc.getLength(), split[i], noAttrs); } else { doc.insertString(doc.getLength(), startStr, noAttrs); } if (keyStr != null) { keys.add(keyStr); SimpleAttributeSet cloned = (SimpleAttributeSet)keyAttrs.clone(); cloned.addAttribute(KEY_ATTRIBUTE, new KeyLinkAction(keyStr)); doc.insertString(doc.getLength(), keyStr, cloned); } if (endStr != null) doc.insertString(doc.getLength(), endStr, noAttrs); if (i != split.length-1) doc.insertString(doc.getLength(), "\n", noAttrs); } catch(javax.swing.text.BadLocationException e) { Logger.error(this, "Error while parsing the text: "+e.toString()+" ; A line will be missing"); e.printStackTrace(); } } return doc; } protected class KeyLinkAction { private String key; public KeyLinkAction(String key) { this.key = key; } public void execute(MouseEvent e) { messagePanel.popMenuOnKey(e, key); } } protected class TextMouseMotionListener extends MouseInputAdapter { private TextPanel txtArea; private StyledDocument doc; public TextMouseMotionListener(TextPanel txtArea, StyledDocument doc) { this.txtArea = txtArea; this.doc = doc; } public void mouseMoved(MouseEvent e) { Element elem = doc.getCharacterElement(txtArea.viewToModel(e.getPoint())); AttributeSet as = elem.getAttributes(); if(StyleConstants.isUnderline(as)) txtArea.setCursor(new Cursor(Cursor.HAND_CURSOR)); else txtArea.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } } protected class TextClickListener extends MouseAdapter { private TextPanel txtArea; private StyledDocument doc; public TextClickListener(TextPanel txtArea, StyledDocument doc) { this.txtArea = txtArea; this.doc = doc; } public void mouseClicked( MouseEvent e ) { Element elem = doc.getCharacterElement(txtArea.viewToModel(e.getPoint())); AttributeSet as = elem.getAttributes(); KeyLinkAction fla = (KeyLinkAction)as.getAttribute(KEY_ATTRIBUTE); if(fla != null) fla.execute(e); } } private JComponent getEditorPane(String txt) { TextPanel a = new TextPanel(); a.setFont(a.getFont().deriveFont((float)13.5)); a.setEditable(false); a.setBackground(Color.WHITE); StyledDocument doc = parseText(txt); a.setStyledDocument(parseText(txt)); a.firePropertyChange("lineWrap", false, true); a.firePropertyChange("wrapStyleWord", false, true); a.addMouseListener(new TextClickListener(a, doc)); a.addMouseMotionListener(new TextMouseMotionListener(a, doc)); /* JScrollPane pane = new JScrollPane(a, JScrollPane.VERTICAL_SCROLLBAR_NEVER, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); */ return a; } public void setRetracted(boolean retracted) { if (!retracted) { area = getEditorPane(msg.getMessage()); this.add(area, BorderLayout.CENTER); upDownButton.setIcon(IconBox.minUp); } else { if (area != null) this.remove(area); area = null; upDownButton.setIcon(IconBox.minDown); } this.retracted = retracted; messagePanel.revalidate(); } public void actionPerformed(ActionEvent e) { if (e.getSource() == upDownButton) { setRetracted(!retracted); } } }