001: // The contents of this file are subject to the Mozilla Public License Version
002: // 1.1
003: //(the "License"); you may not use this file except in compliance with the
004: //License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
005: //
006: //Software distributed under the License is distributed on an "AS IS" basis,
007: //WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
008: //for the specific language governing rights and
009: //limitations under the License.
010: //
011: //The Original Code is "The Columba Project"
012: //
013: //The Initial Developers of the Original Code are Frederik Dietz and Timo
014: // Stich.
015: //Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
016: //
017: //All Rights Reserved.
018: package org.columba.mail.gui.message.viewer;
019:
020: import java.awt.BorderLayout;
021: import java.awt.Color;
022: import java.awt.Cursor;
023: import java.awt.Dimension;
024: import java.awt.Image;
025: import java.awt.Insets;
026: import java.awt.event.ActionEvent;
027: import java.awt.event.ActionListener;
028: import java.awt.event.MouseAdapter;
029: import java.awt.event.MouseEvent;
030: import java.text.DateFormat;
031: import java.util.Iterator;
032: import java.util.LinkedHashMap;
033: import java.util.List;
034: import java.util.Map;
035: import java.util.StringTokenizer;
036: import java.util.Vector;
037:
038: import javax.swing.BorderFactory;
039: import javax.swing.BoxLayout;
040: import javax.swing.ImageIcon;
041: import javax.swing.JButton;
042: import javax.swing.JComponent;
043: import javax.swing.JLabel;
044: import javax.swing.JPanel;
045: import javax.swing.JPopupMenu;
046: import javax.swing.UIManager;
047: import javax.swing.border.Border;
048:
049: import org.columa.core.config.IDefaultItem;
050: import org.columba.core.config.DefaultItem;
051: import org.columba.core.gui.base.AscendingIcon;
052: import org.columba.core.resourceloader.ImageLoader;
053: import org.columba.core.xml.XmlElement;
054: import org.columba.mail.config.MailConfig;
055: import org.columba.mail.folder.IMailbox;
056: import org.columba.mail.gui.frame.MailFrameMediator;
057: import org.columba.mail.gui.frame.ThreePaneMailFrameController;
058: import org.columba.mail.gui.message.IMessageController;
059: import org.columba.mail.gui.message.action.AddToAddressbookAction;
060: import org.columba.mail.gui.message.action.ComposeMessageAction;
061: import org.columba.mail.gui.message.action.OpenAttachmentAction;
062: import org.columba.mail.gui.message.action.SaveAsAttachmentAction;
063: import org.columba.mail.parser.text.HtmlParser;
064: import org.columba.ristretto.message.Address;
065: import org.columba.ristretto.message.BasicHeader;
066: import org.columba.ristretto.message.Header;
067: import org.columba.ristretto.message.MimeHeader;
068: import org.columba.ristretto.message.MimePart;
069: import org.columba.ristretto.message.MimeTree;
070: import org.columba.ristretto.message.MimeType;
071: import org.columba.ristretto.message.StreamableMimePart;
072: import org.frapuccino.dynamicitemlistpanel.DynamicItemListPanel;
073:
074: import com.jgoodies.forms.builder.ButtonBarBuilder;
075: import com.jgoodies.forms.builder.DefaultFormBuilder;
076: import com.jgoodies.forms.layout.FormLayout;
077: import com.jgoodies.forms.layout.RowSpec;
078:
079: /**
080: * Shows the headers of a RFC822 message in a lightgray box in the top of the
081: * message viewer.
082: *
083: * @author fdietz
084: */
085: public class HeaderViewer extends JPanel {
086:
087: // contains headerfields which are to be displayed
088: private Map map;
089:
090: private boolean visible;
091:
092: private HeaderPanel headerTextPane;
093:
094: private boolean hasAttachment;
095:
096: private AttachmentsViewer attachmentViewer;
097:
098: private IMessageController mediator;
099:
100: // TODO (@author fdietz): this should be changed into a "real" window
101: private JPopupMenu attachmentViewerPopup = new JPopupMenu();
102:
103: private static DateFormat DATE_FORMATTER = DateFormat
104: .getDateTimeInstance(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: // setBackground(UIManager.getColor("TextField.background"));
123:
124: setOpaque(false);
125: headerTextPane = new HeaderPanel();
126:
127: attachmentViewer = new AttachmentsViewer(mediator);
128:
129: attachmentViewerPopup.setBorder(new MessageBorder(
130: Color.LIGHT_GRAY, 1, 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.setBackground(UIManager.getColor("TextField.background"));
144: 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()
153: || spamInfoViewer.isVisible())
154: add(top, BorderLayout.NORTH);
155:
156: add(headerTextPane, BorderLayout.CENTER);
157:
158: }
159:
160: /**
161: * @param folder
162: * @param uid
163: * @param mediator
164: * @throws Exception
165: * @see org.columba.mail.gui.message.viewer.IViewer#view(IMailbox,
166: * java.lang.Object, org.columba.mail.gui.frame.MailFrameMediator)
167: */
168: public void view(IMailbox folder, Object uid,
169: MailFrameMediator mediator) throws Exception {
170: // add headerfields which are about to show up
171: XmlElement headerviewerElement = MailConfig.getInstance().get(
172: "options").getElement("/options/headerviewer");
173: IDefaultItem item = new DefaultItem(headerviewerElement);
174: int style = item.getIntegerWithDefault("style", 0);
175:
176: map = new LinkedHashMap();
177: Header header = null;
178: String[] headers = null;
179: switch (style) {
180: case 0:
181: // default
182: headers = new String[] { "Subject", "Date", "From", "To",
183: "Cc" };
184:
185: // get header from folder
186: header = folder.getHeaderFields(uid, headers);
187:
188: // transform headers if necessary
189: for (int i = 0; i < headers.length; i++) {
190: String key = headers[i];
191: JComponent[] value = transformHeaderField(header, key);
192:
193: JButton trailingItem = new JButton("more...");
194: trailingItem = createLinkButton(trailingItem);
195: trailingItem.addActionListener(new ActionListener() {
196: public void actionPerformed(ActionEvent event) {
197: showAddressListDialog();
198: }
199:
200: });
201:
202: DynamicItemListPanel p = new DynamicItemListPanel(2,
203: trailingItem, true);
204: p.setOpaque(false);
205:
206: for (int j = 0; j < value.length; j++) {
207: p.addItem(value[j]);
208: }
209:
210: if (value.length > 0)
211: map.put(key, p);
212: }
213:
214: break;
215: case 1:
216: // custom headers
217: String list = headerviewerElement
218: .getAttribute("headerfields");
219:
220: StringTokenizer tok = new StringTokenizer(list, " ");
221: headers = new String[tok.countTokens()];
222:
223: for (int i = 0; i < headers.length; i++) {
224: headers[i] = tok.nextToken();
225: }
226:
227: // get header from folder
228: header = folder.getHeaderFields(uid, headers);
229:
230: // transform headers if necessary
231: for (int i = 0; i < headers.length; i++) {
232: String key = headers[i];
233: JComponent[] value = transformHeaderField(header, key);
234:
235: if (value.length > 0)
236: map.put(key, value);
237: }
238:
239: break;
240: case 2:
241: // default
242: headers = new String[] { "From" };
243:
244: // get header from folder
245: header = folder.getHeaderFields(uid, headers);
246:
247: // transform headers if necessary
248: for (int i = 0; i < headers.length; i++) {
249: String key = headers[i];
250: JComponent[] value = transformHeaderField(header, key);
251:
252: JButton trailingItem = new JButton("more...");
253: trailingItem = createLinkButton(trailingItem);
254: trailingItem.addActionListener(new ActionListener() {
255: public void actionPerformed(ActionEvent event) {
256: showAddressListDialog();
257: }
258:
259: });
260:
261: DynamicItemListPanel p = new DynamicItemListPanel(2,
262: trailingItem, true);
263: p.setOpaque(false);
264:
265: for (int j = 0; j < value.length; j++) {
266: p.addItem(value[j]);
267: }
268:
269: if (value.length > 0)
270: map.put(key, p);
271: }
272:
273: break;
274: }
275:
276: hasAttachment = ((Boolean) folder.getAttribute(uid,
277: "columba.attachment")).booleanValue();
278:
279: if (hasAttachment) {
280: JComponent[] value = createAttachmentComponentArray(folder,
281: uid);
282:
283: // create a view more button, responsible for
284: // opening the attachment viewer popup
285: JButton moreButton = createAttachmentMoreButton();
286:
287: DynamicItemListPanel p = new DynamicItemListPanel(2, null,
288: true);
289: p.setShowLastSeparator(false);
290: p.setOpaque(false);
291:
292: for (int j = 0; j < value.length; j++) {
293: p.addItem(value[j]);
294: }
295:
296: p.addItem(moreButton);
297:
298: // TODO i18n "attachments" label
299: if (value.length > 0)
300: map.put("Attachments", p);
301:
302: }
303:
304: headerTextPane.setHeader(map);
305:
306: attachmentViewer.view(folder, uid, mediator);
307:
308: // hack to support dockable view title update
309: // TODO replace with listener pattern
310: if (mediator instanceof ThreePaneMailFrameController) {
311: final ThreePaneMailFrameController c = (ThreePaneMailFrameController) mediator;
312: // get header from folder
313: final String title = (String) folder.getAttribute(uid,
314: "columba.subject");
315:
316: // awt-event-thread
317: javax.swing.SwingUtilities.invokeLater(new Runnable() {
318: public void run() {
319: c.getMessageViewerDockable().setTitle(title);
320: }
321: });
322:
323: }
324:
325: visible = true;
326:
327: }
328:
329: private JComponent[] createAttachmentComponentArray(
330: final IMailbox folder, final Object uid) throws Exception {
331: Vector vector = new Vector();
332:
333: MimeTree model = folder.getMimePartTree(uid);
334: List displayedMimeParts = model.getAllLeafs();
335:
336: // remove body part if already shown in text viewer
337: removeBodyParts(model, displayedMimeParts);
338:
339: // Display resulting MimeParts
340: for (int i = 0; i < displayedMimeParts.size(); i++) {
341: final StreamableMimePart mp = (StreamableMimePart) displayedMimeParts
342: .get(i);
343:
344: // create attachment component with text, icon
345: // tooltip, context menu and double-click action
346: JButton button = createAttachmentItem(mp);
347:
348: vector.add(button);
349: }
350:
351: // // create a view more button, responsible for
352: // // opening the attachment viewer popup
353: // JButton moreButton = createAttachmentMoreButton();
354:
355: // vector.add(moreButton);
356:
357: return (JComponent[]) vector.toArray(new JComponent[0]);
358: }
359:
360: /**
361: * create a view more button, responsible for opening the attachment viewer
362: * popup
363: */
364: private JButton createAttachmentMoreButton() {
365: ImageIcon icon = new AscendingIcon();
366: final JButton moreButton = new JButton(icon);
367: moreButton.setMargin(new Insets(0, 1, 0, 0));
368:
369: moreButton.addActionListener(new ActionListener() {
370: public void actionPerformed(ActionEvent event) {
371: toggleAttachmentPopupVisibility();
372: }
373:
374: });
375:
376: return moreButton;
377: }
378:
379: /**
380: * Create attachment component with name, icon, tooltip description, context
381: * menu and double-click action.
382: */
383: private JButton createAttachmentItem(final StreamableMimePart mp) {
384: MimeHeader header = mp.getHeader();
385: MimeType type = header.getMimeType();
386:
387: String contentType = type.getType();
388: String contentSubtype = type.getSubtype();
389:
390: String text = null;
391:
392: // Get Text for Icon
393: if (header.getFileName() != null) {
394: text = header.getFileName();
395: } else {
396: text = contentType + "/" + contentSubtype;
397: }
398:
399: // Get Tooltip for Icon
400: StringBuffer tooltip = new StringBuffer();
401: tooltip.append("<html><body>");
402:
403: if (header.getFileName() != null) {
404: tooltip.append(header.getFileName());
405: tooltip.append(" - ");
406: }
407:
408: tooltip.append("<i>");
409:
410: if (header.getContentDescription() != null) {
411: tooltip.append(header.getContentDescription());
412: } else {
413: tooltip.append(contentType);
414: tooltip.append("/");
415: tooltip.append(contentSubtype);
416: }
417:
418: tooltip.append("</i></body></html>");
419:
420: ImageIcon image = null;
421:
422: image = new AttachmentImageIconLoader().getImageIcon(type
423: .getType(), type.getSubtype());
424:
425: // scale image
426: image = new ImageIcon(image.getImage().getScaledInstance(16,
427: 16, Image.SCALE_SMOOTH));
428:
429: JButton button = new JButton(text, image);
430: button = createLabelButton(button);
431: // button.setBorder(BorderFactory.createEmptyBorder(2, 0, 2, 0));
432: button.setToolTipText(tooltip.toString());
433:
434: button.addActionListener(new OpenAttachmentAction(mediator, mp
435: .getAddress()));
436:
437: button.addMouseListener(new PopupListener(
438: createAttachmentPopupMenu(mp.getAddress())));
439:
440: return button;
441: }
442:
443: /**
444: * Remove the first mimepart of contentype "text", as it is already shown in
445: * text viewer and should not appear in the attachment list again.
446: */
447: private void removeBodyParts(MimeTree model, List displayedMimeParts) {
448: // Remove the BodyPart(s) if any
449: StreamableMimePart bodyPart = (StreamableMimePart) model
450: .getFirstTextPart("plain");
451:
452: if (bodyPart != null) {
453: MimePart bodyParent = bodyPart.getParent();
454:
455: if (bodyParent != null) {
456: if (bodyParent.getHeader().getMimeType().getSubtype()
457: .equals("alternative")) {
458: List bodyParts = bodyParent.getChilds();
459: displayedMimeParts.removeAll(bodyParts);
460: } else {
461: displayedMimeParts.remove(bodyPart);
462: }
463: } else {
464: displayedMimeParts.remove(bodyPart);
465: }
466: }
467: }
468:
469: private void toggleAttachmentPopupVisibility() {
470: if (attachmentPopup == null) {
471: attachmentPopup = new JPopupMenu();
472:
473: JPanel panel = createAttachmentViewerPanel();
474:
475: attachmentPopup.add(panel);
476: }
477:
478: if (attachmentPopup.isVisible())
479: attachmentPopup.setVisible(false);
480: else {
481: attachmentPopup.show(this , 0, getHeight() - 1);
482: }
483: }
484:
485: private JPanel createAttachmentViewerPanel() {
486: JPanel panel = new JPanel();
487: panel.setPreferredSize(new Dimension(HeaderViewer.this
488: .getWidth() - 1,
489: (int) HeaderViewer.this .getHeight() * 2));
490: panel.setLayout(new BorderLayout());
491:
492: JPanel centerPanel = new JPanel();
493: centerPanel.setLayout(new BorderLayout());
494: centerPanel.setBorder(new HeaderSeparatorBorder(
495: Color.LIGHT_GRAY));
496:
497: // TODO i18n "Close" button
498: JButton closeButton = new JButton("Close");
499: closeButton.setDefaultCapable(true);
500: closeButton.setMnemonic('C');
501: closeButton.addActionListener(new ActionListener() {
502: public void actionPerformed(ActionEvent event) {
503: toggleAttachmentPopupVisibility();
504: }
505: });
506:
507: // TODO i18n "Help" button
508: JButton helpButton = new JButton("Help");
509: helpButton.setMnemonic('h');
510:
511: centerPanel.add(attachmentViewer, BorderLayout.CENTER);
512:
513: panel.add(centerPanel, BorderLayout.CENTER);
514:
515: JPanel bottomPanel = new JPanel();
516: bottomPanel.setBackground(UIManager
517: .getColor("TextField.background"));
518: bottomPanel.setBorder(BorderFactory.createEmptyBorder(6, 6, 6,
519: 6));
520:
521: ButtonBarBuilder builder = new ButtonBarBuilder(bottomPanel);
522: // builder.setDefaultButtonBarGapBorder();
523: builder.addGlue();
524: builder.addGriddedButtons(new JButton[] { closeButton,
525: helpButton });
526:
527: panel.add(bottomPanel, BorderLayout.SOUTH);
528:
529: add(panel, BorderLayout.CENTER);
530:
531: return panel;
532: }
533:
534: /**
535: * Generic Popup listener.
536: */
537: class PopupListener extends MouseAdapter {
538: private JPopupMenu menu;
539:
540: public PopupListener(JPopupMenu menu) {
541: this .menu = menu;
542: }
543:
544: public void mousePressed(MouseEvent e) {
545: maybeShowPopup(e);
546: }
547:
548: public void mouseReleased(MouseEvent e) {
549: maybeShowPopup(e);
550: }
551:
552: private void maybeShowPopup(MouseEvent e) {
553: if (e.isPopupTrigger()) {
554: menu.show(e.getComponent(), e.getX(), e.getY());
555: }
556: }
557: }
558:
559: private JPopupMenu createAttachmentPopupMenu(Integer[] address) {
560: JPopupMenu popup = new JPopupMenu();
561: popup.add(new OpenAttachmentAction(mediator, address));
562: popup.add(new SaveAsAttachmentAction(mediator, address));
563:
564: return popup;
565: }
566:
567: private JPopupMenu createAddressPopupMenu(String emailAddress) {
568: JPopupMenu popup = new JPopupMenu();
569: popup.add(new ComposeMessageAction(emailAddress));
570: popup.add(new AddToAddressbookAction(emailAddress));
571:
572: return popup;
573: }
574:
575: /**
576: * Imageloader using a content-type and subtype to determine the image name.
577: * <p>
578: * Automatically falls back to the default icon.
579: */
580: class AttachmentImageIconLoader {
581:
582: /**
583: * Utility constructor.
584: */
585: private AttachmentImageIconLoader() {
586: }
587:
588: /**
589: * Returns the image icon for the content type.
590: *
591: * @param contentType
592: * content type
593: * @param contentSubtype
594: * content sub type
595: * @return an Image Icon for the content type.
596: */
597: public ImageIcon getImageIcon(String contentType,
598: String contentSubtype) {
599: StringBuffer buf = new StringBuffer();
600: buf.append("gnome-");
601: buf.append(contentType);
602: buf.append("-");
603: buf.append(contentSubtype);
604: buf.append(".png");
605:
606: ImageIcon icon = ImageLoader
607: .getMimetypeIcon(buf.toString());
608:
609: if (icon == null) {
610: icon = ImageLoader.getMimetypeIcon("gnome-"
611: + contentType + ".png");
612: }
613:
614: if (icon == null) {
615: icon = ImageLoader.getMimetypeIcon("gnome-text.png");
616: }
617:
618: return icon;
619: }
620: }
621:
622: /**
623: * @return getView
624: * @see org.columba.mail.gui.message.viewer.IViewer#getView()
625: */
626: public JComponent getView() {
627: return this ;
628: }
629:
630: protected JComponent[] transformHeaderField(Header header,
631: String key) {
632: if (header == null)
633: throw new IllegalArgumentException("header == null");
634: if (key == null)
635: throw new IllegalArgumentException("key == null");
636: BasicHeader bHeader = new BasicHeader(header);
637: // message doesn't contain this headerfield
638: if (header.get(key) == null) {
639: return new JComponent[0];
640: }
641:
642: // headerfield is empty
643: if (((String) header.get(key)).length() == 0) {
644: return new JComponent[0];
645: }
646:
647: if (key.equals("Subject")) {
648: String str = bHeader.getSubject();
649:
650: // substitute special characters like:
651: // <,>,&,\t,\n,"
652: str = HtmlParser
653: .substituteSpecialCharactersInHeaderfields(str);
654: return new JComponent[] { new JLabel(str) };
655: } else if (key.equals("To")) {
656:
657: return createRecipientComponentArray(bHeader.getTo());
658: } else if (key.equals("Reply-To")) {
659:
660: return createRecipientComponentArray(bHeader.getReplyTo());
661: } else if (key.equals("Cc")) {
662:
663: return createRecipientComponentArray(bHeader.getCc());
664: } else if (key.equals("Bcc")) {
665:
666: return createRecipientComponentArray(bHeader.getBcc());
667: } else if (key.equals("From")) {
668:
669: return createRecipientComponentArray(new Address[] { (Address) bHeader
670: .getFrom() });
671: } else if (key.equals("Date")) {
672: String str = DATE_FORMATTER.format(bHeader.getDate());
673:
674: // substitute special characters like:
675: // <,>,&,\t,\n,"
676: str = HtmlParser
677: .substituteSpecialCharactersInHeaderfields(str);
678: return new JComponent[] { new JLabel(str) };
679: } else {
680: String str = (String) header.get(key);
681:
682: // substitute special characters like:
683: // <,>,&,\t,\n,"
684: str = HtmlParser
685: .substituteSpecialCharactersInHeaderfields(str);
686: return new JComponent[] { new JLabel(str) };
687: }
688: }
689:
690: private JComponent[] createRecipientComponentArray(
691: Address[] addressArray) {
692: Vector v = new Vector();
693: for (int j = 0; j < addressArray.length; j++) {
694: final Address adr = addressArray[j];
695:
696: JButton button = new JButton();
697:
698: StringBuffer result = new StringBuffer();
699: String displayName = adr.getDisplayName();
700:
701: if ((displayName != null) && (displayName.length() != 0)) {
702: result.append(displayName);
703: result.append(" ");
704: result.append("<" + adr.getMailAddress() + ">");
705:
706: button.setText(adr.getDisplayName());
707:
708: } else {
709: result.append(adr.getMailAddress());
710: button.setText(adr.getMailAddress());
711: }
712:
713: final String label = result.toString();
714: button.setToolTipText(label);
715:
716: // button.setOpaque(false);
717: button = createLinkButton(button);
718:
719: button.addActionListener(new ActionListener() {
720: public void actionPerformed(ActionEvent event) {
721: new ComposeMessageAction(label)
722: .actionPerformed(event);
723:
724: }
725: });
726:
727: button.addMouseListener(new PopupListener(
728: createAddressPopupMenu(label)));
729:
730: v.add(button);
731: }
732: return (JComponent[]) v.toArray(new JComponent[0]);
733: }
734:
735: /**
736: * @see org.columba.mail.gui.message.viewer.IViewer#isVisible()
737: */
738: public boolean isVisible() {
739: return visible;
740: }
741:
742: /**
743: * @throws Exception
744: * @see org.columba.mail.gui.message.viewer.IViewer#updateGUI()
745: */
746: public void updateGUI() throws Exception {
747:
748: headerTextPane.updateGUI();
749:
750: layoutComponents();
751:
752: revalidate();
753:
754: repaint();
755:
756: attachmentViewer.updateGUI();
757: }
758:
759: private void showAddressListDialog() {
760: new AddressListDialog(null);
761:
762: }
763:
764: /**
765: * Shows Subject, From, Date, To message headers and list of attachments.
766: */
767: class HeaderPanel extends JPanel {
768:
769: private Map keys;
770:
771: public HeaderPanel() {
772: super ();
773: setBorder(BorderFactory.createEmptyBorder(5, 10, 0, 10));
774: }
775:
776: /**
777: * @see javax.swing.JComponent#updateUI()
778: */
779: public void updateUI() {
780:
781: // setBackground(UIManager.getColor("TextField.background"));
782: }
783:
784: public void setHeader(Map keys) {
785: if (keys == null)
786: throw new IllegalArgumentException("keys == null");
787:
788: this .keys = keys;
789: }
790:
791: public void updateGUI() {
792: if (keys == null)
793: return;
794:
795: removeAll();
796:
797: // Color backgroundColor =
798: // UIManager.getColor("TextField.background");
799:
800: FormLayout layout = new FormLayout(
801: "right:pref, 3dlu, pref, 3dlu, fill:pref:grow",
802: "top:pref:grow");
803: DefaultFormBuilder builder = new DefaultFormBuilder(layout,
804: this );
805:
806: for (Iterator it = keys.keySet().iterator(); it.hasNext();) {
807: String key = (String) it.next();
808:
809: DynamicItemListPanel value = (DynamicItemListPanel) keys
810: .get(key);
811:
812: JLabel keyLabel = new JLabel("<html><b>" + key
813: + "</b></html>");
814:
815: JLabel separator = new JLabel(":");
816:
817: // JButton trailingItem = new JButton("more...");
818: //
819: // trailingItem = LinkButton.createLinkButton(trailingItem);
820: // trailingItem.addActionListener(new ActionListener() {
821: // public void actionPerformed(ActionEvent event) {
822: // showAddressListDialog();
823: // }
824: //
825: // });
826: //
827: // DynamicItemListPanel p = new DynamicItemListPanel(2,
828: // trailingItem, true);
829: // p.setOpaque(false);
830: //
831: // for (int i = 0; i < value.length; i++) {
832: //
833: // p.addItem(value[i]);
834: // }
835:
836: builder.append(keyLabel);
837: builder.append(separator);
838: builder.append(value);
839:
840: builder.appendRow(new RowSpec("top:pref:grow"));
841:
842: }
843:
844: validate();
845: }
846:
847: }
848:
849: private class ListItem {
850: String name;
851:
852: JComponent component;
853:
854: ImageIcon icon;
855: }
856:
857: private static final Color LINK_COLOR = Color.blue;
858:
859: private static final Color LABEL_COLOR = UIManager
860: .getColor("Label.foreground");
861:
862: private static final Border LINK_BORDER = BorderFactory
863: .createEmptyBorder(0, 0, 1, 0);
864:
865: public static JButton createLinkButton(JButton button) {
866: button.setBorder(LINK_BORDER);
867: button.setForeground(LINK_COLOR);
868: button
869: .setCursor(Cursor
870: .getPredefinedCursor(Cursor.HAND_CURSOR));
871: button.setFocusPainted(false);
872: button.setRequestFocusEnabled(false);
873: button.setText("<html><u>" + button.getText() + "</u></html>");
874: button.setContentAreaFilled(false);
875: // button.addMouseListener(new LinkMouseListener());
876: return button;
877: }
878:
879: public static JButton createLabelButton(JButton button) {
880: button.setBorder(LINK_BORDER);
881: button.setForeground(LABEL_COLOR);
882: button
883: .setCursor(Cursor
884: .getPredefinedCursor(Cursor.HAND_CURSOR));
885: button.setFocusPainted(false);
886: button.setRequestFocusEnabled(false);
887: button.setContentAreaFilled(false);
888: button.setText("<html><u>" + button.getText() + "</u></html>");
889: // button.addMouseListener(new LabelMouseListener());
890: return button;
891: }
892:
893: }
|