001: package net.suberic.pooka.gui;
002:
003: import java.io.*;
004: import java.awt.*;
005: import java.awt.event.*;
006: import javax.swing.*;
007:
008: import javax.activation.*;
009:
010: import net.suberic.pooka.*;
011: import net.suberic.util.swing.*;
012: import net.suberic.util.thread.*;
013:
014: /**
015: * Handles opening, saving, etc. attachments.
016: */
017: public class AttachmentHandler {
018:
019: // i'm hardcoding these, but i doubt that will be too much of a problem.
020:
021: static int minTextWidth = 600;
022: static int maxTextWidth = 800;
023: static int minTextHeight = 600;
024: static int maxTextHeight = 800;
025:
026: MessageProxy mProxy;
027:
028: /**
029: * Creates a new AttachmentHandler instance.
030: */
031: public AttachmentHandler(MessageProxy pProxy) {
032: mProxy = pProxy;
033: }
034:
035: /**
036: * Returns the associated MessageProxy.
037: */
038: public MessageProxy getMessageProxy() {
039: return mProxy;
040: }
041:
042: /**
043: * Returns the associated MessageUI.
044: */
045: public MessageUI getMessageUI() {
046: return mProxy.getMessageUI();
047: }
048:
049: /**
050: * Shows an error message, either on the MessageUI if there is one, or
051: * if not, on the main Pooka frame.
052: */
053: public void showError(String message, Exception ioe) {
054: MessageUI mui = getMessageUI();
055: if (mui != null) {
056: mui.showError(message, ioe);
057: } else {
058: Pooka.getUIFactory().showError(message, ioe);
059: }
060: }
061:
062: /**
063: * Shows an error message, either on the MessageUI if there is one, or
064: * if not, on the main Pooka frame.
065: */
066: public void showError(String message, String title, Exception ioe) {
067: MessageUI mui = getMessageUI();
068: if (mui != null) {
069: mui.showError(message, title, ioe);
070: } else {
071: Pooka.getUIFactory().showError(message, title, ioe);
072: }
073: }
074:
075: /**
076: * This opens up the selected Attachment using the default handler
077: * for the Attachment's Mime type.
078: */
079: public void openAttachment(Attachment pAttachment) {
080: // called on the folder thread.
081:
082: if (pAttachment != null) {
083: DataHandler dh = null;
084: dh = pAttachment.getDataHandler();
085:
086: if (dh != null) {
087: dh.setCommandMap(Pooka.getMailcap());
088:
089: if (Pooka.isDebug()) {
090: CommandInfo[] cis = dh.getAllCommands();
091: if (cis != null && cis.length > 0) {
092: for (int i = 0; i < cis.length; i++) {
093: System.out.println(cis[i].getCommandName()
094: + ", " + cis[i].getCommandClass());
095: }
096: } else {
097: System.out.println("No commands for mimetype.");
098: }
099: } // end debug
100:
101: CommandInfo[] cmds = dh.getPreferredCommands();
102: if (cmds != null && cmds[0] != null) {
103: Object beanViewer = dh.getBean(cmds[0]);
104: if (beanViewer instanceof Frame) {
105: Frame frameViewer = (Frame) beanViewer;
106: try {
107: frameViewer.setTitle(pAttachment.getName());
108: frameViewer.setSize(frameViewer
109: .getPreferredSize());
110: } catch (Exception e) {
111: }
112: frameViewer.show();
113: } else if (beanViewer instanceof Component) {
114: String title = pAttachment.getName();
115: openAttachmentWindow((Component) beanViewer,
116: title, false);
117: } else if (beanViewer instanceof ExternalLauncher) {
118: ((ExternalLauncher) beanViewer).show();
119: } else if (beanViewer instanceof com.sun.mail.handlers.text_plain
120: || beanViewer instanceof com.sun.mail.handlers.text_html) {
121: // sigh
122: JTextPane jtp = new JTextPane();
123: try {
124: String content = (String) pAttachment
125: .getContent();
126: if (pAttachment.isHtml()) {
127: jtp.setContentType("text/html");
128: }
129: jtp.setText(content);
130: jtp.setEditable(false);
131: openAttachmentWindow(new JScrollPane(jtp),
132: pAttachment.getName(), true);
133: } catch (IOException ioe) {
134: showError("Error showing attachment: ",
135: ioe);
136: }
137: } else if (cmds[0].getCommandClass().equals(
138: "net.suberic.pooka.ExternalLauncher")) {
139: try {
140: ExternalLauncher el = new ExternalLauncher();
141:
142: // create a progress dialog for the external launcher
143: int attachmentSize = pAttachment.getSize();
144: if (pAttachment.getEncoding() != null
145: && pAttachment.getEncoding()
146: .equalsIgnoreCase("base64"))
147: attachmentSize = (int) (attachmentSize * .73);
148:
149: ProgressDialog dlg;
150: if (getMessageUI() != null) {
151: dlg = getMessageUI()
152: .createProgressDialog(
153: 0,
154: attachmentSize,
155: 0,
156: "Fetching attachment...",
157: "Fetching attachment");
158: } else {
159: dlg = Pooka.getUIFactory()
160: .createProgressDialog(0,
161: attachmentSize, 0,
162: "Fetching attachment",
163: "Fetching attachment");
164: }
165:
166: final ExternalLauncher fLauncher = el;
167: dlg
168: .addCancelListener(new ProgressDialogListener() {
169: public void dialogCancelled() {
170: fLauncher.cancelSave();
171: }
172: });
173:
174: el.setProgressDialog(dlg);
175:
176: el.setCommandContext(cmds[0]
177: .getCommandName(), null);
178:
179: el.show();
180: } catch (IOException ioe) {
181: //
182: }
183: } else {
184: openWith(pAttachment);
185: }
186: } else if (isWindows()) {
187: try {
188: String mimeType = pAttachment.getMimeType()
189: .toString();
190: if (mimeType.indexOf(';') != -1)
191: mimeType = mimeType.substring(0, mimeType
192: .indexOf(';'));
193:
194: String cmd = "rundll32 url.dll,FileProtocolHandler %s";
195:
196: ExternalLauncher el = new ExternalLauncher();
197:
198: el.setCommandContext(cmd, dh);
199:
200: // create a progress dialog for the external launcher
201: int attachmentSize = pAttachment.getSize();
202: if (pAttachment.getEncoding() != null
203: && pAttachment.getEncoding()
204: .equalsIgnoreCase("base64"))
205: attachmentSize = (int) (attachmentSize * .73);
206:
207: ProgressDialog dlg;
208: if (getMessageUI() != null) {
209: dlg = getMessageUI().createProgressDialog(
210: 0, attachmentSize, 0,
211: "Fetching attachment",
212: "Fetching attachment");
213: } else {
214: dlg = Pooka.getUIFactory()
215: .createProgressDialog(0,
216: attachmentSize, 0,
217: "Fetching attachment",
218: "Fetching attachment");
219: }
220:
221: final ExternalLauncher fLauncher = el;
222: dlg
223: .addCancelListener(new ProgressDialogListener() {
224: public void dialogCancelled() {
225: fLauncher.cancelSave();
226: }
227: });
228:
229: el.setProgressDialog(dlg);
230:
231: if (Pooka.isDebug())
232: System.out
233: .println("opening external launcher with ");
234: el.show();
235: } catch (Exception elException) {
236: getMessageUI()
237: .showError("Error opening attachment",
238: elException);
239: }
240:
241: } else {
242: openWith(pAttachment);
243: }
244: }
245: }
246: }
247:
248: /**
249: * Returns whether or not we're running on a Windows platform.
250: */
251: public boolean isWindows() {
252: return (System.getProperty("os.name").toLowerCase().indexOf(
253: "windows") > -1);
254: }
255:
256: /**
257: * Opens either a JFrame or a JInternalFrame, whichever is appropriate,
258: * with the given Component as a content pane and the given title.
259: */
260: private void openAttachmentWindow(Component pContent,
261: String pTitle, boolean pResize) {
262: // threading: this can be called on any thread, since it calls
263: // SwingUtilities.invokeLater().
264:
265: final Component content = pContent;
266: final String title = pTitle;
267: final boolean resize = pResize;
268:
269: SwingUtilities.invokeLater(new Runnable() {
270: public void run() {
271: MessageUI mui = getMessageUI();
272: if (Pooka.isDebug())
273: System.out.println("opening attachment window.");
274:
275: if ((mui != null && mui instanceof JInternalFrame)
276: || (mui == null && Pooka.getUIFactory() instanceof PookaDesktopPaneUIFactory)) {
277: JDesktopPane desktop = ((PookaDesktopPaneUIFactory) Pooka
278: .getUIFactory()).getMessagePanel();
279: JInternalFrame jif = new JInternalFrame(title,
280: true, true, true, true);
281: jif.getContentPane().add(content);
282: jif.pack();
283: if (resize) {
284: // let's be reasonable here....
285: Dimension frameSize = jif.getSize();
286: if (frameSize.width < minTextWidth) {
287: frameSize.width = minTextWidth;
288: } else if (frameSize.width > maxTextWidth) {
289: frameSize.width = maxTextWidth;
290: }
291:
292: if (frameSize.height < minTextHeight) {
293: frameSize.height = minTextHeight;
294: } else if (frameSize.height > maxTextHeight) {
295: frameSize.height = maxTextHeight;
296: }
297:
298: jif.setSize(frameSize);
299: }
300:
301: desktop.add(jif);
302: if (desktop instanceof MessagePanel) {
303: jif.setLocation(((MessagePanel) desktop)
304: .getNewWindowLocation(jif, false));
305: }
306: jif.setVisible(true);
307: try {
308: jif.setSelected(true);
309: } catch (java.beans.PropertyVetoException e) {
310: }
311: } else {
312: JFrame frame = new JFrame(title);
313: frame.getContentPane().add(content);
314: frame.pack();
315:
316: if (resize) {
317: // let's be reasonable here....
318: Dimension frameSize = frame.getSize();
319: if (frameSize.width < minTextWidth) {
320: frameSize.width = minTextWidth;
321: } else if (frameSize.width > maxTextWidth) {
322: frameSize.width = maxTextWidth;
323: }
324:
325: if (frameSize.height < minTextHeight) {
326: frameSize.height = minTextHeight;
327: } else if (frameSize.height > maxTextHeight) {
328: frameSize.height = maxTextHeight;
329: }
330:
331: frame.setSize(frameSize);
332: }
333: frame.show();
334: }
335: }
336: });
337: }
338:
339: /**
340: * This opens the Attachment with the program of the user's choice.
341: */
342: public void openWith(Attachment pAttachment) {
343: if (Pooka.isDebug())
344: System.out.println("calling AttachmentHandler.openWith()");
345:
346: try {
347: String mimeType = pAttachment.getMimeType().toString();
348: if (mimeType.indexOf(';') != -1)
349: mimeType = mimeType.substring(0, mimeType.indexOf(';'));
350:
351: final String mType = mimeType;
352:
353: final Attachment fAttachment = pAttachment;
354:
355: // have to get the ActionThread for later.
356: ActionThread actionThread = null;
357: Thread currentThread = Thread.currentThread();
358: if (currentThread instanceof ActionThread) {
359: actionThread = (ActionThread) currentThread;
360: }
361: final ActionThread fActionThread = actionThread;
362:
363: SwingUtilities.invokeLater(new Runnable() {
364: public void run() {
365:
366: String inputMessage = Pooka
367: .getProperty(
368: "AttchmentPane.openWith.message",
369: "Enter the command with which \r\nto open the attchment.");
370: String inputTitle = Pooka.getProperty(
371: "AttachmentPane.openWith.title",
372: "Open Attachment With");
373: String makeDefaultLabel = Pooka
374: .getProperty(
375: "AttachmentPane.openWith.makeDefaultMessage",
376: "Make default command?");
377:
378: JLabel toggleMsgLabel = new JLabel(makeDefaultLabel);
379: toggleMsgLabel.setForeground(Color
380: .getColor("Black"));
381: JRadioButton toggleButton = new JRadioButton();
382: JPanel togglePanel = new JPanel();
383: togglePanel.add(toggleMsgLabel);
384: togglePanel.add(toggleButton);
385:
386: Object[] messageArray = new Object[2];
387: messageArray[0] = inputMessage;
388: messageArray[1] = togglePanel;
389: String cmd = null;
390: if (getMessageUI() != null)
391: cmd = getMessageUI().showInputDialog(
392: messageArray, inputTitle);
393: else
394: cmd = Pooka.getUIFactory().showInputDialog(
395: messageArray, inputTitle);
396:
397: if (cmd != null) {
398: if (cmd.indexOf("%s") == -1)
399: cmd = cmd.concat(" %s");
400:
401: if (toggleButton.isSelected()) {
402: String newMailcap = new String(mType
403: .toLowerCase()
404: + ";" + cmd);
405: ((FullMailcapCommandMap) Pooka.getMailcap())
406: .addMailcap(newMailcap);
407: }
408:
409: final DataHandler dh = fAttachment
410: .getDataHandler();
411: final String fCmd = cmd;
412:
413: if (dh != null) {
414: AbstractAction action = new AbstractAction() {
415: public void actionPerformed(
416: java.awt.event.ActionEvent ae) {
417: try {
418: dh.setCommandMap(Pooka
419: .getMailcap());
420: ExternalLauncher el = new ExternalLauncher();
421:
422: el.setCommandContext(fCmd, dh);
423:
424: // create a progress dialog for the external launcher
425: int attachmentSize = fAttachment
426: .getSize();
427: if (fAttachment.getEncoding() != null
428: && fAttachment
429: .getEncoding()
430: .equalsIgnoreCase(
431: "base64"))
432: attachmentSize = (int) (attachmentSize * .73);
433:
434: ProgressDialog dlg;
435: if (getMessageUI() != null) {
436: dlg = getMessageUI()
437: .createProgressDialog(
438: 0,
439: attachmentSize,
440: 0,
441: "Fetching attachment",
442: "Fetching attachment");
443: } else {
444: dlg = Pooka
445: .getUIFactory()
446: .createProgressDialog(
447: 0,
448: attachmentSize,
449: 0,
450: "Fetching attachment",
451: "Fetching attachment");
452: }
453:
454: final ExternalLauncher fLauncher = el;
455: dlg
456: .addCancelListener(new ProgressDialogListener() {
457: public void dialogCancelled() {
458: fLauncher
459: .cancelSave();
460: }
461: });
462:
463: el.setProgressDialog(dlg);
464:
465: if (Pooka.isDebug())
466: System.out
467: .println("opening external launcher with ");
468: el.show();
469: } catch (Exception elException) {
470: getMessageUI()
471: .showError(
472: "Error opening attachment",
473: elException);
474: }
475: }
476: };
477:
478: if (fActionThread != null) {
479: fActionThread.addToQueue(action,
480: new java.awt.event.ActionEvent(
481: AttachmentHandler.this ,
482: 0, "attachment-open"));
483: } else {
484: action
485: .actionPerformed(new java.awt.event.ActionEvent(
486: AttachmentHandler.this ,
487: 0, "attachment-open"));
488: }
489: }
490: }
491: }
492: });
493:
494: } catch (Exception e) {
495: e.printStackTrace();
496: }
497: }
498:
499: /**
500: * This opens up a JFileChooser to let the user choose under what
501: * name and where the selected Attachment should be saved. It then
502: * calls saveFileAs() to save the file.
503: */
504: public void saveAttachment(Attachment pAttachment,
505: Component pComponent) {
506: // usually called on the folder thread. so we need to throw the
507: // filechooser over to the AWTEvent thread.
508:
509: if (pAttachment != null) {
510: final Attachment fAttachment = pAttachment;
511: final Component fComponent = pComponent;
512: final String fileName = pAttachment.getName();
513:
514: SwingUtilities.invokeLater(new Runnable() {
515: public void run() {
516: JFileChooser saveChooser;
517: String currentDirectoryPath = Pooka.getProperty(
518: "Pooka.tmp.currentDirectory", "");
519: if (currentDirectoryPath == "")
520: saveChooser = new JFileChooser();
521: else
522: saveChooser = new JFileChooser(
523: currentDirectoryPath);
524:
525: if (fileName != null)
526: saveChooser.setSelectedFile(new File(fileName));
527:
528: int saveConfirm = saveChooser
529: .showSaveDialog(fComponent);
530: Pooka.getResources()
531: .setProperty(
532: "Pooka.tmp.currentDirectory",
533: saveChooser.getCurrentDirectory()
534: .getPath(), true);
535: if (saveConfirm == JFileChooser.APPROVE_OPTION) {
536: try {
537: // saveFileAs creates a new thread, so don't bother
538: // dispatching this somewhere else.
539: saveFileAs(fAttachment, saveChooser
540: .getSelectedFile());
541: } catch (IOException exc) {
542: showError(Pooka.getProperty(
543: "error.SaveFile",
544: "Error saving file")
545: + ":\n", Pooka.getProperty(
546: "error.SaveFile",
547: "Error saving file"), exc);
548: }
549: }
550: }
551: });
552: }
553: }
554:
555: /**
556: * This opens up a JFileChooser to let the user choose the location
557: * to which to save the attachments. Then it calls saveFileAs() to save
558: * the attachments with their default filenames.
559: */
560: public void saveAllAttachments(Component pComponent) {
561: // usually called on the folder thread. so we need to throw the
562: // filechooser over to the AWTEvent thread.
563: try {
564: final Component fComponent = pComponent;
565: final java.util.List fAttachmentList = mProxy
566: .getAttachments();
567:
568: SwingUtilities.invokeLater(new Runnable() {
569: public void run() {
570: JFileChooser saveChooser;
571: String currentDirectoryPath = Pooka.getProperty(
572: "Pooka.tmp.currentDirectory", "");
573: if (currentDirectoryPath == "")
574: saveChooser = new JFileChooser();
575: else
576: saveChooser = new JFileChooser(
577: currentDirectoryPath);
578:
579: saveChooser
580: .setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
581:
582: int saveConfirm = saveChooser
583: .showSaveDialog(fComponent);
584: File selectedDir = saveChooser.getSelectedFile();
585: Pooka.getResources()
586: .setProperty(
587: "Pooka.tmp.currentDirectory",
588: saveChooser.getCurrentDirectory()
589: .getPath(), true);
590: if (saveConfirm == JFileChooser.APPROVE_OPTION) {
591: for (int i = 0; i < fAttachmentList.size(); i++) {
592: Attachment currentAttachment = (Attachment) fAttachmentList
593: .get(i);
594: String filename = currentAttachment
595: .getName();
596: if (filename == null || filename.equals("")) {
597: filename = "savedFile_" + i;
598: }
599: File currentFile = new File(selectedDir,
600: filename);
601: try {
602: // saveFileAs creates a new thread, so don't bother
603: // dispatching this somewhere else.
604: saveFileAs(currentAttachment,
605: currentFile);
606: } catch (IOException exc) {
607: showError(Pooka.getProperty(
608: "error.SaveFile",
609: "Error saving file")
610: + ":\n", Pooka.getProperty(
611: "error.SaveFile",
612: "Error saving file"), exc);
613: }
614: }
615: }
616: }
617: });
618: } catch (javax.mail.MessagingException me) {
619: showError("Error getting attachment list", me);
620: }
621:
622: }
623:
624: /**
625: * This actually saves the Attachment as the File saveFile.
626: */
627: public void saveFileAs(Attachment mbp, File saveFile)
628: throws IOException {
629: SaveAttachmentThread thread = new SaveAttachmentThread(mbp,
630: saveFile);
631: thread.start();
632: }
633:
634: class SaveAttachmentThread extends Thread {
635:
636: Attachment attachment;
637: File saveFile;
638: ProgressDialog dialog;
639: boolean running = true;
640:
641: SaveAttachmentThread(Attachment newAttachment, File newSaveFile) {
642: attachment = newAttachment;
643: saveFile = newSaveFile;
644: }
645:
646: public void run() {
647: InputStream decodedIS = null;
648: BufferedOutputStream outStream = null;
649:
650: int attachmentSize = 0;
651:
652: try {
653: decodedIS = attachment.getInputStream();
654: attachmentSize = attachment.getSize();
655: if (attachment.getEncoding() != null
656: && attachment.getEncoding().equalsIgnoreCase(
657: "base64"))
658: attachmentSize = (int) (attachmentSize * .73);
659:
660: dialog = createDialog(attachmentSize);
661: dialog.show();
662:
663: outStream = new BufferedOutputStream(
664: new FileOutputStream(saveFile));
665: int b = 0;
666: byte[] buf = new byte[32768];
667:
668: b = decodedIS.read(buf);
669: while (b != -1 && running) {
670: outStream.write(buf, 0, b);
671: dialog.setValue(dialog.getValue() + b);
672: if (dialog.isCancelled())
673: running = false;
674:
675: b = decodedIS.read(buf);
676: }
677:
678: } catch (IOException ioe) {
679: showError("Error saving file", ioe);
680: cancelSave();
681: } finally {
682: if (outStream != null) {
683: try {
684: outStream.flush();
685: outStream.close();
686: } catch (IOException ioe) {
687: }
688: }
689: if (dialog != null)
690: dialog.dispose();
691: }
692: }
693:
694: /**
695: * Creates a progress dialog to show the downloading of an attachment.
696: */
697: public ProgressDialog createDialog(int attachmentSize) {
698: ProgressDialog dlg;
699: if (getMessageUI() != null) {
700: dlg = getMessageUI().createProgressDialog(0,
701: attachmentSize, 0, saveFile.getName(),
702: saveFile.getName());
703: } else {
704: dlg = Pooka.getUIFactory().createProgressDialog(0,
705: attachmentSize, 0, saveFile.getName(),
706: saveFile.getName());
707: }
708:
709: dlg.addCancelListener(new ProgressDialogListener() {
710: public void dialogCancelled() {
711: cancelSave();
712: }
713: });
714: return dlg;
715: }
716:
717: public void cancelSave() {
718: try {
719: saveFile.delete();
720: } catch (Exception e) {
721: }
722: dialog.dispose();
723: }
724: } // SaveAttachmentThread
725:
726: }
|