/*
 * This file is part of KMail.
 * Copyright (c) 2011,2012,2013,2014 Laurent Montel <montel@kde.org>
 *
 * Copyright (c) 2009 Constantin Berzan <exit3219@gmail.com>
 *
 * Based on KMail code by:
 * Copyright (c) 1997 Markus Wuebben <markus.wuebben@kde.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
#include "kmcomposewin.h"

// KMail includes
#include "job/addressvalidationjob.h"
#include "attachmentcontroller.h"
#include "messagecomposer/attachment/attachmentmodel.h"
#include "attachmentview.h"
#include "codecaction.h"
#include <messagecomposer/job/emailaddressresolvejob.h>
#include "kleo_util.h"
#include "kmcommands.h"
#include "editor/kmcomposereditor.h"
#include "kmkernel.h"
#include "settings/globalsettings.h"
#include "kmmainwin.h"
#include "kmmainwidget.h"
#include "mailcomposeradaptor.h" // TODO port all D-Bus stuff...
#include "messageviewer/viewer/stl_util.h"
#include "messageviewer/utils/util.h"
#include "messagecore/utils/stringutil.h"
#include "messagecore/attachment/attachmentcollector.h"
#include "util.h"
#include "editor/snippetwidget.h"
#include "templatesconfiguration_kfg.h"
#include "foldercollectionmonitor.h"
#include "kernel/mailkernel.h"
#include "custommimeheader.h"
#include "pimcommon/autocorrection/widgets/lineeditwithautocorrection.h"
#include "pimcommon/translator/translatorwidget.h"
#include "pimcommon/widgets/customtoolswidget.h"
#include "warningwidgets/attachmentmissingwarning.h"
#include "job/createnewcontactjob.h"
#include "job/savedraftjob.h"
#include "warningwidgets/externaleditorwarning.h"
#include "cryptostateindicatorwidget.h"
#include "validatesendmailshortcut.h"

#include "editor/kmstorageservice.h"
#include "followupreminder/followupreminderselectdatedialog.h"


#include "libkdepim/progresswidget/statusbarprogresswidget.h"
#include "libkdepim/progresswidget/progressstatusbarwidget.h"

#include "pimcommon/util/editorutil.h"
#include "pimcommon/storageservice/storageservicemanager.h"
#include "pimcommon/storageservice/storageserviceprogressmanager.h"

#include "agents/sendlateragent/sendlaterutil.h"
#include "agents/sendlateragent/sendlaterdialog.h"
#include "agents/sendlateragent/sendlaterinfo.h"

// KDEPIM includes
#include <libkpgp/kpgpblock.h>
#include <libkleo/ui/progressdialog.h>
#include <libkleo/ui/keyselectiondialog.h>
#include "kleo/cryptobackendfactory.h"
#include "kleo/exportjob.h"
#include "kleo/specialjob.h"
#include <messageviewer/viewer/objecttreeemptysource.h>

#ifndef QT_NO_CURSOR
#include <messageviewer/utils/kcursorsaver.h>
#endif

#include <messageviewer/viewer/objecttreeparser.h>
#include <messageviewer/viewer/nodehelper.h>
//#include "messageviewer/chiasmuskeyselector.h"
#include <messageviewer/settings/globalsettings.h>
#include <messagecomposer/composer/composer.h>
#include <messagecomposer/part/globalpart.h>
#include <messagecomposer/part/infopart.h>
#include <messagecomposer/part/textpart.h>
#include <settings/messagecomposersettings.h>
#include <messagecomposer/helper/messagehelper.h>
#include <messagecomposer/composer/signaturecontroller.h>
#include <messagecomposer/job/inserttextfilejob.h>
#include <messagecomposer/composer/composerlineedit.h>
#include <messagecore/attachment/attachmentpart.h>
#include "messagecore/settings/globalsettings.h"
#include <templateparser/templateparser.h>
#include <templatesconfiguration.h>
#include "messagecore/helpers/nodehelper.h"
#include <akonadi/kmime/messagestatus.h>
#include "messagecore/helpers/messagehelpers.h"
#include "mailcommon/folder/folderrequester.h"
#include "mailcommon/folder/foldercollection.h"

// LIBKDEPIM includes
#include <libkdepim/addressline/recentaddresses.h>

// KDEPIMLIBS includes
#include <akonadi/changerecorder.h>
#include <akonadi/itemcreatejob.h>
#include <akonadi/entitymimetypefiltermodel.h>
#include <akonadi/itemfetchjob.h>
#include <kpimutils/email.h>
#include <kpimidentities/identitymanager.h>
#include <kpimidentities/identitycombo.h>
#include <kpimidentities/identity.h>
#include <kpimidentities/signature.h>
#include <mailtransport/transportcombobox.h>
#include <mailtransport/transportmanager.h>
#include <mailtransport/transport.h>
#include <kmime/kmime_codecs.h>
#include <kmime/kmime_message.h>
#include <kpimtextedit/selectspecialchar.h>


// KDELIBS includes
#include <kactioncollection.h>
#include <kactionmenu.h>
#include <kapplication.h>
#include <kcharsets.h>
#include <kdebug.h>
#include <kdescendantsproxymodel.h>
#include <kedittoolbar.h>
#include <kinputdialog.h>
#include <kmenu.h>
#include <kmimetype.h>
#include <kmessagebox.h>
#include <krecentfilesaction.h>
#include <kshortcutsdialog.h>
#include <kstandarddirs.h>
#include <kstandardshortcut.h>
#include <kstatusbar.h>
#include <ktempdir.h>
#include <ktoggleaction.h>
#include <ktoolbar.h>
#include <ktoolinvocation.h>
#include <sonnet/dictionarycombobox.h>
#include <krun.h>
#include <KIO/JobUiDelegate>
#include <KPrintPreview>
#include <KFileDialog>

// Qt includes
#include <QClipboard>
#include <QSplitter>
#include <QMimeData>
#include <QTextDocumentWriter>

// System includes
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <memory>
#include <boost/shared_ptr.hpp>

using Sonnet::DictionaryComboBox;
using MailTransport::TransportManager;
using MailTransport::Transport;
using KPIM::RecentAddresses;
using MessageComposer::KMeditor;

KMail::Composer *KMail::makeComposer( const KMime::Message::Ptr &msg, bool lastSignState, bool lastEncryptState, Composer::TemplateContext context,
                                      uint identity, const QString & textSelection,
                                      const QString & customTemplate ) {
    return KMComposeWin::create( msg, lastSignState, lastEncryptState, context, identity, textSelection, customTemplate );
}

KMail::Composer *KMComposeWin::create( const KMime::Message::Ptr &msg, bool lastSignState, bool lastEncryptState, Composer::TemplateContext context,
                                       uint identity, const QString & textSelection,
                                       const QString & customTemplate ) {
    return new KMComposeWin( msg, lastSignState, lastEncryptState, context, identity, textSelection, customTemplate );
}

int KMComposeWin::s_composerNumber = 0;

//-----------------------------------------------------------------------------
KMComposeWin::KMComposeWin( const KMime::Message::Ptr &aMsg, bool lastSignState, bool lastEncryptState, Composer::TemplateContext context, uint id,
                            const QString & textSelection, const QString & customTemplate )
    : KMail::Composer( "kmail-composer#" ),
      mDone( false ),
      mTextSelection( textSelection ),
      mCustomTemplate( customTemplate ),
      mSigningAndEncryptionExplicitlyDisabled( false ),
      mFolder( Akonadi::Collection( -1 ) ),
      mForceDisableHtml( false ),
      mId( id ),
      mContext( context ),
      mSignAction( 0 ), mEncryptAction( 0 ), mRequestMDNAction( 0 ),
      mUrgentAction( 0 ), mAllFieldsAction( 0 ), mFromAction( 0 ),
      mReplyToAction( 0 ), mSubjectAction( 0 ),
      mIdentityAction( 0 ), mTransportAction( 0 ), mFccAction( 0 ),
      mWordWrapAction( 0 ), mFixedFontAction( 0 ), mAutoSpellCheckingAction( 0 ),
      mDictionaryAction( 0 ), mSnippetAction( 0 ), mTranslateAction(0),
      mAppendSignature( 0 ), mPrependSignature( 0 ), mInsertSignatureAtCursorPosition( 0 ),
      mGenerateShortenUrl( 0 ),
      mCodecAction( 0 ),
      mCryptoModuleAction( 0 ),
      mFindText( 0 ),
      mFindNextText( 0 ),
      mReplaceText( 0 ),
      mSelectAll( 0 ),
      //mEncryptChiasmusAction( 0 ),
      mDummyComposer( 0 ),
      mLabelWidth( 0 ),
      mComposerBase( 0 ),
      mSelectSpecialChar( 0 ),
      mPreventFccOverwrite( false ),
      mCheckForForgottenAttachments( true ),
      mIgnoreStickyFields( false ),
      mWasModified( false ),
      mCryptoStateIndicatorWidget(0),
      mStorageService(new KMStorageService(this, this)),
      mSendNowByShortcutUsed(false)
{
    m_verifyMissingAttachment = 0;
    mComposerBase = new MessageComposer::ComposerViewBase( this, this );
    mComposerBase->setIdentityManager( kmkernel->identityManager() );

    connect( mComposerBase, SIGNAL(disableHtml(MessageComposer::ComposerViewBase::Confirmation)),
             this, SLOT(disableHtml(MessageComposer::ComposerViewBase::Confirmation)) );

    connect( mComposerBase, SIGNAL(enableHtml()),
             this, SLOT(enableHtml()) );
    connect( mComposerBase, SIGNAL(failed(QString,MessageComposer::ComposerViewBase::FailedType)), this, SLOT(slotSendFailed(QString,MessageComposer::ComposerViewBase::FailedType)) );
    connect( mComposerBase, SIGNAL(sentSuccessfully()), this, SLOT(slotSendSuccessful()) );
    connect( mComposerBase, SIGNAL(modified(bool)), this, SLOT(setModified(bool)) );

    (void) new MailcomposerAdaptor( this );
    mdbusObjectPath = QLatin1String("/Composer_") + QString::number( ++s_composerNumber );
    QDBusConnection::sessionBus().registerObject( mdbusObjectPath, this );

    MessageComposer::SignatureController* sigController = new MessageComposer::SignatureController( this );
    connect( sigController, SIGNAL(enableHtml()), SLOT(enableHtml()) );
    mComposerBase->setSignatureController( sigController );

    if ( kmkernel->xmlGuiInstance().isValid() ) {
        setComponentData( kmkernel->xmlGuiInstance() );
    }
    mMainWidget = new QWidget( this );
    // splitter between the headers area and the actual editor
    mHeadersToEditorSplitter = new QSplitter( Qt::Vertical, mMainWidget );
    mHeadersToEditorSplitter->setObjectName( QLatin1String("mHeadersToEditorSplitter") );
    mHeadersToEditorSplitter->setChildrenCollapsible( false );
    mHeadersArea = new QWidget( mHeadersToEditorSplitter );
    mHeadersArea->setSizePolicy( mHeadersToEditorSplitter->sizePolicy().horizontalPolicy(),
                                 QSizePolicy::Expanding );
    mHeadersToEditorSplitter->addWidget( mHeadersArea );
    QList<int> defaultSizes;
    defaultSizes << 0;
    mHeadersToEditorSplitter->setSizes( defaultSizes );



    QVBoxLayout *v = new QVBoxLayout( mMainWidget );
    v->setMargin(0);
    v->addWidget( mHeadersToEditorSplitter );
    KPIMIdentities::IdentityCombo* identity = new KPIMIdentities::IdentityCombo( kmkernel->identityManager(),
                                                                                 mHeadersArea );
    identity->setToolTip( i18n( "Select an identity for this message" ) );
    mComposerBase->setIdentityCombo( identity );

    sigController->setIdentityCombo( identity );
    sigController->suspend(); // we have to do identity change tracking ourselves due to the template code

    mDictionaryCombo = new DictionaryComboBox( mHeadersArea );
    mDictionaryCombo->setToolTip( i18n( "Select the dictionary to use when spell-checking this message" ) );

    mFccFolder = new MailCommon::FolderRequester( mHeadersArea );
    mFccFolder->setNotAllowToCreateNewFolder( true );
    mFccFolder->setMustBeReadWrite( true );


    mFccFolder->setToolTip( i18n( "Select the sent-mail folder where a copy of this message will be saved" ) );
    connect( mFccFolder, SIGNAL(folderChanged(Akonadi::Collection)),
             this, SLOT(slotFccFolderChanged(Akonadi::Collection)) );

    MailTransport::TransportComboBox* transport = new MailTransport::TransportComboBox( mHeadersArea );
    transport->setToolTip( i18n( "Select the outgoing account to use for sending this message" ) );
    mComposerBase->setTransportCombo( transport );
    connect(transport, SIGNAL(activated(int)), this, SLOT(slotTransportChanged()));

    mEdtFrom = new MessageComposer::ComposerLineEdit( false, mHeadersArea );
    mEdtFrom->setObjectName( QLatin1String("fromLine") );
    mEdtFrom->setRecentAddressConfig( MessageComposer::MessageComposerSettings::self()->config() );
    mEdtFrom->setToolTip( i18n( "Set the \"From:\" email address for this message" ) );
    mEdtReplyTo = new MessageComposer::ComposerLineEdit( true, mHeadersArea );
    mEdtReplyTo->setObjectName( QLatin1String("replyToLine") );
    mEdtReplyTo->setRecentAddressConfig( MessageComposer::MessageComposerSettings::self()->config() );
    mEdtReplyTo->setToolTip( i18n( "Set the \"Reply-To:\" email address for this message" ) );
    connect( mEdtReplyTo, SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
             SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)) );

    MessageComposer::RecipientsEditor* recipientsEditor = new MessageComposer::RecipientsEditor( mHeadersArea );
    recipientsEditor->setRecentAddressConfig( MessageComposer::MessageComposerSettings::self()->config() );
    connect( recipientsEditor,
             SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
             SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)) );
    connect( recipientsEditor, SIGNAL(sizeHintChanged()), SLOT(recipientEditorSizeHintChanged()) );
    mComposerBase->setRecipientsEditor( recipientsEditor );

    mEdtSubject = new PimCommon::LineEditWithAutoCorrection( mHeadersArea, QLatin1String( "kmail2rc" ) );
    mEdtSubject->setActivateLanguageMenu(false);
    mEdtSubject->setToolTip( i18n( "Set a subject for this message" ) );
    mEdtSubject->setAutocorrection(KMKernel::self()->composerAutoCorrection());
    mLblIdentity = new QLabel( i18n("&Identity:"), mHeadersArea );
    mDictionaryLabel = new QLabel( i18n("&Dictionary:"), mHeadersArea );
    mLblFcc = new QLabel( i18n("&Sent-Mail folder:"), mHeadersArea );
    mLblTransport = new QLabel( i18n("&Mail transport:"), mHeadersArea );
    mLblFrom = new QLabel( i18nc("sender address field", "&From:"), mHeadersArea );
    mLblReplyTo = new QLabel( i18n("&Reply to:"), mHeadersArea );
    mLblSubject = new QLabel( i18nc("@label:textbox Subject of email.", "S&ubject:"), mHeadersArea );
    QString sticky = i18nc("@option:check Sticky identity.", "Sticky");
    mBtnIdentity = new QCheckBox( sticky, mHeadersArea );
    mBtnIdentity->setToolTip( i18n( "Use the selected value as your identity for future messages" ) );
    mBtnFcc = new QCheckBox( sticky, mHeadersArea );
    mBtnFcc->setToolTip( i18n( "Use the selected value as your sent-mail folder for future messages" ) );
    mBtnTransport = new QCheckBox( sticky, mHeadersArea );
    mBtnTransport->setToolTip( i18n( "Use the selected value as your outgoing account for future messages" ) );
    mBtnDictionary = new QCheckBox( sticky, mHeadersArea );
    mBtnDictionary->setToolTip( i18n( "Use the selected value as your dictionary for future messages" ) );

    mShowHeaders = GlobalSettings::self()->headers();
    mDone = false;
    mGrid = 0;
    mFixedFontAction = 0;
    // the attachment view is separated from the editor by a splitter
    mSplitter = new QSplitter( Qt::Vertical, mMainWidget );
    mSplitter->setObjectName( QLatin1String("mSplitter") );
    mSplitter->setChildrenCollapsible( false );
    mSnippetSplitter = new QSplitter( Qt::Horizontal, mSplitter );
    mSnippetSplitter->setObjectName( QLatin1String("mSnippetSplitter") );
    mSnippetSplitter->setChildrenCollapsible( false );
    mSplitter->addWidget( mSnippetSplitter );

    QWidget *editorAndCryptoStateIndicators = new QWidget( mSplitter );
    mCryptoStateIndicatorWidget = new CryptoStateIndicatorWidget;

    QVBoxLayout *vbox = new QVBoxLayout(editorAndCryptoStateIndicators);
    vbox->setMargin(0);
    KMComposerEditor* editor = new KMComposerEditor( this, mCryptoStateIndicatorWidget );

    connect( editor, SIGNAL(textChanged()),
             this, SLOT(slotEditorTextChanged()) );
    mComposerBase->setEditor( editor );
    vbox->addWidget( mCryptoStateIndicatorWidget );
    vbox->addWidget( editor );

    mSnippetSplitter->insertWidget( 0, editorAndCryptoStateIndicators );
    mSnippetSplitter->setOpaqueResize( true );
    sigController->setEditor( editor );

    mHeadersToEditorSplitter->addWidget( mSplitter );
    editor->setAcceptDrops( true );
    connect(sigController, SIGNAL(signatureAdded()), mComposerBase->editor(), SLOT(startExternalEditor()));

    connect( mDictionaryCombo, SIGNAL(dictionaryChanged(QString)),
             this, SLOT(slotSpellCheckingLanguage(QString)) );

    connect( editor, SIGNAL(languageChanged(QString)),
             this, SLOT(slotLanguageChanged(QString)) );
    connect( editor, SIGNAL(spellCheckStatus(QString)),
             this, SLOT(slotSpellCheckingStatus(QString)) );
    connect( editor, SIGNAL(insertModeChanged()),
             this, SLOT(slotOverwriteModeChanged()) );
    connect(editor,SIGNAL(spellCheckingFinished()),this,SLOT(slotCheckSendNow()));
    mSnippetWidget = new SnippetWidget( editor, actionCollection(), mSnippetSplitter );
    mSnippetWidget->setVisible( GlobalSettings::self()->showSnippetManager() );
    mSnippetSplitter->addWidget( mSnippetWidget );
    mSnippetSplitter->setCollapsible( 0, false );

    mSplitter->setOpaqueResize( true );

    mBtnIdentity->setWhatsThis( GlobalSettings::self()->stickyIdentityItem()->whatsThis() );
    mBtnFcc->setWhatsThis( GlobalSettings::self()->stickyFccItem()->whatsThis() );
    mBtnTransport->setWhatsThis( GlobalSettings::self()->stickyTransportItem()->whatsThis() );
    mBtnDictionary->setWhatsThis( GlobalSettings::self()->stickyDictionaryItem()->whatsThis() );

    setCaption( i18n("Composer") );
    setMinimumSize( 200, 200 );

    mBtnIdentity->setFocusPolicy( Qt::NoFocus );
    mBtnFcc->setFocusPolicy( Qt::NoFocus );
    mBtnTransport->setFocusPolicy( Qt::NoFocus );
    mBtnDictionary->setFocusPolicy( Qt::NoFocus );

    mCustomToolsWidget = new PimCommon::CustomToolsWidget(this);
    mSplitter->addWidget(mCustomToolsWidget);
    connect(mCustomToolsWidget, SIGNAL(insertShortUrl(QString)), this, SLOT(slotInsertShortUrl(QString)));

    MessageComposer::AttachmentModel* attachmentModel = new MessageComposer::AttachmentModel( this );
    KMail::AttachmentView *attachmentView = new KMail::AttachmentView( attachmentModel, mSplitter );
    attachmentView->hideIfEmpty();
    connect(attachmentView,SIGNAL(modified(bool)),SLOT(setModified(bool)));
    KMail::AttachmentController* attachmentController = new KMail::AttachmentController( attachmentModel, attachmentView, this );

    mComposerBase->setAttachmentModel( attachmentModel );
    mComposerBase->setAttachmentController( attachmentController );

    mAttachmentMissing = new AttachmentMissingWarning(this);
    connect(mAttachmentMissing, SIGNAL(attachMissingFile()), this, SLOT(slotAttachMissingFile()));
    connect(mAttachmentMissing, SIGNAL(closeAttachMissingFile()), this, SLOT(slotCloseAttachMissingFile()));
    connect(mAttachmentMissing, SIGNAL(explicitClosedMissingAttachment()), this, SLOT(slotExplicitClosedMissingAttachment()));
    v->addWidget(mAttachmentMissing);

    if (GlobalSettings::self()->showForgottenAttachmentWarning()) {
        m_verifyMissingAttachment = new QTimer(this);
        m_verifyMissingAttachment->start(1000*5);
        connect( m_verifyMissingAttachment, SIGNAL(timeout()), this, SLOT(slotVerifyMissingAttachmentTimeout()) );
    }
    connect( attachmentController, SIGNAL(fileAttached()), mAttachmentMissing, SLOT(slotFileAttached()) );

    mExternalEditorWarning = new ExternalEditorWarning(this);
    v->addWidget(mExternalEditorWarning);

    readConfig();
    setupStatusBar(attachmentView->widget());
    setupActions();
    setupEditor();
    rethinkFields();
    slotUpdateSignatureAndEncrypionStateIndicators();

    applyMainWindowSettings( KMKernel::self()->config()->group( "Composer") );

    connect( mEdtSubject, SIGNAL(textChanged()),
             SLOT(slotUpdWinTitle()) );
    connect( identity, SIGNAL(identityChanged(uint)),
             SLOT(slotIdentityChanged(uint)) );
    connect( kmkernel->identityManager(), SIGNAL(changed(uint)),
             SLOT(slotIdentityChanged(uint)) );

    connect( mEdtFrom, SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
             SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)) );
    connect( kmkernel->folderCollectionMonitor(), SIGNAL(collectionRemoved(Akonadi::Collection)),
             SLOT(slotFolderRemoved(Akonadi::Collection)) );
    connect( kmkernel, SIGNAL(configChanged()),
             this, SLOT(slotConfigChanged()) );

    mMainWidget->resize( 480, 510 );
    setCentralWidget( mMainWidget );

    if ( GlobalSettings::self()->useHtmlMarkup() )
        enableHtml();
    else
        disableHtml( MessageComposer::ComposerViewBase::LetUserConfirm );

    if ( GlobalSettings::self()->useExternalEditor() ) {
        editor->setUseExternalEditor( true );
        editor->setExternalEditorPath( GlobalSettings::self()->externalEditor() );
    }

    if ( aMsg ) {
        setMessage( aMsg, lastSignState, lastEncryptState );
    }

    mComposerBase->recipientsEditor()->setFocus();
    editor->updateActionStates(); // set toolbar buttons to correct values

    mDone = true;

    mDummyComposer = new MessageComposer::Composer( this );
    mDummyComposer->globalPart()->setParentWidgetForGui( this );

    connect(mStorageService, SIGNAL(insertShareLink(QString)), this, SLOT(slotShareLinkDone(QString)));
}

//-----------------------------------------------------------------------------
KMComposeWin::~KMComposeWin()
{
    writeConfig();

    // When we have a collection set, store the message back to that collection.
    // Note that when we save the message or sent it, mFolder is set back to 0.
    // So this for example kicks in when opening a draft and then closing the window.
    if ( mFolder.isValid() && mMsg && isModified() ) {
        SaveDraftJob *saveDraftJob = new SaveDraftJob(mMsg, mFolder);
        saveDraftJob->start();
    }

    delete mComposerBase;
}


void KMComposeWin::slotSpellCheckingLanguage(const QString& language)
{
    mComposerBase->editor()->setSpellCheckingLanguage(language );
    mEdtSubject->setSpellCheckingLanguage(language );
}

QString KMComposeWin::dbusObjectPath() const
{
    return mdbusObjectPath;
}

void KMComposeWin::slotEditorTextChanged()
{
    const bool textIsNotEmpty = !mComposerBase->editor()->document()->isEmpty();
    mFindText->setEnabled( textIsNotEmpty );
    mFindNextText->setEnabled( textIsNotEmpty );
    mReplaceText->setEnabled( textIsNotEmpty );
    mSelectAll->setEnabled( textIsNotEmpty );
}

//-----------------------------------------------------------------------------
void KMComposeWin::send( int how )
{
    switch ( how ) {
    case 1:
        slotSendNow();
        break;
    default:
    case 0:
        // TODO: find out, what the default send method is and send it this way
    case 2:
        slotSendLater();
        break;
    }
}

//-----------------------------------------------------------------------------
void KMComposeWin::addAttachmentsAndSend( const KUrl::List &urls, const QString &comment, int how )
{
    kDebug() << "addAttachment and sending!";
    const int nbUrl = urls.count();
    for ( int i =0; i < nbUrl; ++i ) {
        mComposerBase->addAttachmentUrlSync( urls[i], comment );
    }

    send( how );
}

//-----------------------------------------------------------------------------
void KMComposeWin::addAttachment( const KUrl &url, const QString &comment )
{
    mComposerBase->addAttachment( url, comment );
}


void KMComposeWin::addAttachment( const QString& name,
                                  KMime::Headers::contentEncoding cte,
                                  const QString& charset,
                                  const QByteArray& data,
                                  const QByteArray& mimeType )
{
    Q_UNUSED( cte );
    mComposerBase->addAttachment( name, name, charset, data, mimeType );
}

//-----------------------------------------------------------------------------
void KMComposeWin::readConfig( bool reload /* = false */ )
{
    mBtnIdentity->setChecked( GlobalSettings::self()->stickyIdentity() );
    if (mBtnIdentity->isChecked()) {
        mId = ( GlobalSettings::self()->previousIdentity() != 0 ) ?
                    GlobalSettings::self()->previousIdentity() : mId;
    }
    mBtnFcc->setChecked( GlobalSettings::self()->stickyFcc() );
    mBtnTransport->setChecked( GlobalSettings::self()->stickyTransport() );
    const int currentTransport = GlobalSettings::self()->currentTransport().isEmpty() ? -1 : GlobalSettings::self()->currentTransport().toInt();
    mBtnDictionary->setChecked( GlobalSettings::self()->stickyDictionary() );

    mEdtFrom->setCompletionMode( (KGlobalSettings::Completion)GlobalSettings::self()->completionMode() );
    mComposerBase->recipientsEditor()->setCompletionMode( (KGlobalSettings::Completion)GlobalSettings::self()->completionMode() );
    mEdtReplyTo->setCompletionMode( (KGlobalSettings::Completion)GlobalSettings::self()->completionMode() );

    if ( MessageCore::GlobalSettings::self()->useDefaultFonts() ) {
        mBodyFont = KGlobalSettings::generalFont();
        mFixedFont = KGlobalSettings::fixedFont();
    } else {
        mBodyFont = GlobalSettings::self()->composerFont();
        mFixedFont = MessageViewer::GlobalSettings::self()->fixedFont();
    }

    slotUpdateFont();
    mEdtFrom->setFont( mBodyFont );
    mEdtReplyTo->setFont( mBodyFont );
    mEdtSubject->setFont( mBodyFont );

    if ( !reload ) {
        QSize siz = GlobalSettings::self()->composerSize();
        if ( siz.width() < 200 ) {
            siz.setWidth( 200 );
        }
        if ( siz.height() < 200 ) {
            siz.setHeight( 200 );
        }
        resize( siz );

        if ( !GlobalSettings::self()->snippetSplitterPosition().isEmpty() ) {
            mSnippetSplitter->setSizes( GlobalSettings::self()->snippetSplitterPosition() );
        } else {
            QList<int> defaults;
            defaults << (int)(width() * 0.8) << (int)(width() * 0.2);
            mSnippetSplitter->setSizes( defaults );
        }
    }

    mComposerBase->identityCombo()->setCurrentIdentity( mId );
    kDebug() << mComposerBase->identityCombo()->currentIdentityName();
    const KPIMIdentities::Identity & ident =
            kmkernel->identityManager()->identityForUoid( mId );

    if ( mBtnTransport->isChecked() && currentTransport != -1 ) {
        const Transport *transport = TransportManager::self()->transportById( currentTransport );
        if ( transport )
            mComposerBase->transportComboBox()->setCurrentTransport( transport->id() );
    }

    mComposerBase->setAutoSaveInterval( GlobalSettings::self()->autosaveInterval() * 1000 * 60 );


    if ( mBtnDictionary->isChecked() ) {
        mDictionaryCombo->setCurrentByDictionaryName( GlobalSettings::self()->previousDictionary() );
    } else {
        mDictionaryCombo->setCurrentByDictionaryName( ident.dictionary() );
    }

    QString fccName;
    if ( mBtnFcc->isChecked() ) {
        fccName = GlobalSettings::self()->previousFcc();
    } else if ( !ident.fcc().isEmpty() ) {
        fccName = ident.fcc();
    }
    setFcc( fccName );
}

