org.columba.mail.gui.composer.ComposerController.java Source code

Java tutorial

Introduction

Here is the source code for org.columba.mail.gui.composer.ComposerController.java

Source

// The contents of this file are subject to the Mozilla Public License Version
// 1.1
//(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 Columba Project"
//
//The Initial Developers of the Original Code are Frederik Dietz and Timo
// Stich.
//Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
//
//All Rights Reserved.

package org.columba.mail.gui.composer;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.FocusTraversalPolicy;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Observable;
import java.util.Observer;
import java.util.logging.Logger;

import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.EventListenerList;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;

import org.columba.api.gui.frame.IContainer;
import org.columba.core.charset.CharsetEvent;
import org.columba.core.charset.CharsetListener;
import org.columba.core.charset.CharsetOwnerInterface;
import org.columba.core.config.ViewItem;
import org.columba.core.gui.base.LabelWithMnemonic;
import org.columba.core.gui.frame.DefaultFrameController;
import org.columba.core.gui.frame.FrameManager;
import org.columba.core.io.DiskIO;
import org.columba.core.xml.XmlElement;
import org.columba.mail.config.AccountItem;
import org.columba.mail.config.MailConfig;
import org.columba.mail.gui.composer.action.SaveAsDraftAction;
import org.columba.mail.gui.composer.html.HtmlEditorController2;
import org.columba.mail.gui.composer.html.HtmlToolbar;
import org.columba.mail.gui.composer.text.TextEditorController;
import org.columba.mail.gui.message.viewer.MessageBorder;
import org.columba.mail.parser.text.HtmlParser;
import org.columba.mail.util.MailResourceLoader;
import org.frapuccino.swing.MultipleTransferHandler;

import com.jgoodies.forms.debug.FormDebugPanel;
import com.jgoodies.forms.factories.FormFactory;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.ColumnSpec;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.FormSpec;
import com.jgoodies.forms.layout.RowSpec;
import com.jgoodies.forms.layout.Sizes;

/**
 *
 * controller for message composer dialog
 *
 * @author fdietz
 */
public class ComposerController extends DefaultFrameController implements CharsetOwnerInterface, ItemListener {

    /** JDK 1.4+ logging framework logger, used for logging. */
    private static final Logger LOG = Logger.getLogger("org.columba.mail.gui.composer");

    private AttachmentController attachmentController;

    private SubjectController subjectController;

    private PriorityController priorityController;

    private AccountController accountController;

    private AbstractEditorController currentEditorController;

    private HeaderController headerController;

    private ComposerSpellCheck composerSpellCheck;

    private ComposerModel composerModel;

    private EventListenerList listenerList = new EventListenerList();

    private JSplitPane attachmentSplitPane;

    /** Editor viewer resides in this panel */
    private TextEditorPanel editorScrollPane;

    private LabelWithMnemonic subjectLabel;

    private LabelWithMnemonic smtpLabel;

    private LabelWithMnemonic priorityLabel;

    private JPanel centerPanel = new FormDebugPanel();

    private JPanel topPanel;

    private HtmlToolbar htmlToolbar;

    private boolean promptOnDialogClosing = true;

    private SignatureView signatureView;

    private boolean attachmentPanelShown;

    private JPanel editorPanel = new JPanel();

    JPanel toolbarPanel = new JPanel();

    private TextEditorController textEditor;

    private HtmlEditorController2 htmlEditor;

    public ComposerController() {
        this(new ComposerModel(), FrameManager.getInstance().createCustomViewItem("Composer"));

    }

