/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is the Grendel mail/news client. * * The Initial Developer of the Original Code is Netscape Communications * Corporation. Portions created by Netscape are Copyright (C) 1997 * Netscape Communications Corporation. All Rights Reserved. * * Created: Will Scullin , 3 Sep 1997. */ package grendel.ui; import java.awt.BorderLayout; import java.awt.Component; import java.awt.event.ActionEvent; import java.io.FileOutputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.PrintStream; import java.io.StringBufferInputStream; import java.net.URL; import java.net.MalformedURLException; import java.util.Locale; import java.util.ResourceBundle; import com.sun.java.swing.Action; import com.sun.java.swing.AbstractAction; import com.sun.java.swing.BorderFactory; import com.sun.java.swing.JPanel; import com.sun.java.swing.JScrollPane; import com.sun.java.swing.JTextArea; import com.sun.java.swing.UIManager; import com.sun.java.swing.event.ChangeEvent; import com.sun.java.swing.event.EventListenerList; import com.sun.java.swing.text.Document; import com.sun.java.swing.text.BadLocationException; import netscape.orion.toolbars.NSToolbar; import javax.mail.Message; import javax.mail.MessagingException; import grendel.mime.IMimeParser; import grendel.mime.IMimeOperator; import grendel.mime.parser.MimeParserFactory; import grendel.mime.html.MimeHTMLOperatorFactory; import grendel.storage.MessageExtra; import grendel.storage.MessageExtraFactory; import grendel.widgets.StatusEvent; /* #### import calypso.net.URLSource; */ import calypso.util.ByteBuf; import calypso.util.Preferences; import calypso.util.PreferencesFactory; import netscape.orion.uimanager.IUICmd; import lego.document.HTMLDocument; import mg.edge.embed.jfc.URLComponent; import mg.edge.embed.jfc.URLComponentFactory; import mg.magellan.document.IDocument; public class MessagePanel extends GeneralPanel { JTextArea fTextArea; URLComponent fViewer; Thread fMessageLoadThread; Message fMessage; MessageHeader fHeader; MessagePanel fPanel; boolean useMagellan; boolean makeRealHTML; EventListenerList fListeners = new EventListenerList(); IUICmd fActions[] = {ActionFactory.GetNewMailAction(), ActionFactory.GetComposeMessageAction()}; /** * Constructs a new message panel. */ public MessagePanel() { fPanel = this; Preferences prefs = PreferencesFactory.Get(); useMagellan = prefs.getBoolean("usemagellan", true); makeRealHTML = prefs.getBoolean("makerealhtml", true); if (useMagellan) { fViewer = (URLComponent) URLComponentFactory.NewURLComponent(null); Component viewComponent = fViewer.getComponent(); add(viewComponent); } else { fTextArea = new JTextArea(); fTextArea.setEditable(false); fTextArea.setBorder(BorderFactory.createLoweredBevelBorder()); add(new JScrollPane(fTextArea)); } fHeader = new SimpleMessageHeader(); add(BorderLayout.NORTH, fHeader); } /** * Disposes of message panel resources. Currently kills off * message loading thread to avoid having magellan try and * access an invalid peer. */ public void dispose() { synchronized (fPanel) { if (fMessageLoadThread != null) { System.out.println("Killing msg thread"); fMessageLoadThread.stop(); fMessageLoadThread = null; } } } /** * Returns the toolbar associated with this panel. */ public NSToolbar getToolBar() { return buildToolBar("messageToolBar", fActions); } /** * Sets message displayed to the given message. If the argument is * null, clears message area */ public synchronized void setMessage(Message aMessage) { if (fMessageLoadThread != null) { System.out.println("Killing msg thread"); fMessageLoadThread.stop(); notifyStatus(fLabels.getString("messageLoadedStatus")); } fMessageLoadThread = new Thread(new LoadMessageThread(aMessage), "msgLoad"); System.out.println("Starting msg thread"); fMessageLoadThread.start(); } /** * Returns the current message */ public Message getMessage() { return fMessage; } public void addMessagePanelListener(MessagePanelListener l) { fListeners.add(MessagePanelListener.class, l); } public void removeMessagePanelListener(MessagePanelListener l) { fListeners.remove(MessagePanelListener.class, l); } protected void notifyLoading() { Object[] listeners = fListeners.getListenerList(); ChangeEvent changeEvent = null; for (int i = 0; i < listeners.length - 1; i += 2) { if (listeners[i] == MessagePanelListener.class) { // Lazily create the event: if (changeEvent == null) changeEvent = new ChangeEvent(this); ((MessagePanelListener)listeners[i+1]).loadingMessage(changeEvent); } } } protected void notifyLoaded() { Object[] listeners = fListeners.getListenerList(); ChangeEvent changeEvent = null; for (int i = 0; i < listeners.length - 1; i += 2) { if (listeners[i] == MessagePanelListener.class) { // Lazily create the event: if (changeEvent == null) changeEvent = new ChangeEvent(this); ((MessagePanelListener)listeners[i+1]).messageLoaded(changeEvent); } } } protected void notifyStatus(String aStatus) { Object[] listeners = fListeners.getListenerList(); StatusEvent event = null; for (int i = 0; i < listeners.length - 1; i += 2) { if (listeners[i] == MessagePanelListener.class) { // Lazily create the event: if (event == null) event = new StatusEvent(this, aStatus); ((MessagePanelListener)listeners[i+1]).messageStatus(event); } } } public boolean isOpaque() { return true; } class LoadMessageThread implements Runnable { Message fMessage; LoadMessageThread(Message aMessage) { fMessage = aMessage; } public void run() { synchronized (fPanel) { if (fPanel.fMessage == fMessage) { fMessageLoadThread = null; return; // save ourselves some work } fPanel.fMessage = fMessage; } MessageExtra mextra = fMessage != null ? MessageExtraFactory.Get(fMessage) : null; notifyLoading(); notifyStatus(fLabels.getString("messageLoadingStatus")); fHeader.setMessage(fMessage); try { if (!useMagellan) { synchronized (fTextArea) { if (fMessage != null) { fTextArea.setText(""); try { InputStream stream = mextra.getInputStreamWithHeaders(); if (makeRealHTML) { stream = new MakeItHTML(stream).getHTMLInputStream(); } InputStreamReader reader = new InputStreamReader(stream); char buff[] = new char[4096]; int count; while ((count = reader.read(buff, 0, 4096)) != -1) { fTextArea.append(new String(buff, 0, count)); } } catch (MessagingException me) { fTextArea.setText(me.toString()); me.printStackTrace(); } catch (IOException e) { fTextArea.setText(e.toString()); e.printStackTrace(); } } else { fTextArea.setText("This space intentionally left blank"); } } } else { InputStream in = null; if (fMessage != null) { InputStream rawin; try { rawin = mextra.getInputStreamWithHeaders(); if (makeRealHTML) { in = (new MakeItHTML(rawin)).getHTMLInputStream(); } else { in = new StupidHackToMakeHTML(rawin); } } catch (MessagingException me) { throw new Error("Can't get the input stream??? " + me); } catch (IOException e) { throw new Error("Can't get the input stream??? " + e); } } else { // Love them deprecated classes. Oh, well, this is just a temp hack. in = new StringBufferInputStream("This space intentionally left blank"); } synchronized (fViewer) { /* #### URL url = new URL("http://home.netscape.com/"); URLSource source = new URLSource(url, null, in); IDocument doc = new HTMLDocument(source); fViewer.goTo(doc); */ } } } catch (Exception e) { notifyStatus(e.toString()); e.printStackTrace(); fMessage = null; } catch (Error e) { notifyStatus(e.toString()); e.printStackTrace(); fMessage = null; } if (fMessage != null) { try { mextra.setIsRead(true); // ### Probably shouldn't do it // until we know we got some data... } catch (MessagingException e) { // ### Do anything here? For now, we drop it... } notifyStatus(fLabels.getString("messageLoadedStatus")); } synchronized (fPanel) { fMessageLoadThread = null; } notifyLoaded(); } } } /** Debugging hack. */ class TeeInputStream extends InputStream { InputStream real; OutputStream out; TeeInputStream(InputStream r, OutputStream o) { real = r; out = o; } public int read() throws IOException { int c = real.read(); out.write((byte) c); return c; } } class MakeItHTML implements Runnable { protected PipedInputStream inpipe; protected PipedOutputStream outpipe; protected InputStream rawin; MakeItHTML(InputStream in) throws IOException { rawin = in; inpipe = new PipedInputStream(); outpipe = new PipedOutputStream(inpipe); Thread t = new Thread(this); t.start(); } InputStream getHTMLInputStream() { if (false) { return new TeeInputStream(inpipe, System.out); } else if (false) { try { FileOutputStream out = new FileOutputStream("dump.html"); return new TeeInputStream(inpipe, out); } catch (IOException e) { e.printStackTrace(); } } return inpipe; } public void run() { IMimeParser parser = MimeParserFactory.Make("message/rfc822"); IMimeOperator op = MimeHTMLOperatorFactory.Make(parser.getObject(), new PrintStream(outpipe) /*System.out*/); parser.setOperator(op); byte[] bytes = new byte[1024]; ByteBuf buf = new ByteBuf(bytes.length); int bread; do { try { bread = rawin.read(bytes, 0, bytes.length); } catch (IOException e) { break; } if (bread > 0) { buf.setLength(0); buf.append(bytes, 0, bread); // this sucks! parser.pushBytes(buf); } } while (bread >= 0); bytes = null; buf = null; parser.pushEOF(); try { outpipe.close(); } catch (IOException e) { } } } class StupidHackToMakeHTML extends InputStream { InputStream fRaw; ByteBuf buffer = new ByteBuf(); StupidHackToMakeHTML(InputStream raw) { fRaw = raw; buffer.append("
");
  }


  public int read() throws IOException {
    int result;
    if (buffer.length() > 0) {
      result = buffer.byteAt(0);
      buffer.remove(0, 1);
    } else {
      result = fRaw.read();
      if (result == '<') {
        result = '&';
        buffer.append("lt;");
      } else if (result == '>') {
        result = '&';
        buffer.append("gt;");
      } else if (result == '&') {
        buffer.append("amp;");
      } else if (result == '\n') {
        result = '<';
        buffer.append("BR>");
      }
    }
    return result;
  }
}