//-----------------------------------------------------------------------------
void KMComposeWin::writeConfig( void )
{
    GlobalSettings::self()->setHeaders( mShowHeaders );
    GlobalSettings::self()->setStickyFcc( mBtnFcc->isChecked() );
    if ( !mIgnoreStickyFields ) {
        GlobalSettings::self()->setCurrentTransport( mComposerBase->transportComboBox()->currentText() );
        GlobalSettings::self()->setStickyTransport( mBtnTransport->isChecked() );
        GlobalSettings::self()->setStickyDictionary( mBtnDictionary->isChecked() );
        GlobalSettings::self()->setStickyIdentity( mBtnIdentity->isChecked() );
        GlobalSettings::self()->setPreviousIdentity( mComposerBase->identityCombo()->currentIdentity() );
    }
    GlobalSettings::self()->setPreviousFcc( QString::number(mFccFolder->collection().id()) );
    GlobalSettings::self()->setPreviousDictionary( mDictionaryCombo->currentDictionaryName() );
    GlobalSettings::self()->setAutoSpellChecking(
                mAutoSpellCheckingAction->isChecked() );
    MessageViewer::GlobalSettings::self()->setUseFixedFont( mFixedFontAction->isChecked() );
    if ( !mForceDisableHtml )
        GlobalSettings::self()->setUseHtmlMarkup( mComposerBase->editor()->textMode() == KMeditor::Rich );
    GlobalSettings::self()->setComposerSize( size() );
    GlobalSettings::self()->setShowSnippetManager( mSnippetAction->isChecked() );

    saveMainWindowSettings( KMKernel::self()->config()->group( "Composer" ) );
    if ( mSnippetAction->isChecked() )
        GlobalSettings::setSnippetSplitterPosition( mSnippetSplitter->sizes() );

    // make sure config changes are written to disk, cf. bug 127538
    KMKernel::self()->slotSyncConfig();
}

MessageComposer::Composer* KMComposeWin::createSimpleComposer()
{
    QList< QByteArray > charsets = mCodecAction->mimeCharsets();
    if( !mOriginalPreferredCharset.isEmpty() ) {
        charsets.insert( 0, mOriginalPreferredCharset );
    }
    mComposerBase->setFrom( from() );
    mComposerBase->setReplyTo( replyTo() );
    mComposerBase->setSubject( subject() );
    mComposerBase->setCharsets( charsets );
    return mComposerBase->createSimpleComposer();
}