    public ComposerController(ComposerModel model, ViewItem viewItem) {
        super(viewItem);

        // init model (defaults to empty plain text message)
        composerModel = model;

        // init controllers for different parts of the composer
        attachmentController = new AttachmentController(this);
        headerController = new HeaderController(this);
        subjectController = new SubjectController(this);

        // listen to changes in the Subject to update the title bar
        // of the message composer window
        getSubjectController().getView().getDocument().addDocumentListener(new MyDocumentListener());

        priorityController = new PriorityController(this);
        accountController = new AccountController(this);
        accountController.getView().addItemListener(this);
        composerSpellCheck = new ComposerSpellCheck();

        signatureView = new SignatureView(this);

        // set default html or text based on stored option
        // ... can be overridden by setting the composer model
        getModel().setHtml(useHtml());

        htmlEditor = new HtmlEditorController2(this);

        textEditor = new TextEditorController(this);

        // init controller for the editor depending on message type
        if (getModel().isHtml())
            currentEditorController = htmlEditor;
        else
            currentEditorController = textEditor;

        initComponents();

        // add JPanel with useful HTML related actions.
        htmlToolbar = new HtmlToolbar(this);

        layoutComponents();

        showAttachmentPanel();

        XmlElement optionsElement = MailConfig.getInstance().get("composer_options").getElement("/options");
        // Hack to ensure charset is set correctly at start-up
        XmlElement charsetElement = optionsElement.getElement("charset");

        if (charsetElement != null) {
            String charset = charsetElement.getAttribute("name");

            if (charset != null) {
                try {
                    setCharset(Charset.forName(charset));
                } catch (UnsupportedCharsetException ex) {
                    // ignore this
                }
            }
        }

        // Setup DnD for the text and attachment list control.
        ComposerAttachmentTransferHandler dndTransferHandler = new ComposerAttachmentTransferHandler(
                attachmentController);
        attachmentController.getView().setDragEnabled(true);
        attachmentController.getView().setTransferHandler(dndTransferHandler);

        JEditorPane editorComponent = (JEditorPane) getCurrentEditor().getComponent();
        MultipleTransferHandler compositeHandler = new MultipleTransferHandler();
        compositeHandler.addTransferHandler(editorComponent.getTransferHandler());
        compositeHandler.addTransferHandler(dndTransferHandler);
        editorComponent.setDragEnabled(true);
        editorComponent.setTransferHandler(compositeHandler);
    }

    public static boolean useHtml() {
        XmlElement optionsElement = MailConfig.getInstance().get("composer_options").getElement("/options");

        XmlElement htmlElement = optionsElement.getElement("html");

        // create default element if not available
        if (htmlElement == null) {
            htmlElement = optionsElement.addSubElement("html");
        }

        String useHtml = htmlElement.getAttribute("enable", "false");

        return useHtml.equals("true");
    }

    /**
     * Show attachment panel
     * <p>
     * Asks the ComposerModel if message contains attachments. If so, show the
     * attachment panel. Otherwise, hide the attachment panel.
     */
    public void showAttachmentPanel() {
        if (attachmentPanelShown == getAttachmentController().getView().count() > 0)
            return;

        // remove all components from container
        centerPanel.removeAll();

        // re-add all top components like recipient editor/subject editor
        centerPanel.add(topPanel, BorderLayout.NORTH);

        // if message contains attachments
        if (getAttachmentController().getView().count() > 0) {
            // create scrollapen
            JScrollPane attachmentScrollPane = new JScrollPane(getAttachmentController().getView());
            attachmentScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
            attachmentScrollPane.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
            // create splitpane containing the bodytext editor and the
            // attachment panel
            attachmentSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, editorScrollPane, attachmentScrollPane);
            attachmentSplitPane.setDividerLocation(0.80);
            attachmentSplitPane.setBorder(null);

            // add splitpane to the center
            centerPanel.add(attachmentSplitPane, BorderLayout.CENTER);

            // ViewItem viewItem = getViewItem();

            // default value is 200 pixel
            // int pos =
            // viewItem.getIntegerWithDefault("splitpanes","attachment", 200);
            attachmentSplitPane.setDividerLocation(200);

            attachmentPanelShown = true;
        } else {
            // no attachments
            // -> only show bodytext editor
            centerPanel.add(editorPanel, BorderLayout.CENTER);

            attachmentPanelShown = false;
        }

