kde-playground/kdepim/kmail/editor/kmcomposewin.cpp
2015-04-14 21:49:29 +00:00

3672 lines
148 KiB
C++

/*
* 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 "followupreminder/followupremindercreatejob.h"
#include "agents/followupreminderagent/followupreminderutil.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"
#include "widgets/statusbarlabeltoggledstate.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>
#include <widgets/splittercollapser.h>
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),
mFollowUpToggleAction(0),
mStatusBarLabelToggledOverrideMode(0),
mStatusBarLabelSpellCheckingChangeMode(0)
{
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(QString)), this, SLOT(slotSendSuccessful(QString)) );
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") );
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 );
mSnippetSplitterCollapser = new PimCommon::SplitterCollapser(mSnippetSplitter, mSnippetWidget, this);
mSnippetSplitterCollapser->setVisible( GlobalSettings::self()->showSnippetManager() );
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( 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)), this, SLOT(slotSnippetWidgetVisibilityChanged(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() );
mFollowUpToggleAction = new KToggleAction( i18n("Follow Up Mail..."), this );
actionCollection()->addAction( QLatin1String("follow_up_mail"), mFollowUpToggleAction );
connect( mFollowUpToggleAction, SIGNAL(triggered(bool)), this, SLOT(slotFollowUpMail(bool)) );
mFollowUpToggleAction->setEnabled(FollowUpReminder::FollowUpReminderUtil::followupReminderAgentEnabled());
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..." ) );
}
}
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 );
mStatusBarLabelToggledOverrideMode = new StatusBarLabelToggledState(this);
mStatusBarLabelToggledOverrideMode->setStateString(i18n("OVR"), i18n("INS"));
statusBar()->addPermanentWidget(mStatusBarLabelToggledOverrideMode,0 );
connect(mStatusBarLabelToggledOverrideMode, SIGNAL(toggleModeChanged(bool)), this, SLOT(slotOverwriteModeWasChanged(bool)));
mStatusBarLabelSpellCheckingChangeMode = new StatusBarLabelToggledState(this);
mStatusBarLabelSpellCheckingChangeMode->setStateString(i18n( "Spellcheck: on" ), i18n( "Spellcheck: off" ));
statusBar()->addPermanentWidget(mStatusBarLabelSpellCheckingChangeMode, 0 );
connect(mStatusBarLabelSpellCheckingChangeMode, SIGNAL(toggleModeChanged(bool)), this, SLOT(slotAutoSpellCheckingToggled(bool)));
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(const QString &messageId)
{
setModified( false );
addFollowupReminder(messageId);
mComposerBase->cleanupAutoSave();
mFolder = Akonadi::Collection(); // see dtor
close();
}
void KMComposeWin::addFollowupReminder(const QString &messageId)
{
if (mFollowUpDate.isValid()) {
FollowupReminderCreateJob *job = new FollowupReminderCreateJob;
job->setSubject(subject());
job->setMessageId(messageId);
job->setTo(replyTo());
job->setFollowUpReminderDate(mFollowUpDate);
job->setCollectionToDo(mFollowUpCollection);
job->start();
}
}
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::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( validateLineWrapWidth() );
else
disableWordWrap();
}
int KMComposeWin::validateLineWrapWidth()
{
int lineWrap = MessageComposer::MessageComposerSettings::self()->lineWrapWidth();
if ((lineWrap == 0) || (lineWrap > 78))
lineWrap = 78;
else if (lineWrap < 30)
lineWrap = 30;
return lineWrap;
}
//-----------------------------------------------------------------------------
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 );
mStatusBarLabelSpellCheckingChangeMode->setToggleMode(on);
}
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()
{
const bool overwriteMode = mComposerBase->editor()->overwriteMode ();
mComposerBase->editor()->setCursorWidth( overwriteMode ? 5 : 1 );
mStatusBarLabelToggledOverrideMode->setToggleMode(overwriteMode);
}
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(bool toggled)
{
if (toggled) {
QPointer<FollowUpReminderSelectDateDialog> dlg = new FollowUpReminderSelectDateDialog(this);
if (dlg->exec()) {
mFollowUpDate = dlg->selectedDate();
mFollowUpCollection = dlg->collection();
} else {
mFollowUpToggleAction->setChecked(false);
}
delete dlg;
} else {
mFollowUpDate = QDate();
mFollowUpCollection = Akonadi::Collection();
}
}
void KMComposeWin::slotSnippetWidgetVisibilityChanged(bool b)
{
mSnippetWidget->setVisible(b);
mSnippetSplitterCollapser->setVisible(b);
}
void KMComposeWin::slotOverwriteModeWasChanged(bool state)
{
mComposerBase->editor()->setCursorWidth( state ? 5 : 1 );
mComposerBase->editor()->setOverwriteMode (state);
}