bool KMComposeWin::canSignEncryptAttachments() const
{
    return cryptoMessageFormat() != Kleo::InlineOpenPGPFormat;
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotView( void )
{
    if ( !mDone ) {
        return; // otherwise called from rethinkFields during the construction
        // which is not the intended behavior
    }

    //This sucks awfully, but no, I cannot get an activated(int id) from
    // actionContainer()
    KToggleAction *act = ::qobject_cast<KToggleAction *>( sender() );
    if ( !act ) {
        return;
    }
    int id;

    if ( act == mAllFieldsAction ) {
        id = 0;
    } else if ( act == mIdentityAction ) {
        id = HDR_IDENTITY;
    } else if ( act == mTransportAction ) {
        id = HDR_TRANSPORT;
    } else if ( act == mFromAction ) {
        id = HDR_FROM;
    } else if ( act == mReplyToAction ) {
        id = HDR_REPLY_TO;
    } else if ( act == mSubjectAction ) {
        id = HDR_SUBJECT;
    } else if ( act == mFccAction ) {
        id = HDR_FCC;
    } else if ( act == mDictionaryAction ) {
        id = HDR_DICTIONARY;
    } else {
        id = 0;
        kDebug() <<"Something is wrong (Oh, yeah?)";
        return;
    }

    // sanders There's a bug here this logic doesn't work if no
    // fields are shown and then show all fields is selected.
    // Instead of all fields being shown none are.
    if ( !act->isChecked() ) {
        // hide header
        if ( id > 0 ) {
            mShowHeaders = mShowHeaders & ~id;
        } else {
            mShowHeaders = abs( mShowHeaders );
        }
    } else {
        // show header
        if ( id > 0 ) {
            mShowHeaders |= id;
        } else {
            mShowHeaders = -abs( mShowHeaders );
        }
    }
    rethinkFields( true );
}

int KMComposeWin::calcColumnWidth( int which, long allShowing, int width ) const
{
    if ( ( allShowing & which ) == 0 ) {
        return width;
    }

    QLabel *w;
    if ( which == HDR_IDENTITY ) {
        w = mLblIdentity;
    } else if ( which == HDR_DICTIONARY ) {
        w = mDictionaryLabel;
    } else if ( which == HDR_FCC ) {
        w = mLblFcc;
    } else if ( which == HDR_TRANSPORT ) {
        w = mLblTransport;
    } else if ( which == HDR_FROM ) {
        w = mLblFrom;
    } else if ( which == HDR_REPLY_TO ) {
        w = mLblReplyTo;
    } else if ( which == HDR_SUBJECT ) {
        w = mLblSubject;
    } else {
        return width;
    }

    w->setBuddy( mComposerBase->editor() ); // set dummy so we don't calculate width of '&' for this label.
    w->adjustSize();
    w->show();
    return qMax( width, w->sizeHint().width() );
}

void KMComposeWin::rethinkFields( bool fromSlot )
{
    //This sucks even more but again no ids. sorry (sven)
    int mask, row;
    long showHeaders;

    if ( mShowHeaders < 0 ) {
        showHeaders = HDR_ALL;
    } else {
        showHeaders = mShowHeaders;
    }

    for ( mask=1, mNumHeaders=0; mask<=showHeaders; mask<<=1 ) {
        if ( ( showHeaders & mask ) != 0 ) {
            mNumHeaders++;
        }
    }

    delete mGrid;
    mGrid = new QGridLayout( mHeadersArea );
    mGrid->setSpacing( KDialog::spacingHint() );
    mGrid->setMargin( KDialog::marginHint() / 2 );
    mGrid->setColumnStretch( 0, 1 );
    mGrid->setColumnStretch( 1, 100 );
    mGrid->setColumnStretch( 2, 1 );
    mGrid->setRowStretch( mNumHeaders + 1, 100 );

    row = 0;
    kDebug();

    mLabelWidth = mComposerBase->recipientsEditor()->setFirstColumnWidth( 0 );
    mLabelWidth = calcColumnWidth( HDR_IDENTITY, showHeaders, mLabelWidth );
    mLabelWidth = calcColumnWidth( HDR_DICTIONARY, showHeaders, mLabelWidth );
    mLabelWidth = calcColumnWidth( HDR_FCC, showHeaders, mLabelWidth );
    mLabelWidth = calcColumnWidth( HDR_TRANSPORT, showHeaders, mLabelWidth );
    mLabelWidth = calcColumnWidth( HDR_FROM, showHeaders, mLabelWidth );
    mLabelWidth = calcColumnWidth( HDR_REPLY_TO, showHeaders, mLabelWidth );
    mLabelWidth = calcColumnWidth( HDR_SUBJECT, showHeaders, mLabelWidth );

    if ( !fromSlot ) {
        mAllFieldsAction->setChecked( showHeaders == HDR_ALL );
    }

    if ( !fromSlot ) {
        mIdentityAction->setChecked( abs( mShowHeaders )&HDR_IDENTITY );
    }
    rethinkHeaderLine( showHeaders,HDR_IDENTITY, row, mLblIdentity, mComposerBase->identityCombo(),
                       mBtnIdentity );

    if ( !fromSlot ) {
        mDictionaryAction->setChecked( abs( mShowHeaders )&HDR_DICTIONARY );
    }
    rethinkHeaderLine( showHeaders,HDR_DICTIONARY, row, mDictionaryLabel,
                       mDictionaryCombo, mBtnDictionary );

    if ( !fromSlot ) {
        mFccAction->setChecked( abs( mShowHeaders )&HDR_FCC );
    }
    rethinkHeaderLine( showHeaders,HDR_FCC, row, mLblFcc, mFccFolder, mBtnFcc );

    if ( !fromSlot ) {
        mTransportAction->setChecked( abs( mShowHeaders )&HDR_TRANSPORT );
    }
    rethinkHeaderLine( showHeaders,HDR_TRANSPORT, row, mLblTransport, mComposerBase->transportComboBox(),
                       mBtnTransport );

    if ( !fromSlot ) {
        mFromAction->setChecked( abs( mShowHeaders )&HDR_FROM );
    }
    rethinkHeaderLine( showHeaders,HDR_FROM, row, mLblFrom, mEdtFrom );

    QWidget *prevFocus = mEdtFrom;

    if ( !fromSlot ) {
        mReplyToAction->setChecked( abs( mShowHeaders )&HDR_REPLY_TO );
    }
    rethinkHeaderLine( showHeaders, HDR_REPLY_TO, row, mLblReplyTo, mEdtReplyTo );
    if ( showHeaders & HDR_REPLY_TO ) {
        prevFocus = connectFocusMoving( prevFocus, mEdtReplyTo );
    }

    mGrid->addWidget( mComposerBase->recipientsEditor(), row, 0, 1, 3 );
    ++row;
    if ( showHeaders & HDR_REPLY_TO ) {
        connect( mEdtReplyTo, SIGNAL(focusDown()), mComposerBase->recipientsEditor(),
                 SLOT(setFocusTop()) );
        connect( mComposerBase->recipientsEditor(), SIGNAL(focusUp()), mEdtReplyTo,
                 SLOT(setFocus()) );
    } else {
        connect( mEdtFrom, SIGNAL(focusDown()), mComposerBase->recipientsEditor(),
                 SLOT(setFocusTop()) );
        connect( mComposerBase->recipientsEditor(), SIGNAL(focusUp()), mEdtFrom,
                 SLOT(setFocus()) );
    }

    connect( mComposerBase->recipientsEditor(), SIGNAL(focusDown()), mEdtSubject,
             SLOT(setFocus()) );
    connect( mEdtSubject, SIGNAL(focusUp()), mComposerBase->recipientsEditor(),
             SLOT(setFocusBottom()) );

    prevFocus = mComposerBase->recipientsEditor();

    if ( !fromSlot ) {
        mSubjectAction->setChecked( abs( mShowHeaders )&HDR_SUBJECT );
    }
    rethinkHeaderLine(showHeaders,HDR_SUBJECT, row, mLblSubject, mEdtSubject );
    connectFocusMoving( mEdtSubject, mComposerBase->editor() );

    assert( row <= mNumHeaders + 1 );


    mHeadersArea->setMaximumHeight( mHeadersArea->sizeHint().height() );

    mIdentityAction->setEnabled(!mAllFieldsAction->isChecked());
    mDictionaryAction->setEnabled( !mAllFieldsAction->isChecked() );
    mTransportAction->setEnabled(!mAllFieldsAction->isChecked());
    mFromAction->setEnabled(!mAllFieldsAction->isChecked());
    if ( mReplyToAction ) {
        mReplyToAction->setEnabled( !mAllFieldsAction->isChecked() );
    }
    mFccAction->setEnabled( !mAllFieldsAction->isChecked() );
    mSubjectAction->setEnabled( !mAllFieldsAction->isChecked() );
    mComposerBase->recipientsEditor()->setFirstColumnWidth( mLabelWidth );
}

QWidget *KMComposeWin::connectFocusMoving( QWidget *prev, QWidget *next )
{
    connect( prev, SIGNAL(focusDown()), next, SLOT(setFocus()) );
    connect( next, SIGNAL(focusUp()), prev, SLOT(setFocus()) );

    return next;
}

//-----------------------------------------------------------------------------
void KMComposeWin::rethinkHeaderLine( int aValue, int aMask, int &aRow,
                                      QLabel *aLbl, QWidget *aEdt,
                                      QPushButton *aBtn )
{
    if ( aValue & aMask ) {
        aLbl->setFixedWidth( mLabelWidth );
        aLbl->setBuddy( aEdt );
        mGrid->addWidget( aLbl, aRow, 0 );
        aEdt->show();

        if ( aBtn ) {
            mGrid->addWidget( aEdt, aRow, 1 );
            mGrid->addWidget( aBtn, aRow, 2 );
            aBtn->show();
        } else {
            mGrid->addWidget( aEdt, aRow, 1, 1, 2 );
        }
        aRow++;
    } else {
        aLbl->hide();
        aEdt->hide();
        if ( aBtn ) {
            aBtn->hide();
        }
    }
}

//-----------------------------------------------------------------------------
void KMComposeWin::rethinkHeaderLine( int aValue, int aMask, int &aRow,
                                      QLabel *aLbl, QWidget *aCbx,
                                      QCheckBox *aChk )
{
    if ( aValue & aMask ) {
        aLbl->setBuddy( aCbx );
        mGrid->addWidget( aLbl, aRow, 0 );

        mGrid->addWidget( aCbx, aRow, 1 );
        aCbx->show();
        if ( aChk ) {
            mGrid->addWidget( aChk, aRow, 2 );
            aChk->show();
        }
        aRow++;
    } else {
        aLbl->hide();
        aCbx->hide();
        if ( aChk ) {
            aChk->hide();
        }
    }
}

//-----------------------------------------------------------------------------
void KMComposeWin::applyTemplate( uint uoid, uint uOldId )
{
    const KPIMIdentities::Identity &ident = kmkernel->identityManager()->identityForUoid( uoid );
    if ( ident.isNull() )
        return;
    KMime::Headers::Generic *header = new KMime::Headers::Generic( "X-KMail-Templates", mMsg.get(), ident.templates(), "utf-8" );
    mMsg->setHeader( header );

    TemplateParser::TemplateParser::Mode mode;
    switch ( mContext ) {
    case New:
        mode = TemplateParser::TemplateParser::NewMessage;
        break;
    case Reply:
        mode = TemplateParser::TemplateParser::Reply;
        break;
    case ReplyToAll:
        mode = TemplateParser::TemplateParser::ReplyAll;
        break;
    case Forward:
        mode = TemplateParser::TemplateParser::Forward;
        break;
    default:
        return;
    }

    if ( mode == TemplateParser::TemplateParser::NewMessage ) {
        TemplateParser::TemplateParser parser( mMsg, mode );
        parser.setSelection( mTextSelection );
        parser.setAllowDecryption( MessageViewer::GlobalSettings::self()->automaticDecrypt() );
        parser.setIdentityManager( KMKernel::self()->identityManager() );
        if ( !mCustomTemplate.isEmpty() )
            parser.process( mCustomTemplate, mMsg, mCollectionForNewMessage );
        else
            parser.processWithIdentity( uoid, mMsg, mCollectionForNewMessage );
        mComposerBase->updateTemplate( mMsg );
        updateSignature(uoid, uOldId);
        return;
    }

    if ( mMsg->headerByType( "X-KMail-Link-Message" ) ) {
        Akonadi::Item::List items;
        foreach( const QString& serNumStr, mMsg->headerByType( "X-KMail-Link-Message" )->asUnicodeString().split( QLatin1Char(',') ) )
            items << Akonadi::Item( serNumStr.toLongLong() );


        Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob( items, this );
        job->fetchScope().fetchFullPayload( true );
        job->fetchScope().setAncestorRetrieval( Akonadi::ItemFetchScope::Parent );
        job->setProperty( "mode", (int)mode );
        job->setProperty( "uoid", uoid );
        job->setProperty( "uOldid", uOldId );
        connect( job, SIGNAL(result(KJob*)), SLOT(slotDelayedApplyTemplate(KJob*)) );
    }
}

void KMComposeWin::slotDelayedApplyTemplate( KJob *job )
{
    const Akonadi::ItemFetchJob *fetchJob = qobject_cast<Akonadi::ItemFetchJob*>( job );
    const Akonadi::Item::List items = fetchJob->items();

    const TemplateParser::TemplateParser::Mode mode = static_cast<TemplateParser::TemplateParser::Mode>( fetchJob->property( "mode" ).toInt() );
    const uint uoid = fetchJob->property( "uoid" ).toUInt();
    const uint uOldId = fetchJob->property( "uOldid" ).toUInt();

    TemplateParser::TemplateParser parser( mMsg, mode );
    parser.setSelection( mTextSelection );
    parser.setAllowDecryption( MessageViewer::GlobalSettings::self()->automaticDecrypt() );
    parser.setWordWrap( MessageComposer::MessageComposerSettings::self()->wordWrap(), MessageComposer::MessageComposerSettings::self()->lineWrapWidth() );
    parser.setIdentityManager( KMKernel::self()->identityManager() );
    foreach ( const Akonadi::Item &item, items ) {
        if ( !mCustomTemplate.isEmpty() )
            parser.process( mCustomTemplate, MessageCore::Util::message( item ) );
        else
            parser.processWithIdentity( uoid, MessageCore::Util::message( item ) );
    }
    mComposerBase->updateTemplate( mMsg );
    updateSignature(uoid, uOldId);
}

void KMComposeWin::updateSignature(uint uoid, uint uOldId)
{
    const KPIMIdentities::Identity &ident = kmkernel->identityManager()->identityForUoid( uoid );
    const KPIMIdentities::Identity &oldIdentity = kmkernel->identityManager()->identityForUoid( uOldId  );
    mComposerBase->identityChanged( ident, oldIdentity, true );
}

void KMComposeWin::setCollectionForNewMessage( const Akonadi::Collection& folder)
{
    mCollectionForNewMessage = folder;
}

void KMComposeWin::setQuotePrefix( uint uoid )
{
    QString quotePrefix = mMsg->headerByType( "X-KMail-QuotePrefix" ) ? mMsg->headerByType( "X-KMail-QuotePrefix" )->asUnicodeString() : QString();
    if ( quotePrefix.isEmpty() ) {
        // no quote prefix header, set quote prefix according in identity
        // TODO port templates to ComposerViewBase

        if ( mCustomTemplate.isEmpty() ) {
            const KPIMIdentities::Identity &identity = kmkernel->identityManager()->identityForUoidOrDefault( uoid );
            // Get quote prefix from template
            // ( custom templates don't specify custom quotes prefixes )
            TemplateParser::Templates quoteTemplate(
                        TemplateParser::TemplatesConfiguration::configIdString( identity.uoid() ) );
            quotePrefix = quoteTemplate.quoteString();
        }
    }
    mComposerBase->editor()->setQuotePrefixName( MessageCore::StringUtil::formatString( quotePrefix,
                                                                                        mMsg->from()->asUnicodeString() ) );
}

//-----------------------------------------------------------------------------
void KMComposeWin::getTransportMenu()
{
    mActNowMenu->clear();
    mActLaterMenu->clear();

    const QList<Transport*> transports = TransportManager::self()->transports();
    foreach ( Transport *transport, transports ) {
        const QString name = transport->name().replace( QLatin1Char('&'), QLatin1String("&&") );
        QAction *action1 = new QAction( name, mActNowMenu );
        QAction *action2 = new QAction( name, mActLaterMenu );
        action1->setData( transport->id() );
        action2->setData( transport->id() );
        mActNowMenu->addAction( action1 );
        mActLaterMenu->addAction( action2 );
    }
}

//-----------------------------------------------------------------------------
void KMComposeWin::setupActions( void )
{
    KActionMenu *actActionNowMenu, *actActionLaterMenu;

    if ( MessageComposer::MessageComposerSettings::self()->sendImmediate() ) {
        //default = send now, alternative = queue
        KAction *action = new KAction(KIcon(QLatin1String("mail-send")), i18n("&Send Mail"), this);
        actionCollection()->addAction(QLatin1String("send_mail_default"), action );
        connect( action, SIGNAL(triggered(bool)), SLOT(slotSendNow()));

        action = new KAction(KIcon(QLatin1String("mail-send")), i18n("Send Mail Using Shortcut"), this);
        actionCollection()->addAction(QLatin1String("send_mail"), action );
        action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_Return ) );
        connect( action, SIGNAL(triggered(bool)), SLOT(slotSendNowByShortcut()));


        // FIXME: change to mail_send_via icon when this exist.
        actActionNowMenu = new KActionMenu( KIcon( QLatin1String("mail-send") ), i18n("&Send Mail Via"), this );
        actActionNowMenu->setIconText( i18n( "Send" ) );
        actionCollection()->addAction( QLatin1String("send_default_via"), actActionNowMenu );

        action = new KAction( KIcon( QLatin1String("mail-queue") ), i18n("Send &Later"), this );
        actionCollection()->addAction( QLatin1String("send_alternative"), action );
        connect( action, SIGNAL(triggered(bool)), SLOT(slotSendLater()) );
        actActionLaterMenu = new KActionMenu( KIcon( QLatin1String("mail-queue") ), i18n("Send &Later Via"), this );
        actActionLaterMenu->setIconText( i18nc( "Queue the message for sending at a later date", "Queue" ) );
        actionCollection()->addAction( QLatin1String("send_alternative_via"), actActionLaterMenu );

    } else {
        //default = queue, alternative = send now
        KAction *action = new KAction( KIcon( QLatin1String("mail-queue") ), i18n("Send &Later"), this );
        actionCollection()->addAction( QLatin1String("send_mail"), action );
        connect( action, SIGNAL(triggered(bool)), SLOT(slotSendLater()) );
        action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_Return ) );
        actActionLaterMenu = new KActionMenu( KIcon( QLatin1String("mail-queue") ), i18n("Send &Later Via"), this );
        actionCollection()->addAction( QLatin1String("send_default_via"), actActionLaterMenu );

        action = new KAction( KIcon( QLatin1String("mail-send") ), i18n("&Send Mail"), this );
        actionCollection()->addAction( QLatin1String("send_alternative"), action );
        connect( action, SIGNAL(triggered(bool)), SLOT(slotSendNow()) );

        // FIXME: change to mail_send_via icon when this exits.
        actActionNowMenu = new KActionMenu( KIcon( QLatin1String("mail-send") ), i18n("&Send Mail Via"), this );
        actionCollection()->addAction( QLatin1String("send_alternative_via"), actActionNowMenu );

    }

    // needed for sending "default transport"
    actActionNowMenu->setDelayed( true );
    actActionLaterMenu->setDelayed( true );

    connect( actActionNowMenu, SIGNAL(triggered(bool)), this,
             SLOT(slotSendNow()) );
    connect( actActionLaterMenu, SIGNAL(triggered(bool)), this,
             SLOT(slotSendLater()) );

    mActNowMenu = actActionNowMenu->menu();
    mActLaterMenu = actActionLaterMenu->menu();

    connect( mActNowMenu, SIGNAL(triggered(QAction*)), this,
             SLOT(slotSendNowVia(QAction*)) );
    connect( mActNowMenu, SIGNAL(aboutToShow()), this,
             SLOT(getTransportMenu()) );

    connect( mActLaterMenu, SIGNAL(triggered(QAction*)), this,
             SLOT(slotSendLaterVia(QAction*)) );
    connect( mActLaterMenu, SIGNAL(aboutToShow()), this,
             SLOT(getTransportMenu()) );

    KAction *action = new KAction( KIcon( QLatin1String("document-save") ), i18n("Save as &Draft"), this );
    actionCollection()->addAction(QLatin1String("save_in_drafts"), action );
    action->setHelpText(i18n("Save email in Draft folder"));
    action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
    connect( action, SIGNAL(triggered(bool)), SLOT(slotSaveDraft()) );

    action = new KAction( KIcon( QLatin1String("document-save") ), i18n("Save as &Template"), this );
    action->setHelpText(i18n("Save email in Template folder"));
    actionCollection()->addAction( QLatin1String("save_in_templates"), action );
    connect( action, SIGNAL(triggered(bool)), SLOT(slotSaveTemplate()) );

    action = new KAction( KIcon( QLatin1String("document-save") ), i18n("Save as &File"), this );
    action->setHelpText(i18n("Save email as text or html file"));
    actionCollection()->addAction( QLatin1String("save_as_file"), action );
    connect( action, SIGNAL(triggered(bool)), SLOT(slotSaveAsFile()) );

    action = new KAction(KIcon( QLatin1String( "contact-new" ) ), i18n("New AddressBook Contact..."),this);
    actionCollection()->addAction(QLatin1String("kmail_new_addressbook_contact"), action );
    connect(action, SIGNAL(triggered(bool)), this, SLOT(slotCreateAddressBookContact()));



    action = new KAction(KIcon(QLatin1String("document-open")), i18n("&Insert Text File..."), this);
    actionCollection()->addAction(QLatin1String("insert_file"), action );
    connect(action, SIGNAL(triggered(bool)), SLOT(slotInsertFile()));

    mRecentAction = new KRecentFilesAction( KIcon( QLatin1String("document-open") ),
                                            i18n( "&Insert Recent Text File" ), this );
    actionCollection()->addAction(QLatin1String("insert_file_recent"), mRecentAction );
    connect(mRecentAction, SIGNAL(urlSelected(KUrl)),
            SLOT(slotInsertRecentFile(KUrl)));
    connect(mRecentAction, SIGNAL(recentListCleared()),
            SLOT(slotRecentListFileClear()));
    mRecentAction->loadEntries( KMKernel::self()->config()->group( QString() ) );

    action = new KAction(KIcon(QLatin1String("x-office-address-book")), i18n("&Address Book"), this);
    action->setHelpText(i18n("Open Address Book"));
    actionCollection()->addAction(QLatin1String("addressbook"), action );
    if (KStandardDirs::findExe(QLatin1String("kaddressbook")).isEmpty())
        action->setEnabled(false);
    connect(action, SIGNAL(triggered(bool)), SLOT(slotAddrBook()));
    action = new KAction(KIcon(QLatin1String("mail-message-new")), i18n("&New Composer"), this);
    actionCollection()->addAction(QLatin1String("new_composer"), action );
    connect(action, SIGNAL(triggered(bool)), SLOT(slotNewComposer()));
    action->setShortcuts( KStandardShortcut::shortcut( KStandardShortcut::New ) );
    action = new KAction( KIcon( QLatin1String("window-new") ), i18n("New Main &Window"), this );
    actionCollection()->addAction( QLatin1String("open_mailreader"), action );
    connect( action, SIGNAL(triggered(bool)), SLOT(slotNewMailReader()) );

    action = new KAction( i18n("Select &Recipients..."), this );
    actionCollection()->addAction( QLatin1String("select_recipients"), action );
    connect( action, SIGNAL(triggered(bool)),
             mComposerBase->recipientsEditor(), SLOT(selectRecipients()) );
    action = new KAction( i18n("Save &Distribution List..."), this );
    actionCollection()->addAction( QLatin1String("save_distribution_list"), action );
    connect( action, SIGNAL(triggered(bool)),
             mComposerBase->recipientsEditor(), SLOT(saveDistributionList()) );

    KStandardAction::print( this, SLOT(slotPrint()), actionCollection() );
    if(KPrintPreview::isAvailable())
        KStandardAction::printPreview( this, SLOT(slotPrintPreview()), actionCollection() );
    KStandardAction::close( this, SLOT(slotClose()), actionCollection() );

    KStandardAction::undo( this, SLOT(slotUndo()), actionCollection() );
    KStandardAction::redo( this, SLOT(slotRedo()), actionCollection() );
    KStandardAction::cut( this, SLOT(slotCut()), actionCollection() );
    KStandardAction::copy( this, SLOT(slotCopy()), actionCollection() );
    KStandardAction::pasteText( this, SLOT(slotPaste()), actionCollection() );
    mSelectAll = KStandardAction::selectAll( this, SLOT(slotMarkAll()), actionCollection() );

    mFindText = KStandardAction::find( mComposerBase->editor(), SLOT(slotFind()), actionCollection() );
    mFindNextText = KStandardAction::findNext( mComposerBase->editor(), SLOT(slotFindNext()), actionCollection() );

    mReplaceText = KStandardAction::replace( mComposerBase->editor(), SLOT(slotReplace()), actionCollection() );
    actionCollection()->addAction( KStandardAction::Spelling, QLatin1String("spellcheck"),
                                   mComposerBase->editor(), SLOT(checkSpelling()) );

    action = new KAction( i18n("Paste as Attac&hment"), this );
    actionCollection()->addAction( QLatin1String("paste_att"), action );
    connect( action, SIGNAL(triggered(bool)), SLOT(slotPasteAsAttachment()) );

    action = new KAction( i18n("Cl&ean Spaces"), this );
    actionCollection()->addAction( QLatin1String("clean_spaces"), action );
    connect( action, SIGNAL(triggered(bool)), mComposerBase->signatureController(), SLOT(cleanSpace()) );

    mFixedFontAction = new KToggleAction( i18n("Use Fi&xed Font"), this );
    actionCollection()->addAction( QLatin1String("toggle_fixedfont"), mFixedFontAction );
    connect( mFixedFontAction, SIGNAL(triggered(bool)), SLOT(slotUpdateFont()) );
    mFixedFontAction->setChecked( MessageViewer::GlobalSettings::self()->useFixedFont() );

    //these are checkable!!!
    mUrgentAction = new KToggleAction(
                i18nc("@action:inmenu Mark the email as urgent.","&Urgent"), this );
    actionCollection()->addAction( QLatin1String("urgent"), mUrgentAction );
    mRequestMDNAction = new KToggleAction( i18n("&Request Disposition Notification"), this );
    actionCollection()->addAction(QLatin1String("options_request_mdn"), mRequestMDNAction );
    mRequestMDNAction->setChecked(GlobalSettings::self()->requestMDN());
    //----- Message-Encoding Submenu
    mCodecAction = new CodecAction( CodecAction::ComposerMode, this );
    actionCollection()->addAction( QLatin1String("charsets"), mCodecAction );
    mWordWrapAction = new KToggleAction( i18n( "&Wordwrap" ), this );
    actionCollection()->addAction( QLatin1String("wordwrap"), mWordWrapAction );
    mWordWrapAction->setChecked( MessageComposer::MessageComposerSettings::self()->wordWrap() );
    connect( mWordWrapAction, SIGNAL(toggled(bool)), SLOT(slotWordWrapToggled(bool)) );

    mSnippetAction = new KToggleAction( i18n("&Snippets"), this );
    actionCollection()->addAction( QLatin1String("snippets"), mSnippetAction );
    connect( mSnippetAction, SIGNAL(toggled(bool)),
             mSnippetWidget, SLOT(setVisible(bool)) );
    mSnippetAction->setChecked( GlobalSettings::self()->showSnippetManager() );

    mAutoSpellCheckingAction = new KToggleAction( KIcon( QLatin1String("tools-check-spelling") ),
                                                  i18n("&Automatic Spellchecking"),
                                                  this );
    actionCollection()->addAction( QLatin1String("options_auto_spellchecking"), mAutoSpellCheckingAction );
    const bool spellChecking = GlobalSettings::self()->autoSpellChecking();
    const bool useKmailEditor = !GlobalSettings::self()->useExternalEditor();
    const bool spellCheckingEnabled = useKmailEditor && spellChecking;
    mAutoSpellCheckingAction->setEnabled( useKmailEditor );

    mAutoSpellCheckingAction->setChecked( spellCheckingEnabled );
    slotAutoSpellCheckingToggled( spellCheckingEnabled );
    connect( mAutoSpellCheckingAction, SIGNAL(toggled(bool)),
             this, SLOT(slotAutoSpellCheckingToggled(bool)) );
    connect( mComposerBase->editor(), SIGNAL(checkSpellingChanged(bool)),
             this, SLOT(slotAutoSpellCheckingToggled(bool)) );

    connect( mComposerBase->editor(), SIGNAL(textModeChanged(KRichTextEdit::Mode)),
             this, SLOT(slotTextModeChanged(KRichTextEdit::Mode)) );
    connect( mComposerBase->editor(), SIGNAL(externalEditorClosed()), this, SLOT(slotExternalEditorClosed()));
    connect( mComposerBase->editor(), SIGNAL(externalEditorStarted()), this, SLOT(slotExternalEditorStarted()));
    //these are checkable!!!
    markupAction = new KToggleAction( i18n("Rich Text Editing"), this );
    markupAction->setIcon( KIcon( QLatin1String("preferences-desktop-font" )) );
    markupAction->setIconText( i18n("Rich Text") );
    markupAction->setToolTip( i18n( "Toggle rich text editing mode" ) );
    actionCollection()->addAction( QLatin1String("html"), markupAction );
    connect( markupAction, SIGNAL(triggered(bool)), SLOT(slotToggleMarkup()) );

    mAllFieldsAction = new KToggleAction( i18n("&All Fields"), this);
    actionCollection()->addAction( QLatin1String("show_all_fields"), mAllFieldsAction );
    connect( mAllFieldsAction, SIGNAL(triggered(bool)), SLOT(slotView()));
    mIdentityAction = new KToggleAction(i18n("&Identity"), this);
    actionCollection()->addAction(QLatin1String("show_identity"), mIdentityAction );
    connect( mIdentityAction, SIGNAL(triggered(bool)), SLOT(slotView()));
    mDictionaryAction = new KToggleAction(i18n("&Dictionary"), this);
    actionCollection()->addAction(QLatin1String("show_dictionary"), mDictionaryAction );
    connect( mDictionaryAction, SIGNAL(triggered(bool)), SLOT(slotView()));
    mFccAction = new KToggleAction(i18n("&Sent-Mail Folder"), this);
    actionCollection()->addAction(QLatin1String("show_fcc"), mFccAction );
    connect( mFccAction, SIGNAL(triggered(bool)), SLOT(slotView()));
    mTransportAction = new KToggleAction(i18n("&Mail Transport"), this);
    actionCollection()->addAction(QLatin1String("show_transport"), mTransportAction );
    connect( mTransportAction, SIGNAL(triggered(bool)), SLOT(slotView()));
    mFromAction = new KToggleAction(i18n("&From"), this);
    actionCollection()->addAction(QLatin1String("show_from"), mFromAction );
    connect( mFromAction, SIGNAL(triggered(bool)), SLOT(slotView()));
    mReplyToAction = new KToggleAction(i18n("&Reply To"), this);
    actionCollection()->addAction(QLatin1String("show_reply_to"), mReplyToAction );
    connect( mReplyToAction, SIGNAL(triggered(bool)), SLOT(slotView()));
    mSubjectAction = new KToggleAction(
                i18nc("@action:inmenu Show the subject in the composer window.", "S&ubject"), this);
    actionCollection()->addAction(QLatin1String("show_subject"), mSubjectAction );
    connect(mSubjectAction, SIGNAL(triggered(bool)), SLOT(slotView()));
    //end of checkable

    mAppendSignature = new KAction( i18n("Append S&ignature"), this );
    actionCollection()->addAction( QLatin1String("append_signature"), mAppendSignature );
    connect( mAppendSignature, SIGNAL(triggered(bool)), mComposerBase->signatureController(), SLOT(appendSignature()));

    mPrependSignature = new KAction( i18n("Pr&epend Signature"), this );
    actionCollection()->addAction( QLatin1String("prepend_signature"), mPrependSignature );
    connect( mPrependSignature, SIGNAL(triggered(bool)), mComposerBase->signatureController(), SLOT(prependSignature()) );

    mInsertSignatureAtCursorPosition = new KAction( i18n("Insert Signature At C&ursor Position"), this );
    actionCollection()->addAction( QLatin1String("insert_signature_at_cursor_position"), mInsertSignatureAtCursorPosition );
    connect( mInsertSignatureAtCursorPosition, SIGNAL(triggered(bool)), mComposerBase->signatureController(), SLOT(insertSignatureAtCursor()) );


    action = new KAction( i18n("Insert Special Character..."), this );
    actionCollection()->addAction( QLatin1String("insert_special_character"), action );
    connect( action, SIGNAL(triggered(bool)), this, SLOT(insertSpecialCharacter()) );

    KAction *upperCase = new KAction( i18n("Uppercase"), this );
    actionCollection()->addAction( QLatin1String("change_to_uppercase"), upperCase );
    connect( upperCase, SIGNAL(triggered(bool)), this, SLOT(slotUpperCase()) );

    KAction *sentenceCase = new KAction( i18n("Sentence case"), this );
    actionCollection()->addAction( QLatin1String("change_to_sentencecase"), sentenceCase );
    connect( sentenceCase, SIGNAL(triggered(bool)), this, SLOT(slotSentenceCase()) );


    KAction *lowerCase = new KAction( i18n("Lowercase"), this );
    actionCollection()->addAction( QLatin1String("change_to_lowercase"), lowerCase );
    connect( lowerCase, SIGNAL(triggered(bool)), this, SLOT(slotLowerCase()) );

    mChangeCaseMenu = new KActionMenu(i18n("Change Case"), this);
    actionCollection()->addAction(QLatin1String("change_case_menu"), mChangeCaseMenu );
    mChangeCaseMenu->addAction(sentenceCase);
    mChangeCaseMenu->addAction(upperCase);
    mChangeCaseMenu->addAction(lowerCase);

    mComposerBase->attachmentController()->createActions();

    setStandardToolBarMenuEnabled( true );

    KStandardAction::keyBindings( this, SLOT(slotEditKeys()), actionCollection());
    KStandardAction::configureToolbars( this, SLOT(slotEditToolbars()), actionCollection());
    KStandardAction::preferences( kmkernel, SLOT(slotShowConfigurationDialog()), actionCollection() );

    action = new KAction( i18n("&Spellchecker..."), this );
    action->setIconText( i18n("Spellchecker") );
    actionCollection()->addAction( QLatin1String("setup_spellchecker"), action );
    connect( action, SIGNAL(triggered(bool)), SLOT(slotSpellcheckConfig()) );

    mTranslateAction = mCustomToolsWidget->action(PimCommon::CustomToolsWidget::TranslatorTool);
    actionCollection()->addAction( QLatin1String("translator"), mTranslateAction );

    mGenerateShortenUrl = mCustomToolsWidget->action(PimCommon::CustomToolsWidget::ShortUrlTool);
    actionCollection()->addAction( QLatin1String("shorten_url"), mGenerateShortenUrl );
    //Chiamus not supported in kmail2
