0001: /*
0002: * MessageBuffer.java
0003: *
0004: * Copyright (C) 2000-2003 Peter Graves
0005: * $Id: MessageBuffer.java,v 1.16 2003/05/26 17:08:14 piso Exp $
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License
0009: * as published by the Free Software Foundation; either version 2
0010: * of the License, or (at your option) any later version.
0011: *
0012: * This program is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0015: * GNU General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * along with this program; if not, write to the Free Software
0019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
0020: */
0021:
0022: package org.armedbear.j.mail;
0023:
0024: import java.awt.Image;
0025: import java.awt.Rectangle;
0026: import java.io.BufferedReader;
0027: import java.io.IOException;
0028: import java.io.StringReader;
0029: import java.util.ArrayList;
0030: import java.util.Iterator;
0031: import java.util.List;
0032: import java.util.Vector;
0033: import javax.swing.Icon;
0034: import javax.swing.SwingUtilities;
0035: import org.armedbear.j.Annotation;
0036: import org.armedbear.j.Buffer;
0037: import org.armedbear.j.BufferIterator;
0038: import org.armedbear.j.Debug;
0039: import org.armedbear.j.Display;
0040: import org.armedbear.j.Editor;
0041: import org.armedbear.j.EditorIterator;
0042: import org.armedbear.j.File;
0043: import org.armedbear.j.FastStringBuffer;
0044: import org.armedbear.j.FastStringReader;
0045: import org.armedbear.j.Headers;
0046: import org.armedbear.j.ImageLine;
0047: import org.armedbear.j.ImageLoader;
0048: import org.armedbear.j.Line;
0049: import org.armedbear.j.LineSequence;
0050: import org.armedbear.j.Log;
0051: import org.armedbear.j.MessageDialog;
0052: import org.armedbear.j.MessageHeaderLine;
0053: import org.armedbear.j.Position;
0054: import org.armedbear.j.ProgressNotifier;
0055: import org.armedbear.j.Property;
0056: import org.armedbear.j.SaveFileDialog;
0057: import org.armedbear.j.Sidebar;
0058: import org.armedbear.j.SystemBuffer;
0059: import org.armedbear.j.TextLine;
0060: import org.armedbear.j.Utilities;
0061: import org.armedbear.j.WebBuffer;
0062: import org.armedbear.j.WebFormatter;
0063: import org.armedbear.j.WebLine;
0064: import org.armedbear.j.WebLoader;
0065:
0066: public class MessageBuffer extends Buffer {
0067: protected Mailbox mailbox;
0068: protected MailboxEntry entry;
0069: protected Message message;
0070: protected boolean showFullHeaders;
0071: protected boolean showRawText;
0072: protected String allHeaders;
0073: protected String defaultHeaders;
0074: protected String rawBody;
0075: protected String body;
0076: protected String mimeBody;
0077: protected MimePart selectedPart;
0078: protected int headerLineCount;
0079:
0080: private boolean wrap = true;
0081:
0082: protected MessageBuffer() {
0083: super ();
0084: setInitialized(true);
0085: }
0086:
0087: public MessageBuffer(String rawText) {
0088: initializeUndo();
0089: type = TYPE_NORMAL;
0090: lineSeparator = "\n";
0091: mode = MessageMode.getMode();
0092: setFormatter(new MessageFormatter(this ));
0093: readOnly = true;
0094: message = new Message(rawText);
0095: parseMessage();
0096: title = message.getHeaderValue(Headers.SUBJECT);
0097: if (title == null)
0098: title = "";
0099: allHeaders = message.getAllHeaders();
0100: defaultHeaders = getDefaultHeaders(allHeaders);
0101: rawBody = message.getRawBody();
0102: setText();
0103: renumber();
0104: formatter.parseBuffer();
0105: setInitialized(true);
0106: }
0107:
0108: public final boolean isPrimary() {
0109: if (mailbox == null)
0110: return true;
0111: return mailbox.getPreviewBuffer() != this ;
0112: }
0113:
0114: public final boolean isSecondary() {
0115: if (mailbox == null)
0116: return false;
0117: return mailbox.getPreviewBuffer() == this ;
0118: }
0119:
0120: public final Buffer getPrimary() {
0121: if (mailbox != null && mailbox.getPreviewBuffer() == this )
0122: return mailbox;
0123: return null;
0124: }
0125:
0126: public final void promote() {
0127: if (mailbox != null && mailbox.getPreviewBuffer() == this )
0128: mailbox.setPreviewBuffer(null);
0129: }
0130:
0131: public int getHeaderLineCount() {
0132: return headerLineCount;
0133: }
0134:
0135: public int getDisplayHeight() {
0136: int height = 0;
0137: for (Line line = getFirstLine(); line != null; line = line
0138: .nextVisible())
0139: height += line.getHeight();
0140: return height;
0141: }
0142:
0143: public int getDisplayWidth() {
0144: int width = 0;
0145: for (Line line = getFirstLine(); line != null; line = line
0146: .nextVisible()) {
0147: int lineWidth = line.getWidth();
0148: if (lineWidth > width)
0149: width = lineWidth;
0150: }
0151: return Display.getGutterWidth(this ) + width
0152: + Display.getCharWidth();
0153: }
0154:
0155: // Returns cumulative height to top of target line.
0156: public int getY(Line target) {
0157: int y = 0;
0158: for (Line line = getFirstLine(); line != null && line != target; line = line
0159: .nextVisible())
0160: y += line.getHeight();
0161: return y;
0162: }
0163:
0164: protected void setEntry(MailboxEntry entry) {
0165: Debug.assertTrue(entry != null);
0166: reset();
0167: this .entry = entry;
0168: FastStringBuffer sb = new FastStringBuffer();
0169: sb.append(mailbox.getLineNumberForEntry(entry) + 1);
0170: sb.append('/');
0171: sb.append(mailbox.getLineCount());
0172: sb.append(' ');
0173: sb.append(entry.formatSubject());
0174: title = sb.toString();
0175: Sidebar.setUpdateFlagInAllFrames(SIDEBAR_REPAINT_BUFFER_LIST);
0176: }
0177:
0178: protected void reset() {
0179: try {
0180: lockWrite();
0181: } catch (InterruptedException e) {
0182: Log.debug(e);
0183: return;
0184: }
0185: try {
0186: empty();
0187: message = null;
0188: mimeBody = null;
0189: selectedPart = null;
0190: } finally {
0191: unlockWrite();
0192: }
0193: }
0194:
0195: public int load() {
0196: Debug.assertTrue(false); // Shouldn't be called.
0197: return LOAD_COMPLETED;
0198: }
0199:
0200: protected void loadMessage(ProgressNotifier progressNotifier) {
0201: Debug.assertTrue(entry != null);
0202: mailbox.setBusy(true);
0203: message = mailbox.getMessage(entry, progressNotifier);
0204: mailbox.setBusy(false);
0205: if (message == null)
0206: return;
0207: parseMessage();
0208: allHeaders = message.getAllHeaders();
0209: defaultHeaders = getDefaultHeaders(allHeaders);
0210: rawBody = message.getRawBody();
0211: wrap = true;
0212: setText();
0213: formatter.parseBuffer();
0214: int flags = entry.getFlags();
0215: if ((flags & MailboxEntry.SEEN) == 0) {
0216: flags |= MailboxEntry.SEEN;
0217: flags &= ~MailboxEntry.RECENT;
0218: entry.setFlags(flags);
0219: mailbox.setDirty(true);
0220: mailbox.updateEntry(entry);
0221: mailbox.countMessages();
0222: }
0223: Runnable r = new Runnable() {
0224: public void run() {
0225: setBusy(false);
0226: for (EditorIterator it = new EditorIterator(); it
0227: .hasNext();) {
0228: Editor ed = it.nextEditor();
0229: if (ed.getBuffer() == MessageBuffer.this ) {
0230: ed.setDot(getFirstLine(), 0);
0231: ed.setUpdateFlag(REFRAME);
0232: ed.moveCaretToDotCol();
0233: ed.setTopLine(getFirstLine());
0234: ed.setMark(null);
0235: ed.setUpdateFlag(REPAINT);
0236: ed.updateDisplay();
0237: } else if (ed.getBuffer() == mailbox)
0238: ed.updateDisplay();
0239: }
0240: Sidebar.repaintBufferListInAllFrames();
0241: }
0242: };
0243: SwingUtilities.invokeLater(r);
0244: }
0245:
0246: public final MailboxEntry getMailboxEntry() {
0247: return entry;
0248: }
0249:
0250: public final Message getMessage() {
0251: return message;
0252: }
0253:
0254: public final int getMessageNumber() {
0255: Debug.assertTrue(entry != null);
0256: return entry.getMessageNumber();
0257: }
0258:
0259: public final Mailbox getMailbox() {
0260: return mailbox;
0261: }
0262:
0263: public void nextMessage() {
0264: if (entry == null)
0265: return;
0266: Editor editor = Editor.currentEditor();
0267: MailboxEntry nextEntry = mailbox.getNextUndeleted(entry);
0268: if (nextEntry != null) {
0269: mailbox.setDotEntry(nextEntry);
0270: load(editor, nextEntry);
0271: } else {
0272: MailCommands.messageIndex(editor);
0273: editor.status("Last undeleted message");
0274: }
0275: }
0276:
0277: public void previousMessage() {
0278: if (entry == null)
0279: return;
0280: Editor editor = Editor.currentEditor();
0281: MailboxEntry previousEntry = mailbox
0282: .getPreviousUndeleted(entry);
0283: if (previousEntry != null) {
0284: mailbox.setDotEntry(previousEntry);
0285: load(editor, previousEntry);
0286: } else {
0287: MailCommands.messageIndex(editor);
0288: editor.status("First undeleted message");
0289: }
0290: }
0291:
0292: public void nextInThread() {
0293: if (entry == null)
0294: return;
0295: Editor editor = Editor.currentEditor();
0296: MailboxEntry nextEntry = mailbox.getNextInThread(entry);
0297: if (nextEntry != null) {
0298: mailbox.setDotEntry(nextEntry);
0299: load(editor, nextEntry);
0300: } else {
0301: MailCommands.messageIndex(editor);
0302: editor.status("Last message in thread");
0303: }
0304: }
0305:
0306: public void previousInThread() {
0307: if (entry == null)
0308: return;
0309: Editor editor = Editor.currentEditor();
0310: MailboxEntry previousEntry = mailbox.getPreviousInThread(entry);
0311: if (previousEntry != null) {
0312: mailbox.setDotEntry(previousEntry);
0313: load(editor, previousEntry);
0314: } else {
0315: MailCommands.messageIndex(editor);
0316: editor.status("First message in thread");
0317: }
0318: }
0319:
0320: public void parentMessage() {
0321: final Editor editor = Editor.currentEditor();
0322: String inReplyTo = message.getHeaderValue(Headers.IN_REPLY_TO);
0323: Log.debug("inReplyTo = |" + inReplyTo + "|");
0324: if (inReplyTo != null) {
0325: String msgId = extractMessageId(inReplyTo);
0326: if (msgId != null) {
0327: Log.debug("msgId = |" + msgId + "|");
0328: MailboxEntry parentEntry = mailbox
0329: .getEntryForMessageId(msgId);
0330: if (parentEntry != null) {
0331: load(editor, parentEntry);
0332: return;
0333: }
0334: }
0335: }
0336: String references = message.getHeaderValue(Headers.REFERENCES);
0337: Log.debug("references = |" + references + "|");
0338: if (references != null) {
0339: List list = extractAllMessageIds(references);
0340: if (list != null) {
0341: for (int i = 0; i < list.size(); i++) {
0342: String msgId = (String) list.get(i);
0343: if (msgId != null) {
0344: Log.debug("msgId = |" + msgId + "|");
0345: MailboxEntry parentEntry = mailbox
0346: .getEntryForMessageId(msgId);
0347: if (parentEntry != null) {
0348: load(editor, parentEntry);
0349: return;
0350: }
0351: }
0352: }
0353: }
0354: }
0355: editor.status("No parent");
0356: }
0357:
0358: private static String extractMessageId(String s) {
0359: if (s == null)
0360: return null;
0361: int begin = s.indexOf('<');
0362: if (begin < 0)
0363: return null;
0364: int end = s.indexOf('>');
0365: if (end < 0)
0366: return null;
0367: return s.substring(begin, end + 1);
0368: }
0369:
0370: private static List extractAllMessageIds(String s) {
0371: if (s == null)
0372: return null;
0373: ArrayList list = null;
0374: while (s.length() > 2) {
0375: int begin = s.indexOf('<');
0376: if (begin < 0)
0377: break;
0378: int end = s.indexOf('>');
0379: if (end < 0)
0380: break;
0381: String msgId = s.substring(begin, end + 1);
0382: if (list == null)
0383: list = new ArrayList();
0384: Log.debug("adding |" + msgId + "|");
0385: list.add(msgId);
0386: s = s.substring(end + 1);
0387: }
0388: return list;
0389: }
0390:
0391: private void load(Editor editor, MailboxEntry nextEntry) {
0392: if (mailbox.lock()) {
0393: setBusy(true);
0394: setEntry(nextEntry);
0395: Runnable r = new Runnable() {
0396: public void run() {
0397: try {
0398: loadMessage(null);
0399: } finally {
0400: mailbox.unlock();
0401: }
0402: }
0403: };
0404: new Thread(r).start();
0405: } else
0406: editor.status("Mailbox is locked");
0407: }
0408:
0409: // Might return null.
0410: public String quoteBody(int wrapCol) {
0411: String toBeQuoted = mimeBody != null ? mimeBody : rawBody;
0412: if (toBeQuoted == null) {
0413: Log.debug("quoteBody toBeQuoted is null");
0414: return null;
0415: }
0416: if (Utilities.isWhitespace(toBeQuoted)) {
0417: Log.debug("quoteBody toBeQuoted is whitespace");
0418: return null;
0419: }
0420: String wrapped = wrap(toBeQuoted, wrapCol - 2, 8);
0421: FastStringReader reader = new FastStringReader(wrapped);
0422: FastStringBuffer sb = new FastStringBuffer(4096);
0423: String s;
0424: while ((s = reader.readLine()) != null) {
0425: sb.append('>');
0426: if (s.length() > 0) {
0427: sb.append(' ');
0428: sb.append(s);
0429: }
0430: sb.append('\n');
0431: }
0432: return sb.toString();
0433: }
0434:
0435: private static final String wrap(String s, int wrapCol, int tabWidth) {
0436: FastStringReader reader = new FastStringReader(s);
0437: FastStringBuffer sb = new FastStringBuffer(4096);
0438: java.io.StringWriter writer = new java.io.StringWriter();
0439: String line;
0440: while ((line = reader.readLine()) != null) {
0441: if (line.length() == 0) {
0442: if (sb.length() > 0) {
0443: writer.write(Utilities.wrap(sb.toString(), wrapCol,
0444: tabWidth));
0445: sb.setLength(0);
0446: writer.write('\n');
0447: }
0448: writer.write('\n');
0449: } else {
0450: // Line is not empty.
0451: if (sb.length() == 0
0452: && Utilities.getDetabbedLength(line, tabWidth) <= 78) {
0453: // Line is a reasonable length.
0454: writer.write(line);
0455: writer.write('\n');
0456: continue;
0457: }
0458: char c = line.charAt(0);
0459: if (c == ' ' || c == '\t' || c == '>') {
0460: if (sb.length() > 0) {
0461: writer.write(Utilities.wrap(sb.toString(),
0462: wrapCol, tabWidth));
0463: sb.setLength(0);
0464: writer.write('\n');
0465: }
0466: writer.write(line);
0467: writer.write('\n');
0468: } else {
0469: if (sb.length() > 0) {
0470: // Make sure last char is a space or tab.
0471: c = sb.charAt(sb.length() - 1);
0472: if (c != ' ' && c != '\t')
0473: sb.append(' ');
0474: }
0475: sb.append(line);
0476: }
0477: }
0478: }
0479: if (sb.length() > 0) {
0480: writer.write(Utilities.wrap(sb.toString(), wrapCol,
0481: tabWidth));
0482: sb.append('\n');
0483: }
0484: return writer.toString();
0485: }
0486:
0487: public void deleteMessage() {
0488: }
0489:
0490: public void flagMessage() {
0491: }
0492:
0493: public void moveMessage() {
0494: }
0495:
0496: public void bounce() {
0497: Debug.assertTrue(SwingUtilities.isEventDispatchThread());
0498: final Editor editor = Editor.currentEditor();
0499: final MailAddress[] to = MailCommands.bounceGetTo(editor, 1);
0500: if (to == null)
0501: return;
0502: Runnable bounceRunnable = new Runnable() {
0503: public void run() {
0504: Log.debug("MessageBuffer bounceRunnable.run()");
0505: boolean succeeded = false;
0506: try {
0507: succeeded = Mail.bounceMessage(message, to);
0508: } finally {
0509: setBusy(false);
0510: editor.updateDisplayLater();
0511: }
0512: if (succeeded) {
0513: Runnable successRunnable = new Runnable() {
0514: public void run() {
0515: editor.status("Message bounced");
0516: }
0517: };
0518: SwingUtilities.invokeLater(successRunnable);
0519: } else {
0520: Runnable errorRunnable = new Runnable() {
0521: public void run() {
0522: MessageDialog.showMessageDialog(editor,
0523: "Failed", "Bounce Message");
0524: }
0525: };
0526: SwingUtilities.invokeLater(errorRunnable);
0527: }
0528: }
0529: };
0530: setBusy(true);
0531: new Thread(bounceRunnable).start();
0532: }
0533:
0534: protected String getBeautifiedHeaders() {
0535: if (message == null)
0536: return "";
0537: Headers headers = Headers.parse(message.getRawHeaders());
0538: ArrayList names = new ArrayList(); // Header names.
0539: names.add("From");
0540: names.add("To");
0541: names.add("Cc");
0542: names.add("Subject");
0543: names.add("Date");
0544: int width = 0;
0545: for (Iterator it = names.iterator(); it.hasNext();) {
0546: int w = ((String) it.next()).length();
0547: if (w > width)
0548: width = w;
0549: }
0550: FastStringBuffer sb = new FastStringBuffer();
0551: for (Iterator it = names.iterator(); it.hasNext();) {
0552: String name = (String) it.next();
0553: String value = headers.getValue(name);
0554: if (value != null) {
0555: if (name == "From" || name == "To" || name == "Cc") {
0556: MailAddress[] array = MailAddress
0557: .parseAddresses(RFC2047.decode(value));
0558: ArrayList list = new ArrayList();
0559: for (int i = 0; i < array.length; i++)
0560: list.add(array[i]);
0561: String prefix = Utilities.rightJustify(name, width)
0562: .concat(": ");
0563: sb.append(MailUtilities.constructAddressHeader(
0564: prefix, list, prefix.length()));
0565: } else {
0566: // Subject, date.
0567: sb.append(Utilities.rightJustify(name, width));
0568: sb.append(": ");
0569: sb.append(RFC2047.decode(value));
0570: }
0571: sb.append('\n');
0572: }
0573: }
0574: return sb.toString();
0575: }
0576:
0577: protected String getDefaultHeaders(String s) {
0578: FastStringBuffer sb = new FastStringBuffer();
0579: if (s.length() > 0) {
0580: BufferedReader reader = new BufferedReader(
0581: new StringReader(s));
0582: boolean maybeContinuation = false;
0583: try {
0584: while (true) {
0585: String text = reader.readLine();
0586: if (text == null || text.length() == 0)
0587: break;
0588: if (maybeContinuation
0589: && Character.isWhitespace(text.charAt(0))) {
0590: sb.append(text);
0591: sb.append('\n');
0592: } else if (isDefaultHeader(text)) {
0593: sb.append(text);
0594: sb.append('\n');
0595: maybeContinuation = true;
0596: } else
0597: maybeContinuation = false;
0598: }
0599: } catch (IOException e) {
0600: Log.error(e);
0601: }
0602: }
0603: return sb.toString();
0604: }
0605:
0606: private boolean isDefaultHeader(String s) {
0607: s = s.toLowerCase();
0608: if (s.startsWith("from:") || s.startsWith("to:")
0609: || s.startsWith("cc:") || s.startsWith("subject:")
0610: || s.startsWith("date:"))
0611: return true;
0612: return false;
0613: }
0614:
0615: public void viewAttachment() {
0616: final MimePart part = getAttachmentAtDot();
0617: if (part == null)
0618: return;
0619: final Editor editor = Editor.currentEditor();
0620: editor.setWaitCursor();
0621: final String contentType = part.getContentType();
0622: if (contentType != null && contentType.equals("message/rfc822")) {
0623: final String rawText = part.getRawBody();
0624: Buffer buf = null;
0625: // See if we already have this attachment open in a buffer.
0626: for (BufferIterator it = new BufferIterator(); it.hasNext();) {
0627: Buffer b = it.nextBuffer();
0628: if (b instanceof MessageBuffer) {
0629: MessageBuffer mb = (MessageBuffer) b;
0630: Message m = mb.getMessage();
0631: if (m.getRawText().equals(rawText)) {
0632: buf = b;
0633: break;
0634: }
0635: }
0636: }
0637: if (buf == null) // Attachment is not in an existing buffer.
0638: buf = new MessageBuffer(rawText);
0639: editor.makeNext(buf);
0640: editor.switchToBuffer(buf);
0641: return;
0642: }
0643: File cache = part.cacheDecoded();
0644: if (cache == null || !cache.isFile()) {
0645: MessageDialog.showMessageDialog(
0646: "Unable to decode attachment", "View Attachment");
0647: return;
0648: }
0649: if (contentType != null && contentType.equals("text/html")) {
0650: WebBuffer.browse(editor, cache, null);
0651: return;
0652: }
0653: Buffer buf = editor.openFile(cache);
0654: if (buf != null) {
0655: editor.makeNext(buf);
0656: editor.switchToBuffer(buf);
0657: }
0658: }
0659:
0660: public void saveAttachment() {
0661: MimePart part = getAttachmentAtDot();
0662: if (part == null)
0663: return;
0664: final Editor editor = Editor.currentEditor();
0665: SaveFileDialog d = new SaveFileDialog(editor,
0666: "Save Attachment", "File:");
0667: File suggested = File.getInstance(editor.getCurrentDirectory(),
0668: part.getAttachmentFileName());
0669: if (suggested != null)
0670: d.setInitialText(suggested.canonicalPath());
0671: editor.centerDialog(d);
0672: d.show();
0673: File saveAs = d.getDestination();
0674: if (saveAs == null)
0675: return;
0676: editor.repaintNow();
0677: editor.setWaitCursor();
0678: boolean success = part.saveDecoded(saveAs);
0679: editor.setDefaultCursor();
0680: if (!success)
0681: MessageDialog.showMessageDialog(
0682: "Unable to save attachment", "Save Attachment");
0683: }
0684:
0685: private MimePart getAttachmentAtDot() {
0686: Position dot = Editor.currentEditor().getDot();
0687: if (dot != null) {
0688: Annotation annotation = dot.getLine().getAnnotation();
0689: if (annotation != null) {
0690: Object obj = annotation.getUserObject();
0691: if (obj instanceof MimePart)
0692: return (MimePart) obj;
0693: }
0694: }
0695: return null;
0696: }
0697:
0698: public void toggleRaw() {
0699: showRawText = !showRawText;
0700: if (mailbox != null)
0701: mailbox.showRawText = showRawText;
0702: reloadInternal();
0703: FastStringBuffer sb = new FastStringBuffer("Raw mode ");
0704: sb.append(showRawText ? "on" : "off");
0705: Editor.currentEditor().status(sb.toString());
0706: }
0707:
0708: public void toggleHeaders() {
0709: if (mailbox != null)
0710: mailbox.showFullHeaders = showFullHeaders = !showFullHeaders;
0711: reloadInternal();
0712: }
0713:
0714: public void toggleWrap() {
0715: wrap = !wrap;
0716: reloadInternal();
0717: FastStringBuffer sb = new FastStringBuffer("Wrap ");
0718: sb.append(wrap ? "on" : "off");
0719: Editor.currentEditor().status(sb.toString());
0720: }
0721:
0722: private void reloadInternal() {
0723: setText();
0724: formatter.parseBuffer();
0725: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
0726: Editor ed = it.nextEditor();
0727: if (ed.getBuffer() == this ) {
0728: ed.setDot(getFirstLine(), 0);
0729: ed.moveCaretToDotCol();
0730: ed.setMark(null);
0731: ed.setTopLine(getFirstLine());
0732: ed.repaintDisplay();
0733: }
0734: }
0735: }
0736:
0737: protected void parseMessage() {
0738: if (mimeBody != null)
0739: Debug.bug();
0740: message.parse();
0741: List parts = message.getParts();
0742: if (parts == null || parts.size() == 0) {
0743: // Not multipart.
0744: mimeBody = message.getDecodedBody();
0745: return;
0746: }
0747: for (Iterator it = parts.iterator(); it.hasNext();) {
0748: MimePart part = (MimePart) it.next();
0749: final String contentType = part.getContentType();
0750: if (contentType == null || contentType.equals("text/plain")) {
0751: selectedPart = part;
0752: mimeBody = part.getDecodedBody();
0753: return;
0754: }
0755: }
0756: for (Iterator it = parts.iterator(); it.hasNext();) {
0757: MimePart part = (MimePart) it.next();
0758: final String contentType = part.getContentType();
0759: if (contentType == null || contentType.equals("text/html")) {
0760: selectedPart = part;
0761: mimeBody = part.getDecodedBody();
0762: return;
0763: }
0764: }
0765: mimeBody = "";
0766: }
0767:
0768: private List getAttachmentLines() {
0769: List parts = message.getParts();
0770: if (parts == null || parts.size() == 0) {
0771: // Not multipart.
0772: return null;
0773: }
0774: int shown = -1;
0775: ArrayList list = new ArrayList();
0776: list.add(new MessageHeaderLine("Parts/Attachments:"));
0777: for (int i = 0; i < parts.size(); i++) {
0778: FastStringBuffer sb = new FastStringBuffer();
0779: sb.append(" ");
0780: sb.append(i + 1);
0781: MimePart part = (MimePart) parts.get(i);
0782: final String contentType = part.getContentType();
0783: if (part == selectedPart) {
0784: sb.append(" Shown");
0785: } else {
0786: String filename = part.getAttachmentFileName();
0787: if (filename != null && filename.length() > 0) {
0788: if (part.isAttachment())
0789: sb.append(" Attachment:");
0790: else if (part.isInline())
0791: sb.append(" Inline:");
0792: sb.append(' ');
0793: sb.append(filename);
0794: } else if (part.isAttachment()) {
0795: sb.append(" Attachment");
0796: } else if (part.isInline()) {
0797: sb.append(" Inline");
0798: }
0799: }
0800: sb.append(" (");
0801: if (contentType != null) {
0802: sb.append(contentType);
0803: sb.append(", ");
0804: }
0805: final String encoding = part.getTransferEncoding();
0806: if (encoding != null) {
0807: sb.append(encoding);
0808: sb.append(", ");
0809: }
0810: sb.append(part.getSize());
0811: sb.append(" bytes)");
0812: Line line = new MessageHeaderLine(sb.toString());
0813: line.setAnnotation(new Annotation(part));
0814: list.add(line);
0815: }
0816: return list;
0817: }
0818:
0819: protected void setText() {
0820: try {
0821: lockWrite();
0822: } catch (InterruptedException e) {
0823: Log.debug(e);
0824: return;
0825: }
0826: try {
0827: _setText();
0828: setLoaded(true);
0829: } finally {
0830: unlockWrite();
0831: }
0832: }
0833:
0834: private void _setText() {
0835: empty();
0836: if (showRawText) {
0837: body = rawBody;
0838: setText(message.getRawText());
0839: headerLineCount = Utilities.countLines(allHeaders);
0840: if (!(formatter instanceof MessageFormatter))
0841: setFormatter(new MessageFormatter(this ));
0842: return;
0843: }
0844: // Not raw mode.
0845: final String headers;
0846: if (showFullHeaders)
0847: headers = allHeaders;
0848: else if (Editor.preferences().getBooleanProperty(
0849: Property.BEAUTIFY_HEADERS))
0850: headers = getBeautifiedHeaders();
0851: else
0852: headers = defaultHeaders;
0853: final String contentType = message.getContentType();
0854: if (contentType != null) {
0855: if (contentType.startsWith("image/")) {
0856: File cache = message.cacheDecoded();
0857: if (cache != null && cache.isFile()) {
0858: ImageLoader loader = new ImageLoader(cache);
0859: Image image = loader.loadImage();
0860: appendHeaderLines(headers);
0861: appendHeaderLine("");
0862: if (image != null) {
0863: final int lineHeight = new TextLine("")
0864: .getHeight();
0865: final int imageHeight = image.getHeight(null);
0866: final int imageWidth = image.getWidth(null);
0867: int y = 0;
0868: while (y < imageHeight) {
0869: Rectangle r = new Rectangle(0, y,
0870: imageWidth, Math.min(lineHeight,
0871: imageHeight - y));
0872: appendLine(new ImageLine(image, r));
0873: y += lineHeight;
0874: }
0875: }
0876: renumber();
0877: } else {
0878: appendHeaderLines(headers);
0879: appendHeaderLine("");
0880: append(body);
0881: }
0882: renumber();
0883: if (!(formatter instanceof MessageFormatter))
0884: setFormatter(new MessageFormatter(this ));
0885: return;
0886: }
0887: if (contentType.equals("text/html")) {
0888: appendHeaderLines(headers);
0889: appendHeaderLine("");
0890: StringReader reader = new StringReader(message
0891: .getDecodedBody());
0892: WebLoader loader = new WebLoader(reader);
0893: LineSequence lines = loader.load();
0894: Line lastLine = getLastLine();
0895: lastLine.setNext(lines.getFirstLine());
0896: lines.getFirstLine().setPrevious(lastLine);
0897: renumber();
0898: if (!(formatter instanceof WebFormatter))
0899: setFormatter(new WebFormatter(this ));
0900: return;
0901: }
0902: }
0903: appendHeaderLines(headers);
0904: headerLineCount = Utilities.countLines(headers);
0905: List attachmentLines = getAttachmentLines();
0906: if (attachmentLines != null) {
0907: appendHeaderLine("");
0908: ++headerLineCount;
0909: Iterator iter = attachmentLines.iterator();
0910: while (iter.hasNext()) {
0911: Line line = (Line) iter.next();
0912: if (!(line instanceof MessageHeaderLine))
0913: Debug.bug();
0914: appendLine(line);
0915: ++headerLineCount;
0916: }
0917: }
0918: if (mimeBody != null)
0919: body = mimeBody;
0920: else
0921: body = rawBody;
0922: if (selectedPart != null
0923: && "text/html".equals(selectedPart.getContentType())) {
0924: appendLine("");
0925: StringReader reader = new StringReader(mimeBody);
0926: WebLoader loader = new WebLoader(reader);
0927: LineSequence lines = loader.load();
0928: Line lastLine = getLastLine();
0929: lastLine.setNext(lines.getFirstLine());
0930: lines.getFirstLine().setPrevious(lastLine);
0931: if (!(formatter instanceof WebFormatter))
0932: setFormatter(new WebFormatter(this ));
0933: } else {
0934: if (wrap)
0935: body = wrapBody(body);
0936: appendLine("");
0937: append(body);
0938: if (!(formatter instanceof MessageFormatter))
0939: setFormatter(new MessageFormatter(this ));
0940: }
0941: // Don't try to display images inline if the message is too big.
0942: if (message.getSize() < 1024 * 1024) {
0943: List parts = message.getParts();
0944: if (parts != null) {
0945: for (int i = 0; i < parts.size(); i++) {
0946: MimePart part = (MimePart) parts.get(i);
0947: String partContentType = part.getContentType();
0948: if (partContentType == null)
0949: continue;
0950: File cache = null;
0951: if (partContentType
0952: .equals("application/octet-stream")) {
0953: String filename = part.getAttachmentFileName();
0954: if (filename == null)
0955: continue;
0956: filename = filename.toLowerCase();
0957: if (filename.endsWith(".jpg")
0958: || filename.endsWith(".jpeg")
0959: || filename.endsWith(".png")
0960: || filename.endsWith(".gif"))
0961: cache = part.cacheDecoded();
0962: } else if (partContentType.startsWith("image/"))
0963: cache = part.cacheDecoded();
0964: if (cache != null && cache.isFile()) {
0965: ImageLoader loader = new ImageLoader(cache);
0966: Image image = loader.loadImage();
0967: if (image != null) {
0968: if (getLastLine() instanceof ImageLine)
0969: appendLine("");
0970: final int lineHeight = new TextLine("")
0971: .getHeight();
0972: final int imageHeight = image
0973: .getHeight(null);
0974: final int imageWidth = image.getWidth(null);
0975: int y = 0;
0976: while (y < imageHeight) {
0977: Rectangle r = new Rectangle(0, y,
0978: imageWidth, Math.min(
0979: lineHeight, imageHeight
0980: - y));
0981: appendLine(new ImageLine(image, r));
0982: y += lineHeight;
0983: }
0984: }
0985: }
0986: }
0987: }
0988: }
0989: renumber();
0990: }
0991:
0992: private static String wrapBody(String body) {
0993: final int wrapCol = Editor.currentEditor().getDisplay()
0994: .getColumns();
0995: final int tabWidth = 8;
0996: final int IN_DIFF = 1;
0997: SystemBuffer buf = new SystemBuffer();
0998: FastStringReader reader = new FastStringReader(body);
0999: String s;
1000: while ((s = reader.readLine()) != null)
1001: buf.appendLine(s);
1002: boolean containsDiff = false;
1003: boolean inDiff = false;
1004: for (Line line = buf.getFirstLine(); line != null; line = line
1005: .next()) {
1006: if (inDiff) {
1007: if (MessageFormatter.isDiffContinuation(line))
1008: line.setFlags(IN_DIFF);
1009: else {
1010: inDiff = false;
1011: line.setFlags(0);
1012: }
1013: continue;
1014: }
1015: // Not in diff.
1016: if (MessageFormatter.isDiffStart(line)) {
1017: inDiff = true;
1018: line.setFlags(IN_DIFF);
1019: containsDiff = true;
1020: continue;
1021: }
1022: // Not start of diff.
1023: line.setFlags(0);
1024: }
1025: if (!containsDiff)
1026: return Utilities.wrap(body, wrapCol, tabWidth);
1027: // Buffer contains diff.
1028: FastStringBuffer out = new FastStringBuffer();
1029: FastStringBuffer in = new FastStringBuffer();
1030: for (Line line = buf.getFirstLine(); line != null; line = line
1031: .next()) {
1032: if (line.flags() == IN_DIFF) {
1033: if (in.length() > 0) {
1034: out.append(Utilities.wrap(in.toString(), wrapCol,
1035: tabWidth));
1036: in.setLength(0);
1037: }
1038: out.append(line.getText());
1039: out.append('\n');
1040: } else {
1041: // Not in diff.
1042: in.append(line.getText());
1043: in.append('\n');
1044: }
1045: }
1046: if (in.length() > 0)
1047: out
1048: .append(Utilities.wrap(in.toString(), wrapCol,
1049: tabWidth));
1050: return out.toString();
1051: }
1052:
1053: protected void appendHeaderLines(String headers) {
1054: if (headers != null) {
1055: FastStringReader reader = new FastStringReader(headers);
1056: String s;
1057: while ((s = reader.readLine()) != null)
1058: appendHeaderLine(s);
1059: }
1060: }
1061:
1062: protected void appendHeaderLine(String s) {
1063: appendLine(new MessageHeaderLine(s));
1064: }
1065:
1066: public String toString() {
1067: return title;
1068: }
1069:
1070: // For the buffer list.
1071: public Icon getIcon() {
1072: return Utilities.getIconFromFile("message.png");
1073: }
1074:
1075: public String getFileNameForDisplay() {
1076: return "";
1077: }
1078:
1079: private static final String SPLIT_KEY = "MessageBuffer.split";
1080:
1081: public void saveWindowState(Editor editor) {
1082: if (editor.getBuffer() != this )
1083: return;
1084: Editor otherEditor = editor.getOtherEditor();
1085: if (otherEditor != null) {
1086: float height = (float) editor.getHeight();
1087: float split = height
1088: / (editor.getFrame().getEditorPane().getHeight());
1089: Editor.getSessionProperties().setFloatProperty(SPLIT_KEY,
1090: split);
1091: }
1092: }
1093:
1094: public float getSplit() {
1095: return Editor.getSessionProperties().getFloatProperty(
1096: SPLIT_KEY, 0.5F);
1097: }
1098:
1099: public void windowClosing() {
1100: Editor editor = Editor.currentEditor();
1101: if (editor.getBuffer() == this )
1102: saveWindowState(editor);
1103: else {
1104: Editor otherEditor = editor.getOtherEditor();
1105: if (otherEditor != null && otherEditor.getBuffer() == this )
1106: saveWindowState(otherEditor);
1107: }
1108: }
1109:
1110: public void dispose() {
1111: if (mailbox != null && mailbox.getPreviewBuffer() == this )
1112: mailbox.setPreviewBuffer(null);
1113: flushImages();
1114: }
1115:
1116: public void empty() {
1117: flushImages();
1118: super .empty();
1119: }
1120:
1121: private void flushImages() {
1122: for (Line line = getFirstLine(); line != null; line = line
1123: .next()) {
1124: if (line instanceof ImageLine)
1125: ((ImageLine) line).flushImage();
1126: }
1127: }
1128: }
|