0001: // The contents of this file are subject to the Mozilla Public License Version
0002: // 1.1
0003: //(the "License"); you may not use this file except in compliance with the
0004: //License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
0005: //
0006: //Software distributed under the License is distributed on an "AS IS" basis,
0007: //WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0008: //for the specific language governing rights and
0009: //limitations under the License.
0010: //
0011: //The Original Code is "The Columba Project"
0012: //
0013: //The Initial Developers of the Original Code are Frederik Dietz and Timo
0014: // Stich.
0015: //Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
0016: //
0017: //All Rights Reserved.
0018:
0019: package org.columba.mail.gui.composer;
0020:
0021: import java.awt.BorderLayout;
0022: import java.awt.Color;
0023: import java.awt.Component;
0024: import java.awt.Container;
0025: import java.awt.FocusTraversalPolicy;
0026: import java.awt.event.ItemEvent;
0027: import java.awt.event.ItemListener;
0028: import java.awt.event.MouseEvent;
0029: import java.awt.event.MouseListener;
0030: import java.io.File;
0031: import java.io.FileInputStream;
0032: import java.io.IOException;
0033: import java.io.InputStream;
0034: import java.nio.charset.Charset;
0035: import java.nio.charset.UnsupportedCharsetException;
0036: import java.util.List;
0037: import java.util.Observable;
0038: import java.util.Observer;
0039: import java.util.logging.Logger;
0040:
0041: import javax.swing.BorderFactory;
0042: import javax.swing.JComponent;
0043: import javax.swing.JEditorPane;
0044: import javax.swing.JOptionPane;
0045: import javax.swing.JPanel;
0046: import javax.swing.JScrollPane;
0047: import javax.swing.JSplitPane;
0048: import javax.swing.SwingUtilities;
0049: import javax.swing.UIManager;
0050: import javax.swing.border.Border;
0051: import javax.swing.border.LineBorder;
0052: import javax.swing.event.DocumentEvent;
0053: import javax.swing.event.DocumentListener;
0054: import javax.swing.event.EventListenerList;
0055: import javax.swing.text.BadLocationException;
0056: import javax.swing.text.Document;
0057:
0058: import org.columba.api.gui.frame.IContainer;
0059: import org.columba.core.charset.CharsetEvent;
0060: import org.columba.core.charset.CharsetListener;
0061: import org.columba.core.charset.CharsetOwnerInterface;
0062: import org.columba.core.config.ViewItem;
0063: import org.columba.core.gui.base.LabelWithMnemonic;
0064: import org.columba.core.gui.frame.DefaultFrameController;
0065: import org.columba.core.gui.frame.FrameManager;
0066: import org.columba.core.io.DiskIO;
0067: import org.columba.core.xml.XmlElement;
0068: import org.columba.mail.config.AccountItem;
0069: import org.columba.mail.config.MailConfig;
0070: import org.columba.mail.gui.composer.action.SaveAsDraftAction;
0071: import org.columba.mail.gui.composer.html.HtmlEditorController2;
0072: import org.columba.mail.gui.composer.html.HtmlToolbar;
0073: import org.columba.mail.gui.composer.text.TextEditorController;
0074: import org.columba.mail.gui.message.viewer.MessageBorder;
0075: import org.columba.mail.parser.text.HtmlParser;
0076: import org.columba.mail.util.MailResourceLoader;
0077: import org.frapuccino.swing.MultipleTransferHandler;
0078:
0079: import com.jgoodies.forms.debug.FormDebugPanel;
0080: import com.jgoodies.forms.factories.FormFactory;
0081: import com.jgoodies.forms.layout.CellConstraints;
0082: import com.jgoodies.forms.layout.ColumnSpec;
0083: import com.jgoodies.forms.layout.FormLayout;
0084: import com.jgoodies.forms.layout.FormSpec;
0085: import com.jgoodies.forms.layout.RowSpec;
0086: import com.jgoodies.forms.layout.Sizes;
0087:
0088: /**
0089: *
0090: * controller for message composer dialog
0091: *
0092: * @author fdietz
0093: */
0094: public class ComposerController extends DefaultFrameController
0095: implements CharsetOwnerInterface, ItemListener, Observer {
0096:
0097: /** JDK 1.4+ logging framework logger, used for logging. */
0098: private static final Logger LOG = Logger
0099: .getLogger("org.columba.mail.gui.composer");
0100:
0101: private AttachmentController attachmentController;
0102:
0103: private SubjectController subjectController;
0104:
0105: private PriorityController priorityController;
0106:
0107: private AccountController accountController;
0108:
0109: private AbstractEditorController currentEditorController;
0110:
0111: private HeaderController headerController;
0112:
0113: private ComposerSpellCheck composerSpellCheck;
0114:
0115: private ComposerModel composerModel;
0116:
0117: private Charset charset;
0118:
0119: private EventListenerList listenerList = new EventListenerList();
0120:
0121: /** Buffer for listeners used by addContainerListenerForEditor and createView */
0122: private List containerListenerBuffer;
0123:
0124: private JSplitPane attachmentSplitPane;
0125:
0126: /** Editor viewer resides in this panel */
0127: private TextEditorPanel editorScrollPane;
0128:
0129: private LabelWithMnemonic subjectLabel;
0130:
0131: private LabelWithMnemonic smtpLabel;
0132:
0133: private LabelWithMnemonic priorityLabel;
0134:
0135: private JPanel centerPanel = new FormDebugPanel();
0136:
0137: private JPanel topPanel;
0138:
0139: private HtmlToolbar htmlToolbar;
0140:
0141: private boolean promptOnDialogClosing = true;
0142:
0143: private SignatureView signatureView;
0144:
0145: private boolean attachmentPanelShown;
0146:
0147: private JPanel editorPanel = new JPanel();
0148:
0149: JPanel toolbarPanel = new JPanel();
0150:
0151: private TextEditorController textEditor;
0152:
0153: private HtmlEditorController2 htmlEditor;
0154:
0155: public ComposerController() {
0156: this (new ComposerModel(), FrameManager.getInstance()
0157: .createCustomViewItem("Composer"));
0158:
0159: }
0160:
0161: public ComposerController(ComposerModel model, ViewItem viewItem) {
0162: super (viewItem);
0163:
0164: // init model (defaults to empty plain text message)
0165: composerModel = model;
0166:
0167: // init controllers for different parts of the composer
0168: attachmentController = new AttachmentController(this );
0169: headerController = new HeaderController(this );
0170: subjectController = new SubjectController(this );
0171:
0172: // listen to changes in the Subject to update the title bar
0173: // of the message composer window
0174: getSubjectController().getView().getDocument()
0175: .addDocumentListener(new MyDocumentListener());
0176:
0177: priorityController = new PriorityController(this );
0178: accountController = new AccountController(this );
0179: accountController.getView().addItemListener(this );
0180: composerSpellCheck = new ComposerSpellCheck();
0181:
0182: signatureView = new SignatureView(this );
0183:
0184: // set default html or text based on stored option
0185: // ... can be overridden by setting the composer model
0186: XmlElement optionsElement = MailConfig.getInstance().get(
0187: "composer_options").getElement("/options");
0188:
0189: // composer can either edit in html or plain text mode
0190: // listen for configuration changes
0191: initHtmlConfiguration(optionsElement);
0192:
0193: htmlEditor = new HtmlEditorController2(this );
0194:
0195: textEditor = new TextEditorController(this );
0196:
0197: // init controller for the editor depending on message type
0198: if (getModel().isHtml())
0199: currentEditorController = htmlEditor;
0200: else
0201: currentEditorController = textEditor;
0202:
0203: initComponents();
0204:
0205: // add JPanel with useful HTML related actions.
0206: htmlToolbar = new HtmlToolbar(this );
0207:
0208: layoutComponents();
0209:
0210: showAttachmentPanel();
0211:
0212: // Hack to ensure charset is set correctly at start-up
0213: XmlElement charsetElement = optionsElement
0214: .getElement("charset");
0215:
0216: if (charsetElement != null) {
0217: String charset = charsetElement.getAttribute("name");
0218:
0219: if (charset != null) {
0220: try {
0221: setCharset(Charset.forName(charset));
0222: } catch (UnsupportedCharsetException ex) {
0223: // ignore this
0224: }
0225: }
0226: }
0227:
0228: // Setup DnD for the text and attachment list control.
0229: ComposerAttachmentTransferHandler dndTransferHandler = new ComposerAttachmentTransferHandler(
0230: attachmentController);
0231: attachmentController.getView().setDragEnabled(true);
0232: attachmentController.getView().setTransferHandler(
0233: dndTransferHandler);
0234:
0235: JEditorPane editorComponent = (JEditorPane) getCurrentEditor()
0236: .getComponent();
0237: MultipleTransferHandler compositeHandler = new MultipleTransferHandler();
0238: compositeHandler.addTransferHandler(editorComponent
0239: .getTransferHandler());
0240: compositeHandler.addTransferHandler(dndTransferHandler);
0241: editorComponent.setDragEnabled(true);
0242: editorComponent.setTransferHandler(compositeHandler);
0243: }
0244:
0245: private void initHtmlConfiguration(XmlElement optionsElement) {
0246:
0247: XmlElement htmlElement = optionsElement.getElement("html");
0248:
0249: // create default element if not available
0250: if (htmlElement == null) {
0251: htmlElement = optionsElement.addSubElement("html");
0252: }
0253:
0254: String enableHtml = htmlElement.getAttribute("enable", "false");
0255:
0256: // register for configuration changes for the html(enabled/disabled)
0257: // state
0258: htmlElement.addObserver(this );
0259:
0260: // set model based on configuration
0261: if (enableHtml.equals("true")) {
0262: getModel().setHtml(true);
0263: } else {
0264: getModel().setHtml(false);
0265: }
0266: }
0267:
0268: /**
0269: * Show attachment panel
0270: * <p>
0271: * Asks the ComposerModel if message contains attachments. If so, show the
0272: * attachment panel. Otherwise, hide the attachment panel.
0273: */
0274: public void showAttachmentPanel() {
0275: if (attachmentPanelShown == getAttachmentController().getView()
0276: .count() > 0)
0277: return;
0278:
0279: // remove all components from container
0280: centerPanel.removeAll();
0281:
0282: // re-add all top components like recipient editor/subject editor
0283: centerPanel.add(topPanel, BorderLayout.NORTH);
0284:
0285: // if message contains attachments
0286: if (getAttachmentController().getView().count() > 0) {
0287: // create scrollapen
0288: JScrollPane attachmentScrollPane = new JScrollPane(
0289: getAttachmentController().getView());
0290: attachmentScrollPane
0291: .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
0292: attachmentScrollPane.setBorder(BorderFactory
0293: .createEmptyBorder(1, 1, 1, 1));
0294: // create splitpane containing the bodytext editor and the
0295: // attachment panel
0296: attachmentSplitPane = new JSplitPane(
0297: JSplitPane.VERTICAL_SPLIT, editorScrollPane,
0298: attachmentScrollPane);
0299: attachmentSplitPane.setDividerLocation(0.80);
0300: attachmentSplitPane.setBorder(null);
0301:
0302: // add splitpane to the center
0303: centerPanel.add(attachmentSplitPane, BorderLayout.CENTER);
0304:
0305: // ViewItem viewItem = getViewItem();
0306:
0307: // default value is 200 pixel
0308: // int pos =
0309: // viewItem.getIntegerWithDefault("splitpanes","attachment", 200);
0310: attachmentSplitPane.setDividerLocation(200);
0311:
0312: attachmentPanelShown = true;
0313: } else {
0314: // no attachments
0315: // -> only show bodytext editor
0316: centerPanel.add(editorPanel, BorderLayout.CENTER);
0317:
0318: attachmentPanelShown = false;
0319: }
0320:
0321: // re-paint composer-view
0322: SwingUtilities.invokeLater(new Runnable() {
0323: public void run() {
0324: fireLayoutChanged();
0325: }
0326: });
0327:
0328: }
0329:
0330: /**
0331: * @return Returns the attachmentSplitPane.
0332: */
0333: public JSplitPane getAttachmentSplitPane() {
0334: return attachmentSplitPane;
0335: }
0336:
0337: /**
0338: * init components
0339: */
0340: protected void initComponents() {
0341: subjectLabel = new LabelWithMnemonic(MailResourceLoader
0342: .getString("dialog", "composer", "subject"));
0343: smtpLabel = new LabelWithMnemonic(MailResourceLoader.getString(
0344: "dialog", "composer", "identity"));
0345: priorityLabel = new LabelWithMnemonic(MailResourceLoader
0346: .getString("dialog", "composer", "priority"));
0347:
0348: editorScrollPane = new TextEditorPanel();
0349: }
0350:
0351: /**
0352: * Layout components
0353: */
0354: public void layoutComponents() {
0355: centerPanel.removeAll();
0356:
0357: editorPanel.setLayout(new BorderLayout());
0358:
0359: toolbarPanel.setLayout(new BorderLayout());
0360: toolbarPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 4,
0361: 0));
0362: toolbarPanel.add(htmlToolbar);
0363: toolbarPanel.setBackground(UIManager
0364: .getColor("TextArea.background"));
0365:
0366: topPanel = new JPanel();
0367: topPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
0368:
0369: FormLayout layout = new FormLayout(new ColumnSpec[] {
0370: new ColumnSpec("center:max(pref;50dlu)"),
0371: FormFactory.LABEL_COMPONENT_GAP_COLSPEC,
0372: FormFactory.GROWING_BUTTON_COLSPEC,
0373: FormFactory.LABEL_COMPONENT_GAP_COLSPEC,
0374: FormFactory.DEFAULT_COLSPEC,
0375: FormFactory.LABEL_COMPONENT_GAP_COLSPEC,
0376: FormFactory.GROWING_BUTTON_COLSPEC }, new RowSpec[] {
0377: new RowSpec(RowSpec.FILL, Sizes.DEFAULT,
0378: FormSpec.NO_GROW),
0379: FormFactory.LINE_GAP_ROWSPEC,
0380: new RowSpec(RowSpec.FILL, Sizes.DEFAULT,
0381: FormSpec.NO_GROW),
0382: FormFactory.LINE_GAP_ROWSPEC,
0383: new RowSpec(RowSpec.FILL, Sizes.DEFAULT,
0384: FormSpec.NO_GROW),
0385: FormFactory.LINE_GAP_ROWSPEC,
0386: new RowSpec(RowSpec.FILL, Sizes.DEFAULT,
0387: FormSpec.NO_GROW),
0388: FormFactory.LINE_GAP_ROWSPEC,
0389: new RowSpec(RowSpec.FILL, Sizes.DEFAULT,
0390: FormSpec.NO_GROW) });
0391: layout.setRowGroups(new int[][] { { 1, 3, 5, 7, 9 } });
0392: layout.setColumnGroups(new int[][] { { 1 } });
0393:
0394: topPanel.setLayout(layout);
0395:
0396: CellConstraints c = new CellConstraints();
0397:
0398: topPanel.add(smtpLabel, c.xy(1, 1, CellConstraints.CENTER,
0399: CellConstraints.DEFAULT));
0400:
0401: topPanel.add(getAccountController().getView(), c.xy(3, 1));
0402: topPanel.add(priorityLabel, c.xy(5, 1));
0403: topPanel.add(getPriorityController().getView(), c.xy(7, 1));
0404:
0405: getHeaderController().getView().layoutComponents(topPanel);
0406:
0407: topPanel.add(subjectLabel, c.xy(1, 9, CellConstraints.CENTER,
0408: CellConstraints.DEFAULT));
0409:
0410: topPanel.add(getSubjectController().getView(), c.xywh(3, 9, 5,
0411: 1));
0412:
0413: if (composerModel.isHtml())
0414: editorPanel.add(toolbarPanel, BorderLayout.NORTH);
0415:
0416: editorScrollPane.getContentPane().add(
0417: getCurrentEditor().getViewUIComponent(),
0418: BorderLayout.CENTER);
0419:
0420: editorPanel.add(editorScrollPane, BorderLayout.CENTER);
0421:
0422: Border outterBorder = BorderFactory.createCompoundBorder(
0423: BorderFactory.createEmptyBorder(5, 5, 5, 5),
0424: new MessageBorder(Color.LIGHT_GRAY, 1, true));
0425: Border innerBorder = BorderFactory.createCompoundBorder(
0426: outterBorder, new LineBorder(Color.WHITE, 5, true));
0427: editorPanel.setBorder(innerBorder);
0428:
0429: AccountItem item = (AccountItem) getAccountController()
0430: .getView().getSelectedItem();
0431: if (item.getIdentity().getSignature() != null)
0432: editorScrollPane.getContentPane().add(signatureView,
0433: BorderLayout.SOUTH);
0434:
0435: editorScrollPane.addMouseListener(new MouseListener() {
0436:
0437: public void mouseClicked(MouseEvent e) {
0438: currentEditorController.getComponent().requestFocus();
0439: }
0440:
0441: public void mouseEntered(MouseEvent e) {
0442: }
0443:
0444: public void mouseExited(MouseEvent e) {
0445: }
0446:
0447: public void mousePressed(MouseEvent e) {
0448: }
0449:
0450: public void mouseReleased(MouseEvent e) {
0451: }
0452:
0453: });
0454:
0455: centerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0,
0456: 0));
0457: centerPanel.setLayout(new BorderLayout());
0458:
0459: centerPanel.add(topPanel, BorderLayout.NORTH);
0460:
0461: // no attachments
0462: // -> only show bodytext editor
0463: centerPanel.add(editorPanel, BorderLayout.CENTER);
0464:
0465: attachmentPanelShown = false;
0466: }
0467:
0468: /**
0469: * Returns a reference to the panel, that holds the editor view. This is
0470: * used by the ComposerController when adding a listener to that panel.
0471: *
0472: * @return editor panel reference
0473: */
0474: public JPanel getEditorPanel() {
0475: return editorScrollPane.getContentPane();
0476: }
0477:
0478: /**
0479: * Used to update the panel, that holds the editor viewer. This is necessary
0480: * e.g. if the ComposerModel is changed to hold another message type (text /
0481: * html), which the previous editor can not handle. If so a new editor
0482: * controller is created, and thereby a new view.
0483: */
0484: public void layoutEditorContainer() {
0485:
0486: // update panel
0487: editorScrollPane.getContentPane().removeAll();
0488: editorScrollPane.getContentPane().add(
0489: getCurrentEditor().getViewUIComponent(),
0490: BorderLayout.CENTER);
0491:
0492: AccountItem item = (AccountItem) getAccountController()
0493: .getView().getSelectedItem();
0494: if (item.getIdentity().getSignature() != null)
0495: editorScrollPane.getContentPane().add(signatureView,
0496: BorderLayout.SOUTH);
0497:
0498: editorScrollPane.getContentPane().validate();
0499: }
0500:
0501: public boolean isAccountInfoPanelVisible() {
0502: // TODO (@author fdietz): fix account info panel check
0503:
0504: /*
0505: * return isToolbarEnabled(ACCOUNTINFOPANEL);
0506: */
0507:
0508: return true;
0509: }
0510:
0511: /**
0512: * Check if data was entered correctly.
0513: * <p>
0514: * This includes currently a test for an empty subject and a valid recipient
0515: * (to/cc/bcc) list.
0516: *
0517: * @return true, if data was entered correctly
0518: */
0519: public boolean checkState() {
0520: // update ComposerModel based on user-changes in ComposerView
0521: updateComponents(false);
0522:
0523: if (!subjectController.checkState()) {
0524: return false;
0525: }
0526:
0527: return !headerController.checkState();
0528: }
0529:
0530: public void updateComponents(boolean b) {
0531: subjectController.updateComponents(b);
0532: currentEditorController.updateComponents(b);
0533: priorityController.updateComponents(b);
0534: accountController.updateComponents(b);
0535: attachmentController.updateComponents(b);
0536: headerController.updateComponents(b);
0537:
0538: // show attachment panel if necessary
0539: if (b)
0540: showAttachmentPanel();
0541: }
0542:
0543: /**
0544: * @return AccountController
0545: */
0546: public AccountController getAccountController() {
0547: return accountController;
0548: }
0549:
0550: /**
0551: * @return AttachmentController
0552: */
0553: public AttachmentController getAttachmentController() {
0554: return attachmentController;
0555: }
0556:
0557: /**
0558: * @return ComposerSpellCheck
0559: */
0560: public ComposerSpellCheck getComposerSpellCheck() {
0561: return composerSpellCheck;
0562: }
0563:
0564: /**
0565: * @return TextEditorController
0566: */
0567: public AbstractEditorController getCurrentEditor() {
0568: /*
0569: * *20030906, karlpeder* Method signature changed to return an
0570: * AbstractEditorController
0571: */
0572: return currentEditorController;
0573: }
0574:
0575: public TextEditorController getTextEditorController() {
0576: return textEditor;
0577: }
0578:
0579: public HtmlEditorController2 getHtmlEditorController() {
0580: return htmlEditor;
0581: }
0582:
0583: /**
0584: * @return HeaderViewer
0585: */
0586: public HeaderController getHeaderController() {
0587: return headerController;
0588: }
0589:
0590: /**
0591: * @return PriorityController
0592: */
0593: public PriorityController getPriorityController() {
0594: return priorityController;
0595: }
0596:
0597: /**
0598: * @return SubjectController
0599: */
0600: public SubjectController getSubjectController() {
0601: return subjectController;
0602: }
0603:
0604: /**
0605: * @see org.columba.core.gui.FrameController#reset()
0606: */
0607: protected void init() {
0608:
0609: }
0610:
0611: /**
0612: * Returns the composer model
0613: *
0614: * @return Composer model
0615: */
0616: public ComposerModel getModel() {
0617: // if (composerModel == null) // *20030907, karlpeder* initialized in
0618: // init
0619: // composerModel = new ComposerModel();
0620: return composerModel;
0621: }
0622:
0623: /**
0624: * Sets the composer model. If the message type of the new model (html /
0625: * text) is different from the message type of the existing, the editor
0626: * controller is changed and the view is changed accordingly. <br>
0627: * Finally the components are updated according to the new model.
0628: *
0629: * @param model
0630: * New composer model
0631: */
0632: public void setComposerModel(ComposerModel model) {
0633: boolean wasHtml = composerModel.isHtml();
0634: composerModel = model;
0635:
0636: // if (wasHtml != composerModel.isHtml()) {
0637: // setHtmlState(composerModel.isHtml());
0638: // }
0639:
0640: // Update all component according to the new model
0641: updateComponents(true);
0642:
0643: }
0644:
0645: public Charset getCharset() {
0646: return charset;
0647: }
0648:
0649: public void setCharset(Charset charset) {
0650: this .charset = charset;
0651:
0652: ((ComposerModel) getModel()).setCharset(charset);
0653: fireCharsetChanged(new CharsetEvent(this , charset));
0654: }
0655:
0656: public void addCharsetListener(CharsetListener l) {
0657: listenerList.add(CharsetListener.class, l);
0658: }
0659:
0660: public void removeCharsetListener(CharsetListener l) {
0661: listenerList.remove(CharsetListener.class, l);
0662: }
0663:
0664: protected void fireCharsetChanged(CharsetEvent e) {
0665: // Guaranteed to return a non-null array
0666: Object[] listeners = listenerList.getListenerList();
0667:
0668: // Process the listeners last to first, notifying
0669: // those that are interested in this event
0670: for (int i = listeners.length - 2; i >= 0; i -= 2) {
0671: if (listeners[i] == CharsetListener.class) {
0672: ((CharsetListener) listeners[i + 1]).charsetChanged(e);
0673: }
0674: }
0675: }
0676:
0677: public void setHtmlState(boolean enableHtml) {
0678:
0679: composerModel.setHtml(enableHtml);
0680:
0681: // sync model with the current (old) view
0682: updateComponents(false);
0683:
0684: // convert body text to comply with new editor format
0685: String oldBody = composerModel.getBodyText();
0686: String newBody = null;
0687:
0688: if (enableHtml) {
0689: LOG.fine("Converting body text to html");
0690: Charset charset = getCharset();
0691: if (charset == null)
0692: charset = Charset.defaultCharset();
0693: newBody = HtmlParser.textToHtml(oldBody, "", null, charset
0694: .toString());
0695: } else {
0696: LOG.fine("Converting body text to text");
0697: newBody = HtmlParser.htmlToText(oldBody);
0698: }
0699:
0700: composerModel.setBodyText(newBody);
0701:
0702: // switch editor and resync view with model
0703: if (enableHtml)
0704: currentEditorController = htmlEditor;
0705: else
0706: currentEditorController = textEditor;
0707:
0708: // sync view with new update to date model
0709: updateComponents(true);
0710:
0711: // change ui container
0712: layoutEditorContainer();
0713:
0714: // enable/disable html toolbar
0715: if (enableHtml) {
0716: editorPanel.add(toolbarPanel, BorderLayout.NORTH);
0717: } else {
0718: editorPanel.remove(toolbarPanel);
0719: }
0720:
0721: editorPanel.validate();
0722: }
0723:
0724: /**
0725: * @param container
0726: * @see org.columba.core.gui.frame.DefaultFrameController#close(org.columba.api.gui.frame.IContainer)
0727: */
0728: public void close(IContainer container) {
0729:
0730: // don't prompt user if composer should be closed
0731: if (isPromptOnDialogClosing() == false)
0732: return;
0733:
0734: // only prompt user, if composer contains some text
0735: if (currentEditorController.getViewText().length() == 0) {
0736: fireVisibilityChanged(false);
0737:
0738: // close Columba, if composer is only visible frame
0739: FrameManager.getInstance().close(null);
0740:
0741: return;
0742: }
0743:
0744: Object[] options = { "Close", "Cancel", "Save" };
0745: int n = JOptionPane
0746: .showOptionDialog(
0747: container.getFrame(),
0748: "Message wasn't sent. Would you like to save your changes?",
0749: "Warning: Message was modified",
0750: JOptionPane.YES_NO_CANCEL_OPTION,
0751: JOptionPane.QUESTION_MESSAGE, null, options,
0752: options[2]); // default button title
0753:
0754: if (n == 2) {
0755: saveConfiguration();
0756:
0757: // save changes
0758: new SaveAsDraftAction(ComposerController.this )
0759: .actionPerformed(null);
0760:
0761: // close composer
0762: fireVisibilityChanged(false);
0763:
0764: // close Columba, if composer is only visible frame
0765: FrameManager.getInstance().close(null);
0766: } else if (n == 1) {
0767: // cancel question dialog and don't close composer
0768: } else {
0769: saveConfiguration();
0770:
0771: // close composer
0772: fireVisibilityChanged(false);
0773:
0774: // close Columba, if composer is only visible frame
0775: FrameManager.getInstance().close(null);
0776: }
0777:
0778: }
0779:
0780: private void saveConfiguration() {
0781:
0782: // save charset
0783: XmlElement optionsElement = MailConfig.getInstance().get(
0784: "composer_options").getElement("/options");
0785: XmlElement charsetElement = optionsElement
0786: .getElement("charset");
0787:
0788: if (getCharset() == null) {
0789: optionsElement.removeElement(charsetElement);
0790: } else {
0791: if (charsetElement == null) {
0792: charsetElement = new XmlElement("charset");
0793: optionsElement.addElement(charsetElement);
0794: }
0795:
0796: charsetElement.addAttribute("name", getCharset().name());
0797: }
0798:
0799: // save html state
0800: XmlElement htmlElement = optionsElement.getElement("html");
0801: htmlElement.addAttribute("enable", Boolean.toString(getModel()
0802: .isHtml()));
0803: }
0804:
0805: public class ComposerFocusTraversalPolicy extends
0806: FocusTraversalPolicy {
0807:
0808: public Component getComponentAfter(Container focusCycleRoot,
0809: Component aComponent) {
0810: if (aComponent.equals(accountController.getView()))
0811: return priorityController.getView();
0812: else if (aComponent.equals(priorityController.getView()))
0813: return headerController.getView().getToComboBox();
0814: else if (aComponent.equals(headerController.getView()
0815: .getToComboBox()))
0816: return headerController.getView().getCcComboBox();
0817: else if (aComponent.equals(headerController.getView()
0818: .getCcComboBox()))
0819: return headerController.getView().getBccComboBox();
0820: else if (aComponent.equals(headerController.getView()
0821: .getBccComboBox()))
0822: return subjectController.getView();
0823: else if (aComponent.equals(subjectController.getView()))
0824: return currentEditorController.getComponent();
0825:
0826: return headerController.getView().getToComboBox();
0827: }
0828:
0829: public Component getComponentBefore(Container focusCycleRoot,
0830: Component aComponent) {
0831: if (aComponent.equals(currentEditorController
0832: .getComponent()))
0833: return subjectController.getView();
0834: else if (aComponent.equals(subjectController.getView()))
0835: return headerController.getView().getBccComboBox();
0836: else if (aComponent.equals(headerController.getView()
0837: .getBccComboBox()))
0838: return headerController.getView().getCcComboBox();
0839: else if (aComponent.equals(headerController.getView()
0840: .getCcComboBox()))
0841: return headerController.getView().getToComboBox();
0842: else if (aComponent.equals(headerController.getView()
0843: .getToComboBox()))
0844: return priorityController.getView();
0845: else if (aComponent.equals(priorityController.getView()))
0846: return accountController.getView();
0847:
0848: return currentEditorController.getComponent();
0849: }
0850:
0851: public Component getDefaultComponent(Container focusCycleRoot) {
0852: return headerController.getView().getToComboBox();
0853: }
0854:
0855: public Component getLastComponent(Container focusCycleRoot) {
0856: return currentEditorController.getComponent();
0857: }
0858:
0859: public Component getFirstComponent(Container focusCycleRoot) {
0860: return accountController.getView();
0861: }
0862: }
0863:
0864: /**
0865: * @return panel
0866: * @see org.columba.api.gui.frame.IContentPane#getComponent()
0867: */
0868: public JComponent getComponent() {
0869: JPanel panel = new JPanel();
0870: panel.setLayout(new BorderLayout());
0871:
0872: panel.add(centerPanel, BorderLayout.CENTER);
0873:
0874: return panel;
0875: }
0876:
0877: /**
0878: * @see org.columba.api.gui.frame.IFrameMediator#getString(java.lang.String,
0879: * java.lang.String, java.lang.String)
0880: */
0881: public String getString(String sPath, String sName, String sID) {
0882: return MailResourceLoader.getString(sPath, sName, sID);
0883: }
0884:
0885: class MyDocumentListener implements DocumentListener {
0886:
0887: public void changedUpdate(DocumentEvent arg0) {
0888: handleEvent(arg0);
0889: }
0890:
0891: public void insertUpdate(DocumentEvent arg0) {
0892: handleEvent(arg0);
0893: }
0894:
0895: public void removeUpdate(DocumentEvent arg0) {
0896: handleEvent(arg0);
0897: }
0898:
0899: private void handleEvent(DocumentEvent arg0) {
0900: Document doc = arg0.getDocument();
0901: try {
0902: String subject = doc.getText(0, doc.getLength());
0903:
0904: fireTitleChanged(subject);
0905: } catch (BadLocationException e) {
0906: }
0907: }
0908:
0909: }
0910:
0911: /**
0912: * @return Returns the promptOnDialogClosing.
0913: */
0914: public boolean isPromptOnDialogClosing() {
0915: return promptOnDialogClosing;
0916: }
0917:
0918: /**
0919: * @param promptOnDialogClosing
0920: * The promptOnDialogClosing to set.
0921: */
0922: public void setPromptOnDialogClosing(boolean promptOnDialogClosing) {
0923: this .promptOnDialogClosing = promptOnDialogClosing;
0924: }
0925:
0926: public void itemStateChanged(ItemEvent e) {
0927: if (e.getStateChange() == ItemEvent.SELECTED) {
0928:
0929: AccountItem item = (AccountItem) getAccountController()
0930: .getView().getSelectedItem();
0931: if (item.getIdentity().getSignature() != null) {
0932: // show signature viewer
0933: editorScrollPane.getContentPane().add(signatureView,
0934: BorderLayout.SOUTH);
0935: editorScrollPane.revalidate();
0936: } else {
0937: // hide signature viewer
0938: editorScrollPane.getContentPane().remove(signatureView);
0939: editorScrollPane.revalidate();
0940: }
0941: }
0942:
0943: }
0944:
0945: public JPanel getContentPane() {
0946: return (JPanel) getComponent();
0947: }
0948:
0949: /**
0950: * container callbacks
0951: *
0952: * @param container
0953: */
0954:
0955: public void extendMenu(IContainer container) {
0956: try {
0957: InputStream is = DiskIO
0958: .getResourceStream("org/columba/mail/action/composer_menu.xml");
0959: container.extendMenu(this , is);
0960:
0961: } catch (IOException e) {
0962: LOG.severe(e.getMessage());
0963: }
0964: }
0965:
0966: public void extendToolBar(IContainer container) {
0967: try {
0968: File configDirectory = MailConfig.getInstance()
0969: .getConfigDirectory();
0970: InputStream is2 = new FileInputStream(new File(
0971: configDirectory, "composer_toolbar.xml"));
0972: container.extendToolbar(this , is2);
0973: } catch (IOException e) {
0974: e.printStackTrace();
0975: }
0976: }
0977:
0978: public void initFrame(IContainer container) {
0979: container.getFrame().setFocusTraversalPolicy(
0980: new ComposerFocusTraversalPolicy());
0981:
0982: // make sure that JFrame is not closed automatically
0983: // -> we want to prompt the user to save his work
0984: container.setCloseOperation(false);
0985: }
0986:
0987: /**
0988: * Method is called when composer configuration changed
0989: *
0990: * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
0991: */
0992: public void update(Observable observable, Object obj) {
0993: // if (observable instanceof XmlElement) {
0994: // // possibly change btw. html and text
0995: // XmlElement e = (XmlElement) obj;
0996: //
0997: // if (e.getName().equals("html")) {
0998: // String enableHtml = e.getAttribute("enable", "false");
0999: //
1000: // // This action should only be enabled in html mode
1001: // getModel().setHtml(Boolean.valueOf(enableHtml).booleanValue());
1002: // }
1003: // }
1004: }
1005: }
|