#if 0
    if ( Kleo::CryptoBackendFactory::instance()->protocol( QLatin1String("Chiasmus") ) ) {
        KToggleAction *a = new KToggleAction( KIcon( "chiasmus_chi" ), i18n("Encrypt Message with Chiasmus..."), this );
        actionCollection()->addAction( "encrypt_message_chiasmus", a );
        a->setCheckedState( KGuiItem( i18n( "Encrypt Message with Chiasmus..." ), "chiencrypted" ) );
        mEncryptChiasmusAction = a;
        connect( mEncryptChiasmusAction, SIGNAL(toggled(bool)),
                 this, SLOT(slotEncryptChiasmusToggled(bool)) );
    } else {
        mEncryptChiasmusAction = 0;
    }
#endif

    mEncryptAction = new KToggleAction(KIcon(QLatin1String("document-encrypt")), i18n("&Encrypt Message"), this);
    mEncryptAction->setIconText( i18n( "Encrypt" ) );
    actionCollection()->addAction(QLatin1String("encrypt_message"), mEncryptAction );
    mSignAction = new KToggleAction(KIcon(QLatin1String("document-sign")), i18n("&Sign Message"), this);
    mSignAction->setIconText( i18n( "Sign" ) );
    actionCollection()->addAction(QLatin1String("sign_message"), mSignAction );
    const KPIMIdentities::Identity &ident =
            KMKernel::self()->identityManager()->identityForUoidOrDefault( mComposerBase->identityCombo()->currentIdentity() );
    // PENDING(marc): check the uses of this member and split it into
    // smime/openpgp and or enc/sign, if necessary:
    mLastIdentityHasSigningKey = !ident.pgpSigningKey().isEmpty() || !ident.smimeSigningKey().isEmpty();
    mLastIdentityHasEncryptionKey = !ident.pgpEncryptionKey().isEmpty() || !ident.smimeEncryptionKey().isEmpty();

    mLastEncryptActionState = false;
    mLastSignActionState = ident.pgpAutoSign();

    changeCryptoAction();

    connect( mEncryptAction, SIGNAL(triggered(bool)),
             SLOT(slotEncryptToggled(bool)) );
    connect( mSignAction, SIGNAL(triggered(bool)),
             SLOT(slotSignToggled(bool)) );

    QStringList l;
    for ( int i=0 ; i<numCryptoMessageFormats ; ++i ) {
        l.push_back( Kleo::cryptoMessageFormatToLabel( cryptoMessageFormats[i] ) );
    }

    mCryptoModuleAction = new KSelectAction(i18n("&Cryptographic Message Format"), this);
    actionCollection()->addAction(QLatin1String("options_select_crypto"), mCryptoModuleAction );
    connect(mCryptoModuleAction, SIGNAL(triggered(int)), SLOT(slotSelectCryptoModule()));
    mCryptoModuleAction->setItems( l );
    mCryptoModuleAction->setToolTip( i18n( "Select a cryptographic format for this message" ) );

    mComposerBase->editor()->createActions( actionCollection() );
    actionCollection()->addAction( QLatin1String("shared_link"), mStorageService->menuShareLinkServices() );

    createGUI( QLatin1String("kmcomposerui.rc") );
    connect( toolBar( QLatin1String("htmlToolBar") )->toggleViewAction(),
             SIGNAL(toggled(bool)),
             SLOT(htmlToolBarVisibilityChanged(bool)) );


    // In Kontact, this entry would read "Configure Kontact", but bring
    // up KMail's config dialog. That's sensible, though, so fix the label.
    QAction *configureAction = actionCollection()->action( QLatin1String("options_configure") );
    if ( configureAction ) {
        configureAction->setText( i18n("Configure KMail..." ) );
    }

    action = new KAction( i18n("Follow Up Mail..."), this );
    actionCollection()->addAction( QLatin1String("follow_up_mail"), action );
    connect( action, SIGNAL(triggered(bool)), this, SLOT(slotFollowUpMail()) );
}

void KMComposeWin::changeCryptoAction()
{
    const KPIMIdentities::Identity &ident =
            KMKernel::self()->identityManager()->identityForUoidOrDefault( mComposerBase->identityCombo()->currentIdentity() );
    if ( !Kleo::CryptoBackendFactory::instance()->openpgp() && !Kleo::CryptoBackendFactory::instance()->smime() ) {
        // no crypto whatsoever
        mEncryptAction->setEnabled( false );
        setEncryption( false );
        mSignAction->setEnabled( false );
        setSigning( false );
    } else {
        const bool canOpenPGPSign = Kleo::CryptoBackendFactory::instance()->openpgp() &&
                !ident.pgpSigningKey().isEmpty();
        const bool canSMIMESign = Kleo::CryptoBackendFactory::instance()->smime() &&
                !ident.smimeSigningKey().isEmpty();

        setEncryption( false );
        setSigning( ( canOpenPGPSign || canSMIMESign ) && ident.pgpAutoSign() );
    }

}

//-----------------------------------------------------------------------------
void KMComposeWin::setupStatusBar( QWidget *w )
{
    KPIM::ProgressStatusBarWidget * progressStatusBarWidget = new KPIM::ProgressStatusBarWidget(statusBar(), this, PimCommon::StorageServiceProgressManager::progressTypeValue());
    statusBar()->addWidget(w);

    statusBar()->insertItem( QString(), 0, 1 );
    statusBar()->setItemAlignment( 0, Qt::AlignLeft | Qt::AlignVCenter );
    statusBar()->insertPermanentItem( overwriteModeStr(), 4,0 );

    statusBar()->insertPermanentItem( i18n(" Spellcheck: %1 ", QLatin1String( "     " )), 3, 0) ;
    statusBar()->insertPermanentItem( i18n(" Column: %1 ", QLatin1String( "     " ) ), 2, 0 );
    statusBar()->insertPermanentItem(
                i18nc("Shows the linenumber of the cursor position.", " Line: %1 "
                      , QLatin1String( "     " ) ), 1, 0 );
    statusBar()->addPermanentWidget(progressStatusBarWidget->littleProgress());
}

//-----------------------------------------------------------------------------
void KMComposeWin::setupEditor( void )
{
    QFontMetrics fm( mBodyFont );
    mComposerBase->editor()->setTabStopWidth( fm.width( QLatin1Char(' ') ) * 8 );

    slotWordWrapToggled( MessageComposer::MessageComposerSettings::self()->wordWrap() );

    // Font setup
    slotUpdateFont();

    connect( mComposerBase->editor(), SIGNAL(cursorPositionChanged()),
             this, SLOT(slotCursorPositionChanged()) );
    slotCursorPositionChanged();
}

//-----------------------------------------------------------------------------
QString KMComposeWin::subject() const
{
    return MessageComposer::Util::cleanedUpHeaderString( mEdtSubject->toPlainText() );
}

//-----------------------------------------------------------------------------
QString KMComposeWin::from() const
{
    return MessageComposer::Util::cleanedUpHeaderString( mEdtFrom->text() );
}

//-----------------------------------------------------------------------------
QString KMComposeWin::replyTo() const
{
    if ( mEdtReplyTo ) {
        return MessageComposer::Util::cleanedUpHeaderString( mEdtReplyTo->text() );
    } else {
        return QString();
    }
}

//-----------------------------------------------------------------------------
void KMComposeWin::decryptOrStripOffCleartextSignature( QByteArray &body )
{
    QList<Kpgp::Block> pgpBlocks;
    QList<QByteArray> nonPgpBlocks;
    if ( Kpgp::Module::prepareMessageForDecryption( body,
                                                    pgpBlocks, nonPgpBlocks ) ) {
        // Only decrypt/strip off the signature if there is only one OpenPGP
        // block in the message
        if ( pgpBlocks.count() == 1 ) {
            Kpgp::Block &block = pgpBlocks.first();
            if ( ( block.type() == Kpgp::PgpMessageBlock ) ||
                 ( block.type() == Kpgp::ClearsignedBlock ) ) {
                if ( block.type() == Kpgp::PgpMessageBlock ) {
                    // try to decrypt this OpenPGP block
                    block.decrypt();
                } else {
                    // strip off the signature
                    block.verify();
                }
                body = nonPgpBlocks.first();
                body.append( block.text() );
                body.append( nonPgpBlocks.last() );
            }
        }
    }
}

void KMComposeWin::setCurrentTransport( int transportId )
{
    mComposerBase->transportComboBox()->setCurrentTransport( transportId );
}

