1 package org.columba.mail.gui.message.viewer; 19 20 import java.awt.BorderLayout ; 21 import java.awt.Color ; 22 import java.awt.Cursor ; 23 import java.awt.Dimension ; 24 import java.awt.Image ; 25 import java.awt.Insets ; 26 import java.awt.event.ActionEvent ; 27 import java.awt.event.ActionListener ; 28 import java.awt.event.MouseAdapter ; 29 import java.awt.event.MouseEvent ; 30 import java.text.DateFormat ; 31 import java.util.Iterator ; 32 import java.util.LinkedHashMap ; 33 import java.util.List ; 34 import java.util.Map ; 35 import java.util.StringTokenizer ; 36 import java.util.Vector ; 37 38 import javax.swing.BorderFactory ; 39 import javax.swing.BoxLayout ; 40 import javax.swing.ImageIcon ; 41 import javax.swing.JButton ; 42 import javax.swing.JComponent ; 43 import javax.swing.JLabel ; 44 import javax.swing.JPanel ; 45 import javax.swing.JPopupMenu ; 46 import javax.swing.UIManager ; 47 import javax.swing.border.Border ; 48 49 import org.columa.core.config.IDefaultItem; 50 import org.columba.core.config.DefaultItem; 51 import org.columba.core.gui.base.AscendingIcon; 52 import org.columba.core.resourceloader.ImageLoader; 53 import org.columba.core.xml.XmlElement; 54 import org.columba.mail.config.MailConfig; 55 import org.columba.mail.folder.IMailbox; 56 import org.columba.mail.gui.frame.MailFrameMediator; 57 import org.columba.mail.gui.frame.ThreePaneMailFrameController; 58 import org.columba.mail.gui.message.IMessageController; 59 import org.columba.mail.gui.message.action.AddToAddressbookAction; 60 import org.columba.mail.gui.message.action.ComposeMessageAction; 61 import org.columba.mail.gui.message.action.OpenAttachmentAction; 62 import org.columba.mail.gui.message.action.SaveAsAttachmentAction; 63 import org.columba.mail.parser.text.HtmlParser; 64 import org.columba.ristretto.message.Address; 65 import org.columba.ristretto.message.BasicHeader; 66 import org.columba.ristretto.message.Header; 67 import org.columba.ristretto.message.MimeHeader; 68 import org.columba.ristretto.message.MimePart; 69 import org.columba.ristretto.message.MimeTree; 70 import org.columba.ristretto.message.MimeType; 71 import org.columba.ristretto.message.StreamableMimePart; 72 import org.frapuccino.dynamicitemlistpanel.DynamicItemListPanel; 73 74 import com.jgoodies.forms.builder.ButtonBarBuilder; 75 import com.jgoodies.forms.builder.DefaultFormBuilder; 76 import com.jgoodies.forms.layout.FormLayout; 77 import com.jgoodies.forms.layout.RowSpec; 78 79 85 public class HeaderViewer extends JPanel { 86 87 private Map map; 89 90 private boolean visible; 91 92 private HeaderPanel headerTextPane; 93 94 private boolean hasAttachment; 95 96 private AttachmentsViewer attachmentViewer; 97 98 private IMessageController mediator; 99 100 private JPopupMenu attachmentViewerPopup = new JPopupMenu (); 102 103 private static DateFormat DATE_FORMATTER = DateFormat.getDateTimeInstance( 104 DateFormat.LONG, DateFormat.MEDIUM); 105 106 private SecurityStatusViewer securityInfoViewer; 107 108 private SpamStatusViewer spamInfoViewer; 109 110 private JPopupMenu attachmentPopup; 111 112 public HeaderViewer(IMessageController mediator, 113 SecurityStatusViewer securityInfoViewer, 114 SpamStatusViewer spamInfoViewer) { 115 116 this.mediator = mediator; 117 this.securityInfoViewer = securityInfoViewer; 118 this.spamInfoViewer = spamInfoViewer; 119 120 setBorder(new HeaderSeparatorBorder(Color.LIGHT_GRAY)); 121 122 124 setOpaque(false); 125 headerTextPane = new HeaderPanel(); 126 127 attachmentViewer = new AttachmentsViewer(mediator); 128 129 attachmentViewerPopup.setBorder(new MessageBorder(Color.LIGHT_GRAY, 1, 130 true)); 131 132 layoutComponents(); 133 134 visible = false; 135 } 136 137 private void layoutComponents() { 138 removeAll(); 139 140 setLayout(new BorderLayout ()); 141 142 JPanel top = new JPanel (); 143 top.setLayout(new BoxLayout (top, BoxLayout.Y_AXIS)); 145 if (securityInfoViewer.isVisible()) { 146 top.add(securityInfoViewer); 147 } 148 if (spamInfoViewer.isVisible()) { 149 top.add(spamInfoViewer); 150 } 151 152 if (securityInfoViewer.isVisible() || spamInfoViewer.isVisible()) 153 add(top, BorderLayout.NORTH); 154 155 add(headerTextPane, BorderLayout.CENTER); 156 157 } 158 159 167 public void view(IMailbox folder, Object uid, MailFrameMediator mediator) 168 throws Exception { 169 XmlElement headerviewerElement = MailConfig.getInstance() 171 .get("options").getElement("/options/headerviewer"); 172 IDefaultItem item = new DefaultItem(headerviewerElement); 173 int style = item.getIntegerWithDefault("style", 0); 174 175 map = new LinkedHashMap (); 176 Header header = null; 177 String [] headers = null; 178 switch (style) { 179 case 0: 180 headers = new String [] { "Subject", "Date", "From", "To", "Cc" }; 182 183 header = folder.getHeaderFields(uid, headers); 185 186 for (int i = 0; i < headers.length; i++) { 188 String key = headers[i]; 189 JComponent [] value = transformHeaderField(header, key); 190 191 JButton trailingItem = new JButton ("more..."); 192 trailingItem = createLinkButton(trailingItem); 193 trailingItem.addActionListener(new ActionListener () { 194 public void actionPerformed(ActionEvent event) { 195 showAddressListDialog(); 196 } 197 198 }); 199 200 DynamicItemListPanel p = new DynamicItemListPanel(2, 201 trailingItem, true); 202 p.setOpaque(false); 203 204 for (int j = 0; j < value.length; j++) { 205 p.addItem(value[j]); 206 } 207 208 if (value.length > 0) 209 map.put(key, p); 210 } 211 212 break; 213 case 1: 214 String list = headerviewerElement.getAttribute("headerfields"); 216 217 StringTokenizer tok = new StringTokenizer (list, " "); 218 headers = new String [tok.countTokens()]; 219 220 for (int i = 0; i < headers.length; i++) { 221 headers[i] = tok.nextToken(); 222 } 223 224 header = folder.getHeaderFields(uid, headers); 226 227 for (int i = 0; i < headers.length; i++) { 229 String key = headers[i]; 230 JComponent [] value = transformHeaderField(header, key); 231 232 if (value.length > 0) 233 map.put(key, value); 234 } 235 236 break; 237 case 2: 238 headers = new String [] { "From" }; 240 241 header = folder.getHeaderFields(uid, headers); 243 244 for (int i = 0; i < headers.length; i++) { 246 String key = headers[i]; 247 JComponent [] value = transformHeaderField(header, key); 248 249 JButton trailingItem = new JButton ("more..."); 250 trailingItem = createLinkButton(trailingItem); 251 trailingItem.addActionListener(new ActionListener () { 252 public void actionPerformed(ActionEvent event) { 253 showAddressListDialog(); 254 } 255 256 }); 257 258 DynamicItemListPanel p = new DynamicItemListPanel(2, 259 trailingItem, true); 260 p.setOpaque(false); 261 262 for (int j = 0; j < value.length; j++) { 263 p.addItem(value[j]); 264 } 265 266 if (value.length > 0) 267 map.put(key, p); 268 } 269 270 break; 271 } 272 273 hasAttachment = ((Boolean ) folder.getAttribute(uid, 274 "columba.attachment")).booleanValue(); 275 276 if (hasAttachment) { 277 JComponent [] value = createAttachmentComponentArray(folder, uid); 278 279 JButton moreButton = createAttachmentMoreButton(); 282 283 DynamicItemListPanel p = new DynamicItemListPanel(2, null, true); 284 p.setShowLastSeparator(false); 285 p.setOpaque(false); 286 287 for (int j = 0; j < value.length; j++) { 288 p.addItem(value[j]); 289 } 290 291 p.addItem(moreButton); 292 293 if (value.length > 0) 295 map.put("Attachments", p); 296 297 } 298 299 headerTextPane.setHeader(map); 300 301 attachmentViewer.view(folder, uid, mediator); 302 303 if (mediator instanceof ThreePaneMailFrameController) { 306 final ThreePaneMailFrameController c = (ThreePaneMailFrameController) mediator; 307 final String title = (String ) folder.getAttribute(uid, 309 "columba.subject"); 310 311 javax.swing.SwingUtilities.invokeLater(new Runnable () { 313 public void run() { 314 c.getMessageViewerDockable().setTitle(title); 315 } 316 }); 317 318 } 319 320 visible = true; 321 322 } 323 324 private JComponent [] createAttachmentComponentArray(final IMailbox folder, 325 final Object uid) throws Exception { 326 Vector vector = new Vector (); 327 328 MimeTree model = folder.getMimePartTree(uid); 329 List displayedMimeParts = model.getAllLeafs(); 330 331 removeBodyParts(model, displayedMimeParts); 333 334 for (int i = 0; i < displayedMimeParts.size(); i++) { 336 final StreamableMimePart mp = (StreamableMimePart) displayedMimeParts 337 .get(i); 338 339 JButton button = createAttachmentItem(mp); 342 343 vector.add(button); 344 } 345 346 350 352 return (JComponent []) vector.toArray(new JComponent [0]); 353 } 354 355 359 private JButton createAttachmentMoreButton() { 360 ImageIcon icon = new AscendingIcon(); 361 final JButton moreButton = new JButton (icon); 362 moreButton.setMargin(new Insets (0, 1, 0, 0)); 363 364 moreButton.addActionListener(new ActionListener () { 365 public void actionPerformed(ActionEvent event) { 366 toggleAttachmentPopupVisibility(); 367 } 368 369 }); 370 371 return moreButton; 372 } 373 374 378 private JButton createAttachmentItem(final StreamableMimePart mp) { 379 MimeHeader header = mp.getHeader(); 380 MimeType type = header.getMimeType(); 381 382 String contentType = type.getType(); 383 String contentSubtype = type.getSubtype(); 384 385 String text = null; 386 387 if (header.getFileName() != null) { 389 text = header.getFileName(); 390 } else { 391 text = contentType + "/" + contentSubtype; 392 } 393 394 StringBuffer tooltip = new StringBuffer (); 396 tooltip.append("<html><body>"); 397 398 if (header.getFileName() != null) { 399 tooltip.append(header.getFileName()); 400 tooltip.append(" - "); 401 } 402 403 tooltip.append("<i>"); 404 405 if (header.getContentDescription() != null) { 406 tooltip.append(header.getContentDescription()); 407 } else { 408 tooltip.append(contentType); 409 tooltip.append("/"); 410 tooltip.append(contentSubtype); 411 } 412 413 tooltip.append("</i></body></html>"); 414 415 ImageIcon image = null; 416 417 image = new AttachmentImageIconLoader().getImageIcon(type.getType(), 418 type.getSubtype()); 419 420 image = new ImageIcon (image.getImage().getScaledInstance(16, 16, 422 Image.SCALE_SMOOTH)); 423 424 JButton button = new JButton (text, image); 425 button = createLabelButton(button); 426 button.setToolTipText(tooltip.toString()); 428 429 button.addActionListener(new OpenAttachmentAction(mediator, mp 430 .getAddress())); 431 432 button.addMouseListener(new PopupListener(createAttachmentPopupMenu(mp 433 .getAddress()))); 434 435 return button; 436 } 437 438 442 private void removeBodyParts(MimeTree model, List displayedMimeParts) { 443 StreamableMimePart bodyPart = (StreamableMimePart) model 445 .getFirstTextPart("plain"); 446 447 if (bodyPart != null) { 448 MimePart bodyParent = bodyPart.getParent(); 449 450 if (bodyParent != null) { 451 if (bodyParent.getHeader().getMimeType().getSubtype().equals( 452 "alternative")) { 453 List bodyParts = bodyParent.getChilds(); 454 displayedMimeParts.removeAll(bodyParts); 455 } else { 456 displayedMimeParts.remove(bodyPart); 457 } 458 } else { 459 displayedMimeParts.remove(bodyPart); 460 } 461 } 462 } 463 464 private void toggleAttachmentPopupVisibility() { 465 if (attachmentPopup == null) { 466 attachmentPopup = new JPopupMenu (); 467 468 JPanel panel = createAttachmentViewerPanel(); 469 470 attachmentPopup.add(panel); 471 } 472 473 if (attachmentPopup.isVisible()) 474 attachmentPopup.setVisible(false); 475 else { 476 attachmentPopup.show(this, 0, getHeight() - 1); 477 } 478 } 479 480 private JPanel createAttachmentViewerPanel() { 481 JPanel panel = new JPanel (); 482 panel.setPreferredSize(new Dimension (HeaderViewer.this.getWidth() - 1, 483 (int) HeaderViewer.this.getHeight() * 2)); 484 panel.setLayout(new BorderLayout ()); 485 486 JPanel centerPanel = new JPanel (); 487 centerPanel.setLayout(new BorderLayout ()); 488 centerPanel.setBorder(new HeaderSeparatorBorder(Color.LIGHT_GRAY)); 489 490 JButton closeButton = new JButton ("Close"); 492 closeButton.setDefaultCapable(true); 493 closeButton.setMnemonic('C'); 494 closeButton.addActionListener(new ActionListener () { 495 public void actionPerformed(ActionEvent event) { 496 toggleAttachmentPopupVisibility(); 497 } 498 }); 499 500 JButton helpButton = new JButton ("Help"); 502 helpButton.setMnemonic('h'); 503 504 centerPanel.add(attachmentViewer, BorderLayout.CENTER); 505 506 panel.add(centerPanel, BorderLayout.CENTER); 507 508 JPanel bottomPanel = new JPanel (); 509 bottomPanel.setBackground(UIManager.getColor("TextField.background")); 510 bottomPanel.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6)); 511 512 ButtonBarBuilder builder = new ButtonBarBuilder(bottomPanel); 513 builder.addGlue(); 515 builder.addGriddedButtons(new JButton [] { closeButton, helpButton }); 516 517 panel.add(bottomPanel, BorderLayout.SOUTH); 518 519 add(panel, BorderLayout.CENTER); 520 521 return panel; 522 } 523 524 527 class PopupListener extends MouseAdapter { 528 private JPopupMenu menu; 529 530 public PopupListener(JPopupMenu menu) { 531 this.menu = menu; 532 } 533 534 public void mousePressed(MouseEvent e) { 535 maybeShowPopup(e); 536 } 537 538 public void mouseReleased(MouseEvent e) { 539 maybeShowPopup(e); 540 } 541 542 private void maybeShowPopup(MouseEvent e) { 543 if (e.isPopupTrigger()) { 544 menu.show(e.getComponent(), e.getX(), e.getY()); 545 } 546 } 547 } 548 549 private JPopupMenu createAttachmentPopupMenu(Integer [] address) { 550 JPopupMenu popup = new JPopupMenu (); 551 popup.add(new OpenAttachmentAction(mediator, address)); 552 popup.add(new SaveAsAttachmentAction(mediator, address)); 553 554 return popup; 555 } 556 557 private JPopupMenu createAddressPopupMenu(String emailAddress) { 558 JPopupMenu popup = new JPopupMenu (); 559 popup.add(new ComposeMessageAction(emailAddress)); 560 popup.add(new AddToAddressbookAction(emailAddress)); 561 562 return popup; 563 } 564 565 570 class AttachmentImageIconLoader { 571 572 575 private AttachmentImageIconLoader() { 576 } 577 578 587 public ImageIcon getImageIcon(String contentType, String contentSubtype) { 588 StringBuffer buf = new StringBuffer (); 589 buf.append("gnome-"); 590 buf.append(contentType); 591 buf.append("-"); 592 buf.append(contentSubtype); 593 buf.append(".png"); 594 595 ImageIcon icon = ImageLoader.getMimetypeIcon(buf.toString()); 596 597 if (icon == null) { 598 icon = ImageLoader.getMimetypeIcon("gnome-" + contentType 599 + ".png"); 600 } 601 602 if (icon == null) { 603 icon = ImageLoader.getMimetypeIcon("gnome-text.png"); 604 } 605 606 return icon; 607 } 608 } 609 610 614 public JComponent getView() { 615 return this; 616 } 617 618 protected JComponent [] transformHeaderField(Header header, String key) { 619 if (header == null) 620 throw new IllegalArgumentException ("header == null"); 621 if (key == null) 622 throw new IllegalArgumentException ("key == null"); 623 BasicHeader bHeader = new BasicHeader(header); 624 if (header.get(key) == null) { 626 return new JComponent [0]; 627 } 628 629 if (((String ) header.get(key)).length() == 0) { 631 return new JComponent [0]; 632 } 633 634 if (key.equals("Subject")) { 635 String str = bHeader.getSubject(); 636 637 str = HtmlParser.substituteSpecialCharactersInHeaderfields(str); 640 return new JComponent [] { new JLabel (str) }; 641 } else if (key.equals("To")) { 642 643 return createRecipientComponentArray(bHeader.getTo()); 644 } else if (key.equals("Reply-To")) { 645 646 return createRecipientComponentArray(bHeader.getReplyTo()); 647 } else if (key.equals("Cc")) { 648 649 return createRecipientComponentArray(bHeader.getCc()); 650 } else if (key.equals("Bcc")) { 651 652 return createRecipientComponentArray(bHeader.getBcc()); 653 } else if (key.equals("From")) { 654 655 return createRecipientComponentArray(new Address[] { (Address) bHeader 656 .getFrom() }); 657 } else if (key.equals("Date")) { 658 String str = DATE_FORMATTER.format(bHeader.getDate()); 659 660 str = HtmlParser.substituteSpecialCharactersInHeaderfields(str); 663 return new JComponent [] { new JLabel (str) }; 664 } else { 665 String str = (String ) header.get(key); 666 667 str = HtmlParser.substituteSpecialCharactersInHeaderfields(str); 670 return new JComponent [] { new JLabel (str) }; 671 } 672 } 673 674 private JComponent [] createRecipientComponentArray(Address[] addressArray) { 675 Vector v = new Vector (); 676 for (int j = 0; j < addressArray.length; j++) { 677 final Address adr = addressArray[j]; 678 679 JButton button = new JButton (); 680 681 StringBuffer result = new StringBuffer (); 682 String displayName = adr.getDisplayName(); 683 684 if ((displayName != null) && (displayName.length() != 0)) { 685 result.append(displayName); 686 result.append(" "); 687 result.append("<" + adr.getMailAddress() + ">"); 688 689 button.setText(adr.getDisplayName()); 690 691 } else { 692 result.append(adr.getMailAddress()); 693 button.setText(adr.getMailAddress()); 694 } 695 696 final String label = result.toString(); 697 button.setToolTipText(label); 698 699 button = createLinkButton(button); 701 702 button.addActionListener(new ActionListener () { 703 public void actionPerformed(ActionEvent event) { 704 new ComposeMessageAction(label).actionPerformed(event); 705 706 } 707 }); 708 709 button.addMouseListener(new PopupListener( 710 createAddressPopupMenu(label))); 711 712 v.add(button); 713 } 714 return (JComponent []) v.toArray(new JComponent [0]); 715 } 716 717 720 public boolean isVisible() { 721 return visible; 722 } 723 724 728 public void updateGUI() throws Exception { 729 730 headerTextPane.updateGUI(); 731 732 layoutComponents(); 733 734 revalidate(); 735 736 repaint(); 737 738 attachmentViewer.updateGUI(); 739 } 740 741 private void showAddressListDialog() { 742 new AddressListDialog(null); 743 744 } 745 746 749 class HeaderPanel extends JPanel { 750 751 private Map keys; 752 753 public HeaderPanel() { 754 super(); 755 setBorder(BorderFactory.createEmptyBorder(5, 10, 0, 10)); 756 } 757 758 761 public void updateUI() { 762 763 } 765 766 public void setHeader(Map keys) { 767 if (keys == null) 768 throw new IllegalArgumentException ("keys == null"); 769 770 this.keys = keys; 771 } 772 773 public void updateGUI() { 774 if (keys == null) 775 return; 776 777 removeAll(); 778 779 782 FormLayout layout = new FormLayout( 783 "right:pref, 3dlu, pref, 3dlu, fill:pref:grow", 784 "top:pref:grow"); 785 DefaultFormBuilder builder = new DefaultFormBuilder(layout, this); 786 787 for (Iterator it = keys.keySet().iterator(); it.hasNext();) { 788 String key = (String ) it.next(); 789 790 DynamicItemListPanel value = (DynamicItemListPanel) keys 791 .get(key); 792 793 JLabel keyLabel = new JLabel ("<html><b>" + key + "</b></html>"); 794 795 JLabel separator = new JLabel (":"); 796 797 816 builder.append(keyLabel); 817 builder.append(separator); 818 builder.append(value); 819 820 builder.appendRow(new RowSpec("top:pref:grow")); 821 822 } 823 824 validate(); 825 } 826 827 } 828 829 private class ListItem { 830 String name; 831 832 JComponent component; 833 834 ImageIcon icon; 835 } 836 837 private static final Color LINK_COLOR = Color.blue; 838 839 private static final Color LABEL_COLOR = UIManager 840 .getColor("Label.foreground"); 841 842 private static final Border LINK_BORDER = BorderFactory.createEmptyBorder( 843 0, 0, 1, 0); 844 845 public static JButton createLinkButton(JButton button) { 846 button.setBorder(LINK_BORDER); 847 button.setForeground(LINK_COLOR); 848 button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 849 button.setFocusPainted(false); 850 button.setRequestFocusEnabled(false); 851 button.setText("<html><u>" + button.getText() + "</u></html>"); 852 button.setContentAreaFilled(false); 853 return button; 855 } 856 857 public static JButton createLabelButton(JButton button) { 858 button.setBorder(LINK_BORDER); 859 button.setForeground(LABEL_COLOR); 860 button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 861 button.setFocusPainted(false); 862 button.setRequestFocusEnabled(false); 863 button.setContentAreaFilled(false); 864 button.setText("<html><u>" + button.getText() + "</u></html>"); 865 return button; 867 } 868 869 } | Popular Tags |