You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

3020 lines
88 KiB

// kmcomposewin.cpp
// Author: Markus Wuebben <markus.wuebben@kde.org>
// This code is published under the GPL.
// keep this in sync with the define in configuredialog.h
#define DEFAULT_EDITOR_STR "kwrite %f"
#undef GrayScale
#undef Color
#include <qtooltip.h>
#include "kmmessage.h"
#include "kmsender.h"
#include "kmidentity.h"
#include "kfileio.h"
#include "kbusyptr.h"
#include "kmmsgpartdlg.h"
#include <kpgp.h>
#include "kmaddrbookdlg.h"
#include "kmaddrbook.h"
#include "kmfolder.h"
#include "kmfoldermgr.h"
#include "kmfoldercombobox.h"
#include "kmtransport.h"
#include <kaction.h>
#include <kglobalsettings.h>
#include <kcharsets.h>
#include <kcompletionbox.h>
#include <kcursor.h>
#include <kstdaction.h>
#include <kedittoolbar.h>
#include <kkeydialog.h>
#include <kdebug.h>
#include "kmmainwin.h"
#include "kmreaderwin.h"
#include <assert.h>
#include <mimelib/mimepp.h>
#include <kfiledialog.h>
#include <kwin.h>
#include <kmessagebox.h>
#include <kurldrag.h>
#include <kspell.h>
#include <qtabdialog.h>
#include <qregexp.h>
#include <qbuffer.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <ktempfile.h>
#include <fcntl.h>
#include "kmrecentaddr.h"
#include <klocale.h>
#include <kapplication.h>
#include <kstatusbar.h>
#include <qpopupmenu.h>
#include "kmcomposewin.moc"
//-----------------------------------------------------------------------------
KMComposeWin::KMComposeWin(KMMessage *aMsg, QString id)
: KMTopLevelWidget (), MailComposerIface(),
mId( id )
{
mMainWidget = new QWidget(this);
mIdentity = new QComboBox(mMainWidget);
mFcc = new KMFolderComboBox(mMainWidget);
mFcc->showOutboxFolder( FALSE );
mTransport = new QComboBox(true, mMainWidget);
mEdtFrom = new KMLineEdit(this,false,mMainWidget);
mEdtReplyTo = new KMLineEdit(this,false,mMainWidget);
mEdtTo = new KMLineEdit(this,true,mMainWidget);
mEdtCc = new KMLineEdit(this,true,mMainWidget);
mEdtBcc = new KMLineEdit(this,true,mMainWidget);
mEdtSubject = new KMLineEdit(this,false,mMainWidget, "subjectLine");
mLblIdentity = new QLabel(mMainWidget);
mLblFcc = new QLabel(mMainWidget);
mLblTransport = new QLabel(mMainWidget);
mLblFrom = new QLabel(mMainWidget);
mLblReplyTo = new QLabel(mMainWidget);
mLblTo = new QLabel(mMainWidget);
mLblCc = new QLabel(mMainWidget);
mLblBcc = new QLabel(mMainWidget);
mLblSubject = new QLabel(mMainWidget);
mBtnIdentity = new QCheckBox(i18n("Sticky"),mMainWidget);
mBtnFcc = new QCheckBox(i18n("Sticky"),mMainWidget);
mBtnTransport = new QCheckBox(i18n("Sticky"),mMainWidget);
mBtnTo = new QPushButton("...",mMainWidget);
mBtnCc = new QPushButton("...",mMainWidget);
mBtnBcc = new QPushButton("...",mMainWidget);
mBtnFrom = new QPushButton("...",mMainWidget);
mBtnReplyTo = new QPushButton("...",mMainWidget);
//setWFlags( WType_TopLevel | WStyle_Dialog );
mDone = false;
mGrid = NULL;
mAtmListBox = NULL;
mAtmList.setAutoDelete(TRUE);
mAtmTempList.setAutoDelete(TRUE);
mAutoDeleteMsg = FALSE;
mFolder = NULL;
bAutoCharset = TRUE;
mEditor = new KMEdit(mMainWidget, this);
mEditor->setTextFormat(Qt::PlainText);
disableBreaking = false;
QString tip = i18n("Select email address(es)");
QToolTip::add( mBtnTo, tip );
QToolTip::add( mBtnCc, tip );
QToolTip::add( mBtnReplyTo, tip );
mSpellCheckInProgress=FALSE;
setCaption( i18n("Composer") );
setMinimumSize(200,200);
mBtnIdentity->setFocusPolicy(QWidget::NoFocus);
mBtnFcc->setFocusPolicy(QWidget::NoFocus);
mBtnTransport->setFocusPolicy(QWidget::NoFocus);
mBtnTo->setFocusPolicy(QWidget::NoFocus);
mBtnCc->setFocusPolicy(QWidget::NoFocus);
mBtnBcc->setFocusPolicy(QWidget::NoFocus);
mBtnFrom->setFocusPolicy(QWidget::NoFocus);
mBtnReplyTo->setFocusPolicy(QWidget::NoFocus);
mAtmListBox = new QListView(mMainWidget, "mAtmListBox");
mAtmListBox->setFocusPolicy(QWidget::NoFocus);
mAtmListBox->addColumn(i18n("Name"), 200);
mAtmListBox->addColumn(i18n("Size"), 80);
mAtmListBox->addColumn(i18n("Encoding"), 120);
mAtmListBox->addColumn(i18n("Type"), 150);
connect(mAtmListBox,
SIGNAL(rightButtonPressed(QListViewItem *, const QPoint &, int)),
SLOT(slotAttachPopupMenu(QListViewItem *, const QPoint &, int)));
mAttachMenu = 0;
readConfig();
setupStatusBar();
setupEditor();
setupActions();
applyMainWindowSettings(kapp->config(), "Composer");
toolbarAction->setChecked(!toolBar()->isHidden());
statusbarAction->setChecked(!statusBar()->isHidden());
connect(mEdtSubject,SIGNAL(textChanged(const QString&)),
SLOT(slotUpdWinTitle(const QString&)));
connect(mBtnTo,SIGNAL(clicked()),SLOT(slotAddrBookTo()));
connect(mBtnCc,SIGNAL(clicked()),SLOT(slotAddrBookCc()));
connect(mBtnBcc,SIGNAL(clicked()),SLOT(slotAddrBookBcc()));
connect(mBtnReplyTo,SIGNAL(clicked()),SLOT(slotAddrBookReplyTo()));
connect(mBtnFrom,SIGNAL(clicked()),SLOT(slotAddrBookFrom()));
connect(mIdentity,SIGNAL(activated(int)),SLOT(slotIdentityActivated(int)));
connect(mEdtTo,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
connect(mEdtCc,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
connect(mEdtBcc,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
connect(mEdtReplyTo,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
connect(mEdtFrom,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
mMainWidget->resize(480,510);
setCentralWidget(mMainWidget);
rethinkFields();
if (useExtEditor) {
mEditor->setExternalEditor(true);
mEditor->setExternalEditorPath(mExtEditor);
}
mMsg = NULL;
if (aMsg)
setMsg(aMsg);
mEdtTo->setFocus();
mDone = true;
}
//-----------------------------------------------------------------------------
KMComposeWin::~KMComposeWin()
{
writeConfig();
if (mFolder && mMsg)
{
mAutoDeleteMsg = FALSE;
mFolder->addMsg(mMsg);
emit messageQueuedOrDrafted();
}
if (mAutoDeleteMsg && mMsg) delete mMsg;
QMap<KIO::Job*, atmLoadData>::Iterator it = mapAtmLoadData.begin();
while ( it != mapAtmLoadData.end() )
{
KIO::Job *job = it.key();
mapAtmLoadData.remove( it );
job->kill();
it = mapAtmLoadData.begin();
}
}
//-----------------------------------------------------------------------------
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::addAttachment(KURL url,QString /*comment*/)
{
addAttach(url);
}
//-----------------------------------------------------------------------------
void KMComposeWin::addAttachment(const QString &name,
const QCString &cte,
const QByteArray &data,
const QCString &type,
const QCString &subType,
const QCString &paramAttr,
const QString &paramValue,
const QCString &contDisp)
{
if (!data.isEmpty()) {
KMMessagePart *msgPart = new KMMessagePart;
msgPart->setName(name);
msgPart->setCteStr(cte);
msgPart->setBodyEncodedBinary(data);
msgPart->setTypeStr(type);
msgPart->setSubtypeStr(subType);
msgPart->setParameter(paramAttr,paramValue);
msgPart->setContentDisposition(contDisp);
addAttach(msgPart);
}
}
//-----------------------------------------------------------------------------
void KMComposeWin::setBody(QString body)
{
mEditor->setText(body);
}
//-----------------------------------------------------------------------------
bool KMComposeWin::event(QEvent *e)
{
if (e->type() == QEvent::ApplicationPaletteChange)
{
readColorConfig();
return true;
}
return KMTopLevelWidget::event(e);
}
//-----------------------------------------------------------------------------
void KMComposeWin::readColorConfig(void)
{
KConfig *config = kapp->config();
KConfigGroupSaver saver(config, "Reader");
QColor c1=QColor(kapp->palette().active().text());
QColor c4=QColor(kapp->palette().active().base());
if (!config->readBoolEntry("defaultColors",TRUE)) {
foreColor = config->readColorEntry("ForegroundColor",&c1);
backColor = config->readColorEntry("BackgroundColor",&c4);
}
else {
foreColor = c1;
backColor = c4;
}
// Color setup
mPalette = kapp->palette();
QColorGroup cgrp = mPalette.active();
cgrp.setColor( QColorGroup::Base, backColor);
cgrp.setColor( QColorGroup::Text, foreColor);
mPalette.setDisabled(cgrp);
mPalette.setActive(cgrp);
mPalette.setInactive(cgrp);
mEdtTo->setPalette(mPalette);
mEdtFrom->setPalette(mPalette);
mEdtCc->setPalette(mPalette);
mEdtSubject->setPalette(mPalette);
mEdtReplyTo->setPalette(mPalette);
mEdtBcc->setPalette(mPalette);
mTransport->setPalette(mPalette);
mEditor->setPalette(mPalette);
mFcc->setPalette(mPalette);
}
//-----------------------------------------------------------------------------
void KMComposeWin::readConfig(void)
{
KConfig *config = kapp->config();
QCString str;
// int w, h,
int maxTransportItems;
KConfigGroupSaver saver(config, "Composer");
mDefCharset = KMMessage::defaultCharset();
mForceReplyCharset = config->readBoolEntry("force-reply-charset", false );
mAutoSign = config->readEntry("signature","auto") == "auto";
mShowHeaders = config->readNumEntry("headers", HDR_STANDARD);
mWordWrap = config->readNumEntry("word-wrap", 1);
mLineBreak = config->readNumEntry("break-at", 78);
mBtnIdentity->setChecked(config->readBoolEntry("sticky-identity", false));
if (mBtnIdentity->isChecked())
mId = config->readEntry("previous-identity", mId );
mBtnFcc->setChecked(config->readBoolEntry("sticky-fcc", false));
QString previousFcc = kernel->sentFolder()->idString();
if (mBtnFcc->isChecked())
previousFcc = config->readEntry("previous-fcc", previousFcc );
mBtnTransport->setChecked(config->readBoolEntry("sticky-transport", false));
mTransportHistory = config->readListEntry("transport-history");
QString currentTransport = config->readEntry("current-transport");
maxTransportItems = config->readNumEntry("max-transport-items",10);
if ((mLineBreak == 0) || (mLineBreak > 78))
mLineBreak = 78;
if (mLineBreak < 60)
mLineBreak = 60;
mAutoPgpSign = config->readBoolEntry("pgp-auto-sign", false);
mAutoPgpEncrypt = config->readBoolEntry("pgp-auto-encrypt", false);
mConfirmSend = config->readBoolEntry("confirm-before-send", false);
int mode = config->readNumEntry("Completion Mode",
KGlobalSettings::completionMode() );
mEdtFrom->setCompletionMode( (KGlobalSettings::Completion) mode );
mEdtReplyTo->setCompletionMode( (KGlobalSettings::Completion) mode );
mEdtTo->setCompletionMode( (KGlobalSettings::Completion) mode );
mEdtCc->setCompletionMode( (KGlobalSettings::Completion) mode );
mEdtBcc->setCompletionMode( (KGlobalSettings::Completion) mode );
readColorConfig();
{ // area for config group "General"
KConfigGroupSaver saver(config, "General");
mExtEditor = config->readEntry("external-editor", DEFAULT_EDITOR_STR);
useExtEditor = config->readBoolEntry("use-external-editor", FALSE);
int headerCount = config->readNumEntry("mime-header-count", 0);
mCustHeaders.clear();
mCustHeaders.setAutoDelete(true);
for (int i = 0; i < headerCount; i++) {
QString thisGroup;
_StringPair *thisItem = new _StringPair;
thisGroup.sprintf("Mime #%d", i);
KConfigGroupSaver saver(config, thisGroup);
thisItem->name = config->readEntry("name", "");
if ((thisItem->name).length() > 0) {
thisItem->value = config->readEntry("value", "");
mCustHeaders.append(thisItem);
} else {
delete thisItem;
}
}
}
{ // area fo config group "Fonts"
KConfigGroupSaver saver(config, "Fonts");
mUnicodeFont = config->readBoolEntry("unicodeFont",FALSE);
if (!config->readBoolEntry("defaultFonts",TRUE)) {
mBodyFont = QFont("helvetica");
mBodyFont = config->readFontEntry("body-font", &mBodyFont);
}
else
mBodyFont = KGlobalSettings::generalFont();
if (mEditor) mEditor->setFont(mBodyFont);
mEdtFrom->setFont(mBodyFont);
mEdtReplyTo->setFont(mBodyFont);
mEdtTo->setFont(mBodyFont);
mEdtCc->setFont(mBodyFont);
mEdtBcc->setFont(mBodyFont);
mEdtSubject->setFont(mBodyFont);
}
{ // area fo config group "Fonts"
KConfigGroupSaver saver(config, "Geometry");
QSize defaultSize(480,510);
QSize siz = config->readSizeEntry("composer", &defaultSize);
if (siz.width() < 200) siz.setWidth(200);
if (siz.height() < 200) siz.setHeight(200);
resize(siz);
}
mIdentity->clear();
mIdentity->insertStringList( KMIdentity::identities() );
for (int i=0; i < mIdentity->count(); ++i)
if (mIdentity->text(i) == mId) {
mIdentity->setCurrentItem(i);
break;
}
mTransport->insertStringList( KMTransportInfo::availableTransports() );
while (mTransportHistory.count() > (uint)maxTransportItems)
mTransportHistory.remove( mTransportHistory.last() );
mTransport->insertStringList( mTransportHistory );
if (mBtnTransport->isChecked() && !currentTransport.isEmpty())
mTransport->setEditText( currentTransport );
if ( !mBtnFcc->isChecked() )
{
kdDebug(5006) << "KMComposeWin::readConfig. " << mIdentity->currentText() << endl;
KMIdentity i( mIdentity->currentText() );
kdDebug(5006) << "KMComposeWin::readConfig: identity.fcc()='" << i.fcc() << "'" << endl;
i.readConfig();
if ( i.fcc().isEmpty() )
i.setFcc( kernel->sentFolder()->idString() );
previousFcc = i.fcc();
kdDebug() << "KMComposeWin::readConfig: previousFcc=" << previousFcc << endl;
}
mFcc->setFolder( previousFcc );
}
//-----------------------------------------------------------------------------
void KMComposeWin::writeConfig(void)
{
KConfig *config = kapp->config();
QString str;
{
KConfigGroupSaver saver(config, "Composer");
config->writeEntry("signature", mAutoSign?"auto":"manual");
config->writeEntry("headers", mShowHeaders);
config->writeEntry("sticky-transport", mBtnTransport->isChecked());
config->writeEntry("sticky-identity", mBtnIdentity->isChecked());
config->writeEntry("sticky-fcc", mBtnFcc->isChecked());
config->writeEntry("previous-identity", mIdentity->currentText() );
config->writeEntry("current-transport", mTransport->currentText());
config->writeEntry("previous-fcc", mFcc->getFolder()->idString() );
mTransportHistory.remove(mTransport->currentText());
if (KMTransportInfo::availableTransports().findIndex(mTransport
->currentText()) == -1)
mTransportHistory.prepend(mTransport->currentText());
config->writeEntry("transport-history", mTransportHistory );
}
{
KConfigGroupSaver saver(config, "Geometry");
config->writeEntry("composer", size());
saveMainWindowSettings(config, "Composer");
config->sync();
}
}
//-----------------------------------------------------------------------------
void KMComposeWin::deadLetter(void)
{
if (!mMsg) return;
// This method is called when KMail crashed, so we better use as
// basic functions as possible here.
applyChanges();
QCString msgStr = mMsg->asString();
QCString fname = getenv("HOME");
fname += "/dead.letter";
// Security: the file is created in the user's home directory, which
// might be readable by other users. So the file only gets read/write
// permissions for the user himself. Note that we create the file with
// correct permissions, we do not set them after creating the file!
// (dnaber, 2000-02-27):
int fd = open(fname, O_CREAT|O_APPEND|O_WRONLY, S_IWRITE|S_IREAD);
if (fd != -1)
{
QCString startStr = "From " + mMsg->fromEmail() + " " + mMsg->dateShortStr() + "\n";
::write(fd, startStr, startStr.length());
::write(fd, msgStr, msgStr.length());
::write(fd, "\n", 1);
::close(fd);
fprintf(stderr,"appending message to ~/dead.letter\n");
}
else perror("cannot open ~/dead.letter for saving the current message");
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotView(void)
{
if (!mDone)
return; // otherwise called from rethinkFields during the construction
// which is not the intended behavior
int id;
//This sucks awfully, but no, I cannot get an activated(int id) from
// actionContainer()
if (!sender()->isA("KToggleAction"))
return;
KToggleAction *act = (KToggleAction *) sender();
if (act == allFieldsAction)
id = 0;
else if (act == identityAction)
id = HDR_IDENTITY;
else if (act == transportAction)
id = HDR_TRANSPORT;
else if (act == fromAction)
id = HDR_FROM;
else if (act == replyToAction)
id = HDR_REPLY_TO;
else if (act == toAction)
id = HDR_TO;
else if (act == ccAction)
id = HDR_CC;
else if (act == bccAction)
id = HDR_BCC;
else if (act == subjectAction)
id = HDR_SUBJECT;
else if (act == fccAction)
id = HDR_FCC;
else
{
id = 0;
kdDebug(5006) << "Something is wrong (Oh, yeah?)" << endl;
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);
}
void KMComposeWin::rethinkFields(bool fromSlot)
{
//This sucks even more but again no ids. sorry (sven)
int mask, row, numRows;
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++;
numRows = mNumHeaders + 2;
if (mGrid) delete mGrid;
mGrid = new QGridLayout(mMainWidget, numRows, 3, 4, 4);
mGrid->setColStretch(0, 1);
mGrid->setColStretch(1, 100);
mGrid->setColStretch(2, 1);
mGrid->setRowStretch(mNumHeaders, 100);
mEdtList.clear();
row = 0;
kdDebug(5006) << "KMComposeWin::rethinkFields" << endl;
if (!fromSlot) allFieldsAction->setChecked(showHeaders==HDR_ALL);
if (!fromSlot) identityAction->setChecked(abs(mShowHeaders)&HDR_IDENTITY);
rethinkHeaderLine(showHeaders,HDR_IDENTITY, row, i18n("&Identity:"),
mLblIdentity, mIdentity, mBtnIdentity);
if (!fromSlot) fccAction->setChecked(abs(mShowHeaders)&HDR_FCC);
rethinkHeaderLine(showHeaders,HDR_FCC, row, i18n("Sen&t-mail Folder:"),
mLblFcc, mFcc, mBtnFcc);
if (!fromSlot) transportAction->setChecked(abs(mShowHeaders)&HDR_TRANSPORT);
rethinkHeaderLine(showHeaders,HDR_TRANSPORT, row, i18n("Mai&l Transport:"),
mLblTransport, mTransport, mBtnTransport);
if (!fromSlot) fromAction->setChecked(abs(mShowHeaders)&HDR_FROM);
rethinkHeaderLine(showHeaders,HDR_FROM, row, i18n("Fro&m:"),
mLblFrom, mEdtFrom, mBtnFrom);
if (!fromSlot) replyToAction->setChecked(abs(mShowHeaders)&HDR_REPLY_TO);
rethinkHeaderLine(showHeaders,HDR_REPLY_TO,row,i18n("&Reply to:"),
mLblReplyTo, mEdtReplyTo, mBtnReplyTo);
if (!fromSlot) toAction->setChecked(abs(mShowHeaders)&HDR_TO);
rethinkHeaderLine(showHeaders,HDR_TO, row, i18n("&To:"),
mLblTo, mEdtTo, mBtnTo);
if (!fromSlot) ccAction->setChecked(abs(mShowHeaders)&HDR_CC);
rethinkHeaderLine(showHeaders,HDR_CC, row, i18n("&Cc:"),
mLblCc, mEdtCc, mBtnCc);
if (!fromSlot) bccAction->setChecked(abs(mShowHeaders)&HDR_BCC);
rethinkHeaderLine(showHeaders,HDR_BCC, row, i18n("&Bcc:"),
mLblBcc, mEdtBcc, mBtnBcc);
if (!fromSlot) subjectAction->setChecked(abs(mShowHeaders)&HDR_SUBJECT);
rethinkHeaderLine(showHeaders,HDR_SUBJECT, row, i18n("S&ubject:"),
mLblSubject, mEdtSubject);
assert(row<=mNumHeaders);
mGrid->addMultiCellWidget(mEditor, row, mNumHeaders, 0, 2);
mGrid->addMultiCellWidget(mAtmListBox, mNumHeaders+1, mNumHeaders+1, 0, 2);
if (mAtmList.count() > 0) mAtmListBox->show();
else mAtmListBox->hide();
resize(this->size());
repaint();
mGrid->activate();
identityAction->setEnabled(!allFieldsAction->isChecked());
transportAction->setEnabled(!allFieldsAction->isChecked());
fromAction->setEnabled(!allFieldsAction->isChecked());
replyToAction->setEnabled(!allFieldsAction->isChecked());
toAction->setEnabled(!allFieldsAction->isChecked());
ccAction->setEnabled(!allFieldsAction->isChecked());
bccAction->setEnabled(!allFieldsAction->isChecked());
fccAction->setEnabled(!allFieldsAction->isChecked());
subjectAction->setEnabled(!allFieldsAction->isChecked());
}
//-----------------------------------------------------------------------------
void KMComposeWin::rethinkHeaderLine(int aValue, int aMask, int& aRow,
const QString &aLabelStr, QLabel* aLbl,
QLineEdit* aEdt, QPushButton* aBtn)
{
if (aValue & aMask)
{
aLbl->setText(aLabelStr);
aLbl->adjustSize();
aLbl->resize((int)aLbl->sizeHint().width(),aLbl->sizeHint().height() + 6);
aLbl->setMinimumSize(aLbl->size());
aLbl->show();
aLbl->setBuddy(aEdt);
mGrid->addWidget(aLbl, aRow, 0);
aEdt->setBackgroundColor( backColor );
aEdt->show();
aEdt->setMinimumSize(100, aLbl->height()+2);
aEdt->setMaximumSize(1000, aLbl->height()+2);
mEdtList.append(aEdt);
mGrid->addWidget(aEdt, aRow, 1);
if (aBtn)
{
mGrid->addWidget(aBtn, aRow, 2);
aBtn->setFixedSize(aBtn->sizeHint().width(), aLbl->height());
aBtn->show();
}
aRow++;
}
else
{
aLbl->hide();
aEdt->hide();
if (aBtn) aBtn->hide();
}
}
//-----------------------------------------------------------------------------
void KMComposeWin::rethinkHeaderLine(int aValue, int aMask, int& aRow,
const QString &aLabelStr, QLabel* aLbl,
QComboBox* aCbx, QCheckBox* aChk)
{
if (aValue & aMask)
{
aLbl->setText(aLabelStr);
aLbl->adjustSize();
aLbl->resize((int)aLbl->sizeHint().width(),aLbl->sizeHint().height() + 6);
aLbl->setMinimumSize(aLbl->size());
aLbl->show();
aLbl->setBuddy(aCbx);
mGrid->addWidget(aLbl, aRow, 0);
// aCbx->setBackgroundColor( backColor );
aCbx->show();
aCbx->setMinimumSize(100, aLbl->height()+2);
aCbx->setMaximumSize(1000, aLbl->height()+2);
mGrid->addWidget(aCbx, aRow, 1);
mGrid->addWidget(aChk, aRow, 2);
aChk->setFixedSize(aChk->sizeHint().width(), aLbl->height());
aChk->show();
aRow++;
}
else
{
aLbl->hide();
aCbx->hide();
aChk->hide();
}
}
//-----------------------------------------------------------------------------
void KMComposeWin::setupActions(void)
{
if (kernel->msgSender()->sendImmediate()) //default == send now?
{
//default = send now, alternative = queue
(void) new KAction (i18n("&Send"), "mail_send", CTRL+Key_Return,
this, SLOT(slotSendNow()), actionCollection(),
"send_default");
(void) new KAction (i18n("&Queue"), "queue", 0,
this, SLOT(slotSendLater()),
actionCollection(), "send_alternative");
}
else //no, default = send later
{
//default = queue, alternative = send now
(void) new KAction (i18n("&Queue"), "queue",
CTRL+Key_Return,
this, SLOT(slotSendLater()), actionCollection(),
"send_default");
(void) new KAction (i18n("S&end now"), "mail_send", 0,
this, SLOT(slotSendNow()),
actionCollection(), "send_alternative");
}
(void) new KAction (i18n("Save in &drafts folder"), 0,
this, SLOT(slotSaveDraft()),
actionCollection(), "save_in_drafts");
(void) new KAction (i18n("&Insert File..."), "fileopen", 0,
this, SLOT(slotInsertFile()),
actionCollection(), "insert_file");
(void) new KAction (i18n("&Addressbook..."), "contents",0,
this, SLOT(slotAddrBook()),
actionCollection(), "addresbook");
(void) new KAction (i18n("&New Composer..."), "filenew",
KStdAccel::key(KStdAccel::New),
this, SLOT(slotNewComposer()),
actionCollection(), "new_composer");
//KStdAction::save(this, SLOT(), actionCollection(), "save_message");
KStdAction::print (this, SLOT(slotPrint()), actionCollection());
KStdAction::close (this, SLOT(slotClose()), actionCollection());
KStdAction::undo (this, SLOT(slotUndo()), actionCollection());
KStdAction::redo (this, SLOT(slotRedo()), actionCollection());
KStdAction::cut (this, SLOT(slotCut()), actionCollection());
KStdAction::copy (this, SLOT(slotCopy()), actionCollection());
KStdAction::paste (this, SLOT(slotPaste()), actionCollection());
KStdAction::selectAll (this, SLOT(slotMarkAll()), actionCollection());
KStdAction::find (this, SLOT(slotFind()), actionCollection());
KStdAction::replace (this, SLOT(slotReplace()), actionCollection());
KStdAction::spelling (this, SLOT(slotSpellcheck()), actionCollection(), "spellcheck");
(void) new KAction (i18n("Cl&ean Spaces"), 0, this, SLOT(slotCleanSpace()),
actionCollection(), "clean_spaces");
(void) new KToggleAction( i18n("Fixed font widths"), DEFAULT_FIXEDFONTS_KEY, this,
SLOT(slotToggleFixedFont()), actionCollection(), "toggle_fixedfont" );
//these are checkable!!!
urgentAction = new KToggleAction (i18n("&Urgent"), 0,
actionCollection(),
"urgent");
confirmDeliveryAction = new KToggleAction (i18n("&Confirm delivery"), 0,
actionCollection(),
"confirm_delivery");
confirmReadAction = new KToggleAction (i18n("Confirm &read"), 0,
actionCollection(), "confirm_read");
//this is obsolete now, we use a pulldown menu
#if defined CHARSETS
(void) new KAction (i18n("&Charsets..."), 0, this, SLOT(slotConfigureCharsets()),
actionCollection(), "charsets");
#endif
//----- Message-Encoding Submenu
encodingAction = new KSelectAction( i18n( "Set &Encoding" ), "charset",
0, this, SLOT(slotSetCharset() ),
actionCollection(), "charsets" );
wordWrapAction = new KToggleAction (i18n("&Wordwrap"), 0,
actionCollection(), "wordwrap");
wordWrapAction->setChecked(mWordWrap);
connect(wordWrapAction, SIGNAL(toggled(bool)), SLOT(slotWordWrapToggled(bool)));
QStringList encodings = KMMsgBase::supportedEncodings(TRUE);
encodings.prepend( i18n("Auto-detect"));
encodingAction->setItems( encodings );
encodingAction->setCurrentItem( -1 );
//these are checkable!!!
allFieldsAction = new KToggleAction (i18n("&All Fields"), 0, this,
SLOT(slotView()),
actionCollection(), "show_all_fields");
identityAction = new KToggleAction (i18n("&Identity"), 0, this,
SLOT(slotView()),
actionCollection(), "show_identity");
fccAction = new KToggleAction (i18n("Sen&t-mail Folder"), 0, this,
SLOT(slotView()),
actionCollection(), "show_fcc");
transportAction = new KToggleAction (i18n("&Mail Transport"), 0, this,
SLOT(slotView()),
actionCollection(), "show_transport");
fromAction = new KToggleAction (i18n("&From"), 0, this,
SLOT(slotView()),
actionCollection(), "show_from");
replyToAction = new KToggleAction (i18n("&Reply to"), 0, this,
SLOT(slotView()),
actionCollection(), "show_reply_to");
toAction = new KToggleAction (i18n("&To"), 0, this,
SLOT(slotView()),
actionCollection(), "show_to");
ccAction = new KToggleAction (i18n("&Cc"), 0, this,
SLOT(slotView()),
actionCollection(), "show_cc");
bccAction = new KToggleAction (i18n("&Bcc"), 0, this,
SLOT(slotView()),
actionCollection(), "show_bcc");
subjectAction = new KToggleAction (i18n("&Subject"), 0, this,
SLOT(slotView()),
actionCollection(), "show_subject");
//end of checkable
(void) new KAction (i18n("Append S&ignature"), 0, this,
SLOT(slotAppendSignature()),
actionCollection(), "append_signature");
attachPK = new KAction (i18n("Attach &Public Key..."), 0, this,
SLOT(slotInsertPublicKey()),
actionCollection(), "attach_public_key");
attachMPK = new KAction (i18n("Attach My &Public Key"), 0, this,
SLOT(slotInsertMyPublicKey()),
actionCollection(), "attach_my_public_key");
(void) new KAction (i18n("&Attach..."), "attach",
0, this, SLOT(slotAttachFile()),
actionCollection(), "attach");
(void) new KAction (i18n("&Remove"), 0, this,
SLOT(slotAttachRemove()),
actionCollection(), "remove");
(void) new KAction (i18n("&Save..."), "filesave",0,
this, SLOT(slotAttachSave()),
actionCollection(), "attach_save");
(void) new KAction (i18n("Pr&operties..."), 0, this,
SLOT(slotAttachProperties()),
actionCollection(), "attach_properties");
toolbarAction = KStdAction::showToolbar(this, SLOT(slotToggleToolBar()),
actionCollection());
statusbarAction = KStdAction::showStatusbar(this, SLOT(slotToggleStatusBar()),
actionCollection());
KStdAction::keyBindings(this, SLOT(slotEditKeys()), actionCollection());
KStdAction::configureToolbars(this, SLOT(slotEditToolbars()), actionCollection());
(void) new KAction (i18n("&Spellchecker..."), 0, this, SLOT(slotSpellcheckConfig()),
actionCollection(), "setup_spellchecker");
encryptAction = new KToggleAction (i18n("Encrypt message"),
"unlock", 0,
actionCollection(), "encrypt_message");
signAction = new KToggleAction (i18n("Sign message"),
"signature", 0,
actionCollection(), "sign_message");
// get PGP user id for the chosen identity
QString pgpUserId;
QString identStr = i18n( "Default" );
if( !mId.isEmpty() && KMIdentity::identities().contains( mId ) ) {
identStr = mId;
}
KMIdentity ident(identStr);
ident.readConfig();
pgpUserId = ident.pgpIdentity();
if(!Kpgp::Module::getKpgp()->usePGP())
{
attachPK->setEnabled(false);
attachMPK->setEnabled(false);
encryptAction->setEnabled(false);
encryptAction->setChecked(false);
signAction->setEnabled(false);
signAction->setChecked(false);
}
else if (pgpUserId.isEmpty()) {
attachMPK->setEnabled(false);
signAction->setEnabled(false);
signAction->setChecked(false);
}
else
signAction->setChecked(mAutoPgpSign);
createGUI("kmcomposerui.rc");
connect(encryptAction, SIGNAL(toggled(bool)), SLOT(slotEncryptToggled(bool)));
}
//-----------------------------------------------------------------------------
void KMComposeWin::setupStatusBar(void)
{
statusBar()->addWidget( new QLabel( statusBar(), "" ), 1 );
statusBar()->insertItem(QString(i18n(" Column"))+": ",2,0,true);
statusBar()->insertItem(QString(i18n(" Line"))+": ",1,0,true);
}
//-----------------------------------------------------------------------------
void KMComposeWin::updateCursorPosition()
{
int col,line;
QString temp;
line = mEditor->currentLine();
col = mEditor->currentColumn();
temp = QString(" %1: %2 ").arg(i18n("Line")).arg(line+1);
statusBar()->changeItem(temp,1);
temp = QString(" %1: %2 ").arg(i18n("Column")).arg(col+1);
statusBar()->changeItem(temp,2);
}
//-----------------------------------------------------------------------------
void KMComposeWin::setupEditor(void)
{
QPopupMenu* menu;
mEditor->setModified(FALSE);
QFontMetrics fm(mBodyFont);
mEditor->setTabStopWidth(fm.width(QChar(' ')) * 8);
//mEditor->setFocusPolicy(QWidget::ClickFocus);
if (mWordWrap)
{
mEditor->setWordWrap( QMultiLineEdit::FixedColumnWidth );
mEditor->setWrapColumnOrWidth(mLineBreak);
}
else
{
mEditor->setWordWrap( QMultiLineEdit::NoWrap );
}
// Font setup
mEditor->setFont(mBodyFont);
menu = new QPopupMenu(this);
//#ifdef BROKEN
menu->insertItem(i18n("Undo"),mEditor,
SLOT(undo()), KStdAccel::key(KStdAccel::Undo));
menu->insertItem(i18n("Redo"),mEditor,
SLOT(redo()), KStdAccel::key(KStdAccel::Redo));
menu->insertSeparator();
//#endif //BROKEN
menu->insertItem(i18n("Cut"), this, SLOT(slotCut()));
menu->insertItem(i18n("Copy"), this, SLOT(slotCopy()));
menu->insertItem(i18n("Paste"), this, SLOT(slotPaste()));
menu->insertItem(i18n("Mark all"),this, SLOT(slotMarkAll()));
menu->insertSeparator();
menu->insertItem(i18n("Find..."), this, SLOT(slotFind()));
menu->insertItem(i18n("Replace..."), this, SLOT(slotReplace()));
menu->insertSeparator();
menu->insertItem(i18n("Fixed font widths"), this, SLOT(slotToggleFixedFont()));
mEditor->installRBPopup(menu);
updateCursorPosition();
connect(mEditor,SIGNAL(CursorPositionChanged()),SLOT(updateCursorPosition()));
}
//-----------------------------------------------------------------------------
void KMComposeWin::verifyWordWrapLengthIsAdequate(const QString &body)
{
int maxLineLength = 0;
int curPos;
int oldPos = 0;
if (mEditor->QMultiLineEdit::wordWrap() == QMultiLineEdit::FixedColumnWidth) {
for (curPos = 0; curPos < (int)body.length(); ++curPos)
if (body[curPos] == '\n') {
if ((curPos - oldPos) > maxLineLength)
maxLineLength = curPos - oldPos;
oldPos = curPos;
}
if ((curPos - oldPos) > maxLineLength)
maxLineLength = curPos - oldPos;
if (mEditor->wrapColumnOrWidth() < maxLineLength) // column
mEditor->setWrapColumnOrWidth(maxLineLength);
}
}
//-----------------------------------------------------------------------------
void KMComposeWin::setMsg(KMMessage* newMsg, bool mayAutoSign, bool allowDecryption)
{
KMMessagePart bodyPart, *msgPart;
int i, num;
//assert(newMsg!=NULL);
if(!newMsg)
{
kdDebug(5006) << "KMComposeWin::setMsg() : newMsg == NULL!\n" << endl;
return;
}
mMsg = newMsg;
mEdtTo->setText(mMsg->to());
mEdtFrom->setText(mMsg->from());
mEdtCc->setText(mMsg->cc());
mEdtSubject->setText(mMsg->subject());
mEdtReplyTo->setText(mMsg->replyTo());
mEdtBcc->setText(mMsg->bcc());
if (!mBtnIdentity->isChecked() && !newMsg->headerField("X-KMail-Identity").isEmpty())
mId = newMsg->headerField("X-KMail-Identity");
for (int i=0; i < mIdentity->count(); ++i)
if (mIdentity->text(i) == mId) {
mIdentity->setCurrentItem(i);
if (mBtnIdentity->isChecked()) slotIdentityActivated(i);
break;
}
// get PGP user id for the currently selected identity
KMIdentity ident(mIdentity->currentText());
ident.readConfig();
QString pgpUserId = ident.pgpIdentity();
if(Kpgp::Module::getKpgp()->usePGP()) {
if (pgpUserId.isEmpty()) {
attachMPK->setEnabled(false);
signAction->setEnabled(false);
signAction->setChecked(false);
}
else {
attachMPK->setEnabled(true);
signAction->setEnabled(true);
signAction->setChecked(mAutoPgpSign);
}
}
QString transport = newMsg->headerField("X-KMail-Transport");
if (!mBtnTransport->isChecked() && !transport.isEmpty())
mTransport->setEditText( transport );
num = mMsg->numBodyParts();
if (num > 0)
{
QCString bodyDecoded;
mMsg->bodyPart(0, &bodyPart);
int firstAttachment = (bodyPart.typeStr().lower() == "text") ? 1 : 0;
if (firstAttachment)
{
mCharset = bodyPart.charset();
if ((mCharset=="") || (mCharset == "default"))
mCharset = mDefCharset;
bodyDecoded = bodyPart.bodyDecoded();
verifyWordWrapLengthIsAdequate(bodyDecoded);
QTextCodec *codec = KMMsgBase::codecForName(mCharset);
if (codec)
mEditor->setText(codec->toUnicode(bodyDecoded));
else
mEditor->setText(QString::fromLocal8Bit(bodyDecoded));
mEditor->insertLine("\n", -1);
} else mEditor->setText("");
for(i=firstAttachment; i<num; i++)
{
msgPart = new KMMessagePart;
mMsg->bodyPart(i, msgPart);
addAttach(msgPart);
}
} else{
mCharset=mMsg->charset();
if ((mCharset=="") || (mCharset == "default"))
mCharset = mDefCharset;
QCString bodyDecoded = mMsg->bodyDecoded();
Kpgp::Module* pgp = Kpgp::Module::getKpgp();
assert(pgp != NULL);
if (allowDecryption && pgp->setMessage(bodyDecoded))
{
QCString str = pgp->frontmatter();
if ((pgp->isEncrypted() && pgp->decrypt()) || pgp->isSigned())
{
str += pgp->message();
str += pgp->backmatter();
bodyDecoded = str;
}
}
QTextCodec *codec = KMMsgBase::codecForName(mCharset);
if (codec) {
mEditor->setText(codec->toUnicode(bodyDecoded));
} else
mEditor->setText(QString::fromLocal8Bit(bodyDecoded));
}
setCharset(mCharset);
if( mAutoSign && 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.
//
QTimer::singleShot( 0, this, SLOT(slotAppendSignature()) );
}
mEditor->setModified(FALSE);
//put font charset as default charset: jstolarz@kde.org
// QString defCharset = mMsg->charset();
// if ((mAtmList.count() <= 0) && defCharset.isEmpty() &&
// KGlobal::charsets()->isAvailable(mBodyFont.charSet()))
// mMsg->setCharset(KGlobal::charsets()->name(mBodyFont.charSet()));
}
//-----------------------------------------------------------------------------
bool KMComposeWin::applyChanges(void)
{
QString str, atmntStr;
QString temp, replyAddr;
//assert(mMsg!=NULL);
if(!mMsg)
{
kdDebug(5006) << "KMComposeWin::applyChanges() : mMsg == NULL!\n" << endl;
return FALSE;
}
if (bAutoCharset) {
QCString charset = KMMsgBase::autoDetectCharset(mCharset, KMMessage::preferredCharsets(), mEditor->text());
if (charset.isEmpty())
{
KMessageBox::sorry(this,
i18n("No suitable encoding could be found for your message.\n"
"Please set an encoding using the 'Options' menu."));
return false;
}
mCharset = charset;
mMsg->setCharset(mCharset);
}
mMsg->setTo(to());
mMsg->setFrom(from());
mMsg->setCc(cc());
mMsg->setSubject(subject());
mMsg->setReplyTo(replyTo());
mMsg->setBcc(bcc());
KMIdentity id( mIdentity->currentText() );
id.readConfig();
kdDebug() << "KMComposeWin::applyChanges: " << mFcc->currentText() << "==" << id.fcc() << "?" << endl;
KMFolder *f = mFcc->getFolder();
if ( f->idString() == id.fcc() )
mMsg->removeHeaderField("X-KMail-Fcc");
else
mMsg->setFcc( f->idString() );
if (mIdentity->currentText() == i18n("Default"))
mMsg->removeHeaderField("X-KMail-Identity");
else mMsg->setHeaderField("X-KMail-Identity", mIdentity->currentText());
if (!replyTo().isEmpty()) replyAddr = replyTo();
else replyAddr = from();
if (confirmDeliveryAction->isChecked())
mMsg->setHeaderField("Return-Receipt-To", replyAddr);
if (confirmReadAction->isChecked())
mMsg->setHeaderField("X-Chameleon-Return-To", replyAddr);
if (urgentAction->isChecked())
{
mMsg->setHeaderField("X-PRIORITY", "2 (High)");
mMsg->setHeaderField("Priority", "urgent");
}
_StringPair *pCH;
for (pCH = mCustHeaders.first();
pCH != NULL;
pCH = mCustHeaders.next()) {
mMsg->setHeaderField(KMMsgBase::toUsAscii(pCH->name), pCH->value);
}
bool isQP = kernel->msgSender()->sendQuotedPrintable();
if(mAtmList.count() <= 0) {
// If there are no attachments in the list waiting it is a simple
// text message.
mMsg->deleteBodyParts();
mMsg->setAutomaticFields(TRUE);
mMsg->setHeaderField("Content-Type","text/plain");
mMsg->setCteStr(isQP ? "quoted-printable": "8bit");
mMsg->setCharset(mCharset);
QCString body = pgpProcessedMsg();
if (body.isNull()) return FALSE;
if (body.isEmpty()) body = "\n"; // don't crash
if (isQP)
mMsg->setBodyEncoded(body);
else
mMsg->setBody(body);
}
else
{
KMMessagePart bodyPart, *msgPart;
mMsg->deleteBodyParts();
mMsg->removeHeaderField("Content-Type");
mMsg->removeHeaderField("Content-Transfer-Encoding");
mMsg->setAutomaticFields(TRUE);
// create informative header for those that have no mime-capable
// email client
mMsg->setBody("This message is in MIME format.\n\n");
// create bodyPart for editor text.
bodyPart.setTypeStr("text");
bodyPart.setSubtypeStr("plain");
bodyPart.setCteStr(isQP ? "quoted-printable": "8bit");
bodyPart.setCharset(mCharset);
QCString body = pgpProcessedMsg();
if (body.isNull()) return FALSE;
if (body.isEmpty()) body = "\n"; // don't crash
bodyPart.setBodyEncoded(body);
mMsg->addBodyPart(&bodyPart);
// Since there is at least one more attachment create another bodypart
for (msgPart=mAtmList.first(); msgPart; msgPart=mAtmList.next())
mMsg->addBodyPart(msgPart);
}
if (!mAutoDeleteMsg) mEditor->setModified(FALSE);
mEdtFrom->setEdited(FALSE);
mEdtReplyTo->setEdited(FALSE);
mEdtTo->setEdited(FALSE);
mEdtCc->setEdited(FALSE);
mEdtBcc->setEdited(FALSE);
mEdtSubject->setEdited(FALSE);
if (mTransport->lineEdit())
mTransport->lineEdit()->setEdited(FALSE);
// remove fields that contain no data (e.g. an empty Cc: or Bcc:)
mMsg->cleanupHeader();
return TRUE;
}
//-----------------------------------------------------------------------------
bool KMComposeWin::queryClose ()
{
int rc;
if(mEditor->isModified() || mEdtFrom->edited() || mEdtReplyTo->edited() ||
mEdtTo->edited() || mEdtCc->edited() || mEdtBcc->edited() ||
mEdtSubject->edited() ||
(mTransport->lineEdit() && mTransport->lineEdit()->edited()))
{
rc = KMessageBox::warningYesNoCancel(this,
i18n("Do you want to discard the message or save it for later?"),
i18n("Discard or save message"),
i18n("Save as draft"),
// i18n fix by mok: avoid clash with common_texts (breaks translation)
i18n("discard message", "Discard") );
if (rc == KMessageBox::Cancel)
return false;
else if (rc == KMessageBox::Yes)
slotSaveDraft();
}
return true;
}
bool KMComposeWin::queryExit ()
{
return true;
}
//-----------------------------------------------------------------------------
QCString KMComposeWin::pgpProcessedMsg(void)
{
Kpgp::Module *pgp = Kpgp::Module::getKpgp();
bool doSign = signAction->isChecked();
bool doEncrypt = encryptAction->isChecked();
QString text;
QCString cText;
if (disableBreaking)
text = mEditor->text();
else
text = mEditor->brokenText();
text.truncate(text.length()); // to ensure text.size()==text.length()+1
{
// Provide a local scope for newText.
QString newText;
QTextCodec *codec = KMMsgBase::codecForName(mCharset);
if (mCharset == "us-ascii") {
cText = KMMsgBase::toUsAscii(text);
newText = QString::fromLatin1(cText);
} else if (codec == NULL) {
kdDebug(5006) << "Something is wrong and I can not get a codec." << endl;
cText = text.local8Bit();
newText = QString::fromLocal8Bit(cText);
} else {
cText = codec->fromUnicode(text);
newText = codec->toUnicode(cText);
}
if (!text.isEmpty() && (newText != text))
{
QString oldText = mEditor->text();
mEditor->setText(newText);
kernel->kbp()->idle();
bool anyway = (KMessageBox::warningYesNo(0L,
i18n("Not all characters fit into the chosen"
" encoding.\nSend the message anyway?"),
i18n("Some characters will be lost"),
i18n("Yes"), i18n("No, let me change the encoding") ) == KMessageBox::Yes);
if (!anyway)
{
mEditor->setText(oldText);
return QCString();
}
}
}
// determine the list of recipients
QString _to = to();
if(!cc().isEmpty()) _to += "," + cc();
if(!bcc().isEmpty()) _to += "," + bcc();
QStringList recipients = KMMessage::splitEmailAddrList(_to);
if( mAutoPgpEncrypt && !doEncrypt )
{ // check if the message should be encrypted
int status = pgp->encryptionPossible( recipients );
if( status == 1 )
doEncrypt = true;
else if( status == 2 )
{ // the user wants to be asked or has to be asked
kernel->kbp()->idle();
int ret =
KMessageBox::questionYesNo( this,
"Should this message be encrypted?" );
kernel->kbp()->busy();
doEncrypt = ( ret == KMessageBox::Yes );
}
else if( status == -1 )
{ // warn the user that there are conflicting encryption preferences
int ret =
KMessageBox::warningYesNoCancel( this,
"There are conflicting encryption "
"preferences!\n\n"
"Should this message be encrypted?" );
if( ret == KMessageBox::Cancel )
return QCString();
doEncrypt = ( ret == KMessageBox::Yes );
}
}
if (!doSign && !doEncrypt) return cText;
pgp->setMessage(cText, mCharset);
// get PGP user id for the chosen identity
QCString pgpUserId;
QString identStr = i18n( "Default" );
if( !mId.isEmpty() && KMIdentity::identities().contains( mId ) ) {
identStr = mId;
}
KMIdentity ident(identStr);
ident.readConfig();
pgpUserId = ident.pgpIdentity();
if (!doEncrypt)
{ // clearsign the message
if(pgp->sign(pgpUserId)) return pgp->message();
}
else
{ // encrypt the message
if(pgp->encryptFor(recipients, pgpUserId, doSign))
return pgp->message();
}
// in case of an error we end up here
//qWarning(i18n("Error during PGP:") + QString("\n") +
// pgp->lastErrorMsg());
return QCString();
}
//-----------------------------------------------------------------------------
void KMComposeWin::addAttach(const KURL aUrl)
{
KIO::Job *job = KIO::get(aUrl);
atmLoadData ld;
ld.url = aUrl;
ld.data = QByteArray();
ld.insert = false;
mapAtmLoadData.insert(job, ld);
connect(job, SIGNAL(result(KIO::Job *)),
this, SLOT(slotAttachFileResult(KIO::Job *)));
connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)),
this, SLOT(slotAttachFileData(KIO::Job *, const QByteArray &)));
}
//-----------------------------------------------------------------------------
void KMComposeWin::addAttach(const KMMessagePart* msgPart)
{
mAtmList.append(msgPart);
// show the attachment listbox if it does not up to now
if (mAtmList.count()==1)
{
mGrid->setRowStretch(mNumHeaders+1, 50);
mAtmListBox->setMinimumSize(100, 80);
mAtmListBox->setMaximumHeight( 100 );
mAtmListBox->show();
resize(size());
}
// add a line in the attachment listbox
QListViewItem *lvi = new QListViewItem(mAtmListBox);
msgPartToItem(msgPart, lvi);
mAtmItemList.append(lvi);
}
//-----------------------------------------------------------------------------
void KMComposeWin::msgPartToItem(const KMMessagePart* msgPart,
QListViewItem *lvi)
{
unsigned int len;
QString lenStr;
assert(msgPart != NULL);
len = msgPart->size();
if (len > 9999) lenStr.sprintf("%uK", (len>>10));
else lenStr.sprintf("%u", len);
if (!msgPart->fileName().isEmpty())
lvi->setText(0, msgPart->fileName());
else
lvi->setText(0, msgPart->name());
lvi->setText(1, lenStr);
lvi->setText(2, msgPart->contentTransferEncodingStr());
lvi->setText(3, msgPart->typeStr() + "/" + msgPart->subtypeStr());
}
//-----------------------------------------------------------------------------
void KMComposeWin::removeAttach(const QString &aUrl)
{
int idx;
KMMessagePart* msgPart;
for(idx=0,msgPart=mAtmList.first(); msgPart;
msgPart=mAtmList.next(),idx++) {
if (msgPart->name() == aUrl) {
removeAttach(idx);
return;
}
}
}
//-----------------------------------------------------------------------------
void KMComposeWin::removeAttach(int idx)
{
mAtmList.remove(idx);
delete mAtmItemList.take(idx);
if (mAtmList.count()<=0)
{
mAtmListBox->hide();
mGrid->setRowStretch(mNumHeaders+1, 0);
mAtmListBox->setMinimumSize(0, 0);
resize(size());
}
}
//-----------------------------------------------------------------------------
void KMComposeWin::addrBookSelInto(KMLineEdit* aLineEdit)
{
KMAddrBookSelDlg dlg(this, kernel->addrBook());
QString txt;
//assert(aLineEdit!=NULL);
if(!aLineEdit)
{
kdDebug(5006) << "KMComposeWin::addrBookSelInto() : aLineEdit == NULL\n" << endl;
return;
}
if (dlg.exec()==QDialog::Rejected) return;
txt = aLineEdit->text().stripWhiteSpace();
if (!txt.isEmpty())
{
if (txt.right(1).at(0)!=',') txt += ", ";
else txt += ' ';
}
aLineEdit->setText(txt + dlg.address());
}
//-----------------------------------------------------------------------------
void KMComposeWin::setCharset(const QCString& aCharset, bool forceDefault)
{
if ((forceDefault && mForceReplyCharset) || aCharset.isEmpty())
mCharset = mDefCharset;
else
mCharset = aCharset.lower();
if ((mCharset=="") || (mCharset == "default"))
mCharset = mDefCharset;
if (bAutoCharset)
{
encodingAction->setCurrentItem( 0 );
return;
}
QStringList encodings = encodingAction->items();
int i = 0;
bool charsetFound = FALSE;
for ( QStringList::Iterator it = encodings.begin(); it != encodings.end();
++it, i++ )
{
if (i > 0 && ((mCharset == "us-ascii" && i == 1) ||
(i != 1 && KGlobal::charsets()->codecForName(
KGlobal::charsets()->encodingForName(*it))
== KGlobal::charsets()->codecForName(mCharset))))
{
encodingAction->setCurrentItem( i );
slotSetCharset();
charsetFound = TRUE;
break;
}
}
if (!aCharset.isEmpty() && !charsetFound) setCharset("", TRUE);
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotAddrBook()
{
KMAddrBookExternal::launch(this);
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotAddrBookFrom()
{
addrBookSelInto(mEdtFrom);
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotAddrBookReplyTo()
{
addrBookSelInto(mEdtReplyTo);
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotAddrBookTo()
{
addrBookSelInto(mEdtTo);
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotAddrBookCc()
{
addrBookSelInto(mEdtCc);
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotAddrBookBcc()
{
addrBookSelInto(mEdtBcc);
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotAttachFile()
{
// Create File Dialog and return selected file(s)
// We will not care about any permissions, existence or whatsoever in
// this function.
KURL::List files = KFileDialog::getOpenURLs(QString::null, QString::null,
this, i18n("Attach File"));
for (KURL::List::Iterator it = files.begin(); it != files.end(); ++it)
addAttach(*it);
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotAttachFileData(KIO::Job *job, const QByteArray &data)
{
QMap<KIO::Job*, atmLoadData>::Iterator it = mapAtmLoadData.find(job);
assert(it != mapAtmLoadData.end());
QBuffer buff((*it).data);
buff.open(IO_WriteOnly | IO_Append);
buff.writeBlock(data.data(), data.size());
buff.close();
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotAttachFileResult(KIO::Job *job)
{
QMap<KIO::Job*, atmLoadData>::Iterator it = mapAtmLoadData.find(job);
assert(it != mapAtmLoadData.end());
if (job->error())
{
mapAtmLoadData.remove(it);
job->showErrorDialog();
return;
}
if ((*it).insert)
{
int col, line;
mEditor->getCursorPosition(&line, &col);
(*it).data.resize((*it).data.size() + 1);
(*it).data[(*it).data.size() - 1] = '\0';
QTextCodec *codec = KGlobal::charsets()->codecForName((*it).encoding);
if (codec)
mEditor->insertAt(codec->toUnicode((*it).data), line, col);
else
mEditor->insertAt(QString::fromLocal8Bit((*it).data), line, col);
mapAtmLoadData.remove(it);
return;
}
QString name;
QString urlStr = (*it).url.prettyURL();
KMMessagePart* msgPart;
KMMsgPartDlg dlg;
int i;
kernel->kbp()->busy();
i = urlStr.findRev('/');
name = (i>=0 ? urlStr.mid(i+1, 256) : urlStr);
QCString encoding = KMMessage::autoDetectCharset(mCharset,
KMMessage::preferredCharsets(), name);
if (encoding.isEmpty()) encoding = "utf-8";
QCString encName = KMMsgBase::encodeRFC2231String(name, encoding);
bool RFC2231encoded = name != QString(encName);
// create message part
msgPart = new KMMessagePart;
msgPart->setName(name);
msgPart->setCteStr("base64");
msgPart->setBodyEncodedBinary((*it).data);
msgPart->magicSetType();
msgPart->setContentDisposition(QCString("attachment; filename")
+ ((RFC2231encoded) ? "*" : "") + "=\"" + encName + "\"");
mapAtmLoadData.remove(it);
// show properties dialog
kernel->kbp()->idle();
msgPart->setCharset(mCharset);
dlg.setMsgPart(msgPart);
if (!dlg.exec())
{
delete msgPart;
return;
}
if (msgPart->typeStr().lower() != "text") msgPart->setCharset(QCString());
// add the new attachment to the list
addAttach(msgPart);
rethinkFields(); //work around initial-size bug in Qt-1.32
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotInsertFile()
{
KFileDialog fdlg(QString::null, QString::null, this, NULL, TRUE);
fdlg.setCaption(i18n("Include File"));
fdlg.toolBar()->insertCombo(KMMsgBase::supportedEncodings(FALSE), 4711,
false, NULL, NULL, NULL);
KComboBox *combo = fdlg.toolBar()->getCombo(4711);
for (int i = 0; i < combo->count(); i++)
if (KGlobal::charsets()->codecForName(KGlobal::charsets()->
encodingForName(combo->text(i)))
== QTextCodec::codecForLocale()) combo->setCurrentItem(i);
if (!fdlg.exec()) return;
KURL u = fdlg.selectedURL();
if (u.fileName().isEmpty()) return;
KIO::Job *job = KIO::get(u);
atmLoadData ld;
ld.url = u;
ld.data = QByteArray();
ld.insert = true;
ld.encoding = KGlobal::charsets()->encodingForName(
combo->currentText()).latin1();
mapAtmLoadData.insert(job, ld);
connect(job, SIGNAL(result(KIO::Job *)),
this, SLOT(slotAttachFileResult(KIO::Job *)));
connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)),
this, SLOT(slotAttachFileData(KIO::Job *, const QByteArray &)));
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotSetCharset()
{
if (encodingAction->currentItem() == 0)
{
bAutoCharset = true;
return;
}
bAutoCharset = false;
mCharset = KGlobal::charsets()->encodingForName( encodingAction->
currentText() ).latin1();
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotInsertMyPublicKey()
{
QString str, name;
KMMessagePart* msgPart;
kernel->kbp()->busy();
// get PGP user id for the chosen identity
QCString pgpUserId;
QString identStr = i18n( "Default" );
if( !mId.isEmpty() && KMIdentity::identities().contains( mId ) ) {
identStr = mId;
}
KMIdentity ident(identStr);
ident.readConfig();
pgpUserId = ident.pgpIdentity();
str=Kpgp::Module::getKpgp()->getAsciiPublicKey(pgpUserId);
if (str.isEmpty())
{
kernel->kbp()->idle();
KMessageBox::sorry( 0L, i18n("Couldn't get your public key for\n%1.")
.arg(Kpgp::Module::getKpgp()->user()) );
return;
}
// create message part
msgPart = new KMMessagePart;
msgPart->setName(i18n("my pgp key"));
msgPart->setCteStr("base64");
msgPart->setTypeStr("application");
msgPart->setSubtypeStr("pgp-keys");
msgPart->setBodyEncoded(QCString(str.ascii()));
msgPart->setContentDisposition("attachment; filename=public_key.asc");
// add the new attachment to the list
addAttach(msgPart);
rethinkFields(); //work around initial-size bug in Qt-1.32
kernel->kbp()->idle();
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotInsertPublicKey()
{
QString str;
QCString keyID;
KMMessagePart* msgPart;
Kpgp::Module *pgp;
if ( !(pgp = Kpgp::Module::getKpgp()) )
return;
keyID = pgp->selectPublicKey(i18n("Please select the public key which "
"should be attached."));
if (keyID.isEmpty())
return;
str = pgp->getAsciiPublicKey(keyID);
if (!str.isEmpty()) {
// create message part
msgPart = new KMMessagePart;
msgPart->setName(i18n("PGP key 0x%1").arg(keyID));
msgPart->setCteStr("base64");
msgPart->setTypeStr("application");
msgPart->setSubtypeStr("pgp-keys");
msgPart->setBodyEncoded(QCString(str.ascii()));
msgPart->setContentDisposition("attachment; filename=0x" + keyID + ".asc");
// add the new attachment to the list
addAttach(msgPart);
rethinkFields(); //work around initial-size bug in Qt-1.32
} else {
KMessageBox::sorry( 0L, i18n( "Could not attach public key, perhaps its format is invalid\n"
"(e.g. key contains umlaut with wrong encoding)." ) );
}
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotAttachPopupMenu(QListViewItem *, const QPoint &, int)
{
if (!mAttachMenu)
{
mAttachMenu = new QPopupMenu(this);
mAttachMenu->insertItem(i18n("View..."), this, SLOT(slotAttachView()));
mAttachMenu->insertItem(i18n("Save..."), this, SLOT(slotAttachSave()));
mAttachMenu->insertItem(i18n("Properties..."),
this, SLOT(slotAttachProperties()));
mAttachMenu->insertItem(i18n("Remove"), this, SLOT(slotAttachRemove()));
mAttachMenu->insertSeparator();
mAttachMenu->insertItem(i18n("Attach..."), this, SLOT(slotAttachFile()));
}
mAttachMenu->popup(QCursor::pos());
}
//-----------------------------------------------------------------------------
int KMComposeWin::currentAttachmentNum()
{
int idx = -1;
QPtrListIterator<QListViewItem> it(mAtmItemList);
for ( int i = 0; it.current(); ++it, ++i )
if (*it == mAtmListBox->currentItem()) {
idx = i;
break;
}
return idx;
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotAttachProperties()
{
KMMsgPartDlg dlg;
KMMessagePart* msgPart;
int idx = currentAttachmentNum();
if (idx < 0) return;
msgPart = mAtmList.at(idx);
msgPart->setCharset(mCharset);
dlg.setMsgPart(msgPart);
if (dlg.exec())
{
// values may have changed, so recreate the listbox line
msgPartToItem(msgPart, mAtmItemList.at(idx));
}
if (msgPart->typeStr().lower() != "text") msgPart->setCharset(QCString());
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotAttachView()
{
QString str, pname;
KMMessagePart* msgPart;
int idx = currentAttachmentNum();
if (idx < 0) return;
msgPart = mAtmList.at(idx);
pname = msgPart->name();
if (pname.isEmpty()) pname=msgPart->contentDescription();
if (pname.isEmpty()) pname="unnamed";
KTempFile* atmTempFile = new KTempFile();
mAtmTempList.append( atmTempFile );
atmTempFile->setAutoDelete( true );
kByteArrayToFile(msgPart->bodyDecodedBinary(), atmTempFile->name(), false, false,
false);
KMReaderWin::atmView(NULL, msgPart, false, atmTempFile->name(), pname,
KMMsgBase::codecForName(mCharset));
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotAttachSave()
{
KMMessagePart* msgPart;
QString fileName, pname;
int idx = currentAttachmentNum();
if (idx < 0) return;
msgPart = mAtmList.at(idx);
pname = msgPart->name();
if (pname.isEmpty()) pname="unnamed";
KURL url = KFileDialog::getSaveURL(QString::null, QString::null, 0, pname);
if( url.isEmpty() )
return;
kernel->byteArrayToRemoteFile(msgPart->bodyDecodedBinary(), url);
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotAttachRemove()
{
int idx = currentAttachmentNum();
if (idx >= 0)
{
removeAttach(idx);
mEditor->setModified(TRUE);
}
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotFind()
{
mEditor->search();
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotReplace()
{
mEditor->replace();
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotToggleFixedFont()
{
if (!mEditor) return;
QFont fixedfont = KGlobalSettings::fixedFont();
mEditor->setFont( (mEditor->font() == fixedfont) ? mBodyFont : fixedfont );
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotUndo()
{
QWidget* fw = focusWidget();
if (!fw) return;
if (fw->inherits("KEdit"))
((QMultiLineEdit*)fw)->undo();
else if (fw->inherits("KMLineEdit"))
((KMLineEdit*)fw)->undo();
}
void KMComposeWin::slotRedo()
{
QWidget* fw = focusWidget();
if (!fw) return;
if (fw->inherits("KEdit"))
((QMultiLineEdit*)fw)->redo();
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotCut()
{
QWidget* fw = focusWidget();
if (!fw) return;
if (fw->inherits("KEdit"))
((QMultiLineEdit*)fw)->cut();
else if (fw->inherits("KMLineEdit"))
((KMLineEdit*)fw)->cut();
else kdDebug(5006) << "wrong focus widget" << endl;
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotCopy()
{
QWidget* fw = focusWidget();
if (!fw) return;
#ifdef KeyPress
#undef KeyPress
#endif
QKeyEvent k(QEvent::KeyPress, Key_C , 0 , ControlButton);
kapp->notify(fw, &k);
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotPaste()
{
QWidget* fw = focusWidget();
if (!fw) return;
#ifdef KeyPress
#undef KeyPress
#endif
QKeyEvent k(QEvent::KeyPress, Key_V , 0 , ControlButton);
kapp->notify(fw, &k);
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotMarkAll()
{
QWidget* fw = focusWidget();
if (!fw) return;
if (fw->inherits("QLineEdit"))
((QLineEdit*)fw)->selectAll();
else if (fw->inherits("QMultiLineEdit"))
((QMultiLineEdit*)fw)->selectAll();
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotClose()
{
close(FALSE);
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotNewComposer()
{
KMComposeWin* win;
KMMessage* msg = new KMMessage;
msg->initHeader();
win = new KMComposeWin(msg);
win->show();
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotNewMailReader()
{
KMMainWin *kmmwin = new KMMainWin(NULL);
kmmwin->show();
//d->resize(d->size());
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotUpdWinTitle(const QString& text)
{
if (text.isEmpty())
setCaption("("+QString(i18n("unnamed"))+")");
else setCaption(text);
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotEncryptToggled(bool on)
{
if (on) encryptAction->setIcon("lock");
else encryptAction->setIcon("unlock");
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotWordWrapToggled(bool on)
{
if (on)
{
mEditor->setWordWrap( QMultiLineEdit::FixedColumnWidth );
mEditor->setWrapColumnOrWidth(mLineBreak);
}
else
{
mEditor->setWordWrap( QMultiLineEdit::NoWrap );
}
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotPrint()
{
KMReaderWin rw;
applyChanges();
rw.setMsg(mMsg, true);
rw.printMsg();
}
//----------------------------------------------------------------------------
void KMComposeWin::doSend(int aSendNow, bool saveInDrafts)
{
static bool busy = false;
if (busy) return;
busy = true;
if (!saveInDrafts)
{
if (to().isEmpty())
{
mEdtTo->setFocus();
KMessageBox::information(0,i18n("You must specify at least one receiver in the To: field."));
busy = false;
return;
}
if (subject().isEmpty())
{
mEdtSubject->setFocus();
int rc = KMessageBox::questionYesNo(0, i18n("You did not specify a subject. Send message anyway?"),
i18n("No subject specified"), i18n("Yes"), i18n("No, let me specify the subject"));
if( rc == KMessageBox::No )
{
busy = false;
return;
}
}
}
kernel->kbp()->busy();
mMsg->setDateToday();
// If a user sets up their outgoing messages preferences wrong and then
// sends mail that gets 'stuck' in their outbox, they should be able to
// rectify the problem by editing their outgoing preferences and
// resending.
// Hence this following conditional
QString hf = mMsg->headerField("X-KMail-Transport");
if ((mTransport->currentText() != mTransport->text(0)) ||
(!hf.isEmpty() && (hf != mTransport->text(0))))
mMsg->setHeaderField("X-KMail-Transport", mTransport->currentText());
disableBreaking = saveInDrafts;
bool sentOk = applyChanges();
disableBreaking = false;
if (!sentOk)
{
kernel->kbp()->idle();
busy = false;
return;
}
if (saveInDrafts)
sentOk = !(kernel->draftsFolder()->addMsg(mMsg));
else
sentOk = kernel->msgSender()->send(mMsg, aSendNow);
kernel->kbp()->idle();
if (!sentOk)
{
busy = false;
return;
}
if (saveInDrafts || !aSendNow)
emit messageQueuedOrDrafted();
KMRecentAddresses::self()->add( bcc() );
KMRecentAddresses::self()->add( cc() );
KMRecentAddresses::self()->add( to() );
mAutoDeleteMsg = FALSE;
mFolder = NULL;
hide();
delete this;
busy = false;
}
//----------------------------------------------------------------------------
void KMComposeWin::slotSendLater()
{
doSend(FALSE);
}
//----------------------------------------------------------------------------
void KMComposeWin::slotSaveDraft()
{
doSend(FALSE, TRUE);
}
//----------------------------------------------------------------------------
void KMComposeWin::slotSendNow()
{
if (mConfirmSend) {
switch(KMessageBox::warningYesNoCancel(mMainWidget,
i18n("About to send email..."),
i18n("Send Confirmation"),
i18n("Send &Now"),
i18n("Send &Later"))) {
case KMessageBox::Yes: // send now
doSend(TRUE);
break;
case KMessageBox::No: // send later
doSend(FALSE);
break;
case KMessageBox::Cancel: // cancel
break;
default:
; // whoa something weird happened here!
}
return;
}
doSend(TRUE);
}
void KMComposeWin::slotAppendSignature()
{
QString identStr = i18n( "Default" );
if( !mId.isEmpty() && KMIdentity::identities().contains( mId ) )
{
identStr = mId;
}
bool mod = mEditor->isModified();
KMIdentity ident( identStr );
ident.readConfig();
QString sigText = ident.signature();
if( sigText.isEmpty() && ident.useSignatureFile() )
{
// open a file dialog and let the user choose manually
KFileDialog dlg( QDir::homeDirPath(), QString::null, this, 0, TRUE );
dlg.setCaption(i18n("Choose Signature File"));
if( !dlg.exec() )
{
return;
}
KURL url = dlg.selectedURL();
if( url.isEmpty() )
{
return;
}
if( !url.isLocalFile() )
{
KMessageBox::sorry( 0L, i18n( "Only local files are supported." ) );
return;
}
QString sigFileName = url.path();
sigText = QString::fromLocal8Bit(kFileToString(sigFileName, TRUE));
ident.setSignatureFile(sigFileName);
ident.writeConfig(true);
}
mOldSigText = sigText;
if( !sigText.isEmpty() )
{
/* actually "\n-- \n" (note the space) is a convention for attaching
signatures and we should respect it, unless the user has already done so. */
mEditor->sync();
if (!sigText.startsWith("-- \n")) mEditor->append("-- ");
mEditor->append(sigText);
mEditor->update();
mEditor->setModified(mod);
mEditor->setContentsPos( 0, 0 );
}
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotHelp()
{
kapp->invokeHelp();
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotCleanSpace()
{
if (!mEditor) return;
mEditor->cleanWhiteSpace();
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotSpellcheck()
{
if (mSpellCheckInProgress) return;
mSpellCheckInProgress=TRUE;
/*
connect (mEditor, SIGNAL (spellcheck_progress (unsigned)),
this, SLOT (spell_progress (unsigned)));
*/
if(mEditor){
connect (mEditor, SIGNAL (spellcheck_done()),
this, SLOT (slotSpellcheckDone ()));
mEditor->spellcheck();
}
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotSpellcheckDone()
{
kdDebug(5006) << "spell check complete" << endl;
mSpellCheckInProgress=FALSE;
statusBar()->changeItem(i18n("Spellcheck complete."),0);
}
//-----------------------------------------------------------------------------
void KMComposeWin::focusNextPrevEdit(const QWidget* aCur, bool aNext)
{
QWidget* cur;
if (!aCur)
{
cur=mEdtList.last();
}
else
{
for (cur=mEdtList.first(); aCur!=cur && cur; cur=mEdtList.next())
;
if (!cur) return;
if (aNext) cur = mEdtList.next();
else cur = mEdtList.prev();
}
if (cur) cur->setFocus();
else if (aNext) mEditor->setFocus(); //Key up from first doea nothing (sven)
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotIdentityActivated(int)
{
QString identStr = mIdentity->currentText();
if (!KMIdentity::identities().contains(identStr))
return;
KMIdentity ident(identStr);
ident.readConfig();
if(!ident.fullEmailAddr().isNull())
mEdtFrom->setText(ident.fullEmailAddr());
if(!ident.replyToAddr().isNull())
mEdtReplyTo->setText(ident.replyToAddr());
else
mEdtReplyTo->setText("");
if (ident.organization().isEmpty())
mMsg->removeHeaderField("Organization");
else
mMsg->setHeaderField("Organization", ident.organization());
QString edtText = mEditor->text();
int pos = edtText.findRev( "\n-- \n" + mOldSigText);
if (!mBtnTransport->isChecked()) {
if (ident.transport().isEmpty())
mMsg->removeHeaderField("X-KMail-Transport");
else
mMsg->setHeaderField("X-KMail-Transport", ident.transport());
QString transp = ident.transport();
if (transp.isEmpty()) transp = mTransport->text(0);
bool found = false;
int i;
for (i = 0; i < mTransport->count(); i++) {
if (mTransport->text(i) == transp) {
found = true;
mTransport->setCurrentItem(i);
break;
}
}
if (found == false) {
if (i == mTransport->maxCount()) mTransport->setMaxCount(i + 1);
mTransport->insertItem(transp,i);
mTransport->setCurrentItem(i);
}
}
if ( !mBtnFcc->isChecked() )
{
if ( ident.fcc().isEmpty() )
ident.setFcc( kernel->sentFolder()->idString() );
kdDebug(5006) << "KMComposeWin::slotIdentityActivated: mFcc->count() = " << mFcc->count() << endl;
QString theFcc = ident.fcc();
mFcc->setFolder( theFcc );
}
if (((pos >= 0) && (pos + mOldSigText.length() + 5 == edtText.length())) ||
(mOldSigText.isEmpty())) {
if (pos >= 0)
edtText.truncate(pos);
if (!ident.signature().isEmpty() && mAutoSign) {
edtText.append( "\n" );
if (!ident.signature().startsWith("-- \n")) edtText.append( "-- \n" );
edtText.append( ident.signature() );
}
mEditor->setText( edtText );
}
mOldSigText = ident.signature();
// disable certain actions if there is no PGP user identity set
// for this profile
if (ident.pgpIdentity().isEmpty()) {
attachMPK->setEnabled(false);
signAction->setEnabled(false);
signAction->setChecked(false);
}
else {
attachMPK->setEnabled(true);
// don't change the state of the sign button if the button
// was already enabled for the former identity
if (!signAction->isEnabled()) {
signAction->setEnabled(true);
signAction->setChecked(mAutoPgpSign);
}
}
mEditor->setModified(TRUE);
mId = identStr;
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotSpellcheckConfig()
{
KWin kwin;
QTabDialog qtd (this, "tabdialog", true);
KSpellConfig mKSpellConfig (&qtd);
qtd.addTab (&mKSpellConfig, i18n("Spellchecker"));
qtd.setCancelButton ();
kwin.setIcons (qtd.winId(), kapp->icon(), kapp->miniIcon());
if (qtd.exec())
mKSpellConfig.writeGlobalSettings();
}
//-----------------------------------------------------------------------------
void KMComposeWin::slotToggleToolBar()
{
if(toolBar("mainToolBar")->isVisible())
toolBar("mainToolBar")->hide();
else
toolBar("mainToolBar")->show();
}
void KMComposeWin::slotToggleStatusBar()
{
if (statusBar()->isVisible())
statusBar()->hide();
else
statusBar()->show();
}
void KMComposeWin::slotEditToolbars()
{
KEditToolbar dlg(actionCollection(), "kmcomposerui.rc");
if (dlg.exec() == true)
{
createGUI("kmcomposerui.rc");
toolbarAction->setChecked(!toolBar()->isHidden());
}
}
void KMComposeWin::slotEditKeys()
{
KKeyDialog::configureKeys(actionCollection(), xmlFile(), true, this);
}
void KMComposeWin::setReplyFocus( bool hasMessage )
{
mEditor->setFocus();
if ( hasMessage )
mEditor->setCursorPosition( 1, 0 );
}
void KMComposeWin::slotCompletionModeChanged( KGlobalSettings::Completion mode)
{
KConfig *config = KGlobal::config();
KConfigGroupSaver cs( config, "Composer" );
config->writeEntry( "Completion Mode", (int) mode );
config->sync(); // maybe not?
// sync all the lineedits to the same completion mode
mEdtFrom->setCompletionMode( mode );
mEdtReplyTo->setCompletionMode( mode );
mEdtTo->setCompletionMode( mode );
mEdtCc->setCompletionMode( mode );
mEdtBcc->setCompletionMode( mode );
}
//=============================================================================
//
// Class KMLineEdit
//
//=============================================================================
KCompletion * KMLineEdit::s_completion = 0L;
bool KMLineEdit::s_addressesDirty = false;
KMLineEdit::KMLineEdit(KMComposeWin* composer, bool useCompletion,
QWidget *parent, const char *name)
: KMLineEditInherited(parent,name)
{
mComposer = composer;
m_useCompletion = useCompletion;
if ( !s_completion ) {
s_completion = new KCompletion();
s_completion->setOrder( KCompletion::Sorted );
#if KDE_VERSION >= 290
s_completion->setIgnoreCase( true );
#endif
}
installEventFilter(this);
if ( m_useCompletion )
{
setCompletionObject( s_completion, false ); // we handle it ourself
connect( this, SIGNAL( completion(const QString&)),
this, SLOT(slotCompletion() ));
KCompletionBox *box = completionBox();
connect( box, SIGNAL( highlighted( const QString& )),
this, SLOT( slotPopupCompletion( const QString& ) ));
connect( completionBox(), SIGNAL( userCancelled( const QString& )),
SLOT( setText( const QString& )));
// Whenever a new KMLineEdit is created (== a new composer is created),
// we set a dirty flag to reload the addresses upon the first completion.
// The address completions are shared between all KMLineEdits.
// Is there a signal that tells us about addressbook updates?
s_addressesDirty = true;
}
}
//-----------------------------------------------------------------------------
KMLineEdit::~KMLineEdit()
{
removeEventFilter(this);
}
//-----------------------------------------------------------------------------
void KMLineEdit::setFont( const QFont& font )
{
KMLineEditInherited::setFont( font );
if ( m_useCompletion )
completionBox()->setFont( font );
}
//-----------------------------------------------------------------------------
bool KMLineEdit::eventFilter(QObject *o, QEvent *e)
{
#ifdef KeyPress
#undef KeyPress
#endif
if (e->type() == QEvent::KeyPress)
{
QKeyEvent* k = (QKeyEvent*)e;
if (KStdAccel::isEqual( k, KStdAccel::SubstringCompletion ))
{
doCompletion(true);
return TRUE;
}
// ---sven's Return is same Tab and arrow key navigation start ---
if ((k->key() == Key_Enter || k->key() == Key_Return) &&
!completionBox()->isVisible())
{
mComposer->focusNextPrevEdit(this,TRUE);
return TRUE;
}
if (k->state()==ControlButton && k->key() == Key_Right)
{
if ((int)text().length() == cursorPosition()) // at End?
{
doCompletion(true);
return TRUE;
}
return FALSE;
}
if (k->key() == Key_Up)
{
mComposer->focusNextPrevEdit(this,FALSE); // Go up
return TRUE;
}
if (k->key() == Key_Down)
{
mComposer->focusNextPrevEdit(this,TRUE); // Go down
return TRUE;
}
// ---sven's Return is same Tab and arrow key navigation end ---
}
return KMLineEditInherited::eventFilter(o, e);
}
//-----------------------------------------------------------------------------
void KMLineEdit::cursorAtEnd()
{
setCursorPosition( text().length() );
}
void KMLineEdit::undo()
{
QKeyEvent k(QEvent::KeyPress, 90, 26, 16 ); // Ctrl-Z
keyPressEvent( &k );
}
//-----------------------------------------------------------------------------
void KMLineEdit::doCompletion(bool ctrlT)
{
if ( !m_useCompletion )
return;
QString s(text());
QString prevAddr;
int n = s.findRev(',');
if (n>= 0)
{
prevAddr = s.left(n+1) + ' ';
s = s.mid(n+1,255).stripWhiteSpace();
}
KCompletionBox *box = completionBox();
box->clear();
if ( s.isEmpty() )
{
box->hide();
return;
}
KGlobalSettings::Completion mode = completionMode();
if ( s_addressesDirty )
loadAddresses();
QString match;
int curPos = cursorPosition();
if ( mode != KGlobalSettings::CompletionNone )
match = s_completion->makeCompletion( s );
// kdDebug(5006) << "** completion for: " << s << " : " << match << endl;
if ( ctrlT )
{
QStringList addresses = s_completion->items();
QStringList::Iterator it = addresses.begin();
QStringList completions;
for (; it != addresses.end(); ++it)
{
if ((*it).find(s,0,false) >= 0)
completions.append( *it );
}
if (completions.count() > 1) {
m_previousAddresses = prevAddr;
box->insertItems( completions );
box->setCancelledText( text() );
box->popup();
}
else if (completions.count() == 1)
setText(prevAddr + box->text(0));
else
box->hide();
cursorAtEnd();
return;
}
switch ( mode )
{
case KGlobalSettings::CompletionPopup:
{
if ( !match.isNull() )
{
m_previousAddresses = prevAddr;
box->insertItems( s_completion->allMatches( s ));
box->setCancelledText( text() );
box->popup();
}
else
box->hide();
break;
}
case KGlobalSettings::CompletionShell:
{
if ( !match.isNull() && match != s )
{
setText( prevAddr + match );
cursorAtEnd();
}
break;
}
case KGlobalSettings::CompletionMan: // Short-Auto in fact
case KGlobalSettings::CompletionAuto:
{
if ( !match.isNull() && match != s )
{
QString adds = prevAddr + match;
validateAndSet( adds, curPos, curPos, adds.length() );
}
break;
}
default: // fall through
case KGlobalSettings::CompletionNone:
break;
}
}
//-----------------------------------------------------------------------------
void KMLineEdit::slotPopupCompletion( const QString& completion )
{
setText( m_previousAddresses + completion );
cursorAtEnd();
}
//-----------------------------------------------------------------------------
void KMLineEdit::loadAddresses()
{
s_completion->clear();
s_addressesDirty = false;
QStringList recent = KMRecentAddresses::self()->addresses();
QStringList::Iterator it = recent.begin();
for ( ; it != recent.end(); ++it )
s_completion->addItem( *it );
KMAddrBook adb;
adb.readConfig();
if(adb.load() == IO_FatalError)
return;
if (!KMAddrBookExternal::useKAB()) {
QStringList::ConstIterator it = adb.begin();
for ( ; it != adb.end(); ++it )
s_completion->addItem( *it );
}
else {
QStringList addresses;
KabBridge::addresses(&addresses);
QStringList::Iterator it = addresses.begin();
for (; it != addresses.end(); ++it)
s_completion->addItem( *it );
}
}
//-----------------------------------------------------------------------------
void KMLineEdit::dropEvent(QDropEvent *e)
{
QStrList uriList;
if(QUriDrag::canDecode(e) && QUriDrag::decode( e, uriList ))
{
QString ct = text();
for (QStrListIterator it(uriList); it; ++it)
{
if (!ct.isEmpty()) ct.append(", ");
KURL u(*it);
if (u.protocol() == "mailto") ct.append(QString::fromUtf8(u.path().latin1()));
else ct.append(QString::fromUtf8(*it));
}
setText(ct);
}
else QLineEdit::dropEvent(e);
}
//=============================================================================
//
// Class KMEdit
//
//=============================================================================
KMEdit::KMEdit(QWidget *parent, KMComposeWin* composer,
const char *name):
KMEditInherited(parent, name)
{
mComposer = composer;
installEventFilter(this);
KCursor::setAutoHideCursor( this, true, true );
extEditor = false; // the default is to use ourself
mKSpell = NULL;
mTempFile = NULL;
mExtEditorProcess = NULL;
}
//-----------------------------------------------------------------------------
KMEdit::~KMEdit()
{
removeEventFilter(this);
if (mKSpell) delete mKSpell;
}
//-----------------------------------------------------------------------------
QString KMEdit::brokenText()
{
QString temp, line;
int num_lines = numLines();
for (int i = 0; i < num_lines; ++i)
{
int lastLine = 0;
line = textLine(i);
for (int j = 0; j < (int)line.length() - 1; ++j)
{
if (lineOfChar(i, j) > lastLine)
{
lastLine = lineOfChar(i, j);
temp += '\n';
}
temp += line[j];
}
if (i + 1 < num_lines) temp += '\n';
}
return temp;
}
//-----------------------------------------------------------------------------
bool KMEdit::eventFilter(QObject*o, QEvent* e)
{
if (o == this)
KCursor::autoHideEventFilter(o, e);
if (e->type() == QEvent::KeyPress)
{
QKeyEvent *k = (QKeyEvent*)e;
if (extEditor) {
if (k->key() == Key_Up)
{
mComposer->focusNextPrevEdit(0, false); //take me up
return TRUE;
}
if (mTempFile) return TRUE;
QRegExp repFn("\\%f");
QString sysLine = mExtEditor;
mTempFile = new KTempFile();
mTempFile->setAutoDelete(true);
fprintf(mTempFile->fstream(), "%s", (const char *)text().local8Bit());
mTempFile->close();
// replace %f in the system line
sysLine.replace(repFn, mTempFile->name());
mExtEditorProcess = new KProcess();
sysLine += " ";
while (!sysLine.isEmpty())
{
*mExtEditorProcess << sysLine.left(sysLine.find(" ")).local8Bit();
sysLine.remove(0, sysLine.find(" ") + 1);
}
connect(mExtEditorProcess, SIGNAL(processExited(KProcess*)),
SLOT(slotExternalEditorDone(KProcess*)));
if (!mExtEditorProcess->start())
{
KMessageBox::error(NULL, i18n("Unable to start external editor."));
delete mExtEditorProcess;
delete mTempFile;
mExtEditorProcess = NULL;
mTempFile = NULL;
}
return TRUE;
} else {
// ---sven's Arrow key navigation start ---
// Key Up in first line takes you to Subject line.
if (k->key() == Key_Up && k->state() != ShiftButton && currentLine() == 0
&& lineOfChar(0, currentColumn()) == 0)
{
deselect();
mComposer->focusNextPrevEdit(0, false); //take me up
return TRUE;
}
// ---sven's Arrow key navigation end ---
}
}
else if (e->type() == QEvent::Drop)
{
KURL::List urlList;
QDropEvent *de = static_cast<QDropEvent*>(e);
if(QUriDrag::canDecode(de) && KURLDrag::decode( de, urlList ))
{
for (KURL::List::Iterator it = urlList.begin(); it != urlList.end(); ++it)
mComposer->addAttach(*it);
return TRUE;
}
}
return KMEditInherited::eventFilter(o, e);
}
//-----------------------------------------------------------------------------
void KMEdit::slotExternalEditorDone(KProcess* proc)
{
assert(proc == mExtEditorProcess);
setAutoUpdate(false);
clear();
// read data back in from file
insertLine(QString::fromLocal8Bit(kFileToString(mTempFile->name(),
TRUE, FALSE)), -1);
setAutoUpdate(true);
repaint();
delete proc;
delete mTempFile;
mTempFile = NULL;
mExtEditorProcess = NULL;
}
//-----------------------------------------------------------------------------
void KMEdit::spellcheck()
{
mKSpell = new KSpell(this, i18n("Spellcheck - KMail"), this,
SLOT(slotSpellcheck2(KSpell*)));
connect (mKSpell, SIGNAL( death()),
this, SLOT (slotSpellDone()));
connect (mKSpell, SIGNAL (misspelling (QString, QStringList *, unsigned)),
this, SLOT (misspelling (QString, QStringList *, unsigned)));
connect (mKSpell, SIGNAL (corrected (QString, QString, unsigned)),
this, SLOT (corrected (QString, QString, unsigned)));
connect (mKSpell, SIGNAL (done(const QString &)),
this, SLOT (slotSpellResult (const QString&)));
}
//-----------------------------------------------------------------------------
void KMEdit::slotSpellcheck2(KSpell*)
{
spellcheck_start();
//we're going to want to ignore quoted-message lines...
mKSpell->check(text());
}
//-----------------------------------------------------------------------------
void KMEdit::slotSpellResult(const QString &aNewText)
{
spellcheck_stop();
if (mKSpell->dlgResult() == 0)
{
setText(aNewText);
}
mKSpell->cleanUp();
emit spellcheck_done();
}
//-----------------------------------------------------------------------------
void KMEdit::slotSpellDone()
{
KSpell::spellStatus status = mKSpell->status();
delete mKSpell;
mKSpell = 0;
if (status == KSpell::Error)
{
KMessageBox::sorry(this, i18n("ISpell could not be started.\n Please make sure you have ISpell properly configured and in your PATH."));
}
else if (status == KSpell::Crashed)
{
spellcheck_stop();
KMessageBox::sorry(this, i18n("ISpell seems to have crashed."));
}
else
{
emit spellcheck_done();
}
}