void KMComposeWin::setCurrentReplyTo(const QString& replyTo)
{
    if ( mEdtReplyTo ) {
        mEdtReplyTo->setText( replyTo );
    }
}

//-----------------------------------------------------------------------------
void KMComposeWin::setMessage( const KMime::Message::Ptr &newMsg, bool lastSignState, bool lastEncryptState, bool mayAutoSign,
                               bool allowDecryption, bool isModified )
{
    if ( !newMsg ) {
        kDebug() << "newMsg == 0!";
        return;
    }

    if( lastSignState )
        mLastSignActionState = true;

    if ( lastEncryptState )
        mLastEncryptActionState = true;

    mComposerBase->setMessage( newMsg );
    mMsg = newMsg;
    KPIMIdentities::IdentityManager * im = KMKernel::self()->identityManager();

    mEdtFrom->setText( mMsg->from()->asUnicodeString() );
    mEdtSubject->setText( mMsg->subject()->asUnicodeString() );


    // Restore the quote prefix. We can't just use the global quote prefix here,
    // since the prefix is different for each message, it might for example depend
    // on the original sender in a reply.
    if ( mMsg->headerByType( "X-KMail-QuotePrefix" ) )
        mComposerBase->editor()->setQuotePrefixName( mMsg->headerByType( "X-KMail-QuotePrefix" )->asUnicodeString() );

    const bool stickyIdentity = mBtnIdentity->isChecked() && !mIgnoreStickyFields;
    bool messageHasIdentity = false;
    if( newMsg->headerByType("X-KMail-Identity") &&
            !newMsg->headerByType("X-KMail-Identity")->asUnicodeString().isEmpty() )
        messageHasIdentity = true;
    if ( !stickyIdentity && messageHasIdentity )
        mId = newMsg->headerByType( "X-KMail-Identity" )->asUnicodeString().toUInt();

    // don't overwrite the header values with identity specific values
    // unless the identity is sticky
    if ( !stickyIdentity ) {
        disconnect( mComposerBase->identityCombo(),SIGNAL(identityChanged(uint)),
                    this, SLOT(slotIdentityChanged(uint)) ) ;
    }

    // load the mId into the gui, sticky or not, without emitting
    mComposerBase->identityCombo()->setCurrentIdentity( mId );
    const uint idToApply = mId;
    if ( !stickyIdentity ) {
        connect( mComposerBase->identityCombo(),SIGNAL(identityChanged(uint)),
                 this, SLOT(slotIdentityChanged(uint)) );
    } else {
        // load the message's state into the mId, without applying it to the gui
        // that's so we can detect that the id changed (because a sticky was set)
        // on apply()
        if ( messageHasIdentity ) {
            mId = newMsg->headerByType("X-KMail-Identity")->asUnicodeString().toUInt();
        } else {
            mId = im->defaultIdentity().uoid();
        }
    }

    // manually load the identity's value into the fields; either the one from the
    // messge, where appropriate, or the one from the sticky identity. What's in
    // mId might have changed meanwhile, thus the save value
    slotIdentityChanged( idToApply, true /*initalChange*/ );

    const KPIMIdentities::Identity &ident = im->identityForUoid( mComposerBase->identityCombo()->currentIdentity() );

    const bool stickyTransport = mBtnTransport->isChecked() && !mIgnoreStickyFields;
    if( stickyTransport ) {
        mComposerBase->transportComboBox()->setCurrentTransport( ident.transport().toInt() );
    }

    // TODO move the following to ComposerViewBase
    // however, requires the actions to be there as well in order to share with mobile client

    // check for the presence of a DNT header, indicating that MDN's were requested
    if( newMsg->headerByType( "Disposition-Notification-To" ) ) {
        QString mdnAddr = newMsg->headerByType( "Disposition-Notification-To" )->asUnicodeString();
        mRequestMDNAction->setChecked( ( !mdnAddr.isEmpty() &&
                                         im->thatIsMe( mdnAddr ) ) ||
                                       GlobalSettings::self()->requestMDN() );
    }
    // check for presence of a priority header, indicating urgent mail:
    if ( newMsg->headerByType( "X-PRIORITY" ) && newMsg->headerByType("Priority" ) )
    {
        const QString xpriority = newMsg->headerByType( "X-PRIORITY" )->asUnicodeString();
        const QString priority = newMsg->headerByType( "Priority" )->asUnicodeString();
        if ( xpriority == QLatin1String( "2 (High)" ) && priority == QLatin1String( "urgent" ) )
            mUrgentAction->setChecked( true );
    }

    if ( !ident.isXFaceEnabled() || ident.xface().isEmpty() ) {
        if( mMsg->headerByType( "X-Face" ) )
            mMsg->headerByType( "X-Face" )->clear();
    } else {
        QString xface = ident.xface();
        if ( !xface.isEmpty() ) {
            int numNL = ( xface.length() - 1 ) / 70;
            for ( int i = numNL; i > 0; --i ) {
                xface.insert( i * 70, QLatin1String("\n\t") );
            }
            mMsg->setHeader( new KMime::Headers::Generic( "X-Face", mMsg.get(), xface, "utf-8" ) );
        }
    }

    // if these headers are present, the state of the message should be overruled
    if ( mMsg->headerByType( "X-KMail-SignatureActionEnabled" ) )
        mLastSignActionState = (mMsg->headerByType( "X-KMail-SignatureActionEnabled" )->as7BitString().contains( "true" ));
    if ( mMsg->headerByType( "X-KMail-EncryptActionEnabled" ) )
        mLastEncryptActionState = (mMsg->headerByType( "X-KMail-EncryptActionEnabled" )->as7BitString().contains( "true") );
    if ( mMsg->headerByType( "X-KMail-CryptoMessageFormat" ) ) {
        mCryptoModuleAction->setCurrentItem( format2cb( static_cast<Kleo::CryptoMessageFormat>(
                                                            mMsg->headerByType( "X-KMail-CryptoMessageFormat" )->asUnicodeString().toInt() ) ) );
    }

    mLastIdentityHasSigningKey = !ident.pgpSigningKey().isEmpty() || !ident.smimeSigningKey().isEmpty();
    mLastIdentityHasEncryptionKey = !ident.pgpEncryptionKey().isEmpty() || !ident.smimeEncryptionKey().isEmpty();

    if ( Kleo::CryptoBackendFactory::instance()->openpgp() || Kleo::CryptoBackendFactory::instance()->smime() ) {
        const bool canOpenPGPSign = Kleo::CryptoBackendFactory::instance()->openpgp() &&
                !ident.pgpSigningKey().isEmpty();
        const bool canSMIMESign = Kleo::CryptoBackendFactory::instance()->smime() &&
                !ident.smimeSigningKey().isEmpty();

        setEncryption( mLastEncryptActionState );
        setSigning( ( canOpenPGPSign || canSMIMESign ) && mLastSignActionState );
    }
    slotUpdateSignatureAndEncrypionStateIndicators();

    QString kmailFcc;
    if ( mMsg->headerByType( "X-KMail-Fcc" ) ) {
        kmailFcc = mMsg->headerByType( "X-KMail-Fcc" )->asUnicodeString();
    }
    if ( !mBtnFcc->isChecked() ) {
        if ( kmailFcc.isEmpty() ) {
            setFcc( ident.fcc() );
        }
        else
            setFcc( kmailFcc );
    }

    const bool stickyDictionary = mBtnDictionary->isChecked() && !mIgnoreStickyFields;
    if ( !stickyDictionary ) {
        mDictionaryCombo->setCurrentByDictionaryName( ident.dictionary() );
    }

    mEdtReplyTo->setText( mMsg->replyTo()->asUnicodeString() );

    KMime::Content *msgContent = new KMime::Content;
    msgContent->setContent( mMsg->encodedContent() );
    msgContent->parse();
    MessageViewer::EmptySource emptySource;
    MessageViewer::ObjectTreeParser otp( &emptySource );//All default are ok
    emptySource.setAllowDecryption( allowDecryption );
    otp.parseObjectTree( msgContent );

    bool shouldSetCharset = false;
    if ( ( mContext == Reply || mContext == ReplyToAll || mContext == Forward ) && MessageComposer::MessageComposerSettings::forceReplyCharset() )
        shouldSetCharset = true;
    if ( shouldSetCharset && !otp.plainTextContentCharset().isEmpty() )
        mOriginalPreferredCharset = otp.plainTextContentCharset();
    // always set auto charset, but prefer original when composing if force reply is set.
    setAutoCharset();

    delete msgContent;
#if 0 //TODO port to kmime

    /* Handle the special case of non-mime mails */
    if ( mMsg->numBodyParts() == 0 && otp.textualContent().isEmpty() ) {
        mCharset=mMsg->charset();
        if ( mCharset.isEmpty() ||  mCharset == "default" ) {
            mCharset = Util::defaultCharset();
        }

        QByteArray bodyDecoded = mMsg->bodyDecoded();

        if ( allowDecryption ) {
            decryptOrStripOffCleartextSignature( bodyDecoded );
        }

        const QTextCodec *codec = KMail::Util::codecForName( mCharset );
        if ( codec ) {
            mEditor->setText( codec->toUnicode( bodyDecoded ) );
        } else {
            mEditor->setText( QString::fromLocal8Bit( bodyDecoded ) );
        }
    }
#endif

    if( (MessageComposer::MessageComposerSettings::self()->autoTextSignature()==QLatin1String( "auto" )) && mayAutoSign ) {
        //
        // Espen 2000-05-16
        // Delay the signature appending. It may start a fileseletor.
        // Not user friendy if this modal fileseletor opens before the
        // composer.
        //
        if ( MessageComposer::MessageComposerSettings::self()->prependSignature() ) {
            QTimer::singleShot( 0, mComposerBase->signatureController(), SLOT(prependSignature()) );
        } else {
            QTimer::singleShot( 0, mComposerBase->signatureController(), SLOT(appendSignature()) );
        }
    } else {
        mComposerBase->editor()->startExternalEditor();
    }

    setModified( isModified );

    // honor "keep reply in this folder" setting even when the identity is changed later on
    mPreventFccOverwrite = ( !kmailFcc.isEmpty() && ident.fcc() != kmailFcc );
    QTimer::singleShot( 0, this, SLOT(forceAutoSaveMessage()) ); //Force autosaving to make sure this composer reappears if a crash happens before the autosave timer kicks in.
}

void KMComposeWin::setAutoSaveFileName(const QString& fileName)
{
    mComposerBase->setAutoSaveFileName( fileName );
}

//-----------------------------------------------------------------------------
void KMComposeWin::setTextSelection( const QString& selection )
{
    mTextSelection = selection;
}

//-----------------------------------------------------------------------------
void KMComposeWin::setCustomTemplate( const QString& customTemplate )
{
    mCustomTemplate = customTemplate;
}

void KMComposeWin::setSigningAndEncryptionDisabled(bool v)
{
    mSigningAndEncryptionExplicitlyDisabled = v;
}

void KMComposeWin::setFolder(const Akonadi::Collection &aFolder)
{
    mFolder = aFolder;
}

//-----------------------------------------------------------------------------
void KMComposeWin::setFcc( const QString &idString )
{
    // check if the sent-mail folder still exists
    Akonadi::Collection col;
    if ( idString.isEmpty() )
        col = CommonKernel->sentCollectionFolder();
    else
        col = Akonadi::Collection( idString.toLongLong() );

    mComposerBase->setFcc( col );
    mFccFolder->setCollection( col );
}

bool KMComposeWin::isComposerModified() const
{
    return ( mComposerBase->editor()->document()->isModified() ||
             mEdtFrom->isModified() ||
             ( mEdtReplyTo && mEdtReplyTo->isModified() ) ||
             mComposerBase->recipientsEditor()->isModified() ||
             mEdtSubject->document()->isModified() );
}

//-----------------------------------------------------------------------------
bool KMComposeWin::isModified() const
{
    return mWasModified || isComposerModified();
}

//-----------------------------------------------------------------------------
void KMComposeWin::setModified( bool modified )
{
    mWasModified = modified;
    changeModifiedState( modified );
}


void KMComposeWin::changeModifiedState( bool modified )
{
    mComposerBase->editor()->document()->setModified( modified );
    if ( !modified ) {
        mEdtFrom->setModified( false );
        if ( mEdtReplyTo ) mEdtReplyTo->setModified( false );
        mComposerBase->recipientsEditor()->clearModified();
        mEdtSubject->document()->setModified( false );
    }
}

//-----------------------------------------------------------------------------
bool KMComposeWin::queryClose ()
{
    if ( !mComposerBase->editor()->checkExternalEditorFinished() ) {
        return false;
    }
    if ( kmkernel->shuttingDown() || kapp->sessionSaving() ) {
        return true;
    }

    if ( isModified() ) {
        const bool istemplate = ( mFolder.isValid() && CommonKernel->folderIsTemplates( mFolder ) );
        const QString savebut = ( istemplate ?
                                      i18n("Re&save as Template") :
                                      i18n("&Save as Draft") );
        const QString savetext = ( istemplate ?
                                       i18n("Resave this message in the Templates folder. "
                                            "It can then be used at a later time.") :
                                       i18n("Save this message in the Drafts folder. "
                                            "It can then be edited and sent at a later time.") );

        const int rc = KMessageBox::warningYesNoCancel( this,
                                                        i18n("Do you want to save the message for later or discard it?"),
                                                        i18n("Close Composer"),
                                                        KGuiItem(savebut, QLatin1String("document-save"), QString(), savetext),
                                                        KStandardGuiItem::discard(),
                                                        KStandardGuiItem::cancel());
        if ( rc == KMessageBox::Cancel ) {
            return false;
        } else if ( rc == KMessageBox::Yes ) {
            // doSend will close the window. Just return false from this method
            if (istemplate)
                slotSaveTemplate();
            else
                slotSaveDraft();
            return false;
        }
        //else fall through: return true
    }
    mComposerBase->cleanupAutoSave();

    if( !mMiscComposers.isEmpty() ) {
        kWarning() << "Tried to close while composer was active";
        return false;
    }
    return true;
}

//-----------------------------------------------------------------------------
MessageComposer::ComposerViewBase::MissingAttachment KMComposeWin::userForgotAttachment()
{
    bool checkForForgottenAttachments = mCheckForForgottenAttachments && GlobalSettings::self()->showForgottenAttachmentWarning();

    if ( !checkForForgottenAttachments )
        return MessageComposer::ComposerViewBase::NoMissingAttachmentFound;

    mComposerBase->setSubject( subject() ); //be sure the composer knows the subject
    MessageComposer::ComposerViewBase::MissingAttachment missingAttachments = mComposerBase->checkForMissingAttachments( GlobalSettings::self()->attachmentKeywords() );

    return missingAttachments;
}

void KMComposeWin::forceAutoSaveMessage()
{
    autoSaveMessage( true );
}

void KMComposeWin::autoSaveMessage(bool force)
{
    if ( isComposerModified() || force ) {
        applyComposerSetting( mComposerBase );
        mComposerBase->autoSaveMessage();
        if ( !force ) {
            mWasModified = true;
            changeModifiedState( false );
        }
    } else {
        mComposerBase->updateAutoSave();
    }
}

bool KMComposeWin::encryptToSelf()
{
    // return !Kpgp::Module::getKpgp() || Kpgp::Module::getKpgp()->encryptToSelf();
    return MessageComposer::MessageComposerSettings::self()->cryptoEncryptToSelf();
}



void KMComposeWin::slotSendFailed( const QString& msg,MessageComposer::ComposerViewBase::FailedType type)
{
    //   setModified( false );
    setEnabled( true );
    KMessageBox::sorry( mMainWidget, msg,
                        (type == MessageComposer::ComposerViewBase::AutoSave) ? i18n( "Autosave Message Failed" ) : i18n( "Sending Message Failed" ) );
}

void KMComposeWin::slotSendSuccessful()
{
    setModified( false );
    mComposerBase->cleanupAutoSave();
    mFolder = Akonadi::Collection(); // see dtor
    close();
}


const KPIMIdentities::Identity &KMComposeWin::identity() const
{
    return KMKernel::self()->identityManager()->identityForUoidOrDefault( mComposerBase->identityCombo()->currentIdentity() );
}

Kleo::CryptoMessageFormat KMComposeWin::cryptoMessageFormat() const
{
    if ( !mCryptoModuleAction ) {
        return Kleo::AutoFormat;
    }
    return cb2format( mCryptoModuleAction->currentItem() );
}

//-----------------------------------------------------------------------------
void KMComposeWin::addAttach( KMime::Content *msgPart )
{
    mComposerBase->addAttachmentPart( msgPart );
    setModified( true );
}
//-----------------------------------------------------------------------------

QString KMComposeWin::prettyMimeType( const QString &type )
{
    const QString t = type.toLower();
    const KMimeType::Ptr st = KMimeType::mimeType( t );

    if ( !st ) {
        kWarning() <<"unknown mimetype" << t;
        return t;
    }

    const QString pretty = !st->isDefault() ? st->comment() : t;
    if ( pretty.isEmpty() )
        return type;
    else
        return pretty;
}

void KMComposeWin::setAutoCharset()
{
    mCodecAction->setCurrentItem( 0 );
}

// We can't simply use KCodecAction::setCurrentCodec(), since that doesn't
// use fixEncoding().
static QString selectCharset( KSelectAction *root, const QString &encoding )
{
    foreach( QAction *action, root->actions() ) {
        KSelectAction *subMenu = dynamic_cast<KSelectAction *>( action );
        if ( subMenu ) {
            const QString codecNameToSet = selectCharset( subMenu, encoding );
            if ( !codecNameToSet.isEmpty() )
                return codecNameToSet;
        }
        else {
            const QString fixedActionText = MessageViewer::NodeHelper::fixEncoding( action->text() );
            if ( KGlobal::charsets()->codecForName(
                     KGlobal::charsets()->encodingForName( fixedActionText ) )
                 == KGlobal::charsets()->codecForName( encoding ) ) {
                return action->text();
            }
        }
    }
    return QString();
}