        // re-paint composer-view
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                fireLayoutChanged();
            }
        });

    }

    /**
     * @return Returns the attachmentSplitPane.
     */
    public JSplitPane getAttachmentSplitPane() {
        return attachmentSplitPane;
    }

    /**
     * init components
     */
    protected void initComponents() {
        subjectLabel = new LabelWithMnemonic(MailResourceLoader.getString("dialog", "composer", "subject"));
        smtpLabel = new LabelWithMnemonic(MailResourceLoader.getString("dialog", "composer", "identity"));
        priorityLabel = new LabelWithMnemonic(MailResourceLoader.getString("dialog", "composer", "priority"));

        editorScrollPane = new TextEditorPanel();
    }

    /**
     * Layout components
     */
    public void layoutComponents() {
        centerPanel.removeAll();

        editorPanel.setLayout(new BorderLayout());

        toolbarPanel.setLayout(new BorderLayout());
        toolbarPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 4, 0));
        toolbarPanel.add(htmlToolbar);
        toolbarPanel.setBackground(UIManager.getColor("TextArea.background"));

        topPanel = new JPanel();
        topPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));

        FormLayout layout = new FormLayout(
                new ColumnSpec[] { new ColumnSpec("center:max(pref;50dlu)"),
                        FormFactory.LABEL_COMPONENT_GAP_COLSPEC, FormFactory.GROWING_BUTTON_COLSPEC,
                        FormFactory.LABEL_COMPONENT_GAP_COLSPEC, FormFactory.DEFAULT_COLSPEC,
                        FormFactory.LABEL_COMPONENT_GAP_COLSPEC, FormFactory.GROWING_BUTTON_COLSPEC },
                new RowSpec[] { new RowSpec(RowSpec.FILL, Sizes.DEFAULT, FormSpec.NO_GROW),
                        FormFactory.LINE_GAP_ROWSPEC, new RowSpec(RowSpec.FILL, Sizes.DEFAULT, FormSpec.NO_GROW),
                        FormFactory.LINE_GAP_ROWSPEC, new RowSpec(RowSpec.FILL, Sizes.DEFAULT, FormSpec.NO_GROW),
                        FormFactory.LINE_GAP_ROWSPEC, new RowSpec(RowSpec.FILL, Sizes.DEFAULT, FormSpec.NO_GROW),
                        FormFactory.LINE_GAP_ROWSPEC, new RowSpec(RowSpec.FILL, Sizes.DEFAULT, FormSpec.NO_GROW) });
        layout.setRowGroups(new int[][] { { 1, 3, 5, 7, 9 } });
        layout.setColumnGroups(new int[][] { { 1 } });

        topPanel.setLayout(layout);

        CellConstraints c = new CellConstraints();

        topPanel.add(smtpLabel, c.xy(1, 1, CellConstraints.CENTER, CellConstraints.DEFAULT));

        topPanel.add(getAccountController().getView(), c.xy(3, 1));
        topPanel.add(priorityLabel, c.xy(5, 1));
        topPanel.add(getPriorityController().getView(), c.xy(7, 1));

        getHeaderController().getView().layoutComponents(topPanel);

        topPanel.add(subjectLabel, c.xy(1, 9, CellConstraints.CENTER, CellConstraints.DEFAULT));

        topPanel.add(getSubjectController().getView(), c.xywh(3, 9, 5, 1));

        if (composerModel.isHtml())
            editorPanel.add(toolbarPanel, BorderLayout.NORTH);

        editorScrollPane.getContentPane().add(getCurrentEditor().getViewUIComponent(), BorderLayout.CENTER);

        editorPanel.add(editorScrollPane, BorderLayout.CENTER);

        Border outterBorder = BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5),
                new MessageBorder(Color.LIGHT_GRAY, 1, true));
        Border innerBorder = BorderFactory.createCompoundBorder(outterBorder, new LineBorder(Color.WHITE, 5, true));
        editorPanel.setBorder(innerBorder);

        AccountItem item = (AccountItem) getAccountController().getView().getSelectedItem();
        if (item.getIdentity().getSignature() != null)
            editorScrollPane.getContentPane().add(signatureView, BorderLayout.SOUTH);

        editorScrollPane.addMouseListener(new MouseListener() {

            public void mouseClicked(MouseEvent e) {
                currentEditorController.getComponent().requestFocus();
            }

            public void mouseEntered(MouseEvent e) {
            }

            public void mouseExited(MouseEvent e) {
            }

            public void mousePressed(MouseEvent e) {
            }

            public void mouseReleased(MouseEvent e) {
            }

        });

        centerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
        centerPanel.setLayout(new BorderLayout());

        centerPanel.add(topPanel, BorderLayout.NORTH);

        centerPanel.add(editorPanel, BorderLayout.CENTER);

        attachmentPanelShown = false;
    }

    /**
     * Returns a reference to the panel, that holds the editor view. This is
     * used by the ComposerController when adding a listener to that panel.
     *
     * @return editor panel reference
     */
    public JPanel getEditorPanel() {
        return editorScrollPane.getContentPane();
    }

    /**
     * Used to update the panel, that holds the editor viewer. This is necessary
     * e.g. if the ComposerModel is changed to hold another message type (text /
     * html), which the previous editor can not handle. If so a new editor
     * controller is created, and thereby a new view.
     */
    public void layoutEditorContainer() {

        // update panel
        editorScrollPane.getContentPane().removeAll();
        editorScrollPane.getContentPane().add(getCurrentEditor().getViewUIComponent(), BorderLayout.CENTER);

        AccountItem item = (AccountItem) getAccountController().getView().getSelectedItem();
        if (item.getIdentity().getSignature() != null)
            editorScrollPane.getContentPane().add(signatureView, BorderLayout.SOUTH);

        editorScrollPane.getContentPane().validate();
    }

    public boolean isAccountInfoPanelVisible() {
        // TODO (@author fdietz): fix account info panel check

        /*
         * return isToolbarEnabled(ACCOUNTINFOPANEL);
         */

        return true;
    }

    /**
     * Check if data was entered correctly.
     * <p>
     * This includes currently a test for an empty subject and a valid recipient
     * (to/cc/bcc) list.
     *
     * @return true, if data was entered correctly
     */
    public boolean checkState() {
        // update ComposerModel based on user-changes in ComposerView
        updateComponents(false);

        if (!subjectController.checkState()) {
            return false;
        }

        return headerController.checkState();
    }

    public void updateComponents(boolean b) {
        subjectController.updateComponents(b);
        currentEditorController.updateComponents(b);
        priorityController.updateComponents(b);
        accountController.updateComponents(b);
        attachmentController.updateComponents(b);
        headerController.updateComponents(b);

        // show attachment panel if necessary
        if (b)
            showAttachmentPanel();
    }

    /**
     * @return AccountController
     */
    public AccountController getAccountController() {
        return accountController;
    }

    /**
     * @return AttachmentController
     */
    public AttachmentController getAttachmentController() {
        return attachmentController;
    }

    /**
     * @return ComposerSpellCheck
     */
    public ComposerSpellCheck getComposerSpellCheck() {
        return composerSpellCheck;
    }

    /**
     * @return TextEditorController
     */
    public AbstractEditorController getCurrentEditor() {
        return currentEditorController;
    }

    public TextEditorController getTextEditorController() {
        return textEditor;
    }

    public HtmlEditorController2 getHtmlEditorController() {
        return htmlEditor;
    }

    /**
     * @return HeaderViewer
     */
    public HeaderController getHeaderController() {
        return headerController;
    }

    /**
     * @return PriorityController
     */
    public PriorityController getPriorityController() {
        return priorityController;
    }

    /**
     * @return SubjectController
     */
    public SubjectController getSubjectController() {
        return subjectController;
    }

    /**
     * @see org.columba.core.gui.FrameController#reset()
     */
    protected void init() {

    }

    /**
     * Returns the composer model
     *
     * @return Composer model
     */
    public ComposerModel getModel() {
        return composerModel;
    }

    /**
     * Sets the composer model. If the message type of the new model (html /
     * text) is different from the message type of the existing, the editor
     * controller is changed and the view is changed accordingly. <br>
     * Finally the components are updated according to the new model.
     *
     * @param model
     *            New composer model
     */
    public void setComposerModel(ComposerModel model) {
        boolean wasHtml = composerModel.isHtml();
        composerModel = model;

        if (wasHtml != composerModel.isHtml()) {
            updateEditorHtmlState();
        } else {
            updateComponents(true);
        }

        fireCharsetChanged(new CharsetEvent(this, getModel().getCharset()));
    }

    public Charset getCharset() {
        return getModel().getCharset();
    }

    public void setCharset(Charset charset) {
        getModel().setCharset(charset);
        fireCharsetChanged(new CharsetEvent(this, charset));
    }

    public void addCharsetListener(CharsetListener l) {
        listenerList.add(CharsetListener.class, l);
    }

    public void removeCharsetListener(CharsetListener l) {
        listenerList.remove(CharsetListener.class, l);
    }

    protected void fireCharsetChanged(CharsetEvent e) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();

        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == CharsetListener.class) {
                ((CharsetListener) listeners[i + 1]).charsetChanged(e);
            }
        }
    }

    private void updateEditorHtmlState() {
        // switch editor and resync view with model
        if (composerModel.isHtml())
            currentEditorController = htmlEditor;
        else
            currentEditorController = textEditor;

        // sync view with new update to date model
        updateComponents(true);

        // change ui container
        layoutEditorContainer();

        // enable/disable html toolbar
        if (composerModel.isHtml()) {
            editorPanel.add(toolbarPanel, BorderLayout.NORTH);
        } else {
            editorPanel.remove(toolbarPanel);
        }

        editorPanel.validate();
    }

    public void setHtmlState(boolean enableHtml) {
        if (enableHtml == composerModel.isHtml())
            return;

        composerModel.setHtml(enableHtml);

        // sync model with the current (old) view
        updateComponents(false);

        // convert body text to comply with new editor format
        String oldBody = composerModel.getBodyText();
        String newBody = null;

        if (enableHtml) {
            LOG.fine("Converting body text to html");
            Charset charset = getCharset();
            if (charset == null)
                charset = Charset.defaultCharset();
            newBody = HtmlParser.textToHtml(oldBody, "", null, charset.toString());
        } else {
            LOG.fine("Converting body text to text");
            newBody = HtmlParser.htmlToText(oldBody);
        }

        composerModel.setBodyText(newBody);

        updateEditorHtmlState();
    }

    /**
     * @param container
     * @see org.columba.core.gui.frame.DefaultFrameController#close(org.columba.api.gui.frame.IContainer)
     */
    public void close(IContainer container) {
        // don't prompt user if composer should be closed
        if (isPromptOnDialogClosing() == false)
            return;

        // only prompt user, if composer contains some text
        if (currentEditorController.getViewText().length() == 0) {
            fireVisibilityChanged(false);

            // close Columba, if composer is only visible frame
            FrameManager.getInstance().close(null);

            return;
        }

        Object[] options = { "Close", "Cancel", "Save" };
        int n = JOptionPane.showOptionDialog(container.getFrame(),
                "Message wasn't sent. Would you like to save your changes?", "Warning: Message was modified",
                JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[2]); // default button title

        if (n == 2) {
            saveConfiguration();

            // save changes
            new SaveAsDraftAction(ComposerController.this).actionPerformed(null);

            // close composer
            fireVisibilityChanged(false);

            // close Columba, if composer is only visible frame
            FrameManager.getInstance().close(null);
        } else if (n == 1) {
            // cancel question dialog and don't close composer
        } else {
            saveConfiguration();

            // close composer
            fireVisibilityChanged(false);

            // close Columba, if composer is only visible frame
            FrameManager.getInstance().close(null);
        }

    }

    private void saveConfiguration() {
        // save charset
        XmlElement optionsElement = MailConfig.getInstance().get("composer_options").getElement("/options");
        XmlElement charsetElement = optionsElement.getElement("charset");

        if (getCharset() == null) {
            optionsElement.removeElement(charsetElement);
        } else {
            if (charsetElement == null) {
                charsetElement = new XmlElement("charset");
                optionsElement.addElement(charsetElement);
            }

            charsetElement.addAttribute("name", getCharset().name());
        }

        // save html state
        XmlElement htmlElement = optionsElement.getElement("html");
        htmlElement.addAttribute("enable", Boolean.toString(getModel().isHtml()));
    }

    public class ComposerFocusTraversalPolicy extends FocusTraversalPolicy {

        public Component getComponentAfter(Container focusCycleRoot, Component aComponent) {
            if (aComponent.equals(accountController.getView()))
                return priorityController.getView();
            else if (aComponent.equals(priorityController.getView()))
                return headerController.getView().getToComboBox();
            else if (aComponent.equals(headerController.getView().getToComboBox()))
                return headerController.getView().getCcComboBox();
            else if (aComponent.equals(headerController.getView().getCcComboBox()))
                return headerController.getView().getBccComboBox();
            else if (aComponent.equals(headerController.getView().getBccComboBox()))
                return subjectController.getView();
            else if (aComponent.equals(subjectController.getView()))
                return currentEditorController.getComponent();

            return headerController.getView().getToComboBox();
        }

        public Component getComponentBefore(Container focusCycleRoot, Component aComponent) {
            if (aComponent.equals(currentEditorController.getComponent()))
                return subjectController.getView();
            else if (aComponent.equals(subjectController.getView()))
                return headerController.getView().getBccComboBox();
            else if (aComponent.equals(headerController.getView().getBccComboBox()))
                return headerController.getView().getCcComboBox();
            else if (aComponent.equals(headerController.getView().getCcComboBox()))
                return headerController.getView().getToComboBox();
            else if (aComponent.equals(headerController.getView().getToComboBox()))
                return priorityController.getView();
            else if (aComponent.equals(priorityController.getView()))
                return accountController.getView();

            return currentEditorController.getComponent();
        }

        public Component getDefaultComponent(Container focusCycleRoot) {
            return headerController.getView().getToComboBox();
        }

        public Component getLastComponent(Container focusCycleRoot) {
            return currentEditorController.getComponent();
        }

        public Component getFirstComponent(Container focusCycleRoot) {
            return accountController.getView();
        }
    }

    /**
     * @return panel
     * @see org.columba.api.gui.frame.IContentPane#getComponent()
     */
    public JComponent getComponent() {
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());

        panel.add(centerPanel, BorderLayout.CENTER);

        return panel;
    }

    /**
     * @see org.columba.api.gui.frame.IFrameMediator#getString(java.lang.String,
     *      java.lang.String, java.lang.String)
     */
    public String getString(String sPath, String sName, String sID) {
        return MailResourceLoader.getString(sPath, sName, sID);
    }

    class MyDocumentListener implements DocumentListener {

        public void changedUpdate(DocumentEvent arg0) {
            handleEvent(arg0);
        }

        public void insertUpdate(DocumentEvent arg0) {
            handleEvent(arg0);
        }

        public void removeUpdate(DocumentEvent arg0) {
            handleEvent(arg0);
        }

        private void handleEvent(DocumentEvent arg0) {
            Document doc = arg0.getDocument();
            try {
                String subject = doc.getText(0, doc.getLength());

                fireTitleChanged(subject);
            } catch (BadLocationException e) {
            }
        }

    }

    /**
     * @return Returns the promptOnDialogClosing.
     */
    public boolean isPromptOnDialogClosing() {
        return promptOnDialogClosing;
    }

    /**
     * @param promptOnDialogClosing
     *            The promptOnDialogClosing to set.
     */
    public void setPromptOnDialogClosing(boolean promptOnDialogClosing) {
        this.promptOnDialogClosing = promptOnDialogClosing;
    }

    public void itemStateChanged(ItemEvent e) {
        if (e.getStateChange() == ItemEvent.SELECTED) {

            AccountItem item = (AccountItem) getAccountController().getView().getSelectedItem();
            if (item.getIdentity().getSignature() != null) {
                // show signature viewer
                editorScrollPane.getContentPane().add(signatureView, BorderLayout.SOUTH);
                editorScrollPane.revalidate();
            } else {
                // hide signature viewer
                editorScrollPane.getContentPane().remove(signatureView);
                editorScrollPane.revalidate();
            }
        }

    }

    public JPanel getContentPane() {
        return (JPanel) getComponent();
    }

    /**
     * container callbacks
     *
     * @param container
     */

    public void extendMenu(IContainer container) {
        try {
            InputStream is = DiskIO.getResourceStream("org/columba/mail/action/composer_menu.xml");
            container.extendMenu(this, is);

        } catch (IOException e) {
            LOG.severe(e.getMessage());
        }
    }

    public void extendToolBar(IContainer container) {
        try {
            File configDirectory = MailConfig.getInstance().getConfigDirectory();
            InputStream is2 = new FileInputStream(new File(configDirectory, "composer_toolbar.xml"));
            container.extendToolbar(this, is2);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void initFrame(IContainer container) {
        container.getFrame().setFocusTraversalPolicy(new ComposerFocusTraversalPolicy());

        // make sure that JFrame is not closed automatically
        // -> we want to prompt the user to save his work
        container.setCloseOperation(false);
    }
}