//-----------------------------------------------------------------------------
void KMComposeWin::setCharset( const QByteArray &charset )
{
    const QString codecNameToSet = selectCharset( mCodecAction, QString::fromLatin1(charset) );
    if ( codecNameToSet.isEmpty() ) {
        kWarning() << "Could not find charset" << charset;
        setAutoCharset();
    }
    else
        mCodecAction->setCurrentCodec( codecNameToSet );
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotAddrBook()
{
    KRun::runCommand(QLatin1String("kaddressbook"), window());
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotInsertFile()
{
    KUrl u = mComposerBase->editor()->insertFile();
    if ( u.isEmpty() )
        return;

    mRecentAction->addUrl( u );
    // Prevent race condition updating list when multiple composers are open
    {
        const QString encoding = MessageViewer::NodeHelper::encodingForName( u.fileEncoding() );
        QStringList urls = GlobalSettings::self()->recentUrls();
        QStringList encodings = GlobalSettings::self()->recentEncodings();
        // Prevent config file from growing without bound
        // Would be nicer to get this constant from KRecentFilesAction
        const int mMaxRecentFiles = 30;
        while ( urls.count() > mMaxRecentFiles )
            urls.removeLast();
        while ( encodings.count() > mMaxRecentFiles )
            encodings.removeLast();
        // sanity check
        if ( urls.count() != encodings.count() ) {
            urls.clear();
            encodings.clear();
        }
        urls.prepend( u.prettyUrl() );
        encodings.prepend( encoding );
        GlobalSettings::self()->setRecentUrls( urls );
        GlobalSettings::self()->setRecentEncodings( encodings );
        mRecentAction->saveEntries( KMKernel::self()->config()->group( QString() ) );
    }
    slotInsertRecentFile( u );
}


void KMComposeWin::slotRecentListFileClear()
{
    KSharedConfig::Ptr config = KMKernel::self()->config();
    KConfigGroup group( config, "Composer" );
    group.deleteEntry("recent-urls");
    group.deleteEntry("recent-encodings");
    mRecentAction->saveEntries( config->group( QString() ) );
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotInsertRecentFile( const KUrl &u )
{
    if ( u.fileName().isEmpty() ) {
        return;
    }

    // Get the encoding previously used when inserting this file
    QString encoding;
    const QStringList urls = GlobalSettings::self()->recentUrls();
    const QStringList encodings = GlobalSettings::self()->recentEncodings();
    const int index = urls.indexOf( u.prettyUrl() );
    if ( index != -1 ) {
        encoding = encodings[ index ];
    } else {
        kDebug()<<" encoding not found so we can't insert text"; //see InsertTextFileJob
        return;
    }


    MessageComposer::InsertTextFileJob *job = new MessageComposer::InsertTextFileJob( mComposerBase->editor(), u );
    job->setEncoding( encoding );
    job->start();
    // Don't care about the result for now
    // TODO: we should probably show an error message if it fails...
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotSelectCryptoModule( bool init )
{
    if ( !init )
        setModified( true );

    mComposerBase->attachmentModel()->setEncryptEnabled( canSignEncryptAttachments() );
    mComposerBase->attachmentModel()->setSignEnabled( canSignEncryptAttachments() );
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotUpdateFont()
{
    kDebug();
    if ( !mFixedFontAction ) {
        return;
    }
    mComposerBase->editor()->setFontForWholeText( mFixedFontAction->isChecked() ?
                                                      mFixedFont : mBodyFont );
}

QString KMComposeWin::smartQuote( const QString & msg )
{
    return MessageCore::StringUtil::smartQuote( msg, MessageComposer::MessageComposerSettings::self()->lineWrapWidth() );
}


bool KMComposeWin::insertFromMimeData( const QMimeData *source, bool forceAttachment )
{
    // If this is a PNG image, either add it as an attachment or as an inline image
    if ( source->hasImage() && source->hasFormat( QLatin1String("image/png") ) ) {
        // Get the image data before showing the dialog, since that processes events which can delete
        // the QMimeData object behind our back
        const QByteArray imageData = source->data( QLatin1String("image/png") );
        if ( imageData.isEmpty() ) {
            return true;
        }
        if ( !forceAttachment ) {
            if ( mComposerBase->editor()->textMode() == KRichTextEdit::Rich && mComposerBase->editor()->isEnableImageActions() ) {
                QImage image = qvariant_cast<QImage>( source->imageData() );
                QFileInfo fi( source->text() );

                KMenu menu;
                const QAction *addAsInlineImageAction = menu.addAction( i18n("Add as &Inline Image") );
                /*const QAction *addAsAttachmentAction = */menu.addAction( i18n("Add as &Attachment") );
                const QAction *selectedAction = menu.exec( QCursor::pos() );
                if ( selectedAction == addAsInlineImageAction ) {
                    // Let the textedit from kdepimlibs handle inline images
                    mComposerBase->editor()->insertImage( image, fi );
                    return true;
                } else if( !selectedAction ) {
                    return true;
                }
                // else fall through
            }
        }
        // Ok, when we reached this point, the user wants to add the image as an attachment.
        // Ask for the filename first.
        bool ok;
        const QString attName =
                KInputDialog::getText( i18n("KMail"), i18n( "Name of the attachment:" ), QString(), &ok, this );
        if ( !ok ) {
            return true;
        }
        addAttachment( attName, KMime::Headers::CEbase64, QString(), imageData, "image/png" );
        return true;
    }

    // If this is a URL list, add those files as attachments or text
    const KUrl::List urlList = KUrl::List::fromMimeData( source );
    if ( !urlList.isEmpty() ) {
        //Search if it's message items.
        Akonadi::Item::List items;
        Akonadi::Collection::List collections;
        bool allLocalURLs = true;

        foreach ( const KUrl &url, urlList ) {
            if ( !url.isLocalFile() ) {
                allLocalURLs = false;
            }
            const Akonadi::Item item = Akonadi::Item::fromUrl( url );
            if ( item.isValid() ) {
                items << item;
            } else {
                const Akonadi::Collection collection = Akonadi::Collection::fromUrl( url );
                if ( collection.isValid() )
                    collections << collection;
            }
        }

        if ( items.isEmpty() && collections.isEmpty() ) {
            if ( allLocalURLs || forceAttachment ) {
                foreach( const KUrl &url, urlList ) {
                    addAttachment( url, QString() );
                }
            } else {
                KMenu p;
                const QAction *addAsTextAction = p.addAction( i18np("Add URL into Message", "Add URLs into Message", urlList.size() ) );
                const QAction *addAsAttachmentAction = p.addAction( i18np("Add File as &Attachment", "Add Files as &Attachment", urlList.size() ) );
                const QAction *selectedAction = p.exec( QCursor::pos() );

                if ( selectedAction == addAsTextAction ) {
                    foreach( const KUrl &url, urlList ) {
                        mComposerBase->editor()->insertLink(url.url());
                    }
                } else if ( selectedAction == addAsAttachmentAction ) {
                    foreach( const KUrl &url, urlList ) {
                        addAttachment( url, QString() );
                    }
                }
            }
            return true;
        } else {
            if ( !items.isEmpty() ){
                Akonadi::ItemFetchJob *itemFetchJob = new Akonadi::ItemFetchJob( items, this );
                itemFetchJob->fetchScope().fetchFullPayload( true );
                itemFetchJob->fetchScope().setAncestorRetrieval( Akonadi::ItemFetchScope::Parent );
                connect( itemFetchJob, SIGNAL(result(KJob*)), this, SLOT(slotFetchJob(KJob*)) );
            }
            if ( !collections.isEmpty() ) {
                //TODO
            }
            return true;
        }
    }
    return false;
}

void KMComposeWin::slotPasteAsAttachment()
{
    const QMimeData *mimeData = QApplication::clipboard()->mimeData();
    if ( insertFromMimeData( mimeData, true ) )
        return;
    if( mimeData->hasText() ) {
        bool ok;
        const QString attName = KInputDialog::getText(
                    i18n( "Insert clipboard text as attachment" ),
                    i18n( "Name of the attachment:" ),
                    QString(), &ok, this );
        if( ok ) {
            mComposerBase->addAttachment( attName, attName, QLatin1String("utf-8"), QApplication::clipboard()->text().toUtf8(), "text/plain" );
        }
        return;
    }
}


void KMComposeWin::slotFetchJob(KJob*job)
{
    if ( job->error() ) {
        if ( static_cast<KIO::Job*>(job)->ui() )
            static_cast<KIO::Job*>(job)->ui()->showErrorMessage();
        else
            kDebug()<<" job->errorString() :"<<job->errorString();
        return;
    }
    Akonadi::ItemFetchJob *fjob = dynamic_cast<Akonadi::ItemFetchJob*>( job );
    if ( !fjob )
        return;
    const Akonadi::Item::List items = fjob->items();

    if ( items.isEmpty() )
        return;

    if ( items.first().mimeType() == KMime::Message::mimeType() ) {
        uint identity = 0;
        if ( items.at( 0 ).isValid() && items.at( 0 ).parentCollection().isValid() ) {
            QSharedPointer<MailCommon::FolderCollection> fd( MailCommon::FolderCollection::forCollection( items.at( 0 ).parentCollection(), false ) );
            if ( fd )
                identity = fd->identity();
        }
        KMCommand *command = new KMForwardAttachedCommand( this, items,identity, this );
        command->start();
    } else {
        foreach ( const Akonadi::Item &item, items ) {
            QString attachmentName = QLatin1String( "attachment" );
            if ( item.hasPayload<KABC::Addressee>() ) {
                const KABC::Addressee contact = item.payload<KABC::Addressee>();
                attachmentName = contact.realName() + QLatin1String( ".vcf" );
                //Workaround about broken kaddressbook fields.
                QByteArray data = item.payloadData();
                data.replace("X-messaging/aim-All",("X-AIM"));
                data.replace("X-messaging/icq-All",("X-ICQ"));
                data.replace("X-messaging/xmpp-All",("X-JABBER"));
                data.replace("X-messaging/msn-All",("X-MSN"));
                data.replace("X-messaging/yahoo-All",("X-YAHOO"));
                data.replace("X-messaging/gadu-All",("X-GADUGADU"));
                data.replace("X-messaging/skype-All",("X-SKYPE"));
                data.replace("X-messaging/groupwise-All",("X-GROUPWISE"));
                data.replace(("X-messaging/sms-All"),("X-SMS"));
                data.replace(("X-messaging/meanwhile-All"),("X-MEANWHILE"));
                data.replace(("X-messaging/irc-All"),("X-IRC"));
                data.replace(("X-messaging/googletalk-All"),("X-GOOGLETALK"));
                addAttachment( attachmentName, KMime::Headers::CEbase64, QString(), data, item.mimeType().toLatin1() );
            } else {
                addAttachment( attachmentName, KMime::Headers::CEbase64, QString(), item.payloadData(), item.mimeType().toLatin1() );
            }
        }
    }
}

QString KMComposeWin::addQuotesToText( const QString &inputText ) const
{
    QString answer( inputText );
    const QString indentStr = mComposerBase->editor()->quotePrefixName();
    answer.replace( QLatin1Char('\n'), QLatin1Char('\n') + indentStr );
    answer.prepend( indentStr );
    answer += QLatin1Char('\n');
    return MessageCore::StringUtil::smartQuote( answer, MessageComposer::MessageComposerSettings::self()->lineWrapWidth() );
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotUndo()
{
    QWidget *fw = focusWidget();
    if ( !fw ) {
        return;
    }

    if (::qobject_cast<PimCommon::LineEditWithAutoCorrection*>( fw )) {
        static_cast<PimCommon::LineEditWithAutoCorrection*>( fw )->undo();
    }else if ( ::qobject_cast<KMComposerEditor*>( fw ) ) {
        static_cast<KTextEdit*>( fw )->undo();
    } else if (::qobject_cast<KLineEdit*>( fw )) {
        static_cast<KLineEdit*>( fw )->undo();
    }
}

void KMComposeWin::slotRedo()
{
    QWidget *fw = focusWidget();
    if ( !fw ) {
        return;
    }

    if (::qobject_cast<PimCommon::LineEditWithAutoCorrection*>( fw )) {
        static_cast<PimCommon::LineEditWithAutoCorrection*>( fw )->redo();
    } else if ( ::qobject_cast<KMComposerEditor*>( fw ) ) {
        static_cast<KTextEdit*>( fw )->redo();
    } else if (::qobject_cast<KLineEdit*>( fw )) {
        static_cast<KLineEdit*>( fw )->redo();
    }
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotCut()
{
    QWidget *fw = focusWidget();
    if ( !fw ) {
        return;
    }

    if ( ::qobject_cast<PimCommon::LineEditWithAutoCorrection*>( fw ) ) {
        static_cast<PimCommon::LineEditWithAutoCorrection*>( fw )->cut();
    } else if ( ::qobject_cast<KMComposerEditor*>( fw ) ) {
        static_cast<KTextEdit*>(fw)->cut();
    } else if ( ::qobject_cast<KLineEdit*>( fw ) ) {
        static_cast<KLineEdit*>( fw )->cut();
    }
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotCopy()
{
    QWidget *fw = focusWidget();
    if ( !fw ) {
        return;
    }

    if ( ::qobject_cast<PimCommon::LineEditWithAutoCorrection*>( fw ) ) {
        static_cast<PimCommon::LineEditWithAutoCorrection*>( fw )->copy();
    } else if ( ::qobject_cast<KMComposerEditor*>( fw ) ) {
        static_cast<KTextEdit*>(fw)->copy();
    } else if ( ::qobject_cast<KLineEdit*>( fw ) ) {
        static_cast<KLineEdit*>( fw )->copy();
    }
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotPaste()
{
    QWidget * const fw = focusWidget();
    if ( !fw ) {
        return;
    }
    if ( ::qobject_cast<PimCommon::LineEditWithAutoCorrection*>( fw ) ) {
        static_cast<PimCommon::LineEditWithAutoCorrection*>( fw )->paste();
    } else if ( ::qobject_cast<KMComposerEditor*>( fw ) ) {
        static_cast<KTextEdit*>(fw)->paste();
    } else if ( ::qobject_cast<KLineEdit*>( fw ) ) {
        static_cast<KLineEdit*>( fw )->paste();
    }
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotMarkAll()
{
    QWidget *fw = focusWidget();
    if ( !fw ) {
        return;
    }

    if (::qobject_cast<PimCommon::LineEditWithAutoCorrection*>( fw )) {
        static_cast<PimCommon::LineEditWithAutoCorrection*>( fw )->selectAll();
    } else if ( ::qobject_cast<KLineEdit*>( fw ) ) {
        static_cast<KLineEdit*>( fw )->selectAll();
    } else if (::qobject_cast<KMComposerEditor*>( fw )) {
        static_cast<KTextEdit*>( fw )->selectAll();
    }
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotClose()
{
    close();
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotNewComposer()
{
    KMComposeWin *win;
    KMime::Message::Ptr msg( new KMime::Message );

    MessageHelper::initHeader( msg, KMKernel::self()->identityManager() );
    win = new KMComposeWin( msg, false, false, KMail::Composer::New );
    win->setCollectionForNewMessage(mCollectionForNewMessage);
    win->show();
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotNewMailReader()
{
    KMMainWin *kmmwin = new KMMainWin( 0 );
    kmmwin->show();
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotUpdWinTitle()
{
    QString s( mEdtSubject->toPlainText() );
    // Remove characters that show badly in most window decorations:
    // newlines tend to become boxes.
    if ( s.isEmpty() ) {
        setCaption( QLatin1Char('(') + i18n("unnamed") + QLatin1Char(')') );
    } else {
        setCaption( s.replace( QLatin1Char('\n'), QLatin1Char(' ') ) );
    }
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotEncryptToggled( bool on )
{
    setEncryption( on, true );
    slotUpdateSignatureAndEncrypionStateIndicators();
}

//-----------------------------------------------------------------------------
void KMComposeWin::setEncryption( bool encrypt, bool setByUser )
{
    bool wasModified = isModified();
    if ( setByUser ) {
        setModified( true );
    }
    if ( !mEncryptAction->isEnabled() ) {
        encrypt = false;
    }
    // check if the user wants to encrypt messages to himself and if he defined
    // an encryption key for the current identity
    else if ( encrypt && encryptToSelf() && !mLastIdentityHasEncryptionKey ) {
        if ( setByUser ) {
            KMessageBox::sorry( this,
                                i18n("<qt><p>You have requested that messages be "
                                     "encrypted to yourself, but the currently selected "
                                     "identity does not define an (OpenPGP or S/MIME) "
                                     "encryption key to use for this.</p>"
                                     "<p>Please select the key(s) to use "
                                     "in the identity configuration.</p>"
                                     "</qt>"),
                                i18n("Undefined Encryption Key") );
            setModified( wasModified );
        }
        encrypt = false;
    }

    // make sure the mEncryptAction is in the right state
    mEncryptAction->setChecked( encrypt );
    if(!setByUser) {
        slotUpdateSignatureAndEncrypionStateIndicators();
    }
    // show the appropriate icon
    if ( encrypt ) {
        mEncryptAction->setIcon( KIcon( QLatin1String("document-encrypt") ) );
    } else {
        mEncryptAction->setIcon( KIcon( QLatin1String("document-decrypt") ) );
    }

    // mark the attachments for (no) encryption
    if( canSignEncryptAttachments() ) {
        mComposerBase->attachmentModel()->setEncryptSelected( encrypt );
    }
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotSignToggled( bool on )
{
    setSigning( on, true );
    slotUpdateSignatureAndEncrypionStateIndicators();
}

//-----------------------------------------------------------------------------
void KMComposeWin::setSigning( bool sign, bool setByUser )
{
    bool wasModified = isModified();
    if ( setByUser ) {
        setModified( true );
    }
    if ( !mSignAction->isEnabled() ) {
        sign = false;
    }

    // check if the user defined a signing key for the current identity
    if ( sign && !mLastIdentityHasSigningKey ) {
        if ( setByUser ) {
            KMessageBox::sorry( this,
                                i18n("<qt><p>In order to be able to sign "
                                     "this message you first have to "
                                     "define the (OpenPGP or S/MIME) signing key "
                                     "to use.</p>"
                                     "<p>Please select the key to use "
                                     "in the identity configuration.</p>"
                                     "</qt>"),
                                i18n("Undefined Signing Key") );
            setModified( wasModified );
        }
        sign = false;
    }

    // make sure the mSignAction is in the right state
    mSignAction->setChecked( sign );

    if(!setByUser) {
        slotUpdateSignatureAndEncrypionStateIndicators();
    }
    // mark the attachments for (no) signing
    if ( canSignEncryptAttachments() ) {
        mComposerBase->attachmentModel()->setSignSelected( sign );
    }
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotWordWrapToggled( bool on )
{
    if ( on )
        mComposerBase->editor()->enableWordWrap( MessageComposer::MessageComposerSettings::self()->lineWrapWidth() );
    else
        disableWordWrap();
}

//-----------------------------------------------------------------------------
void KMComposeWin::disableWordWrap()
{
    mComposerBase->editor()->disableWordWrap();
}

//-----------------------------------------------------------------------------
void KMComposeWin::forceDisableHtml()
{
    mForceDisableHtml = true;
    disableHtml( MessageComposer::ComposerViewBase::NoConfirmationNeeded );
    markupAction->setEnabled( false );
    // FIXME: Remove the toggle toolbar action somehow
}

bool KMComposeWin::isComposing() const
{
    return mComposerBase && mComposerBase->isComposing();
}

void KMComposeWin::disableForgottenAttachmentsCheck()
{
    mCheckForForgottenAttachments = false;
}

void KMComposeWin::ignoreStickyFields()
{
    mIgnoreStickyFields = true;
    mBtnTransport->setChecked( false );
    mBtnDictionary->setChecked( false );
    mBtnIdentity->setChecked( false );
    mBtnTransport->setEnabled( false );
    mBtnDictionary->setEnabled( false );
    mBtnIdentity->setEnabled( false );
}


void KMComposeWin::slotPrint()
{
    printComposer(false);
}

void KMComposeWin::slotPrintPreview()
{
    printComposer(true);
}

void KMComposeWin::printComposer(bool preview)
{
    MessageComposer::Composer* composer = createSimpleComposer();
    mMiscComposers.append( composer );
    composer->setProperty("preview",preview);
    connect( composer, SIGNAL(result(KJob*)),
             this, SLOT(slotPrintComposeResult(KJob*)) );
    composer->start();
}

void KMComposeWin::slotPrintComposeResult( KJob *job )
{
    const bool preview = job->property("preview").toBool();
    printComposeResult( job, preview );
}

void KMComposeWin::printComposeResult( KJob *job, bool preview )
{
    Q_ASSERT( dynamic_cast< MessageComposer::Composer* >( job ) );
    MessageComposer::Composer* composer = dynamic_cast< MessageComposer::Composer* >( job );
    Q_ASSERT( mMiscComposers.contains( composer ) );
    mMiscComposers.removeAll( composer );

    if( composer->error() == MessageComposer::Composer::NoError ) {

        Q_ASSERT( composer->resultMessages().size() == 1 );
        Akonadi::Item printItem;
        printItem.setPayload<KMime::Message::Ptr>( composer->resultMessages().first() );
        const bool isHtml = mComposerBase->editor()->textMode() == KMeditor::Rich;
        const MessageViewer::Viewer::DisplayFormatMessage format = isHtml ? MessageViewer::Viewer::Html : MessageViewer::Viewer::Text;
        KMPrintCommand *command = new KMPrintCommand( this, printItem,0,
                                                      0, format, isHtml );
        command->setPrintPreview( preview );
        command->start();
    } else {
        if ( static_cast<KIO::Job*>(job)->ui() ) {
            static_cast<KIO::Job*>(job)->ui()->showErrorMessage();
        } else {
            kWarning() << "Composer for printing failed:" << composer->errorString();
        }
    }

}

//----------------------------------------------------------------------------
void KMComposeWin::doSend( MessageComposer::MessageSender::SendMethod method,
                           MessageComposer::MessageSender::SaveIn saveIn )
{
    if ( mStorageService->numProgressUpdateFile() > 0) {
        KMessageBox::sorry( this, i18np( "There is %1 file upload in progress.",
                                         "There are %1 file uploads in progress.",
                                         mStorageService->numProgressUpdateFile() ) );
        return;
    }
    // TODO integrate with MDA online status
    if ( method == MessageComposer::MessageSender::SendImmediate ) {
        if( !MessageComposer::Util::sendMailDispatcherIsOnline() ) {
            method = MessageComposer::MessageSender::SendLater;
        }
    }


    if ( saveIn == MessageComposer::MessageSender::SaveInNone ) { // don't save as draft or template, send immediately
        if ( KPIMUtils::firstEmailAddress( from() ).isEmpty() ) {
            if ( !( mShowHeaders & HDR_FROM ) ) {
                mShowHeaders |= HDR_FROM;
                rethinkFields( false );
            }
            mEdtFrom->setFocus();
            KMessageBox::sorry( this,
                                i18n("You must enter your email address in the "
                                     "From: field. You should also set your email "
                                     "address for all identities, so that you do "
                                     "not have to enter it for each message.") );
            return;
        }
        if ( mComposerBase->to().isEmpty() ) {
            if ( mComposerBase->cc().isEmpty() && mComposerBase->bcc().isEmpty() ) {
                KMessageBox::information( this,
                                          i18n("You must specify at least one receiver, "
                                               "either in the To: field or as CC or as BCC.") );

                return;
            } else {
                int rc = KMessageBox::questionYesNo( this,
                                                     i18n("To: field is empty. "
                                                          "Send message anyway?"),
                                                     i18n("No To: specified"),
                                                     KStandardGuiItem::yes(),
                                                     KStandardGuiItem::no(),
                                                     QLatin1String(":kmail_no_to_field_specified") );
                if ( rc == KMessageBox::No ) {
                    return;
                }
            }
        }

        if ( subject().isEmpty() ) {
            mEdtSubject->setFocus();
            int rc =
                    KMessageBox::questionYesNo( this,
                                                i18n("You did not specify a subject. "
                                                     "Send message anyway?"),
                                                i18n("No Subject Specified"),
                                                KGuiItem(i18n("S&end as Is")),
                                                KGuiItem(i18n("&Specify the Subject")),
                                                QLatin1String("no_subject_specified") );
            if ( rc == KMessageBox::No ) {
                return;
            }
        }

        const MessageComposer::ComposerViewBase::MissingAttachment forgotAttachment = userForgotAttachment();
        if ( (forgotAttachment == MessageComposer::ComposerViewBase::FoundMissingAttachmentAndAddedAttachment) ||
             (forgotAttachment == MessageComposer::ComposerViewBase::FoundMissingAttachmentAndCancel) ) {
            return;
        }


        setEnabled( false );
        // Validate the To:, CC: and BCC fields
        const QStringList recipients = QStringList() << mComposerBase->to().trimmed() << mComposerBase->cc().trimmed() << mComposerBase->bcc().trimmed();

        AddressValidationJob *job = new AddressValidationJob( recipients.join( QLatin1String( ", ") ), this, this );
        const KPIMIdentities::Identity &ident = KMKernel::self()->identityManager()->identityForUoid( mComposerBase->identityCombo()->currentIdentity() );
        QString defaultDomainName;
        if ( !ident.isNull() ) {
            defaultDomainName = ident.defaultDomainName();
        }
        job->setDefaultDomain(defaultDomainName);
        job->setProperty( "method", static_cast<int>( method ) );
        job->setProperty( "saveIn", static_cast<int>( saveIn ) );
        connect( job, SIGNAL(result(KJob*)), SLOT(slotDoDelayedSend(KJob*)) );
        job->start();

        // we'll call send from within slotDoDelaySend
    } else {
        if( saveIn == MessageComposer::MessageSender::SaveInDrafts && mEncryptAction->isChecked() &&
                !GlobalSettings::self()->neverEncryptDrafts() &&
                mComposerBase->to().isEmpty() && mComposerBase->cc().isEmpty() ) {

            KMessageBox::information( this, i18n("You must specify at least one receiver "
                                                 "in order to be able to encrypt a draft.")
                                      );
            return;
        }
        doDelayedSend( method, saveIn );
    }
}

void KMComposeWin::slotDoDelayedSend( KJob *job )
{
    if ( job->error() ) {
        KMessageBox::error( this, job->errorText() );
        setEnabled(true);
        return;
    }

    const AddressValidationJob *validateJob = qobject_cast<AddressValidationJob*>( job );

    // Abort sending if one of the recipient addresses is invalid ...
    if ( !validateJob->isValid() ) {
        setEnabled(true);
        return;
    }

    // ... otherwise continue as usual
    const MessageComposer::MessageSender::SendMethod method = static_cast<MessageComposer::MessageSender::SendMethod>( job->property( "method" ).toInt() );
    const MessageComposer::MessageSender::SaveIn saveIn = static_cast<MessageComposer::MessageSender::SaveIn>( job->property( "saveIn" ).toInt() );

    doDelayedSend( method, saveIn );
}

void KMComposeWin::applyComposerSetting( MessageComposer::ComposerViewBase* mComposerBase )
{

    QList< QByteArray > charsets = mCodecAction->mimeCharsets();
    if( !mOriginalPreferredCharset.isEmpty() ) {
        charsets.insert( 0, mOriginalPreferredCharset );
    }
    mComposerBase->setFrom( from() );
    mComposerBase->setReplyTo( replyTo() );
    mComposerBase->setSubject( subject() );
    mComposerBase->setCharsets( charsets );
    mComposerBase->setUrgent( mUrgentAction->isChecked() );
    mComposerBase->setMDNRequested( mRequestMDNAction->isChecked() );
}


void KMComposeWin::doDelayedSend( MessageComposer::MessageSender::SendMethod method, MessageComposer::MessageSender::SaveIn saveIn )
{
#ifndef QT_NO_CURSOR
    MessageViewer::KCursorSaver busy( MessageViewer::KBusyPtr::busy() );
#endif
    applyComposerSetting( mComposerBase );
    if ( mForceDisableHtml )
        disableHtml( MessageComposer::ComposerViewBase::NoConfirmationNeeded );
    bool sign = mSignAction->isChecked();
    bool encrypt = mEncryptAction->isChecked();

    mComposerBase->setCryptoOptions( sign, encrypt, cryptoMessageFormat(),
                                     ( ( saveIn != MessageComposer::MessageSender::SaveInNone && GlobalSettings::self()->neverEncryptDrafts() )
                                       || mSigningAndEncryptionExplicitlyDisabled ) );

    const int num = GlobalSettings::self()->customMessageHeadersCount();
    QMap<QByteArray, QString> customHeader;
    for (int ix=0; ix<num; ++ix) {
        CustomMimeHeader customMimeHeader( QString::number(ix) );
        customMimeHeader.readConfig();
        customHeader.insert(customMimeHeader.custHeaderName().toLatin1(), customMimeHeader.custHeaderValue() );
    }

    QMapIterator<QByteArray, QString> extraCustomHeader(mExtraHeaders);
    while (extraCustomHeader.hasNext()) {
        extraCustomHeader.next();
        customHeader.insert(extraCustomHeader.key(), extraCustomHeader.value() );
    }

    mComposerBase->setCustomHeader( customHeader );
    mComposerBase->send( method, saveIn, false );
}

//----------------------------------------------------------------------------
void KMComposeWin::slotSendLater()
{
    if ( !TransportManager::self()->showTransportCreationDialog( this, TransportManager::IfNoTransportExists ) )
        return;
    if ( !checkRecipientNumber() )
        return;
    if ( mComposerBase->editor()->checkExternalEditorFinished() ) {
        const bool wasRegistered = (SendLater::SendLaterUtil::sentLaterAgentWasRegistered() && SendLater::SendLaterUtil::sentLaterAgentEnabled());
        if (wasRegistered) {
            SendLater::SendLaterInfo *info = 0;
            QPointer<SendLater::SendLaterDialog> dlg = new SendLater::SendLaterDialog(info, this);
            if (dlg->exec()) {
                info = dlg->info();
                const SendLater::SendLaterDialog::SendLaterAction action = dlg->action();
                delete dlg;
                switch (action) {
                case SendLater::SendLaterDialog::Unknown:
                    kDebug()<<"Sendlater action \"Unknown\": Need to fix it.";
                    break;
                case SendLater::SendLaterDialog::Canceled:
                    return;
                    break;
                case SendLater::SendLaterDialog::PutInOutbox:
                    doSend( MessageComposer::MessageSender::SendLater );
                    break;
                case SendLater::SendLaterDialog::SendDeliveryAtTime:
                {
                    mComposerBase->setSendLaterInfo(info);
                    if (info->isRecurrence()) {
                        doSend( MessageComposer::MessageSender::SendLater, MessageComposer::MessageSender::SaveInTemplates );
                    } else {
                        doSend( MessageComposer::MessageSender::SendLater, MessageComposer::MessageSender::SaveInDrafts );
                    }
                    break;
                }
                }
            } else {
                delete dlg;
            }
        } else {
            doSend( MessageComposer::MessageSender::SendLater );
        }
    }
}

//----------------------------------------------------------------------------
void KMComposeWin::slotSaveDraft()
{
    if ( mComposerBase->editor()->checkExternalEditorFinished() ) {
        doSend( MessageComposer::MessageSender::SendLater, MessageComposer::MessageSender::SaveInDrafts );
    }
}

//----------------------------------------------------------------------------
void KMComposeWin::slotSaveTemplate()
{
    if ( mComposerBase->editor()->checkExternalEditorFinished() ) {
        doSend( MessageComposer::MessageSender::SendLater, MessageComposer::MessageSender::SaveInTemplates );
    }
}

//----------------------------------------------------------------------------
void KMComposeWin::slotSendNowVia( QAction *item )
{
    const QList<int> availTransports= TransportManager::self()->transportIds();
    const int transport = item->data().toInt();
    if ( availTransports.contains( transport ) ) {
        mComposerBase->transportComboBox()->setCurrentTransport( transport );
        slotSendNow();
    }
}

//----------------------------------------------------------------------------
void KMComposeWin::slotSendLaterVia( QAction *item )
{
    const QList<int> availTransports= TransportManager::self()->transportIds();
    const int transport = item->data().toInt();
    if ( availTransports.contains( transport ) ) {
        mComposerBase->transportComboBox()->setCurrentTransport( transport );
        slotSendLater();
    }
}

//----------------------------------------------------------------------------
void KMComposeWin::sendNow(bool shortcutUsed)
{
    if ( !mComposerBase->editor()->checkExternalEditorFinished() ) {
        return;
    }
    if ( !TransportManager::self()->showTransportCreationDialog( this, TransportManager::IfNoTransportExists ) )
        return;
    if ( !checkRecipientNumber() )
        return;
    mSendNowByShortcutUsed = shortcutUsed;
    if( GlobalSettings::self()->checkSpellingBeforeSend()) {
        mComposerBase->editor()->forceSpellChecking();
    } else {
        slotCheckSendNow();
    }
}

void KMComposeWin::slotSendNowByShortcut()
{
    sendNow(true);
}

void KMComposeWin::slotSendNow()
{
    sendNow(false);
}

void KMComposeWin::confirmBeforeSend()
{
    const int rc = KMessageBox::warningYesNoCancel( mMainWidget,
                                                    i18n("About to send email..."),
                                                    i18n("Send Confirmation"),
                                                    KGuiItem( i18n("&Send Now") ),
                                                    KGuiItem( i18n("Send &Later") ) );

    if ( rc == KMessageBox::Yes ) {
        doSend( MessageComposer::MessageSender::SendImmediate );
    } else if ( rc == KMessageBox::No ) {
        doSend( MessageComposer::MessageSender::SendLater );
    }
}

void KMComposeWin::slotCheckSendNow()
{
    if ( GlobalSettings::self()->confirmBeforeSend() ) {
        confirmBeforeSend();
    } else {
        if (mSendNowByShortcutUsed) {
            if (!GlobalSettings::self()->checkSendDefaultActionShortcut()) {
                ValidateSendMailShortcut validateShortcut(actionCollection(), this);
                if (!validateShortcut.validate()) {
                    return;
                }
            }
            if (GlobalSettings::self()->confirmBeforeSendWhenUseShortcut()) {
                confirmBeforeSend();
                return;
            }
        }
        doSend( MessageComposer::MessageSender::SendImmediate );
    }
}

//----------------------------------------------------------------------------
bool KMComposeWin::checkRecipientNumber() const
{
    const int thresHold = GlobalSettings::self()->recipientThreshold();
    if ( GlobalSettings::self()->tooManyRecipients() && mComposerBase->recipientsEditor()->recipients().count() > thresHold ) {
        if ( KMessageBox::questionYesNo( mMainWidget,
                                         i18n("You are trying to send the mail to more than %1 recipients. Send message anyway?", thresHold),
                                         i18n("Too many recipients"),
                                         KGuiItem( i18n("&Send as Is") ),
                                         KGuiItem( i18n("&Edit Recipients") ) ) == KMessageBox::No ) {
            return false;
        }
    }
    return true;
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotHelp()
{
    KToolInvocation::invokeHelp();
}

//-----------------------------------------------------------------------------
void KMComposeWin::enableHtml()
{
    if ( mForceDisableHtml ) {
        disableHtml( MessageComposer::ComposerViewBase::NoConfirmationNeeded );
        return;
    }

    mComposerBase->editor()->enableRichTextMode();
    if ( !toolBar( QLatin1String("htmlToolBar") )->isVisible() ) {
        // Use singleshot, as we we might actually be called from a slot that wanted to disable the
        // toolbar (but the messagebox in disableHtml() prevented that and called us).
        // The toolbar can't correctly deal with being enabled right in a slot called from the "disabled"
        // signal, so wait one event loop run for that.
        QTimer::singleShot( 0, toolBar( QLatin1String("htmlToolBar") ), SLOT(show()) );
    }
    if ( !markupAction->isChecked() )
        markupAction->setChecked( true );

    mComposerBase->editor()->updateActionStates();
    mComposerBase->editor()->setActionsEnabled( true );
}



//-----------------------------------------------------------------------------
void KMComposeWin::disableHtml( MessageComposer::ComposerViewBase::Confirmation confirmation )
{
    bool forcePlainTextMarkup = false;
    if ( confirmation == MessageComposer::ComposerViewBase::LetUserConfirm && mComposerBase->editor()->isFormattingUsed() && !mForceDisableHtml ) {
        int choice = KMessageBox::warningYesNoCancel( this, i18n( "Turning HTML mode off "
                                                                  "will cause the text to lose the formatting. Are you sure?" ),
                                                      i18n( "Lose the formatting?" ), KGuiItem( i18n( "Lose Formatting" ) ), KGuiItem( i18n( "Add Markup Plain Text" ) ) , KStandardGuiItem::cancel(),
                                                      QLatin1String("LoseFormattingWarning") );

        switch(choice) {
        case KMessageBox::Cancel:
            enableHtml();
            return;
        case KMessageBox::No:
            forcePlainTextMarkup = true;
            break;
        case KMessageBox::Yes:
            break;
        }
    }

    mComposerBase->editor()->forcePlainTextMarkup(forcePlainTextMarkup);
    mComposerBase->editor()->switchToPlainText();
    mComposerBase->editor()->setActionsEnabled( false );

    slotUpdateFont();
    if ( toolBar( QLatin1String("htmlToolBar") )->isVisible() ) {
        // See the comment in enableHtml() why we use a singleshot timer, similar situation here.
        QTimer::singleShot( 0, toolBar( QLatin1String("htmlToolBar") ), SLOT(hide()) );
    }
    if ( markupAction->isChecked() )
        markupAction->setChecked( false );
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotToggleMarkup()
{
    htmlToolBarVisibilityChanged( markupAction->isChecked() );
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotTextModeChanged( MessageComposer::KMeditor::Mode mode )
{
    if ( mode == KMeditor::Plain )
        disableHtml( MessageComposer::ComposerViewBase::NoConfirmationNeeded ); // ### Can this happen at all?
    else
        enableHtml();
}

//-----------------------------------------------------------------------------
void KMComposeWin::htmlToolBarVisibilityChanged( bool visible )
{
    if ( visible )
        enableHtml();
    else
        disableHtml( MessageComposer::ComposerViewBase::LetUserConfirm );
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotAutoSpellCheckingToggled( bool on )
{
    mAutoSpellCheckingAction->setChecked( on );
    if ( on != mComposerBase->editor()->checkSpellingEnabled() )
        mComposerBase->editor()->setCheckSpellingEnabled( on );
    if ( on != mEdtSubject->checkSpellingEnabled() )
        mEdtSubject->setCheckSpellingEnabled( on );


    QString temp;
    if ( on ) {
        temp = i18n( "Spellcheck: on" );
    } else {
        temp = i18n( "Spellcheck: off" );
    }
    statusBar()->changeItem( temp, 3 );
}

void KMComposeWin::slotSpellCheckingStatus(const QString & status)
{
    statusBar()->changeItem( status, 0 );
    QTimer::singleShot( 2000, this, SLOT(slotSpellcheckDoneClearStatus()) );
}

void KMComposeWin::slotSpellcheckDoneClearStatus()
{
    statusBar()->changeItem(QString(), 0);
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotIdentityChanged( uint uoid, bool initalChange )
{
    if( mMsg == 0 ) {
        kDebug() << "Trying to change identity but mMsg == 0!";
        return;
    }

    const KPIMIdentities::Identity &ident =
            KMKernel::self()->identityManager()->identityForUoid( uoid );
    if ( ident.isNull() ) {
        return;
    }
    bool wasModified(isModified());
    emit identityChanged( identity() );
    if ( !ident.fullEmailAddr().isNull() ) {
        mEdtFrom->setText( ident.fullEmailAddr() );
    }

    // make sure the From field is shown if it does not contain a valid email address
    if ( KPIMUtils::firstEmailAddress( from() ).isEmpty() ) {
        mShowHeaders |= HDR_FROM;
    }
    if ( mEdtReplyTo ) {
        mEdtReplyTo->setText( ident.replyToAddr() );
    }

    // remove BCC of old identity and add BCC of new identity (if they differ)
    const KPIMIdentities::Identity &oldIdentity =
            KMKernel::self()->identityManager()->identityForUoidOrDefault( mId );


    if ( ident.organization().isEmpty() ) {
        mMsg->organization()->clear();
    } else {
        KMime::Headers::Organization * const organization
                = new KMime::Headers::Organization( mMsg.get(), ident.organization(), "utf-8" );
        mMsg->setHeader( organization );
    }
    if ( !ident.isXFaceEnabled() || ident.xface().isEmpty() ) {
        mMsg->removeHeader( "X-Face" );
    } else {
        QString xface = ident.xface();
        if ( !xface.isEmpty() ) {
            int numNL = ( xface.length() - 1 ) / 70;
            for ( int i = numNL; i > 0; --i ) {
                xface.insert( i*70, QLatin1String("\n\t") );
            }
            KMime::Headers::Generic *header = new KMime::Headers::Generic( "X-Face", mMsg.get(), xface, "utf-8" );
            mMsg->setHeader( header );
        }
    }
    // If the transport sticky checkbox is not checked, set the transport
    // from the new identity
    if ( !mBtnTransport->isChecked() && !mIgnoreStickyFields ) {
        const int transportId = ident.transport().isEmpty() ? -1 : ident.transport().toInt();
        const Transport *transport = TransportManager::self()->transportById( transportId, true );
        if ( !transport ) {
            mMsg->removeHeader( "X-KMail-Transport" );
            mComposerBase->transportComboBox()->setCurrentTransport( TransportManager::self()->defaultTransportId() );
        } else {
            KMime::Headers::Generic *header = new KMime::Headers::Generic( "X-KMail-Transport", mMsg.get(), QString::number( transport->id() ), "utf-8" );
            mMsg->setHeader( header );
            mComposerBase->transportComboBox()->setCurrentTransport( transport->id() );
        }
    }

    const bool fccIsDisabled = ident.disabledFcc();
    if (fccIsDisabled) {
        KMime::Headers::Generic *header = new KMime::Headers::Generic( "X-KMail-FccDisabled", mMsg.get(), QLatin1String("true"), "utf-8" );
        mMsg->setHeader( header );
    } else {
        mMsg->removeHeader( "X-KMail-FccDisabled" );
    }
    mFccFolder->setEnabled(!fccIsDisabled);


    if ( !mBtnDictionary->isChecked() && !mIgnoreStickyFields ) {
        mDictionaryCombo->setCurrentByDictionaryName( ident.dictionary() );
    }
    slotSpellCheckingLanguage( mDictionaryCombo->currentDictionary() );
    if ( !mBtnFcc->isChecked() && !mPreventFccOverwrite ) {
        setFcc( ident.fcc() );
    }

    // if unmodified, apply new template, if one is set
    if ( !wasModified && !( ident.templates().isEmpty() && mCustomTemplate.isEmpty() ) &&
         !initalChange ) {
        applyTemplate( uoid, mId );
    } else {
        mComposerBase->identityChanged( ident, oldIdentity, false );
        mEdtSubject->setAutocorrectionLanguage(ident.autocorrectionLanguage());
    }


    // disable certain actions if there is no PGP user identity set
    // for this profile
    bool bNewIdentityHasSigningKey = !ident.pgpSigningKey().isEmpty() || !ident.smimeSigningKey().isEmpty();
    bool bNewIdentityHasEncryptionKey = !ident.pgpSigningKey().isEmpty() || !ident.smimeSigningKey().isEmpty();
    // save the state of the sign and encrypt button
    if ( !bNewIdentityHasEncryptionKey && mLastIdentityHasEncryptionKey ) {
        mLastEncryptActionState = mEncryptAction->isChecked();
        setEncryption( false );
    }
    if ( !bNewIdentityHasSigningKey && mLastIdentityHasSigningKey ) {
        mLastSignActionState = mSignAction->isChecked();
        setSigning( false );
    }
    // restore the last state of the sign and encrypt button
    if ( bNewIdentityHasEncryptionKey && !mLastIdentityHasEncryptionKey ) {
        setEncryption( mLastEncryptActionState );
    }
    if ( bNewIdentityHasSigningKey && !mLastIdentityHasSigningKey ) {
        setSigning( mLastSignActionState );
    }

    mCryptoModuleAction->setCurrentItem( format2cb(
                                             Kleo::stringToCryptoMessageFormat( ident.preferredCryptoMessageFormat() ) ) );
    slotSelectCryptoModule( true );

    mLastIdentityHasSigningKey = bNewIdentityHasSigningKey;
    mLastIdentityHasEncryptionKey = bNewIdentityHasEncryptionKey;
    const KPIMIdentities::Signature sig = const_cast<KPIMIdentities::Identity&>( ident ).signature();
    bool isEnabledSignature = sig.isEnabledSignature();
    mAppendSignature->setEnabled(isEnabledSignature);
    mPrependSignature->setEnabled(isEnabledSignature);
    mInsertSignatureAtCursorPosition->setEnabled(isEnabledSignature);

    mId = uoid;
    changeCryptoAction();
    // make sure the From and BCC fields are shown if necessary
    rethinkFields( false );
    setModified(wasModified);
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotSpellcheckConfig()
{
    static_cast<KMComposerEditor *>(mComposerBase->editor())->showSpellConfigDialog( QLatin1String("kmail2rc") );
}

//-----------------------------------------------------------------------------
void KMComposeWin::slotEditToolbars()
{
    saveMainWindowSettings( KMKernel::self()->config()->group( "Composer") );
    KEditToolBar dlg( guiFactory(), this );

    connect( &dlg, SIGNAL(newToolBarConfig()),
             SLOT(slotUpdateToolbars()) );

    dlg.exec();
}

void KMComposeWin::slotUpdateToolbars()
{
    createGUI( QLatin1String("kmcomposerui.rc") );
    applyMainWindowSettings( KMKernel::self()->config()->group( "Composer") );
}

void KMComposeWin::slotEditKeys()
{
    KShortcutsDialog::configure( actionCollection(),
                                 KShortcutsEditor::LetterShortcutsDisallowed );
}

void KMComposeWin::setFocusToEditor()
{
    // The cursor position is already set by setMsg(), so we only need to set the
    // focus here.
    mComposerBase->editor()->setFocus();
}

void KMComposeWin::setFocusToSubject()
{
    mEdtSubject->setFocus();
}

void KMComposeWin::slotCompletionModeChanged( KGlobalSettings::Completion mode )
{
    GlobalSettings::self()->setCompletionMode( (int) mode );

    // sync all the lineedits to the same completion mode
    mEdtFrom->setCompletionMode( mode );
    mEdtReplyTo->setCompletionMode( mode );
    mComposerBase->recipientsEditor()->setCompletionMode( mode );
}

void KMComposeWin::slotConfigChanged()
{
    readConfig( true /*reload*/);
    mComposerBase->updateAutoSave();
    rethinkFields();
    slotWordWrapToggled( mWordWrapAction->isChecked() );
}

/*
 * checks if the drafts-folder has been deleted
 * that is not nice so we set the system-drafts-folder
 */
void KMComposeWin::slotFolderRemoved( const Akonadi::Collection & col )
{
    kDebug() << "you killed me.";
    // TODO: need to handle templates here?
    if ( ( mFolder.isValid() ) && ( col.id() == mFolder.id() ) ) {
        mFolder = CommonKernel->draftsCollectionFolder();
        kDebug() << "restoring drafts to" << mFolder.id();
    }
}


void KMComposeWin::slotOverwriteModeChanged()
{
    mComposerBase->editor()->setCursorWidth( mComposerBase->editor()->overwriteMode () ? 5 : 1 );
    statusBar()->changeItem( overwriteModeStr(), 4 );
}

QString KMComposeWin::overwriteModeStr() const
{
    return mComposerBase->editor()->overwriteMode () ? i18n("OVR") : i18n ("INS");
}

void KMComposeWin::slotCursorPositionChanged()
{
    // Change Line/Column info in status bar
    int col, line;
    QString temp;
    line = mComposerBase->editor()->linePosition();
    col = mComposerBase->editor()->columnNumber();
    temp = i18nc("Shows the linenumber of the cursor position.", " Line: %1 ", line + 1 );
    statusBar()->changeItem( temp, 1 );
    temp = i18n( " Column: %1 ", col + 1 );
    statusBar()->changeItem( temp, 2 );

    // Show link target in status bar
    if ( mComposerBase->editor()->textCursor().charFormat().isAnchor() ) {
        const QString text = mComposerBase->editor()->currentLinkText();
        const QString url = mComposerBase->editor()->currentLinkUrl();
        statusBar()->changeItem( text + QLatin1String(" -> ") + url, 0 );
    }
    else {
        statusBar()->changeItem( QString(), 0 );
    }
}

#if 0
namespace {
class KToggleActionResetter {
    KToggleAction *mAction;
    bool mOn;

public:
    KToggleActionResetter( KToggleAction *action, bool on )
        : mAction( action ), mOn( on ) {}
    ~KToggleActionResetter() {
        if ( mAction ) {
            mAction->setChecked( mOn );
        }
    }
    void disable() { mAction = 0; }
};
}

void KMComposeWin::slotEncryptChiasmusToggled( bool on )
{
    if ( !on ) {
        return;
    }

    KToggleActionResetter resetter( mEncryptChiasmusAction, false );

    const Kleo::CryptoBackend::Protocol *chiasmus =
            Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );

    if ( !chiasmus ) {
        const QString msg = Kleo::CryptoBackendFactory::instance()->knowsAboutProtocol( "Chiasmus" ) ?
                    i18n( "Please configure a Crypto Backend to use for "
                          "Chiasmus encryption first.\n"
                          "You can do this in the Crypto Backends tab of "
                          "the configure dialog's Security page." ) :
                    i18n( "It looks as though libkleopatra was compiled without "
                          "Chiasmus support. You might want to recompile "
                          "libkleopatra with --enable-chiasmus.");
        KMessageBox::information( this, msg, i18n("No Chiasmus Backend Configured" ) );
        return;
    }

    std::auto_ptr<Kleo::SpecialJob> job( chiasmus->specialJob( "x-obtain-keys", QMap<QString,QVariant>() ) );
    if ( !job.get() ) {
        const QString msg = i18n( "Chiasmus backend does not offer the "
                                  "\"x-obtain-keys\" function. Please report this bug." );
        KMessageBox::error( this, msg, i18n( "Chiasmus Backend Error" ) );
        return;
    }

    if ( job->exec() ) {
        job->showErrorDialog( this, i18n( "Chiasmus Backend Error" ) );
        return;
    }

    const QVariant result = job->property( "result" );
    if ( result.type() != QVariant::StringList ) {
        const QString msg = i18n( "Unexpected return value from Chiasmus backend: "
                                  "The \"x-obtain-keys\" function did not return a "
                                  "string list. Please report this bug." );
        KMessageBox::error( this, msg, i18n( "Chiasmus Backend Error" ) );
        return;
    }

    const QStringList keys = result.toStringList();
    if ( keys.empty() ) {
        const QString msg = i18n( "No keys have been found. Please check that a "
                                  "valid key path has been set in the Chiasmus "
                                  "configuration." );
        KMessageBox::information( this, msg, i18n( "No Chiasmus Keys Found" ) );
        return;
    }

    MessageViewer::ChiasmusKeySelector selectorDlg( this, i18n( "Chiasmus Encryption Key Selection" ),
                                                    keys, GlobalSettings::chiasmusKey(),
                                                    GlobalSettings::chiasmusOptions() );

    if ( selectorDlg.exec() != KDialog::Accepted ) {
        return;
    }

    GlobalSettings::setChiasmusOptions( selectorDlg.options() );
    GlobalSettings::setChiasmusKey( selectorDlg.key() );
    assert( !GlobalSettings::chiasmusKey().isEmpty() );
    resetter.disable();
}
#endif

void KMComposeWin::recipientEditorSizeHintChanged()
{
    QTimer::singleShot( 1, this, SLOT(setMaximumHeaderSize()) );
}

void KMComposeWin::setMaximumHeaderSize()
{
    mHeadersArea->setMaximumHeight( mHeadersArea->sizeHint().height() );
}

void KMComposeWin::slotUpdateSignatureAndEncrypionStateIndicators()
{
    mCryptoStateIndicatorWidget->updateSignatureAndEncrypionStateIndicators(mSignAction->isChecked(), mEncryptAction->isChecked());
}

void KMComposeWin::slotLanguageChanged( const QString &language )
{
    mDictionaryCombo->setCurrentByDictionary( language );
}


void KMComposeWin::slotFccFolderChanged(const Akonadi::Collection& collection)
{
    mComposerBase->setFcc( collection );
    mComposerBase->editor()->document()->setModified(true);
}

void KMComposeWin::insertSpecialCharacter()
{
    if(!mSelectSpecialChar) {
        mSelectSpecialChar = new KPIMTextEdit::SelectSpecialChar(this);
        mSelectSpecialChar->setCaption(i18n("Insert Special Character"));
        mSelectSpecialChar->setOkButtonText(i18n("Insert"));
        connect(mSelectSpecialChar,SIGNAL(charSelected(QChar)),this,SLOT(charSelected(QChar)));
    }
    mSelectSpecialChar->show();
}

void KMComposeWin::charSelected(const QChar& c)
{
    mComposerBase->editor()->insertPlainText(c);
}

void KMComposeWin::slotSaveAsFile()
{
    QPointer<KFileDialog> dlg = new KFileDialog(KUrl(),QString(),this);
    dlg->setOperationMode(KFileDialog::Saving);
    dlg->setConfirmOverwrite(true);
    if(mComposerBase->editor()->textMode() == KMeditor::Rich ) {
        dlg->setFilter( QString::fromLatin1("text/html text/plain application/vnd.oasis.opendocument.text") );
    } else {
        dlg->setFilter( QString::fromLatin1("text/plain") );
    }

    if(dlg->exec()) {
        QTextDocumentWriter writer;
        const QString filename = dlg->selectedUrl().path();
        writer.setFileName(dlg->selectedUrl().path());
        if (dlg->currentFilter() == QString::fromLatin1("text/plain") || filename.endsWith(QLatin1String(".txt"))) {
            writer.setFormat("plaintext");
        } else if (dlg->currentFilter() == QString::fromLatin1("text/html")|| filename.endsWith(QLatin1String(".html"))) {
            writer.setFormat("HTML");
        } else if (dlg->currentFilter() == QString::fromLatin1("application/vnd.oasis.opendocument.text") || filename.endsWith(QLatin1String(".odf"))) {
            writer.setFormat("ODF");
        } else {
            writer.setFormat("plaintext");
        }
        if (!writer.write(mComposerBase->editor()->document())) {
            qDebug()<<" Error during writing";
        }
    }
    delete dlg;
}

void KMComposeWin::slotCreateAddressBookContact()
{
    CreateNewContactJob *job = new CreateNewContactJob( this, this );
    job->start();
}

void KMComposeWin::slotAttachMissingFile()
{
    mComposerBase->attachmentController()->showAddAttachmentDialog();
}

void KMComposeWin::slotCloseAttachMissingFile()
{
    if(m_verifyMissingAttachment) {
        m_verifyMissingAttachment->start();
    }
}

void KMComposeWin::slotVerifyMissingAttachmentTimeout()
{
    if( mComposerBase->hasMissingAttachments( GlobalSettings::self()->attachmentKeywords() )) {
        mAttachmentMissing->animatedShow();
    } else {
        m_verifyMissingAttachment->start();
    }
}

void KMComposeWin::slotExplicitClosedMissingAttachment()
{
    if(m_verifyMissingAttachment) {
        m_verifyMissingAttachment->stop();
        delete m_verifyMissingAttachment;
        m_verifyMissingAttachment = 0;
    }
}

void KMComposeWin::addExtraCustomHeaders( const QMap<QByteArray, QString> &headers)
{
    mExtraHeaders = headers;
}

void KMComposeWin::slotSentenceCase()
{
    QTextCursor textCursor = mComposerBase->editor()->textCursor();
    PimCommon::EditorUtil::sentenceCase(textCursor);
}

void KMComposeWin::slotUpperCase()
{
    QTextCursor textCursor = mComposerBase->editor()->textCursor();
    PimCommon::EditorUtil::upperCase(textCursor);
}

void KMComposeWin::slotLowerCase()
{
    QTextCursor textCursor = mComposerBase->editor()->textCursor();
    PimCommon::EditorUtil::lowerCase(textCursor);
}

void KMComposeWin::slotExternalEditorStarted()
{
    mComposerBase->identityCombo()->setEnabled(false);
    mExternalEditorWarning->show();
}

void KMComposeWin::slotExternalEditorClosed()
{
    mComposerBase->identityCombo()->setEnabled(true);
    mExternalEditorWarning->hide();
}

void KMComposeWin::slotInsertShortUrl(const QString &url)
{
    mComposerBase->editor()->insertLink(url);
}

void KMComposeWin::slotShareLinkDone(const QString &link)
{
    mComposerBase->editor()->insertShareLink(link);
}

void KMComposeWin::slotTransportChanged()
{
    mComposerBase->editor()->document()->setModified(true);
}

void KMComposeWin::slotFollowUpMail()
{
    QPointer<FollowUpReminderSelectDateDialog> dlg = new FollowUpReminderSelectDateDialog(this);
    if (dlg->exec()) {
        //TODO
    }
    delete dlg;
}
