kmail

kmcomposewin.cpp

00001 // -*- mode: C++; c-file-style: "gnu" -*-
00002 // kmcomposewin.cpp
00003 // Author: Markus Wuebben <markus.wuebben@kde.org>
00004 // This code is published under the GPL.
00005 
00006 #undef GrayScale
00007 #undef Color
00008 #include <config.h>
00009 
00010 #define REALLY_WANT_KMCOMPOSEWIN_H
00011 #include "kmcomposewin.h"
00012 #undef REALLY_WANT_KMCOMPOSEWIN_H
00013 
00014 #include "kmedit.h"
00015 #include "kmlineeditspell.h"
00016 #include "kmatmlistview.h"
00017 
00018 #include "kmmainwin.h"
00019 #include "kmreadermainwin.h"
00020 #include "messagesender.h"
00021 #include "kmmsgpartdlg.h"
00022 #include <kpgpblock.h>
00023 #include <kaddrbook.h>
00024 #include "kmaddrbook.h"
00025 #include "kmmsgdict.h"
00026 #include "kmfolderimap.h"
00027 #include "kmfoldermgr.h"
00028 #include "kmfoldercombobox.h"
00029 #include "kmtransport.h"
00030 #include "kmcommands.h"
00031 #include "kcursorsaver.h"
00032 #include "partNode.h"
00033 #include "encodingdetector.h"
00034 #include "attachmentlistview.h"
00035 #include "transportmanager.h"
00036 using KMail::AttachmentListView;
00037 #include "dictionarycombobox.h"
00038 using KMail::DictionaryComboBox;
00039 #include "addressesdialog.h"
00040 using KPIM::AddressesDialog;
00041 #include "addresseeemailselection.h"
00042 using KPIM::AddresseeEmailSelection;
00043 using KPIM::AddresseeSelectorDialog;
00044 #include <maillistdrag.h>
00045 using KPIM::MailListDrag;
00046 #include "recentaddresses.h"
00047 using KRecentAddress::RecentAddresses;
00048 #include "kleo_util.h"
00049 #include "stl_util.h"
00050 #include "recipientseditor.h"
00051 #include "editorwatcher.h"
00052 
00053 #include "attachmentcollector.h"
00054 #include "objecttreeparser.h"
00055 
00056 #include "kmfoldermaildir.h"
00057 
00058 #include <libkpimidentities/identitymanager.h>
00059 #include <libkpimidentities/identitycombo.h>
00060 #include <libkpimidentities/identity.h>
00061 #include <libkdepim/kfileio.h>
00062 #include <libemailfunctions/email.h>
00063 #include <kleo/cryptobackendfactory.h>
00064 #include <kleo/exportjob.h>
00065 #include <kleo/specialjob.h>
00066 #include <ui/progressdialog.h>
00067 #include <ui/keyselectiondialog.h>
00068 
00069 #include <gpgmepp/context.h>
00070 #include <gpgmepp/key.h>
00071 
00072 #include <kabc/vcardconverter.h>
00073 #include <libkdepim/kvcarddrag.h>
00074 #include <kio/netaccess.h>
00075 
00076 #include "klistboxdialog.h"
00077 
00078 #include "messagecomposer.h"
00079 #include "chiasmuskeyselector.h"
00080 
00081 #include <kcharsets.h>
00082 #include <kcompletionbox.h>
00083 #include <kcursor.h>
00084 #include <kcombobox.h>
00085 #include <kstdaccel.h>
00086 #include <kpopupmenu.h>
00087 #include <kedittoolbar.h>
00088 #include <kkeydialog.h>
00089 #include <kdebug.h>
00090 #include <kfiledialog.h>
00091 #include <kwin.h>
00092 #include <kinputdialog.h>
00093 #include <kmessagebox.h>
00094 #include <kurldrag.h>
00095 #include <kio/scheduler.h>
00096 #include <ktempfile.h>
00097 #include <klocale.h>
00098 #include <kapplication.h>
00099 #include <kstatusbar.h>
00100 #include <kaction.h>
00101 #include <kstdaction.h>
00102 #include <kdirwatch.h>
00103 #include <kstdguiitem.h>
00104 #include <kiconloader.h>
00105 #include <kpushbutton.h>
00106 #include <kuserprofile.h>
00107 #include <krun.h>
00108 #include <ktempdir.h>
00109 #include <kstandarddirs.h>
00110 //#include <keditlistbox.h>
00111 #include "globalsettings.h"
00112 #include "replyphrases.h"
00113 
00114 #include <kspell.h>
00115 #include <kspelldlg.h>
00116 #include <spellingfilter.h>
00117 #include <ksyntaxhighlighter.h>
00118 #include <kcolordialog.h>
00119 #include <kzip.h>
00120 #include <ksavefile.h>
00121 
00122 #include <qtabdialog.h>
00123 #include <qregexp.h>
00124 #include <qbuffer.h>
00125 #include <qtooltip.h>
00126 #include <qtextcodec.h>
00127 #include <qheader.h>
00128 #include <qwhatsthis.h>
00129 #include <qfontdatabase.h>
00130 
00131 #include <mimelib/mimepp.h>
00132 
00133 #include <algorithm>
00134 #include <memory>
00135 
00136 #include <sys/stat.h>
00137 #include <sys/types.h>
00138 #include <stdlib.h>
00139 #include <unistd.h>
00140 #include <errno.h>
00141 #include <fcntl.h>
00142 #include <assert.h>
00143 
00144 #include "kmcomposewin.moc"
00145 
00146 #include "snippetwidget.h"
00147 
00148 KMail::Composer * KMail::makeComposer( KMMessage * msg, uint identitiy ) {
00149   return KMComposeWin::create( msg, identitiy );
00150 }
00151 
00152 KMail::Composer * KMComposeWin::create( KMMessage * msg, uint identitiy ) {
00153   return new KMComposeWin( msg, identitiy );
00154 }
00155 
00156 //-----------------------------------------------------------------------------
00157 KMComposeWin::KMComposeWin( KMMessage *aMsg, uint id  )
00158   : MailComposerIface(), KMail::Composer( "kmail-composer#" ),
00159     mSpellCheckInProgress( false ),
00160     mDone( false ),
00161     mAtmModified( false ),
00162     mMsg( 0 ),
00163     mAttachMenu( 0 ),
00164     mSigningAndEncryptionExplicitlyDisabled( false ),
00165     mFolder( 0 ),
00166     mUseHTMLEditor( false ),
00167     mId( id ),
00168     mAttachPK( 0 ), mAttachMPK( 0 ),
00169     mAttachRemoveAction( 0 ), mAttachSaveAction( 0 ), mAttachPropertiesAction( 0 ),
00170     mAppendSignatureAction( 0 ), mPrependSignatureAction( 0 ), mInsertSignatureAction( 0 ),
00171     mSignAction( 0 ), mEncryptAction( 0 ), mRequestMDNAction( 0 ),
00172     mUrgentAction( 0 ), mAllFieldsAction( 0 ), mFromAction( 0 ),
00173     mReplyToAction( 0 ), mToAction( 0 ), mCcAction( 0 ), mBccAction( 0 ),
00174     mSubjectAction( 0 ),
00175     mIdentityAction( 0 ), mTransportAction( 0 ), mFccAction( 0 ),
00176     mWordWrapAction( 0 ), mFixedFontAction( 0 ), mAutoSpellCheckingAction( 0 ),
00177     mDictionaryAction( 0 ), mSnippetAction( 0 ),
00178     mEncodingAction( 0 ),
00179     mCryptoModuleAction( 0 ),
00180     mEncryptChiasmusAction( 0 ),
00181     mEncryptWithChiasmus( false ),
00182     mComposer( 0 ),
00183     mLabelWidth( 0 ),
00184     mAutoSaveTimer( 0 ), mLastAutoSaveErrno( 0 ),
00185     mSignatureStateIndicator( 0 ), mEncryptionStateIndicator( 0 ),
00186     mPreserveUserCursorPosition( false ),
00187     mPreventFccOverwrite( false ),
00188     mCheckForRecipients( true )
00189 {
00190   mClassicalRecipients = GlobalSettings::self()->recipientsEditorType() ==
00191     GlobalSettings::EnumRecipientsEditorType::Classic;
00192 
00193   mSubjectTextWasSpellChecked = false;
00194   if (kmkernel->xmlGuiInstance())
00195     setInstance( kmkernel->xmlGuiInstance() );
00196   mMainWidget = new QWidget(this);
00197   // splitter between the headers area and the actual editor
00198   mHeadersToEditorSplitter = new QSplitter( Qt::Vertical, mMainWidget, "mHeadersToEditorSplitter" );
00199   mHeadersToEditorSplitter->setChildrenCollapsible( false );
00200   mHeadersArea = new QWidget( mHeadersToEditorSplitter );
00201   mHeadersArea->setSizePolicy( mHeadersToEditorSplitter->sizePolicy().horData(), QSizePolicy::Maximum );
00202   QVBoxLayout *v = new QVBoxLayout( mMainWidget );
00203   v->addWidget( mHeadersToEditorSplitter );
00204   mIdentity = new KPIM::IdentityCombo(kmkernel->identityManager(), mHeadersArea);
00205   mDictionaryCombo = new DictionaryComboBox( mHeadersArea );
00206   mFcc = new KMFolderComboBox(mHeadersArea);
00207   mFcc->showOutboxFolder( false );
00208   mTransport = new QComboBox(true, mHeadersArea);
00209   mEdtFrom = new KMLineEdit(false,mHeadersArea, "fromLine");
00210 
00211   mEdtReplyTo = new KMLineEdit(true,mHeadersArea, "replyToLine");
00212   mLblReplyTo = new QLabel(mHeadersArea);
00213   connect(mEdtReplyTo,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
00214           SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
00215 
00216   if ( mClassicalRecipients ) {
00217     mRecipientsEditor = 0;
00218 
00219     mEdtTo = new KMLineEdit(true,mHeadersArea, "toLine");
00220     mEdtCc = new KMLineEdit(true,mHeadersArea, "ccLine");
00221     mEdtBcc = new KMLineEdit(true,mHeadersArea, "bccLine");
00222 
00223     mLblTo = new QLabel(mHeadersArea);
00224     mLblCc = new QLabel(mHeadersArea);
00225     mLblBcc = new QLabel(mHeadersArea);
00226 
00227     mBtnTo = new QPushButton("...",mHeadersArea);
00228     mBtnCc = new QPushButton("...",mHeadersArea);
00229     mBtnBcc = new QPushButton("...",mHeadersArea);
00230     //mBtnFrom = new QPushButton("...",mHeadersArea);
00231 
00232     QString tip = i18n("Select email address(es)");
00233     QToolTip::add( mBtnTo, tip );
00234     QToolTip::add( mBtnCc, tip );
00235     QToolTip::add( mBtnBcc, tip );
00236 
00237     mBtnTo->setFocusPolicy(QWidget::NoFocus);
00238     mBtnCc->setFocusPolicy(QWidget::NoFocus);
00239     mBtnBcc->setFocusPolicy(QWidget::NoFocus);
00240     //mBtnFrom->setFocusPolicy(QWidget::NoFocus);
00241 
00242     connect(mBtnTo,SIGNAL(clicked()),SLOT(slotAddrBookTo()));
00243     connect(mBtnCc,SIGNAL(clicked()),SLOT(slotAddrBookTo()));
00244     connect(mBtnBcc,SIGNAL(clicked()),SLOT(slotAddrBookTo()));
00245     //connect(mBtnFrom,SIGNAL(clicked()),SLOT(slotAddrBookFrom()));
00246 
00247     connect(mEdtTo,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
00248             SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
00249     connect(mEdtCc,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
00250             SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
00251     connect(mEdtBcc,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
00252             SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
00253 
00254     mEdtTo->setFocus();
00255   } else {
00256     mEdtTo = 0;
00257     mEdtCc = 0;
00258     mEdtBcc = 0;
00259 
00260     mLblTo = 0;
00261     mLblCc = 0;
00262     mLblBcc = 0;
00263 
00264     mBtnTo = 0;
00265     mBtnCc = 0;
00266     mBtnBcc = 0;
00267     //mBtnFrom = 0;
00268 
00269     mRecipientsEditor = new RecipientsEditor( mHeadersArea );
00270     connect( mRecipientsEditor,
00271              SIGNAL( completionModeChanged( KGlobalSettings::Completion ) ),
00272              SLOT( slotCompletionModeChanged( KGlobalSettings::Completion ) ) );
00273     connect( mRecipientsEditor, SIGNAL(sizeHintChanged()), SLOT(recipientEditorSizeHintChanged()) );
00274 
00275     mRecipientsEditor->setFocus();
00276   }
00277   mEdtSubject = new KMLineEditSpell(false,mHeadersArea, "subjectLine");
00278   mLblIdentity = new QLabel(mHeadersArea);
00279   mDictionaryLabel = new QLabel( mHeadersArea );
00280   mLblFcc = new QLabel(mHeadersArea);
00281   mLblTransport = new QLabel(mHeadersArea);
00282   mLblFrom = new QLabel(mHeadersArea);
00283   mLblSubject = new QLabel(mHeadersArea);
00284   QString sticky = i18n("Sticky");
00285   mBtnIdentity = new QCheckBox(sticky,mHeadersArea);
00286   mBtnFcc = new QCheckBox(sticky,mHeadersArea);
00287   mBtnTransport = new QCheckBox(sticky,mHeadersArea);
00288 
00289   //setWFlags( WType_TopLevel | WStyle_Dialog );
00290   mHtmlMarkup = GlobalSettings::self()->useHtmlMarkup();
00291   mShowHeaders = GlobalSettings::self()->headers();
00292   mDone = false;
00293   mGrid = 0;
00294   mAtmListView = 0;
00295   mAtmList.setAutoDelete(true);
00296   mAtmTempList.setAutoDelete(true);
00297   mAtmModified = false;
00298   mAutoDeleteMsg = false;
00299   mFolder = 0;
00300   mAutoCharset = true;
00301   mFixedFontAction = 0;
00302   mTempDir = 0;
00303   // the attachment view is separated from the editor by a splitter
00304   mSplitter = new QSplitter( Qt::Vertical, mHeadersToEditorSplitter, "mSplitter" );
00305   mSplitter->setChildrenCollapsible( false );
00306   mSnippetSplitter = new QSplitter( Qt::Horizontal, mSplitter, "mSnippetSplitter");
00307   mSnippetSplitter->setChildrenCollapsible( false );
00308 
00309   QWidget *editorAndCryptoStateIndicators = new QWidget( mSnippetSplitter );
00310   QVBoxLayout *vbox = new QVBoxLayout( editorAndCryptoStateIndicators );
00311   QHBoxLayout *hbox = new QHBoxLayout( vbox );
00312   {
00313       mSignatureStateIndicator = new QLabel( editorAndCryptoStateIndicators );
00314       mSignatureStateIndicator->setAlignment( Qt::AlignHCenter );
00315       hbox->addWidget( mSignatureStateIndicator );
00316 
00317       KConfigGroup reader( KMKernel::config(), "Reader" );
00318       QPalette p( mSignatureStateIndicator->palette() );
00319 
00320       QColor defaultSignedColor( 0x40, 0xFF, 0x40 ); // light green // pgp ok, trusted key
00321       QColor defaultEncryptedColor( 0x00, 0x80, 0xFF ); // light blue // pgp encrypted
00322       p.setColor( QColorGroup::Background, reader.readColorEntry( "PGPMessageOkKeyOk", &defaultSignedColor ) );
00323       mSignatureStateIndicator->setPalette( p );
00324 
00325       mEncryptionStateIndicator = new QLabel( editorAndCryptoStateIndicators );
00326       mEncryptionStateIndicator->setAlignment( Qt::AlignHCenter );
00327       hbox->addWidget( mEncryptionStateIndicator );
00328       p.setColor( QColorGroup::Background, reader.readColorEntry( "PGPMessageEncr" , &defaultEncryptedColor ) );
00329       mEncryptionStateIndicator->setPalette( p );
00330   }
00331 
00332   mEditor = new KMEdit( editorAndCryptoStateIndicators, this, mDictionaryCombo->spellConfig() );
00333   vbox->addWidget( mEditor );
00334 
00335   mSnippetWidget = new SnippetWidget( mEditor, actionCollection(), mSnippetSplitter );
00336   mSnippetWidget->setShown( GlobalSettings::self()->showSnippetManager() );
00337 
00338   //  mSplitter->moveToFirst( editorAndCryptoStateIndicators );
00339   mSplitter->setOpaqueResize( true );
00340 
00341   mEditor->initializeAutoSpellChecking();
00342   mEditor->setTextFormat(Qt::PlainText);
00343   mEditor->setAcceptDrops( true );
00344 
00345   QWhatsThis::add( mBtnIdentity,
00346     GlobalSettings::self()->stickyIdentityItem()->whatsThis() );
00347   QWhatsThis::add( mBtnFcc,
00348     GlobalSettings::self()->stickyFccItem()->whatsThis() );
00349   QWhatsThis::add( mBtnTransport,
00350     GlobalSettings::self()->stickyTransportItem()->whatsThis() );
00351 
00352   mSpellCheckInProgress=false;
00353 
00354   setCaption( i18n("Composer") );
00355   setMinimumSize(200,200);
00356 
00357   mBtnIdentity->setFocusPolicy(QWidget::NoFocus);
00358   mBtnFcc->setFocusPolicy(QWidget::NoFocus);
00359   mBtnTransport->setFocusPolicy(QWidget::NoFocus);
00360 
00361   mAtmListView = new AttachmentListView( this, mSplitter,
00362                                          "attachment list view" );
00363   mAtmListView->setSelectionMode( QListView::Extended );
00364   mAtmListView->addColumn( i18n("Name"), 200 );
00365   mAtmListView->addColumn( i18n("Size"), 80 );
00366   mAtmListView->addColumn( i18n("Encoding"), 120 );
00367   int atmColType = mAtmListView->addColumn( i18n("Type"), 120 );
00368   // Stretch "Type".
00369   mAtmListView->header()->setStretchEnabled( true, atmColType );
00370   mAtmEncryptColWidth = 80;
00371   mAtmSignColWidth = 80;
00372   mAtmCompressColWidth = 100;
00373   mAtmColCompress = mAtmListView->addColumn( i18n("Compress"),
00374                                             mAtmCompressColWidth );
00375   mAtmColEncrypt = mAtmListView->addColumn( i18n("Encrypt"),
00376                                             mAtmEncryptColWidth );
00377   mAtmColSign    = mAtmListView->addColumn( i18n("Sign"),
00378                                             mAtmSignColWidth );
00379   mAtmListView->setColumnWidth( mAtmColEncrypt, 0 );
00380   mAtmListView->setColumnWidth( mAtmColSign,    0 );
00381   mAtmListView->setAllColumnsShowFocus( true );
00382 
00383   connect( mAtmListView,
00384            SIGNAL( doubleClicked( QListViewItem* ) ),
00385            SLOT( slotAttachEdit() ) );
00386   connect( mAtmListView,
00387            SIGNAL( rightButtonPressed( QListViewItem*, const QPoint&, int ) ),
00388            SLOT( slotAttachPopupMenu( QListViewItem*, const QPoint&, int ) ) );
00389   connect( mAtmListView,
00390            SIGNAL( selectionChanged() ),
00391            SLOT( slotUpdateAttachActions() ) );
00392   connect( mAtmListView,
00393            SIGNAL( attachmentDeleted() ),
00394            SLOT( slotAttachRemove() ) );
00395   connect( mAtmListView,
00396            SIGNAL( dragStarted() ),
00397            SLOT( slotAttachmentDragStarted() ) );
00398   mAttachMenu = 0;
00399 
00400   readConfig();
00401   setupStatusBar();
00402   setupActions();
00403   setupEditor();
00404   slotUpdateSignatureAndEncrypionStateIndicators();
00405 
00406   applyMainWindowSettings(KMKernel::config(), "Composer");
00407 
00408   connect( mEdtSubject, SIGNAL( subjectTextSpellChecked() ),
00409            SLOT( slotSubjectTextSpellChecked() ) );
00410   connect(mEdtSubject,SIGNAL(textChanged(const QString&)),
00411           SLOT(slotUpdWinTitle(const QString&)));
00412   connect(mIdentity,SIGNAL(identityChanged(uint)),
00413           SLOT(slotIdentityChanged(uint)));
00414   connect( kmkernel->identityManager(), SIGNAL(changed(uint)),
00415           SLOT(slotIdentityChanged(uint)));
00416 
00417   connect(mEdtFrom,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
00418           SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
00419   connect(kmkernel->folderMgr(),SIGNAL(folderRemoved(KMFolder*)),
00420                                   SLOT(slotFolderRemoved(KMFolder*)));
00421   connect(kmkernel->imapFolderMgr(),SIGNAL(folderRemoved(KMFolder*)),
00422                                   SLOT(slotFolderRemoved(KMFolder*)));
00423   connect(kmkernel->dimapFolderMgr(),SIGNAL(folderRemoved(KMFolder*)),
00424                                   SLOT(slotFolderRemoved(KMFolder*)));
00425   connect( kmkernel, SIGNAL( configChanged() ),
00426            this, SLOT( slotConfigChanged() ) );
00427 
00428   connect (mEditor, SIGNAL (spellcheck_done(int)),
00429     this, SLOT (slotSpellcheckDone (int)));
00430   connect (mEditor, SIGNAL( attachPNGImageData(const QByteArray &) ),
00431     this, SLOT ( slotAttachPNGImageData(const QByteArray &) ) );
00432   connect (mEditor, SIGNAL( focusChanged(bool) ),
00433     this, SLOT (editorFocusChanged(bool)) );
00434 
00435   mMainWidget->resize(480,510);
00436   setCentralWidget(mMainWidget);
00437   rethinkFields();
00438 
00439   if ( !mClassicalRecipients ) {
00440     // This is ugly, but if it isn't called the line edits in the recipients
00441     // editor aren't wide enough until the first resize event comes.
00442     rethinkFields();
00443   }
00444 
00445   if ( GlobalSettings::self()->useExternalEditor() ) {
00446     mEditor->setUseExternalEditor(true);
00447     mEditor->setExternalEditorPath( GlobalSettings::self()->externalEditor() );
00448   }
00449 
00450   initAutoSave();
00451   slotUpdateSignatureActions();
00452   mMsg = 0;
00453   if (aMsg)
00454     setMsg(aMsg);
00455   fontChanged( mEditor->currentFont() ); // set toolbar buttons to correct values
00456 
00457   mDone = true;
00458 }
00459 
00460 //-----------------------------------------------------------------------------
00461 KMComposeWin::~KMComposeWin()
00462 {
00463   writeConfig();
00464   if (mFolder && mMsg)
00465   {
00466     mAutoDeleteMsg = false;
00467     mFolder->addMsg(mMsg);
00468     // Ensure that the message is correctly and fully parsed
00469     mFolder->unGetMsg( mFolder->count() - 1 );
00470   }
00471   if (mAutoDeleteMsg) {
00472     delete mMsg;
00473     mMsg = 0;
00474   }
00475   QMap<KIO::Job*, atmLoadData>::Iterator it = mMapAtmLoadData.begin();
00476   while ( it != mMapAtmLoadData.end() )
00477   {
00478     KIO::Job *job = it.key();
00479     mMapAtmLoadData.remove( it );
00480     job->kill();
00481     it = mMapAtmLoadData.begin();
00482   }
00483   deleteAll( mComposedMessages );
00484 
00485   for ( std::set<KTempDir*>::iterator it = mTempDirs.begin() ; it != mTempDirs.end() ; ++it ) {
00486       delete *it;
00487   }
00488 }
00489 
00490 void KMComposeWin::setAutoDeleteWindow( bool f )
00491 {
00492   if ( f )
00493     setWFlags( getWFlags() | WDestructiveClose );
00494   else
00495     setWFlags( getWFlags() & ~WDestructiveClose );
00496 }
00497 
00498 //-----------------------------------------------------------------------------
00499 void KMComposeWin::send(int how)
00500 {
00501   switch (how) {
00502     case 1:
00503       slotSendNow();
00504       break;
00505     default:
00506     case 0:
00507       // TODO: find out, what the default send method is and send it this way
00508     case 2:
00509       slotSendLater();
00510       break;
00511   }
00512 }
00513 
00514 //-----------------------------------------------------------------------------
00515 void KMComposeWin::addAttachmentsAndSend(const KURL::List &urls, const QString &/*comment*/, int how)
00516 {
00517   if (urls.isEmpty())
00518   {
00519     send(how);
00520     return;
00521   }
00522   mAttachFilesSend = how;
00523   mAttachFilesPending = urls;
00524   connect(this, SIGNAL(attachmentAdded(const KURL&, bool)), SLOT(slotAttachedFile(const KURL&)));
00525   for( KURL::List::ConstIterator itr = urls.begin(); itr != urls.end(); ++itr ) {
00526     if (!addAttach( *itr ))
00527       mAttachFilesPending.remove(mAttachFilesPending.find(*itr)); // only remove one copy of the url
00528   }
00529 
00530   if (mAttachFilesPending.isEmpty() && mAttachFilesSend == how)
00531   {
00532     send(mAttachFilesSend);
00533     mAttachFilesSend = -1;
00534   }
00535 }
00536 
00537 void KMComposeWin::slotAttachedFile(const KURL &url)
00538 {
00539   if (mAttachFilesPending.isEmpty())
00540     return;
00541   mAttachFilesPending.remove(mAttachFilesPending.find(url)); // only remove one copy of url
00542   if (mAttachFilesPending.isEmpty())
00543   {
00544     send(mAttachFilesSend);
00545     mAttachFilesSend = -1;
00546   }
00547 }
00548 
00549 //-----------------------------------------------------------------------------
00550 void KMComposeWin::addAttachment(KURL url,QString /*comment*/)
00551 {
00552   addAttach(url);
00553 }
00554 
00555 //-----------------------------------------------------------------------------
00556 void KMComposeWin::addAttachment(const QString &name,
00557                                  const QCString &/*cte*/,
00558                                  const QByteArray &data,
00559                                  const QCString &type,
00560                                  const QCString &subType,
00561                                  const QCString &paramAttr,
00562                                  const QString &paramValue,
00563                                  const QCString &contDisp)
00564 {
00565   if (!data.isEmpty()) {
00566     KMMessagePart *msgPart = new KMMessagePart;
00567     msgPart->setName(name);
00568     if( type == "message" && subType == "rfc822" ) {
00569        msgPart->setMessageBody( data );
00570     } else {
00571        QValueList<int> dummy;
00572        msgPart->setBodyAndGuessCte(data, dummy,
00573               kmkernel->msgSender()->sendQuotedPrintable());
00574     }
00575     msgPart->setTypeStr(type);
00576     msgPart->setSubtypeStr(subType);
00577     msgPart->setParameter(paramAttr,paramValue);
00578     msgPart->setContentDisposition(contDisp);
00579     addAttach(msgPart);
00580   }
00581 }
00582 
00583 //-----------------------------------------------------------------------------
00584 void KMComposeWin::slotAttachPNGImageData(const QByteArray &image)
00585 {
00586   bool ok;
00587 
00588   QString attName = KInputDialog::getText( "KMail", i18n("Name of the attachment:"), QString::null, &ok, this );
00589   if ( !ok )
00590     return;
00591 
00592   if ( !attName.lower().endsWith(".png") ) attName += ".png";
00593 
00594   addAttachment( attName, "base64", image, "image", "png", QCString(), QString(), QCString() );
00595 }
00596 
00597 //-----------------------------------------------------------------------------
00598 void KMComposeWin::setBody(QString body)
00599 {
00600   mEditor->setText(body);
00601 }
00602 
00603 //-----------------------------------------------------------------------------
00604 bool KMComposeWin::event(QEvent *e)
00605 {
00606   if (e->type() == QEvent::ApplicationPaletteChange)
00607   {
00608      readColorConfig();
00609   }
00610   return KMail::Composer::event(e);
00611 }
00612 
00613 
00614 //-----------------------------------------------------------------------------
00615 void KMComposeWin::readColorConfig(void)
00616 {
00617   if ( GlobalSettings::self()->useDefaultColors() ) {
00618     mForeColor = QColor(kapp->palette().active().text());
00619     mBackColor = QColor(kapp->palette().active().base());
00620   } else {
00621     mForeColor = GlobalSettings::self()->foregroundColor();
00622     mBackColor = GlobalSettings::self()->backgroundColor();
00623   }
00624 
00625   // Color setup
00626   mPalette = kapp->palette();
00627   QColorGroup cgrp  = mPalette.active();
00628   cgrp.setColor( QColorGroup::Base, mBackColor);
00629   cgrp.setColor( QColorGroup::Text, mForeColor);
00630   mPalette.setDisabled(cgrp);
00631   mPalette.setActive(cgrp);
00632   mPalette.setInactive(cgrp);
00633 
00634   mEdtFrom->setPalette(mPalette);
00635   mEdtReplyTo->setPalette(mPalette);
00636   if ( mClassicalRecipients ) {
00637     mEdtTo->setPalette(mPalette);
00638     mEdtCc->setPalette(mPalette);
00639     mEdtBcc->setPalette(mPalette);
00640   }
00641   mEdtSubject->setPalette(mPalette);
00642   mTransport->setPalette(mPalette);
00643   mEditor->setPalette(mPalette);
00644   mFcc->setPalette(mPalette);
00645 }
00646 
00647 //-----------------------------------------------------------------------------
00648 void KMComposeWin::readConfig( bool reload /* = false */ )
00649 {
00650   mDefCharset = KMMessage::defaultCharset();
00651   mBtnIdentity->setChecked( GlobalSettings::self()->stickyIdentity() );
00652   if (mBtnIdentity->isChecked()) {
00653     mId = (GlobalSettings::self()->previousIdentity()!=0) ?
00654            GlobalSettings::self()->previousIdentity() : mId;
00655   }
00656   mBtnFcc->setChecked( GlobalSettings::self()->stickyFcc() );
00657   mBtnTransport->setChecked( GlobalSettings::self()->stickyTransport() );
00658   QStringList transportHistory = GlobalSettings::self()->transportHistory();
00659   QString currentTransport = GlobalSettings::self()->currentTransport();
00660 
00661   mEdtFrom->setCompletionMode( (KGlobalSettings::Completion)GlobalSettings::self()->completionMode() );
00662   mEdtReplyTo->setCompletionMode( (KGlobalSettings::Completion)GlobalSettings::self()->completionMode() );
00663   if ( mClassicalRecipients ) {
00664     mEdtTo->setCompletionMode( (KGlobalSettings::Completion)GlobalSettings::self()->completionMode() );
00665     mEdtCc->setCompletionMode( (KGlobalSettings::Completion)GlobalSettings::self()->completionMode() );
00666     mEdtBcc->setCompletionMode( (KGlobalSettings::Completion)GlobalSettings::self()->completionMode() );
00667   }
00668   else
00669     mRecipientsEditor->setCompletionMode( (KGlobalSettings::Completion)GlobalSettings::self()->completionMode() );
00670 
00671   readColorConfig();
00672 
00673   if ( GlobalSettings::self()->useDefaultFonts() ) {
00674     mBodyFont = KGlobalSettings::generalFont();
00675     mFixedFont = KGlobalSettings::fixedFont();
00676   } else {
00677     mBodyFont = GlobalSettings::self()->composerFont();
00678     mFixedFont = GlobalSettings::self()->fixedFont();
00679   }
00680 
00681   slotUpdateFont();
00682   mEdtFrom->setFont(mBodyFont);
00683   mEdtReplyTo->setFont(mBodyFont);
00684   if ( mClassicalRecipients ) {
00685     mEdtTo->setFont(mBodyFont);
00686     mEdtCc->setFont(mBodyFont);
00687     mEdtBcc->setFont(mBodyFont);
00688   }
00689   mEdtSubject->setFont(mBodyFont);
00690 
00691   if ( !reload ) {
00692     QSize siz = GlobalSettings::self()->composerSize();
00693     if (siz.width() < 200) siz.setWidth(200);
00694     if (siz.height() < 200) siz.setHeight(200);
00695     resize(siz);
00696 
00697     if ( !GlobalSettings::self()->snippetSplitterPosition().isEmpty() ) {
00698       mSnippetSplitter->setSizes( GlobalSettings::self()->snippetSplitterPosition() );
00699     } else {
00700       QValueList<int> defaults;
00701       defaults << (int)(width() * 0.8) << (int)(width() * 0.2);
00702       mSnippetSplitter->setSizes( defaults );
00703     }
00704   }
00705 
00706   mIdentity->setCurrentIdentity( mId );
00707 
00708   kdDebug(5006) << "KMComposeWin::readConfig. " << mIdentity->currentIdentityName() << endl;
00709   const KPIM::Identity & ident =
00710     kmkernel->identityManager()->identityForUoid( mIdentity->currentIdentity() );
00711 
00712   mDictionaryCombo->setCurrentByDictionary( ident.dictionary() );
00713 
00714   mTransport->clear();
00715   mTransport->insertStringList( KMTransportInfo::availableTransports() );
00716   while ( transportHistory.count() > (uint)GlobalSettings::self()->maxTransportEntries() )
00717     transportHistory.remove( transportHistory.last() );
00718   mTransport->insertStringList( transportHistory );
00719   mTransport->setCurrentText( GlobalSettings::self()->defaultTransport() );
00720   if ( mBtnTransport->isChecked() ) {
00721     setTransport( currentTransport );
00722   }
00723 
00724   QString fccName = "";
00725   if ( mBtnFcc->isChecked() ) {
00726     fccName = GlobalSettings::self()->previousFcc();
00727   } else if ( !ident.fcc().isEmpty() ) {
00728       fccName = ident.fcc();
00729   }
00730 
00731   setFcc( fccName );
00732 }
00733 
00734 //-----------------------------------------------------------------------------
00735 void KMComposeWin::writeConfig(void)
00736 {
00737   GlobalSettings::self()->setHeaders( mShowHeaders );
00738   GlobalSettings::self()->setStickyTransport( mBtnTransport->isChecked() );
00739   GlobalSettings::self()->setStickyIdentity( mBtnIdentity->isChecked() );
00740   GlobalSettings::self()->setStickyFcc( mBtnFcc->isChecked() );
00741   GlobalSettings::self()->setPreviousIdentity( mIdentity->currentIdentity() );
00742   GlobalSettings::self()->setCurrentTransport( mTransport->currentText() );
00743   GlobalSettings::self()->setPreviousFcc( mFcc->getFolder()->idString() );
00744   GlobalSettings::self()->setAutoSpellChecking(
00745                         mAutoSpellCheckingAction->isChecked() );
00746   QStringList transportHistory = GlobalSettings::self()->transportHistory();
00747   transportHistory.remove(mTransport->currentText());
00748     if (KMTransportInfo::availableTransports().findIndex(mTransport
00749     ->currentText()) == -1) {
00750       transportHistory.prepend(mTransport->currentText());
00751   }
00752   GlobalSettings::self()->setTransportHistory( transportHistory );
00753   GlobalSettings::self()->setUseFixedFont( mFixedFontAction->isChecked() );
00754   GlobalSettings::self()->setUseHtmlMarkup( mHtmlMarkup );
00755   GlobalSettings::self()->setComposerSize( size() );
00756   GlobalSettings::self()->setShowSnippetManager( mSnippetAction->isChecked() );
00757 
00758   KConfigGroupSaver saver( KMKernel::config(), "Geometry" );
00759   saveMainWindowSettings( KMKernel::config(), "Composer" );
00760   GlobalSettings::setSnippetSplitterPosition( mSnippetSplitter->sizes() );
00761 
00762   // make sure config changes are written to disk, cf. bug 127538
00763   GlobalSettings::self()->writeConfig();
00764 }
00765 
00766 //-----------------------------------------------------------------------------
00767 void KMComposeWin::autoSaveMessage()
00768 {
00769   kdDebug(5006) << k_funcinfo << endl;
00770   if ( !mMsg || mComposer || mAutoSaveFilename.isEmpty() )
00771     return;
00772   kdDebug(5006) << k_funcinfo << "autosaving message" << endl;
00773 
00774   if ( mAutoSaveTimer )
00775     mAutoSaveTimer->stop();
00776 
00777   connect( this, SIGNAL( applyChangesDone( bool ) ),
00778            this, SLOT( slotContinueAutoSave() ) );
00779   // This method is called when KMail crashed, so don't try signing/encryption
00780   // and don't disable controls because it is also called from a timer and
00781   // then the disabling is distracting.
00782   applyChanges( true, true );
00783 
00784   // Don't continue before the applyChanges is done!
00785 }
00786 
00787 void KMComposeWin::slotContinueAutoSave()
00788 {
00789   disconnect( this, SIGNAL( applyChangesDone( bool ) ),
00790               this, SLOT( slotContinueAutoSave() ) );
00791 
00792   // Ok, it's done now - continue dead letter saving
00793   if ( mComposedMessages.isEmpty() ) {
00794     kdDebug(5006) << "Composing the message failed." << endl;
00795     return;
00796   }
00797   KMMessage *msg = mComposedMessages.first();
00798   if ( !msg ) // a bit of extra defensiveness
00799     return;
00800 
00801   kdDebug(5006) << k_funcinfo << "opening autoSaveFile " << mAutoSaveFilename
00802                 << endl;
00803   const QString filename =
00804     KMKernel::localDataPath() + "autosave/cur/" + mAutoSaveFilename;
00805   KSaveFile autoSaveFile( filename, 0600 );
00806   int status = autoSaveFile.status();
00807   kdDebug(5006) << k_funcinfo << "autoSaveFile.status() = " << status << endl;
00808   if ( status == 0 ) { // no error
00809     kdDebug(5006) << "autosaving message in " << filename << endl;
00810     int fd = autoSaveFile.handle();
00811     const DwString& msgStr = msg->asDwString();
00812     if ( ::write( fd, msgStr.data(), msgStr.length() ) == -1 )
00813       status = errno;
00814   }
00815   if ( status == 0 ) {
00816     kdDebug(5006) << k_funcinfo << "closing autoSaveFile" << endl;
00817     autoSaveFile.close();
00818     mLastAutoSaveErrno = 0;
00819   }
00820   else {
00821     kdDebug(5006) << k_funcinfo << "autosaving failed" << endl;
00822     autoSaveFile.abort();
00823     if ( status != mLastAutoSaveErrno ) {
00824       // don't show the same error message twice
00825       KMessageBox::queuedMessageBox( 0, KMessageBox::Sorry,
00826                                      i18n("Autosaving the message as %1 "
00827                                           "failed.\n"
00828                                           "Reason: %2" )
00829                                      .arg( filename, strerror( status ) ),
00830                                      i18n("Autosaving Failed") );
00831       mLastAutoSaveErrno = status;
00832     }
00833   }
00834 
00835   if ( autoSaveInterval() > 0 )
00836     updateAutoSave();
00837 }
00838 
00839 //-----------------------------------------------------------------------------
00840 void KMComposeWin::slotView(void)
00841 {
00842   if (!mDone)
00843     return; // otherwise called from rethinkFields during the construction
00844             // which is not the intended behavior
00845   int id;
00846 
00847   //This sucks awfully, but no, I cannot get an activated(int id) from
00848   // actionContainer()
00849   if (!sender()->isA("KToggleAction"))
00850     return;
00851   KToggleAction *act = (KToggleAction *) sender();
00852 
00853   if (act == mAllFieldsAction)
00854     id = 0;
00855   else if (act == mIdentityAction)
00856     id = HDR_IDENTITY;
00857   else if (act == mTransportAction)
00858     id = HDR_TRANSPORT;
00859   else if (act == mFromAction)
00860     id = HDR_FROM;
00861   else if (act == mReplyToAction)
00862     id = HDR_REPLY_TO;
00863   else if (act == mToAction)
00864     id = HDR_TO;
00865   else if (act == mCcAction)
00866     id = HDR_CC;
00867   else  if (act == mBccAction)
00868     id = HDR_BCC;
00869   else if (act == mSubjectAction)
00870     id = HDR_SUBJECT;
00871   else if (act == mFccAction)
00872     id = HDR_FCC;
00873   else if ( act == mDictionaryAction )
00874     id = HDR_DICTIONARY;
00875   else
00876    {
00877      id = 0;
00878      kdDebug(5006) << "Something is wrong (Oh, yeah?)" << endl;
00879      return;
00880    }
00881 
00882   // sanders There's a bug here this logic doesn't work if no
00883   // fields are shown and then show all fields is selected.
00884   // Instead of all fields being shown none are.
00885   if (!act->isChecked())
00886   {
00887     // hide header
00888     if (id > 0) mShowHeaders = mShowHeaders & ~id;
00889     else mShowHeaders = abs(mShowHeaders);
00890   }
00891   else
00892   {
00893     // show header
00894     if (id > 0) mShowHeaders |= id;
00895     else mShowHeaders = -abs(mShowHeaders);
00896   }
00897   rethinkFields(true);
00898 }
00899 
00900 int KMComposeWin::calcColumnWidth(int which, long allShowing, int width)
00901 {
00902   if ( (allShowing & which) == 0 )
00903     return width;
00904 
00905   QLabel *w;
00906   if ( which == HDR_IDENTITY )
00907     w = mLblIdentity;
00908   else if ( which == HDR_DICTIONARY )
00909     w = mDictionaryLabel;
00910   else if ( which == HDR_FCC )
00911     w = mLblFcc;
00912   else if ( which == HDR_TRANSPORT )
00913     w = mLblTransport;
00914   else if ( which == HDR_FROM )
00915     w = mLblFrom;
00916   else if ( which == HDR_REPLY_TO )
00917     w = mLblReplyTo;
00918   else if ( which == HDR_SUBJECT )
00919     w = mLblSubject;
00920   else
00921     return width;
00922 
00923   w->setBuddy( mEditor ); // set dummy so we don't calculate width of '&' for this label.
00924   w->adjustSize();
00925   w->show();
00926   return QMAX( width, w->sizeHint().width() );
00927 }
00928 
00929 void KMComposeWin::rethinkFields(bool fromSlot)
00930 {
00931   //This sucks even more but again no ids. sorry (sven)
00932   int mask, row, numRows;
00933   long showHeaders;
00934 
00935   if (mShowHeaders < 0)
00936     showHeaders = HDR_ALL;
00937   else
00938     showHeaders = mShowHeaders;
00939 
00940   for (mask=1,mNumHeaders=0; mask<=showHeaders; mask<<=1)
00941     if ((showHeaders&mask) != 0) mNumHeaders++;
00942 
00943   numRows = mNumHeaders + 1;
00944 
00945   delete mGrid;
00946 
00947   mGrid = new QGridLayout( mHeadersArea, numRows, 3, KDialogBase::marginHint()/2, KDialogBase::spacingHint());
00948   mGrid->setColStretch(0, 1);
00949   mGrid->setColStretch(1, 100);
00950   mGrid->setColStretch(2, 1);
00951   mGrid->setRowStretch(mNumHeaders, 100);
00952 
00953   row = 0;
00954   kdDebug(5006) << "KMComposeWin::rethinkFields" << endl;
00955   if (mRecipientsEditor)
00956     mLabelWidth = mRecipientsEditor->setFirstColumnWidth( 0 );
00957   mLabelWidth = calcColumnWidth( HDR_IDENTITY, showHeaders, mLabelWidth );
00958   mLabelWidth = calcColumnWidth( HDR_DICTIONARY, showHeaders, mLabelWidth );
00959   mLabelWidth = calcColumnWidth( HDR_FCC, showHeaders, mLabelWidth );
00960   mLabelWidth = calcColumnWidth( HDR_TRANSPORT, showHeaders, mLabelWidth );
00961   mLabelWidth = calcColumnWidth( HDR_FROM, showHeaders, mLabelWidth );
00962   mLabelWidth = calcColumnWidth( HDR_REPLY_TO, showHeaders, mLabelWidth );
00963   mLabelWidth = calcColumnWidth( HDR_SUBJECT, showHeaders, mLabelWidth );
00964 
00965   if (!fromSlot) mAllFieldsAction->setChecked(showHeaders==HDR_ALL);
00966 
00967   if (!fromSlot) mIdentityAction->setChecked(abs(mShowHeaders)&HDR_IDENTITY);
00968   rethinkHeaderLine(showHeaders,HDR_IDENTITY, row, i18n("&Identity:"),
00969                     mLblIdentity, mIdentity, mBtnIdentity);
00970 
00971   if (!fromSlot) mDictionaryAction->setChecked(abs(mShowHeaders)&HDR_DICTIONARY);
00972   rethinkHeaderLine(showHeaders,HDR_DICTIONARY, row, i18n("&Dictionary:"),
00973                     mDictionaryLabel, mDictionaryCombo, 0 );
00974 
00975   if (!fromSlot) mFccAction->setChecked(abs(mShowHeaders)&HDR_FCC);
00976   rethinkHeaderLine(showHeaders,HDR_FCC, row, i18n("&Sent-Mail folder:"),
00977                     mLblFcc, mFcc, mBtnFcc);
00978 
00979   if (!fromSlot) mTransportAction->setChecked(abs(mShowHeaders)&HDR_TRANSPORT);
00980   rethinkHeaderLine(showHeaders,HDR_TRANSPORT, row, i18n("&Mail transport:"),
00981                     mLblTransport, mTransport, mBtnTransport);
00982 
00983   if (!fromSlot) mFromAction->setChecked(abs(mShowHeaders)&HDR_FROM);
00984   rethinkHeaderLine(showHeaders,HDR_FROM, row, i18n("sender address field", "&From:"),
00985                     mLblFrom, mEdtFrom /*, mBtnFrom */ );
00986 
00987   QWidget *prevFocus = mEdtFrom;
00988 
00989   if (!fromSlot) mReplyToAction->setChecked(abs(mShowHeaders)&HDR_REPLY_TO);
00990   rethinkHeaderLine(showHeaders,HDR_REPLY_TO,row,i18n("&Reply to:"),
00991                   mLblReplyTo, mEdtReplyTo, 0);
00992   if ( showHeaders & HDR_REPLY_TO ) {
00993     prevFocus = connectFocusMoving( prevFocus, mEdtReplyTo );
00994   }
00995 
00996   if ( mClassicalRecipients ) {
00997     if (!fromSlot) mToAction->setChecked(abs(mShowHeaders)&HDR_TO);
00998     rethinkHeaderLine(showHeaders, HDR_TO, row, i18n("recipient address field", "&To:"),
00999                     mLblTo, mEdtTo, mBtnTo,
01000                     i18n("Primary Recipients"),
01001                     i18n("<qt>The email addresses you put "
01002                          "in this field receive a copy of the email.</qt>"));
01003     if ( showHeaders & HDR_TO ) {
01004       prevFocus = connectFocusMoving( prevFocus, mEdtTo );
01005     }
01006 
01007     if (!fromSlot) mCcAction->setChecked(abs(mShowHeaders)&HDR_CC);
01008     rethinkHeaderLine(showHeaders, HDR_CC, row, i18n("&Copy to (CC):"),
01009                     mLblCc, mEdtCc, mBtnCc,
01010                     i18n("Additional Recipients"),
01011                     i18n("<qt>The email addresses you put "
01012                          "in this field receive a copy of the email. "
01013                          "Technically it is the same thing as putting all the "
01014                          "addresses in the <b>To:</b> field but differs in "
01015                          "that it usually symbolises the receiver of the "
01016                          "Carbon Copy (CC) is a listener, not the main "
01017                          "recipient.</qt>"));
01018     if ( showHeaders & HDR_CC ) {
01019       prevFocus = connectFocusMoving( prevFocus, mEdtCc );
01020     }
01021 
01022     if (!fromSlot) mBccAction->setChecked(abs(mShowHeaders)&HDR_BCC);
01023     rethinkHeaderLine(showHeaders,HDR_BCC, row, i18n("&Blind copy to (BCC):"),
01024                     mLblBcc, mEdtBcc, mBtnBcc,
01025                     i18n("Hidden Recipients"),
01026                     i18n("<qt>Essentially the same thing "
01027                          "as the <b>Copy To:</b> field but differs in that "
01028                          "all other recipients do not see who receives a "
01029                          "blind copy.</qt>"));
01030     if ( showHeaders & HDR_BCC ) {
01031       prevFocus = connectFocusMoving( prevFocus, mEdtBcc );
01032     }
01033   } else {
01034     mGrid->addMultiCellWidget( mRecipientsEditor, row, row, 0, 2 );
01035     ++row;
01036 
01037     if ( showHeaders & HDR_REPLY_TO ) {
01038       connect( mEdtReplyTo, SIGNAL( focusDown() ), mRecipientsEditor,
01039         SLOT( setFocusTop() ) );
01040     } else {
01041     connect( mEdtFrom, SIGNAL( focusDown() ), mRecipientsEditor,
01042       SLOT( setFocusTop() ) );
01043     }
01044     if ( showHeaders & HDR_REPLY_TO ) {
01045       connect( mRecipientsEditor, SIGNAL( focusUp() ), mEdtReplyTo, SLOT( setFocus() ) );
01046     } else {
01047       connect( mRecipientsEditor, SIGNAL( focusUp() ), mEdtFrom, SLOT( setFocus() ) );
01048     }
01049 
01050     connect( mRecipientsEditor, SIGNAL( focusDown() ), mEdtSubject,
01051       SLOT( setFocus() ) );
01052     connect( mEdtSubject, SIGNAL( focusUp() ), mRecipientsEditor,
01053       SLOT( setFocusBottom() ) );
01054 
01055     prevFocus = mRecipientsEditor;
01056   }
01057   if (!fromSlot) mSubjectAction->setChecked(abs(mShowHeaders)&HDR_SUBJECT);
01058   rethinkHeaderLine(showHeaders,HDR_SUBJECT, row, i18n("S&ubject:"),
01059                     mLblSubject, mEdtSubject);
01060   connectFocusMoving( mEdtSubject, mEditor );
01061 
01062   assert(row<=mNumHeaders);
01063 
01064 
01065   if( !mAtmList.isEmpty() )
01066     mAtmListView->show();
01067   else
01068     mAtmListView->hide();
01069   resize(this->size());
01070   repaint();
01071 
01072   mHeadersArea->setMaximumHeight( mHeadersArea->sizeHint().height() );
01073   mGrid->activate();
01074   mHeadersArea->show();
01075 
01076   slotUpdateAttachActions();
01077   mIdentityAction->setEnabled(!mAllFieldsAction->isChecked());
01078   mDictionaryAction->setEnabled( !mAllFieldsAction->isChecked() );
01079   mTransportAction->setEnabled(!mAllFieldsAction->isChecked());
01080   mFromAction->setEnabled(!mAllFieldsAction->isChecked());
01081   if ( mReplyToAction ) mReplyToAction->setEnabled(!mAllFieldsAction->isChecked());
01082   if ( mToAction ) mToAction->setEnabled(!mAllFieldsAction->isChecked());
01083   if ( mCcAction ) mCcAction->setEnabled(!mAllFieldsAction->isChecked());
01084   if ( mBccAction ) mBccAction->setEnabled(!mAllFieldsAction->isChecked());
01085   mFccAction->setEnabled(!mAllFieldsAction->isChecked());
01086   mSubjectAction->setEnabled(!mAllFieldsAction->isChecked());
01087   if (mRecipientsEditor)
01088     mRecipientsEditor->setFirstColumnWidth( mLabelWidth );
01089 }
01090 
01091 QWidget *KMComposeWin::connectFocusMoving( QWidget *prev, QWidget *next )
01092 {
01093   connect( prev, SIGNAL( focusDown() ), next, SLOT( setFocus() ) );
01094   connect( next, SIGNAL( focusUp() ), prev, SLOT( setFocus() ) );
01095 
01096   return next;
01097 }
01098 
01099 //-----------------------------------------------------------------------------
01100 void KMComposeWin::rethinkHeaderLine(int aValue, int aMask, int& aRow,
01101                                      const QString &aLabelStr, QLabel* aLbl,
01102                                      QLineEdit* aEdt, QPushButton* aBtn,
01103                                      const QString &toolTip, const QString &whatsThis )
01104 {
01105   if (aValue & aMask)
01106   {
01107     aLbl->setText(aLabelStr);
01108     if ( !toolTip.isEmpty() )
01109       QToolTip::add( aLbl, toolTip );
01110     if ( !whatsThis.isEmpty() )
01111       QWhatsThis::add( aLbl, whatsThis );
01112     aLbl->setFixedWidth( mLabelWidth );
01113     aLbl->setBuddy(aEdt);
01114     mGrid->addWidget(aLbl, aRow, 0);
01115     aEdt->setBackgroundColor( mBackColor );
01116     aEdt->show();
01117 
01118     if (aBtn) {
01119       mGrid->addWidget(aEdt, aRow, 1);
01120 
01121       mGrid->addWidget(aBtn, aRow, 2);
01122       aBtn->show();
01123     } else {
01124       mGrid->addMultiCellWidget(aEdt, aRow, aRow, 1, 2 );
01125     }
01126     aRow++;
01127   }
01128   else
01129   {
01130     aLbl->hide();
01131     aEdt->hide();
01132     if (aBtn) aBtn->hide();
01133   }
01134 }
01135 
01136 //-----------------------------------------------------------------------------
01137 void KMComposeWin::rethinkHeaderLine(int aValue, int aMask, int& aRow,
01138                                      const QString &aLabelStr, QLabel* aLbl,
01139                                      QComboBox* aCbx, QCheckBox* aChk)
01140 {
01141   if (aValue & aMask)
01142   {
01143     aLbl->setText(aLabelStr);
01144     aLbl->adjustSize();
01145     aLbl->resize((int)aLbl->sizeHint().width(),aLbl->sizeHint().height() + 6);
01146     aLbl->setMinimumSize(aLbl->size());
01147     aLbl->show();
01148     aLbl->setBuddy(aCbx);
01149     mGrid->addWidget(aLbl, aRow, 0);
01150     aCbx->show();
01151     aCbx->setMinimumSize(100, aLbl->height()+2);
01152 
01153     mGrid->addWidget(aCbx, aRow, 1);
01154     if ( aChk ) {
01155       mGrid->addWidget(aChk, aRow, 2);
01156       aChk->setFixedSize(aChk->sizeHint().width(), aLbl->height());
01157       aChk->show();
01158     }
01159     aRow++;
01160   }
01161   else
01162   {
01163     aLbl->hide();
01164     aCbx->hide();
01165     if ( aChk )
01166       aChk->hide();
01167   }
01168 }
01169 
01170 //-----------------------------------------------------------------------------
01171 void KMComposeWin::getTransportMenu()
01172 {
01173   QStringList availTransports;
01174 
01175   mActNowMenu->clear();
01176   mActLaterMenu->clear();
01177   availTransports = KMail::TransportManager::transportNames();
01178   QStringList::Iterator it;
01179   int id = 0;
01180   for(it = availTransports.begin(); it != availTransports.end() ; ++it, id++)
01181   {
01182     mActNowMenu->insertItem((*it).replace("&", "&&"), id);
01183     mActLaterMenu->insertItem((*it).replace("&", "&&"), id);
01184   }
01185 }
01186 
01187 
01188 //-----------------------------------------------------------------------------
01189 void KMComposeWin::setupActions(void)
01190 {
01191   KActionMenu *actActionNowMenu, *actActionLaterMenu;
01192 
01193   if (kmkernel->msgSender()->sendImmediate()) //default == send now?
01194   {
01195     //default = send now, alternative = queue
01196     ( void )  new KAction( i18n("&Send Mail"), "mail_send", CTRL+Key_Return,
01197                         this, SLOT(slotSendNow()), actionCollection(),"send_default");
01198 
01199     // FIXME: change to mail_send_via icon when this exits.
01200     actActionNowMenu =  new KActionMenu (i18n("&Send Mail Via"), "mail_send",
01201             actionCollection(), "send_default_via" );
01202 
01203     (void) new KAction (i18n("Send &Later"), "queue", 0, this,
01204             SLOT(slotSendLater()), actionCollection(),"send_alternative");
01205     actActionLaterMenu = new KActionMenu (i18n("Send &Later Via"), "queue",
01206             actionCollection(), "send_alternative_via" );
01207 
01208   }
01209   else //no, default = send later
01210   {
01211     //default = queue, alternative = send now
01212     (void) new KAction (i18n("Send &Later"), "queue",
01213                         CTRL+Key_Return,
01214                         this, SLOT(slotSendLater()), actionCollection(),"send_default");
01215     actActionLaterMenu = new KActionMenu (i18n("Send &Later Via"), "queue",
01216             actionCollection(), "send_default_via" );
01217 
01218    ( void )  new KAction( i18n("&Send Mail"), "mail_send", 0,
01219                         this, SLOT(slotSendNow()), actionCollection(),"send_alternative");
01220 
01221     // FIXME: change to mail_send_via icon when this exits.
01222     actActionNowMenu =  new KActionMenu (i18n("&Send Mail Via"), "mail_send",
01223             actionCollection(), "send_alternative_via" );
01224 
01225   }
01226 
01227   // needed for sending "default transport"
01228   actActionNowMenu->setDelayed(true);
01229   actActionLaterMenu->setDelayed(true);
01230 
01231   connect(  actActionNowMenu, SIGNAL(  activated() ), this,
01232             SLOT( slotSendNow() ) );
01233   connect(  actActionLaterMenu, SIGNAL(  activated() ), this,
01234             SLOT( slotSendLater() ) );
01235 
01236 
01237   mActNowMenu = actActionNowMenu->popupMenu();
01238   mActLaterMenu = actActionLaterMenu->popupMenu();
01239 
01240   connect(  mActNowMenu, SIGNAL(  activated( int ) ), this,
01241             SLOT( slotSendNowVia( int ) ) );
01242   connect(  mActNowMenu, SIGNAL(  aboutToShow() ), this,
01243             SLOT( getTransportMenu() ) );
01244 
01245   connect(  mActLaterMenu, SIGNAL(  activated( int ) ), this,
01246           SLOT( slotSendLaterVia( int ) ) );
01247   connect(  mActLaterMenu, SIGNAL(  aboutToShow() ), this,
01248           SLOT( getTransportMenu() ) );
01249 
01250 
01251 
01252 
01253   (void) new KAction (i18n("Save as &Draft"), "filesave", 0,
01254                       this, SLOT(slotSaveDraft()),
01255                       actionCollection(), "save_in_drafts");
01256   (void) new KAction (i18n("Save as &Template"), "filesave", 0,
01257                       this, SLOT(slotSaveTemplate()),
01258                       actionCollection(), "save_in_templates");
01259   (void) new KAction (i18n("&Insert File..."), "fileopen", 0,
01260                       this,  SLOT(slotInsertFile()),
01261                       actionCollection(), "insert_file");
01262   mRecentAction = new KRecentFilesAction (i18n("&Insert File Recent"),
01263               "fileopen", 0,
01264               this,  SLOT(slotInsertRecentFile(const KURL&)),
01265               actionCollection(), "insert_file_recent");
01266 
01267   mRecentAction->loadEntries( KMKernel::config() );
01268 
01269   (void) new KAction (i18n("&Address Book"), "contents",0,
01270                       this, SLOT(slotAddrBook()),
01271                       actionCollection(), "addressbook");
01272   (void) new KAction (i18n("&New Composer"), "mail_new",
01273                       KStdAccel::shortcut(KStdAccel::New),
01274                       this, SLOT(slotNewComposer()),
01275                       actionCollection(), "new_composer");
01276   (void) new KAction (i18n("New Main &Window"), "window_new", 0,
01277                       this, SLOT(slotNewMailReader()),
01278                       actionCollection(), "open_mailreader");
01279 
01280   if ( !mClassicalRecipients ) {
01281     new KAction( i18n("Select &Recipients..."), CTRL + Key_L, mRecipientsEditor,
01282       SLOT( selectRecipients() ), actionCollection(), "select_recipients" );
01283     new KAction( i18n("Save &Distribution List..."), 0, mRecipientsEditor,
01284       SLOT( saveDistributionList() ), actionCollection(),
01285       "save_distribution_list" );
01286   }
01287 
01288   //KStdAction::save(this, SLOT(), actionCollection(), "save_message");
01289   KStdAction::print (this, SLOT(slotPrint()), actionCollection());
01290   KStdAction::close (this, SLOT(slotClose()), actionCollection());
01291 
01292   KStdAction::undo (this, SLOT(slotUndo()), actionCollection());
01293   KStdAction::redo (this, SLOT(slotRedo()), actionCollection());
01294   KStdAction::cut (this, SLOT(slotCut()), actionCollection());
01295   KStdAction::copy (this, SLOT(slotCopy()), actionCollection());
01296   KStdAction::pasteText (this, SLOT(slotPasteClipboard()), actionCollection());
01297   KStdAction::selectAll (this, SLOT(slotMarkAll()), actionCollection());
01298 
01299   KStdAction::find (this, SLOT(slotFind()), actionCollection());
01300   KStdAction::findNext(this, SLOT(slotSearchAgain()), actionCollection());
01301 
01302   KStdAction::replace (this, SLOT(slotReplace()), actionCollection());
01303   KStdAction::spelling (this, SLOT(slotSpellcheck()), actionCollection(), "spellcheck");
01304 
01305   mPasteQuotation = new KAction (i18n("Pa&ste as Quotation"),0,this,SLOT( slotPasteClipboardAsQuotation()),
01306                       actionCollection(), "paste_quoted");
01307 
01308   (void) new KAction (i18n("Paste as Attac&hment"),0,this,SLOT( slotPasteClipboardAsAttachment()),
01309                       actionCollection(), "paste_att");
01310 
01311   KAction * addq = new KAction(i18n("Add &Quote Characters"), 0, this,
01312               SLOT(slotAddQuotes()), actionCollection(), "tools_quote");
01313   connect( mEditor, SIGNAL(selectionAvailable(bool)),
01314            addq, SLOT(setEnabled(bool)) );
01315 
01316   KAction * remq = new KAction(i18n("Re&move Quote Characters"), 0, this,
01317               SLOT(slotRemoveQuotes()), actionCollection(), "tools_unquote");
01318   connect( mEditor, SIGNAL(selectionAvailable(bool)),
01319            remq, SLOT(setEnabled(bool)) );
01320 
01321 
01322   (void) new KAction (i18n("Cl&ean Spaces"), 0, this, SLOT(slotCleanSpace()),
01323                       actionCollection(), "clean_spaces");
01324 
01325   mFixedFontAction = new KToggleAction( i18n("Use Fi&xed Font"), 0, this,
01326                       SLOT(slotUpdateFont()), actionCollection(), "toggle_fixedfont" );
01327   mFixedFontAction->setChecked( GlobalSettings::self()->useFixedFont() );
01328 
01329   //these are checkable!!!
01330   mUrgentAction = new KToggleAction (i18n("&Urgent"), 0,
01331                                     actionCollection(),
01332                                     "urgent");
01333   mRequestMDNAction = new KToggleAction ( i18n("&Request Disposition Notification"), 0,
01334                                          actionCollection(),
01335                                          "options_request_mdn");
01336   mRequestMDNAction->setChecked(GlobalSettings::self()->requestMDN());
01337   //----- Message-Encoding Submenu
01338   mEncodingAction = new KSelectAction( i18n( "Se&t Encoding" ), "charset",
01339                                       0, this, SLOT(slotSetCharset() ),
01340                                       actionCollection(), "charsets" );
01341   mWordWrapAction = new KToggleAction (i18n("&Wordwrap"), 0,
01342                       actionCollection(), "wordwrap");
01343   mWordWrapAction->setChecked(GlobalSettings::self()->wordWrap());
01344   connect(mWordWrapAction, SIGNAL(toggled(bool)), SLOT(slotWordWrapToggled(bool)));
01345 
01346   mSnippetAction = new KToggleAction ( i18n("&Snippets"), 0,
01347                                        actionCollection(), "snippets");
01348   connect(mSnippetAction, SIGNAL(toggled(bool)), mSnippetWidget, SLOT(setShown(bool)) );
01349   mSnippetAction->setChecked( GlobalSettings::self()->showSnippetManager() );
01350 
01351   mAutoSpellCheckingAction =
01352     new KToggleAction( i18n( "&Automatic Spellchecking" ), "spellcheck", 0,
01353                        actionCollection(), "options_auto_spellchecking" );
01354   const bool spellChecking = GlobalSettings::self()->autoSpellChecking();
01355   mAutoSpellCheckingAction->setEnabled( !GlobalSettings::self()->useExternalEditor() );
01356   mAutoSpellCheckingAction->setChecked( !GlobalSettings::self()->useExternalEditor() && spellChecking );
01357   slotAutoSpellCheckingToggled( !GlobalSettings::self()->useExternalEditor() && spellChecking );
01358   connect( mAutoSpellCheckingAction, SIGNAL( toggled( bool ) ),
01359            this, SLOT( slotAutoSpellCheckingToggled( bool ) ) );
01360 
01361   QStringList encodings = KMMsgBase::supportedEncodings(true);
01362   encodings.prepend( i18n("Auto-Detect"));
01363   mEncodingAction->setItems( encodings );
01364   mEncodingAction->setCurrentItem( -1 );
01365 
01366   //these are checkable!!!
01367   markupAction = new KToggleAction (i18n("Formatting (HTML)"), 0, this,
01368                                     SLOT(slotToggleMarkup()),
01369                       actionCollection(), "html");
01370 
01371   mAllFieldsAction = new KToggleAction (i18n("&All Fields"), 0, this,
01372                                        SLOT(slotView()),
01373                                        actionCollection(), "show_all_fields");
01374   mIdentityAction = new KToggleAction (i18n("&Identity"), 0, this,
01375                                       SLOT(slotView()),
01376                                       actionCollection(), "show_identity");
01377   mDictionaryAction = new KToggleAction (i18n("&Dictionary"), 0, this,
01378                                          SLOT(slotView()),
01379                                          actionCollection(), "show_dictionary");
01380   mFccAction = new KToggleAction (i18n("&Sent-Mail Folder"), 0, this,
01381                                  SLOT(slotView()),
01382                                  actionCollection(), "show_fcc");
01383   mTransportAction = new KToggleAction (i18n("&Mail Transport"), 0, this,
01384                                       SLOT(slotView()),
01385                                       actionCollection(), "show_transport");
01386   mFromAction = new KToggleAction (i18n("&From"), 0, this,
01387                                   SLOT(slotView()),
01388                                   actionCollection(), "show_from");
01389   mReplyToAction = new KToggleAction (i18n("&Reply To"), 0, this,
01390                                        SLOT(slotView()),
01391                                        actionCollection(), "show_reply_to");
01392   if ( mClassicalRecipients ) {
01393     mToAction = new KToggleAction (i18n("&To"), 0, this,
01394                                   SLOT(slotView()),
01395                                   actionCollection(), "show_to");
01396     mCcAction = new KToggleAction (i18n("&CC"), 0, this,
01397                                   SLOT(slotView()),
01398                                   actionCollection(), "show_cc");
01399     mBccAction = new KToggleAction (i18n("&BCC"), 0, this,
01400                                    SLOT(slotView()),
01401                                    actionCollection(), "show_bcc");
01402   }
01403   mSubjectAction = new KToggleAction (i18n("S&ubject"), 0, this,
01404                                      SLOT(slotView()),
01405                                      actionCollection(), "show_subject");
01406   //end of checkable
01407 
01408   mAppendSignatureAction = new KAction (i18n("Append S&ignature"), 0, this,
01409                       SLOT(slotAppendSignature()),
01410                       actionCollection(), "append_signature");
01411   mPrependSignatureAction =  new KAction (i18n("Prepend S&ignature"), 0, this,
01412                       SLOT(slotPrependSignature()),
01413                       actionCollection(), "prepend_signature");
01414 
01415   mInsertSignatureAction =  new KAction (i18n("Insert Signature At C&ursor Position"), "edit", 0, this,
01416                       SLOT(slotInsertSignatureAtCursor()),
01417                       actionCollection(), "insert_signature_at_cursor_position");
01418 
01419   mAttachPK  = new KAction (i18n("Attach &Public Key..."), 0, this,
01420                            SLOT(slotInsertPublicKey()),
01421                            actionCollection(), "attach_public_key");
01422   mAttachMPK = new KAction (i18n("Attach &My Public Key"), 0, this,
01423                            SLOT(slotInsertMyPublicKey()),
01424                            actionCollection(), "attach_my_public_key");
01425   (void) new KAction (i18n("&Attach File..."), "attach",
01426                       0, this, SLOT(slotAttachFile()),
01427                       actionCollection(), "attach");
01428   mAttachRemoveAction = new KAction (i18n("&Remove Attachment"), 0, this,
01429                       SLOT(slotAttachRemove()),
01430                       actionCollection(), "remove");
01431   mAttachSaveAction = new KAction (i18n("&Save Attachment As..."), "filesave",0,
01432                       this, SLOT(slotAttachSave()),
01433                       actionCollection(), "attach_save");
01434   mAttachPropertiesAction = new KAction (i18n("Attachment Pr&operties"), 0, this,
01435                       SLOT(slotAttachProperties()),
01436                       actionCollection(), "attach_properties");
01437 
01438   setStandardToolBarMenuEnabled(true);
01439 
01440   KStdAction::keyBindings(this, SLOT(slotEditKeys()), actionCollection());
01441   KStdAction::configureToolbars(this, SLOT(slotEditToolbars()), actionCollection());
01442   KStdAction::preferences(kmkernel, SLOT(slotShowConfigurationDialog()), actionCollection());
01443 
01444   (void) new KAction (i18n("&Spellchecker..."), 0, this, SLOT(slotSpellcheckConfig()),
01445                       actionCollection(), "setup_spellchecker");
01446 
01447   if ( Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" ) ) {
01448     KToggleAction * a = new KToggleAction( i18n( "Encrypt Message with Chiasmus..." ),
01449                                            "chidecrypted", 0, actionCollection(),
01450                                            "encrypt_message_chiasmus" );
01451     a->setCheckedState( KGuiItem( i18n( "Encrypt Message with Chiasmus..." ), "chiencrypted" ) );
01452     mEncryptChiasmusAction = a;
01453     connect( mEncryptChiasmusAction, SIGNAL(toggled(bool)),
01454              this, SLOT(slotEncryptChiasmusToggled(bool)) );
01455   } else {
01456     mEncryptChiasmusAction = 0;
01457   }
01458 
01459   mEncryptAction = new KToggleAction (i18n("&Encrypt Message"),
01460                                      "decrypted", 0,
01461                                      actionCollection(), "encrypt_message");
01462   mSignAction = new KToggleAction (i18n("&Sign Message"),
01463                                   "signature", 0,
01464                                   actionCollection(), "sign_message");
01465   // get PGP user id for the chosen identity
01466   const KPIM::Identity & ident =
01467     kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() );
01468   // PENDING(marc): check the uses of this member and split it into
01469   // smime/openpgp and or enc/sign, if necessary:
01470   mLastIdentityHasSigningKey = !ident.pgpSigningKey().isEmpty() || !ident.smimeSigningKey().isEmpty();
01471   mLastIdentityHasEncryptionKey = !ident.pgpEncryptionKey().isEmpty() || !ident.smimeEncryptionKey().isEmpty();
01472 
01473   mLastEncryptActionState = false;
01474   mLastSignActionState = GlobalSettings::self()->pgpAutoSign();
01475 
01476   // "Attach public key" is only possible if OpenPGP support is available:
01477   mAttachPK->setEnabled( Kleo::CryptoBackendFactory::instance()->openpgp() );
01478 
01479   // "Attach my public key" is only possible if OpenPGP support is
01480   // available and the user specified his key for the current identity:
01481   mAttachMPK->setEnabled( Kleo::CryptoBackendFactory::instance()->openpgp() &&
01482               !ident.pgpEncryptionKey().isEmpty() );
01483 
01484   if ( !Kleo::CryptoBackendFactory::instance()->openpgp() && !Kleo::CryptoBackendFactory::instance()->smime() ) {
01485     // no crypto whatsoever
01486     mEncryptAction->setEnabled( false );
01487     setEncryption( false );
01488     mSignAction->setEnabled( false );
01489     setSigning( false );
01490   } else {
01491     const bool canOpenPGPSign = Kleo::CryptoBackendFactory::instance()->openpgp()
01492       && !ident.pgpSigningKey().isEmpty();
01493     const bool canSMIMESign = Kleo::CryptoBackendFactory::instance()->smime()
01494       && !ident.smimeSigningKey().isEmpty();
01495 
01496     setEncryption( false );
01497     setSigning( ( canOpenPGPSign || canSMIMESign ) && GlobalSettings::self()->pgpAutoSign() );
01498   }
01499 
01500   connect(mEncryptAction, SIGNAL(toggled(bool)),
01501                          SLOT(slotEncryptToggled( bool )));
01502   connect(mSignAction,    SIGNAL(toggled(bool)),
01503                          SLOT(slotSignToggled(    bool )));
01504 
01505   QStringList l;
01506   for ( int i = 0 ; i < numCryptoMessageFormats ; ++i )
01507     l.push_back( Kleo::cryptoMessageFormatToLabel( cryptoMessageFormats[i] ) );
01508 
01509   mCryptoModuleAction = new KSelectAction( i18n( "&Cryptographic Message Format" ), 0,
01510                        this, SLOT(slotSelectCryptoModule()),
01511                        actionCollection(), "options_select_crypto" );
01512   mCryptoModuleAction->setItems( l );
01513   mCryptoModuleAction->setCurrentItem( format2cb( ident.preferredCryptoMessageFormat() ) );
01514   slotSelectCryptoModule( true /* initialize */ );
01515 
01516   QStringList styleItems;
01517   styleItems << i18n( "Standard" );
01518   styleItems << i18n( "Bulleted List (Disc)" );
01519   styleItems << i18n( "Bulleted List (Circle)" );
01520   styleItems << i18n( "Bulleted List (Square)" );
01521   styleItems << i18n( "Ordered List (Decimal)" );
01522   styleItems << i18n( "Ordered List (Alpha lower)" );
01523   styleItems << i18n( "Ordered List (Alpha upper)" );
01524 
01525   listAction = new KSelectAction( i18n( "Select Style" ), 0, actionCollection(),
01526                                  "text_list" );
01527   listAction->setItems( styleItems );
01528   connect( listAction, SIGNAL( activated( const QString& ) ),
01529            SLOT( slotListAction( const QString& ) ) );
01530   fontAction = new KFontAction( "Select Font", 0, actionCollection(),
01531                                "text_font" );
01532   connect( fontAction, SIGNAL( activated( const QString& ) ),
01533            SLOT( slotFontAction( const QString& ) ) );
01534   fontSizeAction = new KFontSizeAction( "Select Size", 0, actionCollection(),
01535                                        "text_size" );
01536   connect( fontSizeAction, SIGNAL( fontSizeChanged( int ) ),
01537            SLOT( slotSizeAction( int ) ) );
01538 
01539   alignLeftAction = new KToggleAction (i18n("Align Left"), "text_left", 0,
01540                       this, SLOT(slotAlignLeft()), actionCollection(),
01541                       "align_left");
01542   alignLeftAction->setChecked( true );
01543   alignRightAction = new KToggleAction (i18n("Align Right"), "text_right", 0,
01544                       this, SLOT(slotAlignRight()), actionCollection(),
01545                       "align_right");
01546   alignCenterAction = new KToggleAction (i18n("Align Center"), "text_center", 0,
01547                        this, SLOT(slotAlignCenter()), actionCollection(),
01548                        "align_center");
01549   textBoldAction = new KToggleAction( i18n("&Bold"), "text_bold", CTRL+Key_B,
01550                                      this, SLOT(slotTextBold()),
01551                                      actionCollection(), "text_bold");
01552   textItalicAction = new KToggleAction( i18n("&Italic"), "text_italic", CTRL+Key_I,
01553                                        this, SLOT(slotTextItalic()),
01554                                        actionCollection(), "text_italic");
01555   textUnderAction = new KToggleAction( i18n("&Underline"), "text_under", CTRL+Key_U,
01556                                      this, SLOT(slotTextUnder()),
01557                                      actionCollection(), "text_under");
01558   actionFormatReset = new KAction( i18n( "Reset Font Settings" ), "eraser", 0,
01559                                      this, SLOT( slotFormatReset() ),
01560                                      actionCollection(), "format_reset");
01561   actionFormatColor = new KAction( i18n( "Text Color..." ), "colorize", 0,
01562                                      this, SLOT( slotTextColor() ),
01563                                      actionCollection(), "format_color");
01564 
01565   //  editorFocusChanged(false);
01566   createGUI("kmcomposerui.rc");
01567 
01568   connect( toolBar("htmlToolBar"), SIGNAL( visibilityChanged(bool) ),
01569            this, SLOT( htmlToolBarVisibilityChanged(bool) ) );
01570 
01571   // In Kontact, this entry would read "Configure Kontact", but bring
01572   // up KMail's config dialog. That's sensible, though, so fix the label.
01573   KAction* configureAction = actionCollection()->action("options_configure" );
01574   if ( configureAction )
01575     configureAction->setText( i18n("Configure KMail..." ) );
01576 }
01577 
01578 //-----------------------------------------------------------------------------
01579 void KMComposeWin::setupStatusBar(void)
01580 {
01581   statusBar()->insertItem("", 0, 1);
01582   statusBar()->setItemAlignment(0, AlignLeft | AlignVCenter);
01583 
01584   statusBar()->insertItem(i18n( " Spellcheck: %1 ").arg( "   " ), 3, 0, true );
01585   statusBar()->insertItem(i18n( " Column: %1 ").arg("     "), 2, 0, true);
01586   statusBar()->insertItem(i18n( " Line: %1 ").arg("     "), 1, 0, true);
01587 }
01588 
01589 
01590 //-----------------------------------------------------------------------------
01591 void KMComposeWin::updateCursorPosition()
01592 {
01593   int col,line;
01594   QString temp;
01595   line = mEditor->currentLine();
01596   col = mEditor->currentColumn();
01597   temp = i18n(" Line: %1 ").arg(line+1);
01598   statusBar()->changeItem(temp,1);
01599   temp = i18n(" Column: %1 ").arg(col+1);
01600   statusBar()->changeItem(temp,2);
01601 }
01602 
01603 
01604 //-----------------------------------------------------------------------------
01605 void KMComposeWin::setupEditor(void)
01606 {
01607   //QPopupMenu* menu;
01608   mEditor->setModified(false);
01609   QFontMetrics fm(mBodyFont);
01610   mEditor->setTabStopWidth(fm.width(QChar(' ')) * 8);
01611   //mEditor->setFocusPolicy(QWidget::ClickFocus);
01612 
01613   slotWordWrapToggled( GlobalSettings::self()->wordWrap() );
01614 
01615   // Font setup
01616   slotUpdateFont();
01617 
01618   /* installRBPopup() is broken in kdelibs, we should wait for
01619           the new klibtextedit (dnaber, 2002-01-01)
01620   menu = new QPopupMenu(this);
01621   //#ifdef BROKEN
01622   menu->insertItem(i18n("Undo"),mEditor,
01623                    SLOT(undo()), KStdAccel::shortcut(KStdAccel::Undo));
01624   menu->insertItem(i18n("Redo"),mEditor,
01625                    SLOT(redo()), KStdAccel::shortcut(KStdAccel::Redo));
01626   menu->insertSeparator();
01627   //#endif //BROKEN
01628   menu->insertItem(i18n("Cut"), this, SLOT(slotCut()));
01629   menu->insertItem(i18n("Copy"), this, SLOT(slotCopy()));
01630   menu->insertItem(i18n("Paste"), this, SLOT(slotPasteClipboard()));
01631   menu->insertItem(i18n("Mark All"),this, SLOT(slotMarkAll()));
01632   menu->insertSeparator();
01633   menu->insertItem(i18n("Find..."), this, SLOT(slotFind()));
01634   menu->insertItem(i18n("Replace..."), this, SLOT(slotReplace()));
01635   menu->insertSeparator();
01636   menu->insertItem(i18n("Fixed Font Widths"), this, SLOT(slotUpdateFont()));
01637   mEditor->installRBPopup(menu);
01638   */
01639   updateCursorPosition();
01640   connect(mEditor,SIGNAL(CursorPositionChanged()),SLOT(updateCursorPosition()));
01641   connect( mEditor, SIGNAL( currentFontChanged( const QFont & ) ),
01642           this, SLOT( fontChanged( const QFont & ) ) );
01643   connect( mEditor, SIGNAL( currentAlignmentChanged( int ) ),
01644           this, SLOT( alignmentChanged( int ) ) );
01645 
01646 }
01647 
01648 
01649 //-----------------------------------------------------------------------------
01650 static QString cleanedUpHeaderString( const QString & s )
01651 {
01652   // remove invalid characters from the header strings
01653   QString res( s );
01654   res.replace( '\r', "" );
01655   res.replace( '\n', " " );
01656   return res.stripWhiteSpace();
01657 }
01658 
01659 //-----------------------------------------------------------------------------
01660 QString KMComposeWin::subject() const
01661 {
01662   return cleanedUpHeaderString( mEdtSubject->text() );
01663 }
01664 
01665 //-----------------------------------------------------------------------------
01666 QString KMComposeWin::to() const
01667 {
01668   if ( mEdtTo ) {
01669     return cleanedUpHeaderString( mEdtTo->text() );
01670   } else if ( mRecipientsEditor ) {
01671     return mRecipientsEditor->recipientString( Recipient::To );
01672   } else {
01673     return QString::null;
01674   }
01675 }
01676 
01677 //-----------------------------------------------------------------------------
01678 QString KMComposeWin::cc() const
01679 {
01680   if ( mEdtCc && !mEdtCc->isHidden() ) {
01681     return cleanedUpHeaderString( mEdtCc->text() );
01682   } else if ( mRecipientsEditor ) {
01683     return mRecipientsEditor->recipientString( Recipient::Cc );
01684   } else {
01685     return QString::null;
01686   }
01687 }
01688 
01689 //-----------------------------------------------------------------------------
01690 QString KMComposeWin::bcc() const
01691 {
01692   if ( mEdtBcc && !mEdtBcc->isHidden() ) {
01693     return cleanedUpHeaderString( mEdtBcc->text() );
01694   } else if ( mRecipientsEditor ) {
01695     return mRecipientsEditor->recipientString( Recipient::Bcc );
01696   } else {
01697     return QString::null;
01698   }
01699 }
01700 
01701 //-----------------------------------------------------------------------------
01702 QString KMComposeWin::from() const
01703 {
01704   return cleanedUpHeaderString( mEdtFrom->text() );
01705 }
01706 
01707 //-----------------------------------------------------------------------------
01708 QString KMComposeWin::replyTo() const
01709 {
01710   if ( mEdtReplyTo ) {
01711     return cleanedUpHeaderString( mEdtReplyTo->text() );
01712   } else {
01713     return QString::null;
01714   }
01715 }
01716 
01717 //-----------------------------------------------------------------------------
01718 void KMComposeWin::verifyWordWrapLengthIsAdequate(const QString &body)
01719 {
01720   int maxLineLength = 0;
01721   int curPos;
01722   int oldPos = 0;
01723   if (mEditor->QTextEdit::wordWrap() == QTextEdit::FixedColumnWidth) {
01724     for (curPos = 0; curPos < (int)body.length(); ++curPos)
01725         if (body[curPos] == '\n') {
01726           if ((curPos - oldPos) > maxLineLength)
01727             maxLineLength = curPos - oldPos;
01728           oldPos = curPos;
01729         }
01730     if ((curPos - oldPos) > maxLineLength)
01731       maxLineLength = curPos - oldPos;
01732     if (mEditor->wrapColumnOrWidth() < maxLineLength) // column
01733       mEditor->setWrapColumnOrWidth(maxLineLength);
01734   }
01735 }
01736 
01737 //-----------------------------------------------------------------------------
01738 void KMComposeWin::decryptOrStripOffCleartextSignature( QCString& body )
01739 {
01740   QPtrList<Kpgp::Block> pgpBlocks;
01741   QStrList nonPgpBlocks;
01742   if( Kpgp::Module::prepareMessageForDecryption( body,
01743                                                  pgpBlocks, nonPgpBlocks ) )
01744   {
01745     // Only decrypt/strip off the signature if there is only one OpenPGP
01746     // block in the message
01747     if( pgpBlocks.count() == 1 )
01748     {
01749       Kpgp::Block* block = pgpBlocks.first();
01750       if( ( block->type() == Kpgp::PgpMessageBlock ) ||
01751           ( block->type() == Kpgp::ClearsignedBlock ) )
01752       {
01753         if( block->type() == Kpgp::PgpMessageBlock )
01754           // try to decrypt this OpenPGP block
01755           block->decrypt();
01756         else
01757           // strip off the signature
01758           block->verify();
01759 
01760         body = nonPgpBlocks.first()
01761              + block->text()
01762              + nonPgpBlocks.last();
01763       }
01764     }
01765   }
01766 }
01767 
01768 //-----------------------------------------------------------------------------
01769 void KMComposeWin::setTransport( const QString & transport )
01770 {
01771   kdDebug(5006) << "KMComposeWin::setTransport( \"" << transport << "\" )" << endl;
01772   // Don't change the transport combobox if transport is empty
01773   if ( transport.isEmpty() )
01774     return;
01775 
01776   bool transportFound = false;
01777   for ( int i = 0; i < mTransport->count(); ++i ) {
01778     if ( mTransport->text(i) == transport ) {
01779       transportFound = true;
01780       mTransport->setCurrentItem(i);
01781       kdDebug(5006) << "transport found, it's no. " << i << " in the list" << endl;
01782       break;
01783     }
01784   }
01785   if ( !transportFound ) { // unknown transport
01786     kdDebug(5006) << "unknown transport \"" << transport << "\"" << endl;
01787     if ( transport.startsWith("smtp://") || transport.startsWith("smtps://") ||
01788          transport.startsWith("file://") ) {
01789       // set custom transport
01790       mTransport->setEditText( transport );
01791     }
01792     else {
01793       // neither known nor custom transport -> use default transport
01794       mTransport->setCurrentText( GlobalSettings::self()->defaultTransport() );
01795     }
01796   }
01797 }
01798 
01799 //-----------------------------------------------------------------------------
01800 void KMComposeWin::setMsg(KMMessage* newMsg, bool mayAutoSign,
01801                           bool allowDecryption, bool isModified)
01802 {
01803   //assert(newMsg!=0);
01804   if(!newMsg)
01805     {
01806       kdDebug(5006) << "KMComposeWin::setMsg() : newMsg == 0!" << endl;
01807       return;
01808     }
01809   mMsg = newMsg;
01810   KPIM::IdentityManager * im = kmkernel->identityManager();
01811 
01812   mEdtFrom->setText(mMsg->from());
01813   mEdtReplyTo->setText(mMsg->replyTo());
01814   if ( mClassicalRecipients ) {
01815     mEdtTo->setText(mMsg->to());
01816     mEdtCc->setText(mMsg->cc());
01817     mEdtBcc->setText(mMsg->bcc());
01818   } else {
01819     mRecipientsEditor->setRecipientString( mMsg->to(), Recipient::To );
01820     mRecipientsEditor->setRecipientString( mMsg->cc(), Recipient::Cc );
01821     mRecipientsEditor->setRecipientString( mMsg->bcc(), Recipient::Bcc );
01822     mRecipientsEditor->setFocusBottom();
01823   }
01824   mEdtSubject->setText(mMsg->subject());
01825 
01826   const bool stickyIdentity = mBtnIdentity->isChecked();
01827   const bool messageHasIdentity = !newMsg->headerField("X-KMail-Identity").isEmpty();
01828   if (!stickyIdentity && messageHasIdentity)
01829     mId = newMsg->headerField("X-KMail-Identity").stripWhiteSpace().toUInt();
01830 
01831   // don't overwrite the header values with identity specific values
01832   // unless the identity is sticky
01833   if ( !stickyIdentity ) {
01834     disconnect(mIdentity,SIGNAL(identityChanged(uint)),
01835                this, SLOT(slotIdentityChanged(uint)));
01836   }
01837   // load the mId into the gui, sticky or not, without emitting
01838   mIdentity->setCurrentIdentity( mId );
01839   const uint idToApply = mId;
01840   if ( !stickyIdentity ) {
01841     connect(mIdentity,SIGNAL(identityChanged(uint)),
01842             this, SLOT(slotIdentityChanged(uint)));
01843   }  else {
01844     // load the message's state into the mId, without applying it to the gui
01845     // that's so we can detect that the id changed (because a sticky was set)
01846     // on apply()
01847     if ( messageHasIdentity )
01848       mId = newMsg->headerField("X-KMail-Identity").stripWhiteSpace().toUInt();
01849     else
01850       mId = im->defaultIdentity().uoid();
01851   }
01852   // manually load the identity's value into the fields; either the one from the
01853   // messge, where appropriate, or the one from the sticky identity. What's in
01854   // mId might have changed meanwhile, thus the save value
01855   slotIdentityChanged( idToApply );
01856 
01857   const KPIM::Identity & ident = im->identityForUoid( mIdentity->currentIdentity() );
01858 
01859   // check for the presence of a DNT header, indicating that MDN's were
01860   // requested
01861   QString mdnAddr = newMsg->headerField("Disposition-Notification-To");
01862   mRequestMDNAction->setChecked( ( !mdnAddr.isEmpty() &&
01863                                   im->thatIsMe( mdnAddr ) ) ||
01864                                   GlobalSettings::self()->requestMDN() );
01865 
01866   // check for presence of a priority header, indicating urgent mail:
01867   mUrgentAction->setChecked( newMsg->isUrgent() );
01868 
01869   if (!ident.isXFaceEnabled() || ident.xface().isEmpty())
01870     mMsg->removeHeaderField("X-Face");
01871   else
01872   {
01873     QString xface = ident.xface();
01874     if (!xface.isEmpty())
01875     {
01876       int numNL = ( xface.length() - 1 ) / 70;
01877       for ( int i = numNL; i > 0; --i )
01878         xface.insert( i*70, "\n\t" );
01879       mMsg->setHeaderField("X-Face", xface);
01880     }
01881   }
01882 
01883   // enable/disable encryption if the message was/wasn't encrypted
01884   switch ( mMsg->encryptionState() ) {
01885     case KMMsgFullyEncrypted: // fall through
01886     case KMMsgPartiallyEncrypted:
01887       mLastEncryptActionState = true;
01888       break;
01889     case KMMsgNotEncrypted:
01890       mLastEncryptActionState = false;
01891       break;
01892     default: // nothing
01893       break;
01894   }
01895 
01896   // enable/disable signing if the message was/wasn't signed
01897   switch ( mMsg->signatureState() ) {
01898     case KMMsgFullySigned: // fall through
01899     case KMMsgPartiallySigned:
01900       mLastSignActionState = true;
01901       break;
01902     case KMMsgNotSigned:
01903       mLastSignActionState = false;
01904       break;
01905     default: // nothing
01906       break;
01907   }
01908 
01909   // if these headers are present, the state of the message should be overruled
01910   if ( mMsg->headers().FindField( "X-KMail-SignatureActionEnabled" ) )
01911     mLastSignActionState = (mMsg->headerField( "X-KMail-SignatureActionEnabled" ) == "true");
01912   if ( mMsg->headers().FindField( "X-KMail-EncryptActionEnabled" ) )
01913     mLastEncryptActionState = (mMsg->headerField( "X-KMail-EncryptActionEnabled" ) == "true");
01914   if ( mMsg->headers().FindField( "X-KMail-CryptoMessageFormat" ) )
01915     mCryptoModuleAction->setCurrentItem( format2cb( static_cast<Kleo::CryptoMessageFormat>(
01916                     mMsg->headerField( "X-KMail-CryptoMessageFormat" ).toInt() ) ) );
01917 
01918   mLastIdentityHasSigningKey = !ident.pgpSigningKey().isEmpty() || !ident.smimeSigningKey().isEmpty();
01919   mLastIdentityHasEncryptionKey = !ident.pgpEncryptionKey().isEmpty() || !ident.smimeEncryptionKey().isEmpty();
01920 
01921   if ( Kleo::CryptoBackendFactory::instance()->openpgp() || Kleo::CryptoBackendFactory::instance()->smime() ) {
01922     const bool canOpenPGPSign = Kleo::CryptoBackendFactory::instance()->openpgp()
01923       && !ident.pgpSigningKey().isEmpty();
01924     const bool canSMIMESign = Kleo::CryptoBackendFactory::instance()->smime()
01925       && !ident.smimeSigningKey().isEmpty();
01926 
01927     setEncryption( mLastEncryptActionState );
01928     setSigning( ( canOpenPGPSign || canSMIMESign ) && mLastSignActionState );
01929   }
01930   slotUpdateSignatureAndEncrypionStateIndicators();
01931 
01932   // "Attach my public key" is only possible if the user uses OpenPGP
01933   // support and he specified his key:
01934   mAttachMPK->setEnabled( Kleo::CryptoBackendFactory::instance()->openpgp() &&
01935               !ident.pgpEncryptionKey().isEmpty() );
01936 
01937   QString transport = newMsg->headerField("X-KMail-Transport");
01938   if (!mBtnTransport->isChecked() && !transport.isEmpty())
01939     setTransport( transport );
01940 
01941   if (!mBtnFcc->isChecked())
01942   {
01943     if (!mMsg->fcc().isEmpty())
01944       setFcc(mMsg->fcc());
01945     else
01946       setFcc(ident.fcc());
01947   }
01948 
01949   mDictionaryCombo->setCurrentByDictionary( ident.dictionary() );
01950 
01951   partNode * root = partNode::fromMessage( mMsg );
01952 
01953   KMail::ObjectTreeParser otp; // all defaults are ok
01954   otp.parseObjectTree( root );
01955 
01956   KMail::AttachmentCollector ac;
01957   ac.collectAttachmentsFrom( root );
01958 
01959   for ( std::vector<partNode*>::const_iterator it = ac.attachments().begin() ; it != ac.attachments().end() ; ++it )
01960     addAttach( new KMMessagePart( (*it)->msgPart() ) );
01961 
01962   mEditor->setText( otp.textualContent() );
01963   mCharset = otp.textualContentCharset();
01964   if ( partNode * n = root->findType( DwMime::kTypeText, DwMime::kSubtypeHtml ) )
01965     if ( partNode * p = n->parentNode() )
01966       if ( p->hasType( DwMime::kTypeMultipart ) &&
01967            p->hasSubType( DwMime::kSubtypeAlternative ) )
01968         if ( mMsg->headerField( "X-KMail-Markup" ) == "true" ) {
01969           toggleMarkup( true );
01970 
01971           // get cte decoded body part
01972           mCharset = n->msgPart().charset();
01973           QCString bodyDecoded = n->msgPart().bodyDecoded();
01974 
01975           // respect html part charset
01976           const QTextCodec *codec = KMMsgBase::codecForName( mCharset );
01977           if ( codec ) {
01978             mEditor->setText( codec->toUnicode( bodyDecoded ) );
01979           } else {
01980             mEditor->setText( QString::fromLocal8Bit( bodyDecoded ) );
01981           }
01982         }
01983 
01984   if ( mCharset.isEmpty() )
01985     mCharset = mMsg->charset();
01986   if ( mCharset.isEmpty() )
01987     mCharset = mDefCharset;
01988   setCharset( mCharset );
01989 
01990   /* Handle the special case of non-mime mails */
01991   if ( mMsg->numBodyParts() == 0 && otp.textualContent().isEmpty() ) {
01992     mCharset=mMsg->charset();
01993     if ( mCharset.isEmpty() ||  mCharset == "default" )
01994       mCharset = mDefCharset;
01995 
01996     QCString bodyDecoded = mMsg->bodyDecoded();
01997 
01998     if( allowDecryption )
01999       decryptOrStripOffCleartextSignature( bodyDecoded );
02000 
02001     const QTextCodec *codec = KMMsgBase::codecForName(mCharset);
02002     if (codec) {
02003       mEditor->setText(codec->toUnicode(bodyDecoded));
02004     } else
02005       mEditor->setText(QString::fromLocal8Bit(bodyDecoded));
02006   }
02007 #ifdef BROKEN_FOR_OPAQUE_SIGNED_OR_ENCRYPTED_MAILS
02008   const int num = mMsg->numBodyParts();
02009   kdDebug(5006) << "KMComposeWin::setMsg() mMsg->numBodyParts="
02010                 << mMsg->numBodyParts() << endl;
02011 
02012   if ( num > 0 ) {
02013     KMMessagePart bodyPart;
02014     int firstAttachment = 0;
02015 
02016     mMsg->bodyPart(1, &bodyPart);
02017     if ( bodyPart.typeStr().lower() == "text" &&
02018          bodyPart.subtypeStr().lower() == "html" ) {
02019       // check whether we are inside a mp/al body part
02020       partNode *root = partNode::fromMessage( mMsg );
02021       partNode *node = root->findType( DwMime::kTypeText,
02022                                        DwMime::kSubtypeHtml );
02023       if ( node && node->parentNode() &&
02024            node->parentNode()->hasType( DwMime::kTypeMultipart ) &&
02025            node->parentNode()->hasSubType( DwMime::kSubtypeAlternative ) ) {
02026         // we have a mp/al body part with a text and an html body
02027       kdDebug(5006) << "KMComposeWin::setMsg() : text/html found" << endl;
02028       firstAttachment = 2;
02029         if ( mMsg->headerField( "X-KMail-Markup" ) == "true" )
02030           toggleMarkup( true );
02031       }
02032       delete root; root = 0;
02033     }
02034     if ( firstAttachment == 0 ) {
02035         mMsg->bodyPart(0, &bodyPart);
02036         if ( bodyPart.typeStr().lower() == "text" ) {
02037           // we have a mp/mx body with a text body
02038         kdDebug(5006) << "KMComposeWin::setMsg() : text/* found" << endl;
02039           firstAttachment = 1;
02040         }
02041       }
02042 
02043     if ( firstAttachment != 0 ) // there's text to show
02044     {
02045       mCharset = bodyPart.charset();
02046       if ( mCharset.isEmpty() || mCharset == "default" )
02047         mCharset = mDefCharset;
02048 
02049       QCString bodyDecoded = bodyPart.bodyDecoded();
02050 
02051       if( allowDecryption )
02052         decryptOrStripOffCleartextSignature( bodyDecoded );
02053 
02054       // As nobody seems to know the purpose of the following line and
02055       // as it breaks word wrapping of long lines if drafts with attachments
02056       // are opened for editting in the composer (cf. Bug#41102) I comment it
02057       // out. Ingo, 2002-04-21
02058       //verifyWordWrapLengthIsAdequate(bodyDecoded);
02059 
02060       const QTextCodec *codec = KMMsgBase::codecForName(mCharset);
02061       if (codec)
02062         mEditor->setText(codec->toUnicode(bodyDecoded));
02063       else
02064         mEditor->setText(QString::fromLocal8Bit(bodyDecoded));
02065       //mEditor->insertLine("\n", -1); <-- why ?
02066     } else mEditor->setText("");
02067     for( int i = firstAttachment; i < num; ++i )
02068     {
02069       KMMessagePart *msgPart = new KMMessagePart;
02070       mMsg->bodyPart(i, msgPart);
02071       QCString mimeType = msgPart->typeStr().lower() + '/'
02072                         + msgPart->subtypeStr().lower();
02073       // don't add the detached signature as attachment when editting a
02074       // PGP/MIME signed message
02075       if( mimeType != "application/pgp-signature" ) {
02076         addAttach(msgPart);
02077       }
02078     }
02079   } else{
02080     mCharset=mMsg->charset();
02081     if ( mCharset.isEmpty() ||  mCharset == "default" )
02082       mCharset = mDefCharset;
02083 
02084     QCString bodyDecoded = mMsg->bodyDecoded();
02085 
02086     if( allowDecryption )
02087       decryptOrStripOffCleartextSignature( bodyDecoded );
02088 
02089     const QTextCodec *codec = KMMsgBase::codecForName(mCharset);
02090     if (codec) {
02091       mEditor->setText(codec->toUnicode(bodyDecoded));
02092     } else
02093       mEditor->setText(QString::fromLocal8Bit(bodyDecoded));
02094   }
02095 
02096   setCharset(mCharset);
02097 #endif // BROKEN_FOR_OPAQUE_SIGNED_OR_ENCRYPTED_MAILS
02098 
02099   if( (GlobalSettings::self()->autoTextSignature()=="auto") && mayAutoSign ) {
02100     //
02101     // Espen 2000-05-16
02102     // Delay the signature appending. It may start a fileseletor.
02103     // Not user friendy if this modal fileseletor opens before the
02104     // composer.
02105     //
02106     //QTimer::singleShot( 200, this, SLOT(slotAppendSignature()) );
02107       if ( GlobalSettings::self()->prependSignature() ) {
02108         QTimer::singleShot( 0, this, SLOT(slotPrependSignature()) );
02109       } else {
02110         QTimer::singleShot( 0, this, SLOT(slotAppendSignature()) );
02111       }
02112   }
02113 
02114   if ( mMsg->getCursorPos() > 0 ) {
02115     // The message has a cursor position explicitly set, so avoid
02116     // changing it when appending the signature.
02117     mPreserveUserCursorPosition = true;
02118   }
02119   setModified( isModified );
02120 
02121   // do this even for new messages
02122   mEditor->setCursorPositionFromStart( (unsigned int) mMsg->getCursorPos() );
02123 
02124   // honor "keep reply in this folder" setting even when the identity is changed later on
02125   mPreventFccOverwrite = ( !newMsg->fcc().isEmpty() && ident.fcc() != newMsg->fcc() );
02126 }
02127 
02128 
02129 //-----------------------------------------------------------------------------
02130 void KMComposeWin::setFcc( const QString &idString )
02131 {
02132   // check if the sent-mail folder still exists
02133   if ( ! idString.isEmpty() && kmkernel->findFolderById( idString ) ) {
02134     mFcc->setFolder( idString );
02135   } else {
02136     mFcc->setFolder( kmkernel->sentFolder() );
02137   }
02138 }
02139 
02140 
02141 //-----------------------------------------------------------------------------
02142 bool KMComposeWin::isModified() const
02143 {
02144   return ( mEditor->isModified() ||
02145            mEdtFrom->edited() ||
02146            ( mEdtReplyTo && mEdtReplyTo->edited() ) ||
02147            ( mEdtTo && mEdtTo->edited() ) ||
02148            ( mEdtCc && mEdtCc->edited() ) ||
02149            ( mEdtBcc && mEdtBcc->edited() ) ||
02150            ( mRecipientsEditor && mRecipientsEditor->isModified() ) ||
02151            mEdtSubject->edited() ||
02152            mAtmModified ||
02153            ( mTransport->lineEdit() && mTransport->lineEdit()->edited() ) );
02154 }
02155 
02156 
02157 //-----------------------------------------------------------------------------
02158 void KMComposeWin::setModified( bool modified )
02159 {
02160   mEditor->setModified( modified );
02161   if ( !modified ) {
02162     mEdtFrom->setEdited( false );
02163     if ( mEdtReplyTo ) mEdtReplyTo->setEdited( false );
02164     if ( mEdtTo ) mEdtTo->setEdited( false );
02165     if ( mEdtCc ) mEdtCc->setEdited( false );
02166     if ( mEdtBcc ) mEdtBcc->setEdited( false );
02167     if ( mRecipientsEditor ) mRecipientsEditor->clearModified();
02168     mEdtSubject->setEdited( false );
02169     mAtmModified =  false ;
02170     if ( mTransport->lineEdit() )
02171       mTransport->lineEdit()->setEdited( false );
02172   }
02173 }
02174 
02175 
02176 //-----------------------------------------------------------------------------
02177 bool KMComposeWin::queryClose ()
02178 {
02179   if ( !mEditor->checkExternalEditorFinished() )
02180     return false;
02181   if ( kmkernel->shuttingDown() || kapp->sessionSaving() )
02182     return true;
02183   if ( mComposer && mComposer->isPerformingSignOperation() ) // since the non-gpg-agent gpg plugin gets a passphrase using QDialog::exec()
02184     return false;                                            // the user can try to close the window, which destroys mComposer mid-call.
02185 
02186   if ( isModified() ) {
02187     bool istemplate = ( mFolder!=0 && mFolder->isTemplates() );
02188     const QString savebut = ( istemplate ?
02189                               i18n("Re&save as Template") :
02190                               i18n("&Save as Draft") );
02191     const QString savetext = ( istemplate ?
02192                                i18n("Resave this message in the Templates folder. "
02193                                     "It can then be used at a later time.") :
02194                                i18n("Save this message in the Drafts folder. "
02195                                     "It can then be edited and sent at a later time.") );
02196 
02197     const int rc = KMessageBox::warningYesNoCancel( this,
02198            i18n("Do you want to save the message for later or discard it?"),
02199            i18n("Close Composer"),
02200            KGuiItem(savebut, "filesave", QString::null, savetext),
02201            KStdGuiItem::discard() );
02202     if ( rc == KMessageBox::Cancel )
02203       return false;
02204     else if ( rc == KMessageBox::Yes ) {
02205       // doSend will close the window. Just return false from this method
02206       if ( istemplate ) {
02207         slotSaveTemplate();
02208       } else {
02209         slotSaveDraft();
02210       }
02211       return false;
02212     }
02213   }
02214   cleanupAutoSave();
02215   return true;
02216 }
02217 
02218 //-----------------------------------------------------------------------------
02219 bool KMComposeWin::userForgotAttachment()
02220 {
02221   bool checkForForgottenAttachments = GlobalSettings::self()->showForgottenAttachmentWarning();
02222 
02223   if ( !checkForForgottenAttachments || ( mAtmList.count() > 0 ) )
02224     return false;
02225 
02226 
02227   QStringList attachWordsList = GlobalSettings::self()->attachmentKeywords();
02228 
02229   if ( attachWordsList.isEmpty() ) {
02230     // default value (FIXME: this is duplicated in configuredialog.cpp)
02231     attachWordsList << QString::fromLatin1("attachment")
02232                     << QString::fromLatin1("attached");
02233     if ( QString::fromLatin1("attachment") != i18n("attachment") )
02234       attachWordsList << i18n("attachment");
02235     if ( QString::fromLatin1("attached") != i18n("attached") )
02236       attachWordsList << i18n("attached");
02237   }
02238 
02239   QRegExp rx ( QString::fromLatin1("\\b") +
02240                attachWordsList.join("\\b|\\b") +
02241                QString::fromLatin1("\\b") );
02242   rx.setCaseSensitive( false );
02243 
02244   bool gotMatch = false;
02245 
02246   // check whether the subject contains one of the attachment key words
02247   // unless the message is a reply or a forwarded message
02248   QString subj = subject();
02249   gotMatch =    ( KMMessage::stripOffPrefixes( subj ) == subj )
02250              && ( rx.search( subj ) >= 0 );
02251 
02252   if ( !gotMatch ) {
02253     // check whether the non-quoted text contains one of the attachment key
02254     // words
02255     QRegExp quotationRx ("^([ \\t]*([|>:}#]|[A-Za-z]+>))+");
02256     for ( int i = 0; i < mEditor->numLines(); ++i ) {
02257       QString line = mEditor->textLine( i );
02258       gotMatch =    ( quotationRx.search( line ) < 0 )
02259                  && ( rx.search( line ) >= 0 );
02260       if ( gotMatch )
02261         break;
02262     }
02263   }
02264 
02265   if ( !gotMatch )
02266     return false;
02267 
02268   int rc = KMessageBox::warningYesNoCancel( this,
02269              i18n("The message you have composed seems to refer to an "
02270                   "attached file but you have not attached anything.\n"
02271                   "Do you want to attach a file to your message?"),
02272              i18n("File Attachment Reminder"),
02273              i18n("&Attach File..."),
02274              i18n("&Send as Is") );
02275   if ( rc == KMessageBox::Cancel )
02276     return true;
02277   if ( rc == KMessageBox::Yes ) {
02278     slotAttachFile();
02279     //preceed with editing
02280     return true;
02281   }
02282   return false;
02283 }
02284 
02285 //-----------------------------------------------------------------------------
02286 void KMComposeWin::applyChanges( bool dontSignNorEncrypt, bool dontDisable )
02287 {
02288   kdDebug(5006) << "entering KMComposeWin::applyChanges" << endl;
02289 
02290   if(!mMsg || mComposer) {
02291     kdDebug(5006) << "KMComposeWin::applyChanges() : mMsg == 0!\n" << endl;
02292     emit applyChangesDone( false );
02293     return;
02294   }
02295 
02296   // Make new job and execute it
02297   mComposer = new MessageComposer( this );
02298   connect( mComposer, SIGNAL( done( bool ) ),
02299            this, SLOT( slotComposerDone( bool ) ) );
02300 
02301   // TODO: Add a cancel button for the following operations?
02302   // Disable any input to the window, so that we have a snapshot of the
02303   // composed stuff
02304   if ( !dontDisable ) setEnabled( false );
02305   // apply the current state to the composer and let it do it's thing
02306   mComposer->setDisableBreaking( mDisableBreaking ); // FIXME
02307   mComposer->applyChanges( dontSignNorEncrypt );
02308 }
02309 
02310 void KMComposeWin::slotComposerDone( bool rc )
02311 {
02312   deleteAll( mComposedMessages );
02313   mComposedMessages = mComposer->composedMessageList();
02314   emit applyChangesDone( rc );
02315   delete mComposer;
02316   mComposer = 0;
02317 
02318   // re-enable the composewin, the messsage composition is now done
02319   setEnabled( true );
02320 }
02321 
02322 const KPIM::Identity & KMComposeWin::identity() const {
02323   return kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() );
02324 }
02325 
02326 uint KMComposeWin::identityUid() const {
02327   return mIdentity->currentIdentity();
02328 }
02329 
02330 Kleo::CryptoMessageFormat KMComposeWin::cryptoMessageFormat() const {
02331   if ( !mCryptoModuleAction )
02332     return Kleo::AutoFormat;
02333   return cb2format( mCryptoModuleAction->currentItem() );
02334 }
02335 
02336 bool KMComposeWin::encryptToSelf() const {
02337 //   return !Kpgp::Module::getKpgp() || Kpgp::Module::getKpgp()->encryptToSelf();
02338     KConfigGroup group( KMKernel::config(), "Composer" );
02339     return group.readBoolEntry( "crypto-encrypt-to-self", true );
02340 }
02341 
02342 bool KMComposeWin::queryExit ()
02343 {
02344   return true;
02345 }
02346 
02347 //-----------------------------------------------------------------------------
02348 bool KMComposeWin::addAttach(const KURL aUrl)
02349 {
02350   if ( !aUrl.isValid() ) {
02351     KMessageBox::sorry( this, i18n( "<qt><p>KMail could not recognize the location of the attachment (%1);</p>"
02352                                  "<p>you have to specify the full path if you wish to attach a file.</p></qt>" )
02353                         .arg( aUrl.prettyURL() ) );
02354     return false;
02355   }
02356 
02357   const int maxAttachmentSize = GlobalSettings::maximumAttachmentSize();
02358   const uint maximumAttachmentSizeInByte = maxAttachmentSize*1024*1024;
02359   if ( aUrl.isLocalFile() && QFileInfo( aUrl.pathOrURL() ).size() > maximumAttachmentSizeInByte ) {
02360     KMessageBox::sorry( this, i18n( "<qt><p>Your administrator has disallowed attaching files bigger than %1 MB.</p>" ).arg( maxAttachmentSize ) );
02361     return false;
02362   }
02363 
02364   KIO::TransferJob *job = KIO::get(aUrl);
02365   KIO::Scheduler::scheduleJob( job );
02366   atmLoadData ld;
02367   ld.url = aUrl;
02368   ld.data = QByteArray();
02369   ld.insert = false;
02370   if( !aUrl.fileEncoding().isEmpty() )
02371     ld.encoding = aUrl.fileEncoding().latin1();
02372 
02373   mMapAtmLoadData.insert(job, ld);
02374   mAttachJobs[job] = aUrl;
02375   connect(job, SIGNAL(result(KIO::Job *)),
02376           this, SLOT(slotAttachFileResult(KIO::Job *)));
02377   connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)),
02378           this, SLOT(slotAttachFileData(KIO::Job *, const QByteArray &)));
02379   return true;
02380 }
02381 
02382 
02383 //-----------------------------------------------------------------------------
02384 void KMComposeWin::addAttach(const KMMessagePart* msgPart)
02385 {
02386   mAtmList.append(msgPart);
02387 
02388   // show the attachment listbox if it does not up to now
02389   if (mAtmList.count()==1)
02390   {
02391     mAtmListView->resize(mAtmListView->width(), 50);
02392     mAtmListView->show();
02393     resize(size());
02394   }
02395 
02396   // add a line in the attachment listbox
02397   KMAtmListViewItem *lvi = new KMAtmListViewItem( mAtmListView );
02398   msgPartToItem(msgPart, lvi);
02399   mAtmItemList.append(lvi);
02400 
02401   // the Attach file job has finished, so the possibly present tmp dir can be deleted now.
02402   if ( mTempDir != 0 ) {
02403     delete mTempDir;
02404     mTempDir = 0;
02405   }
02406 
02407   connect( lvi, SIGNAL( compress( int ) ),
02408       this, SLOT( compressAttach( int ) ) );
02409   connect( lvi, SIGNAL( uncompress( int ) ),
02410       this, SLOT( uncompressAttach( int ) ) );
02411 
02412   slotUpdateAttachActions();
02413 }
02414 
02415 
02416 //-----------------------------------------------------------------------------
02417 void KMComposeWin::slotUpdateAttachActions()
02418 {
02419   int selectedCount = 0;
02420   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ++it ) {
02421     if ( (*it)->isSelected() ) {
02422       ++selectedCount;
02423     }
02424   }
02425 
02426   mAttachRemoveAction->setEnabled( selectedCount >= 1 );
02427   mAttachSaveAction->setEnabled( selectedCount == 1 );
02428   mAttachPropertiesAction->setEnabled( selectedCount == 1 );
02429 }
02430 
02431 
02432 //-----------------------------------------------------------------------------
02433 
02434 QString KMComposeWin::prettyMimeType( const QString& type )
02435 {
02436   QString t = type.lower();
02437   KServiceType::Ptr st = KServiceType::serviceType( t );
02438   return st ? st->comment() : t;
02439 }
02440 
02441 void KMComposeWin::msgPartToItem(const KMMessagePart* msgPart,
02442                                  KMAtmListViewItem *lvi, bool loadDefaults)
02443 {
02444   assert(msgPart != 0);
02445 
02446   if (!msgPart->fileName().isEmpty())
02447     lvi->setText(0, msgPart->fileName());
02448   else
02449     lvi->setText(0, msgPart->name());
02450   lvi->setText(1, KIO::convertSize( msgPart->decodedSize()));
02451   lvi->setText(2, msgPart->contentTransferEncodingStr());
02452   lvi->setText(3, prettyMimeType(msgPart->typeStr() + "/" + msgPart->subtypeStr()));
02453   lvi->setAttachmentSize(msgPart->decodedSize());
02454 
02455   if ( loadDefaults ) {
02456     if( canSignEncryptAttachments() ) {
02457       lvi->enableCryptoCBs( true );
02458       lvi->setEncrypt( mEncryptAction->isChecked() );
02459       lvi->setSign(    mSignAction->isChecked() );
02460     } else {
02461       lvi->enableCryptoCBs( false );
02462     }
02463   }
02464 }
02465 
02466 
02467 //-----------------------------------------------------------------------------
02468 void KMComposeWin::removeAttach(const QString &aUrl)
02469 {
02470   int idx;
02471   KMMessagePart* msgPart;
02472   for(idx=0,msgPart=mAtmList.first(); msgPart;
02473       msgPart=mAtmList.next(),idx++) {
02474     if (msgPart->name() == aUrl) {
02475       removeAttach(idx);
02476       return;
02477     }
02478   }
02479 }
02480 
02481 
02482 //-----------------------------------------------------------------------------
02483 void KMComposeWin::removeAttach(int idx)
02484 {
02485   mAtmModified = true;
02486   mAtmList.remove(idx);
02487   delete mAtmItemList.take(idx);
02488 
02489   if( mAtmList.isEmpty() )
02490   {
02491     mAtmListView->hide();
02492     mAtmListView->setMinimumSize(0, 0);
02493     resize(size());
02494   }
02495 }
02496 
02497 
02498 //-----------------------------------------------------------------------------
02499 bool KMComposeWin::encryptFlagOfAttachment(int idx)
02500 {
02501   return (int)(mAtmItemList.count()) > idx
02502     ? static_cast<KMAtmListViewItem*>( mAtmItemList.at( idx ) )->isEncrypt()
02503     : false;
02504 }
02505 
02506 
02507 //-----------------------------------------------------------------------------
02508 bool KMComposeWin::signFlagOfAttachment(int idx)
02509 {
02510   return (int)(mAtmItemList.count()) > idx
02511     ? ((KMAtmListViewItem*)(mAtmItemList.at( idx )))->isSign()
02512     : false;
02513 }
02514 
02515 
02516 //-----------------------------------------------------------------------------
02517 void KMComposeWin::addrBookSelInto()
02518 {
02519   if ( mClassicalRecipients ) {
02520     if ( GlobalSettings::self()->addresseeSelectorType() ==
02521          GlobalSettings::EnumAddresseeSelectorType::New ) {
02522       addrBookSelIntoNew();
02523     } else {
02524       addrBookSelIntoOld();
02525     }
02526   } else {
02527     kdWarning() << "To be implemented: call recipients picker." << endl;
02528   }
02529 }
02530 
02531 void KMComposeWin::addrBookSelIntoOld()
02532 {
02533   AddressesDialog dlg( this );
02534   QString txt;
02535   QStringList lst;
02536 
02537   txt = to();
02538   if ( !txt.isEmpty() ) {
02539       lst = KPIM::splitEmailAddrList( txt );
02540       dlg.setSelectedTo( lst );
02541   }
02542 
02543   txt = mEdtCc->text();
02544   if ( !txt.isEmpty() ) {
02545       lst = KPIM::splitEmailAddrList( txt );
02546       dlg.setSelectedCC( lst );
02547   }
02548 
02549   txt = mEdtBcc->text();
02550   if ( !txt.isEmpty() ) {
02551       lst = KPIM::splitEmailAddrList( txt );
02552       dlg.setSelectedBCC( lst );
02553   }
02554 
02555   dlg.setRecentAddresses( RecentAddresses::self( KMKernel::config() )->kabcAddresses() );
02556 
02557   if (dlg.exec()==QDialog::Rejected) return;
02558 
02559   mEdtTo->setText( dlg.to().join(", ") );
02560   mEdtTo->setEdited( true );
02561 
02562   mEdtCc->setText( dlg.cc().join(", ") );
02563   mEdtCc->setEdited( true );
02564 
02565   mEdtBcc->setText( dlg.bcc().join(", ") );
02566   mEdtBcc->setEdited( true );
02567 
02568   //Make sure BCC field is shown if needed
02569   if ( !mEdtBcc->text().isEmpty() ) {
02570     mShowHeaders |= HDR_BCC;
02571     rethinkFields( false );
02572   }
02573 }
02574 
02575 void KMComposeWin::addrBookSelIntoNew()
02576 {
02577   AddresseeEmailSelection selection;
02578 
02579   AddresseeSelectorDialog dlg( &selection );
02580 
02581   QString txt;
02582   QStringList lst;
02583 
02584   txt = to();
02585   if ( !txt.isEmpty() ) {
02586       lst = KPIM::splitEmailAddrList( txt );
02587       selection.setSelectedTo( lst );
02588   }
02589 
02590   txt = mEdtCc->text();
02591   if ( !txt.isEmpty() ) {
02592       lst = KPIM::splitEmailAddrList( txt );
02593       selection.setSelectedCC( lst );
02594   }
02595 
02596   txt = mEdtBcc->text();
02597   if ( !txt.isEmpty() ) {
02598       lst = KPIM::splitEmailAddrList( txt );
02599       selection.setSelectedBCC( lst );
02600   }
02601 
02602   if (dlg.exec()==QDialog::Rejected) return;
02603 
02604   QStringList list = selection.to() + selection.toDistributionLists();
02605   mEdtTo->setText( list.join(", ") );
02606   mEdtTo->setEdited( true );
02607 
02608   list = selection.cc() + selection.ccDistributionLists();
02609   mEdtCc->setText( list.join(", ") );
02610   mEdtCc->setEdited( true );
02611 
02612   list = selection.bcc() + selection.bccDistributionLists();
02613   mEdtBcc->setText( list.join(", ") );
02614   mEdtBcc->setEdited( true );
02615 
02616   //Make sure BCC field is shown if needed
02617   if ( !mEdtBcc->text().isEmpty() ) {
02618     mShowHeaders |= HDR_BCC;
02619     rethinkFields( false );
02620   }
02621 }
02622 
02623 
02624 //-----------------------------------------------------------------------------
02625 void KMComposeWin::setCharset(const QCString& aCharset, bool forceDefault)
02626 {
02627   if ((forceDefault && GlobalSettings::self()->forceReplyCharset()) || aCharset.isEmpty())
02628     mCharset = mDefCharset;
02629   else
02630     mCharset = aCharset.lower();
02631 
02632   if ( mCharset.isEmpty() || mCharset == "default" )
02633      mCharset = mDefCharset;
02634 
02635   if (mAutoCharset)
02636   {
02637     mEncodingAction->setCurrentItem( 0 );
02638     return;
02639   }
02640 
02641   QStringList encodings = mEncodingAction->items();
02642   int i = 0;
02643   bool charsetFound = false;
02644   for ( QStringList::Iterator it = encodings.begin(); it != encodings.end();
02645      ++it, i++ )
02646   {
02647     if (i > 0 && ((mCharset == "us-ascii" && i == 1) ||
02648      (i != 1 && KGlobal::charsets()->codecForName(
02649       KGlobal::charsets()->encodingForName(*it))
02650       == KGlobal::charsets()->codecForName(mCharset))))
02651     {
02652       mEncodingAction->setCurrentItem( i );
02653       slotSetCharset();
02654       charsetFound = true;
02655       break;
02656     }
02657   }
02658   if (!aCharset.isEmpty() && !charsetFound) setCharset("", true);
02659 }
02660 
02661 
02662 //-----------------------------------------------------------------------------
02663 void KMComposeWin::slotAddrBook()
02664 {
02665   KAddrBookExternal::openAddressBook(this);
02666 }
02667 
02668 
02669 //-----------------------------------------------------------------------------
02670 void KMComposeWin::slotAddrBookFrom()
02671 {
02672   addrBookSelInto();
02673 }
02674 
02675 
02676 //-----------------------------------------------------------------------------
02677 void KMComposeWin::slotAddrBookReplyTo()
02678 {
02679   addrBookSelInto();
02680 }
02681 
02682 
02683 //-----------------------------------------------------------------------------
02684 void KMComposeWin::slotAddrBookTo()
02685 {
02686   addrBookSelInto();
02687 }
02688 
02689 //-----------------------------------------------------------------------------
02690 void KMComposeWin::slotAttachFile()
02691 {
02692   // Create File Dialog and return selected file(s)
02693   // We will not care about any permissions, existence or whatsoever in
02694   // this function.
02695 
02696   KFileDialog fdlg(QString::null, QString::null, this, 0, true);
02697   fdlg.setOperationMode( KFileDialog::Other );
02698   fdlg.setCaption(i18n("Attach File"));
02699   fdlg.okButton()->setGuiItem(KGuiItem(i18n("&Attach"),"fileopen"));
02700   fdlg.setMode(KFile::Files);
02701   fdlg.exec();
02702   KURL::List files = fdlg.selectedURLs();
02703 
02704   for (KURL::List::Iterator it = files.begin(); it != files.end(); ++it)
02705     addAttach(*it);
02706 }
02707 
02708 
02709 //-----------------------------------------------------------------------------
02710 void KMComposeWin::slotAttachFileData(KIO::Job *job, const QByteArray &data)
02711 {
02712   QMap<KIO::Job*, atmLoadData>::Iterator it = mMapAtmLoadData.find(job);
02713   assert(it != mMapAtmLoadData.end());
02714   QBuffer buff((*it).data);
02715   buff.open(IO_WriteOnly | IO_Append);
02716   buff.writeBlock(data.data(), data.size());
02717   buff.close();
02718 }
02719 
02720 
02721 //-----------------------------------------------------------------------------
02722 void KMComposeWin::slotAttachFileResult(KIO::Job *job)
02723 {
02724   QMap<KIO::Job*, atmLoadData>::Iterator it = mMapAtmLoadData.find(job);
02725   assert(it != mMapAtmLoadData.end());
02726   KURL attachURL;
02727   QMap<KIO::Job*, KURL>::iterator jit = mAttachJobs.find(job);
02728   bool attachURLfound = (jit != mAttachJobs.end());
02729   if (attachURLfound)
02730   {
02731     attachURL = jit.data();
02732     mAttachJobs.remove(jit);
02733   }
02734   if (job->error())
02735   {
02736     mMapAtmLoadData.remove(it);
02737     job->showErrorDialog();
02738     if (attachURLfound)
02739       emit attachmentAdded(attachURL, false);
02740     return;
02741   }
02742   if ((*it).insert)
02743   {
02744     (*it).data.resize((*it).data.size() + 1);
02745     (*it).data[(*it).data.size() - 1] = '\0';
02746     if ( const QTextCodec * codec = KGlobal::charsets()->codecForName((*it).encoding) )
02747       mEditor->insert( codec->toUnicode( (*it).data ) );
02748     else
02749       mEditor->insert( QString::fromLocal8Bit( (*it).data ) );
02750     mMapAtmLoadData.remove(it);
02751     if (attachURLfound)
02752       emit attachmentAdded(attachURL, true);
02753     return;
02754   }
02755   QCString partCharset;
02756   if ( !( *it ).url.fileEncoding().isEmpty() ) {
02757     partCharset = QCString( ( *it ).url.fileEncoding().latin1() );
02758   } else {
02759     EncodingDetector ed;
02760     KLocale *loc = KGlobal::locale();
02761     ed.setAutoDetectLanguage( EncodingDetector::scriptForLanguageCode ( loc->language() ) );
02762     ed.analyze( (*it).data );
02763     partCharset = ed.encoding();
02764     if ( partCharset.isEmpty() ) //shouldn't happen
02765       partCharset = mCharset;
02766   }
02767 
02768   KMMessagePart* msgPart;
02769 
02770   KCursorSaver busy(KBusyPtr::busy());
02771   QString name( (*it).url.fileName() );
02772   // ask the job for the mime type of the file
02773   QString mimeType = static_cast<KIO::MimetypeJob*>(job)->mimetype();
02774 
02775   if ( name.isEmpty() ) {
02776     // URL ends with '/' (e.g. http://www.kde.org/)
02777     // guess a reasonable filename
02778     if( mimeType == "text/html" )
02779       name = "index.html";
02780     else {
02781       // try to determine a reasonable extension
02782       QStringList patterns( KMimeType::mimeType( mimeType )->patterns() );
02783       QString ext;
02784       if( !patterns.isEmpty() ) {
02785         ext = patterns[0];
02786         int i = ext.findRev( '.' );
02787         if( i == -1 )
02788           ext.prepend( '.' );
02789         else if( i > 0 )
02790           ext = ext.mid( i );
02791       }
02792       name = QString("unknown") += ext;
02793     }
02794   }
02795 
02796   name.truncate( 256 ); // is this needed?
02797 
02798   QCString encoding = KMMsgBase::autoDetectCharset(partCharset,
02799     KMMessage::preferredCharsets(), name);
02800   if ( encoding.isEmpty() )
02801     encoding = "utf-8";
02802 
02803   QCString encName;
02804   if ( GlobalSettings::self()->outlookCompatibleAttachments() )
02805     encName = KMMsgBase::encodeRFC2047String( name, encoding );
02806   else
02807     encName = KMMsgBase::encodeRFC2231String( name, encoding );
02808   bool RFC2231encoded = false;
02809   if ( !GlobalSettings::self()->outlookCompatibleAttachments() )
02810     RFC2231encoded = name != QString( encName );
02811 
02812   // create message part
02813   msgPart = new KMMessagePart;
02814   msgPart->setName(name);
02815   QValueList<int> allowedCTEs;
02816   if ( mimeType == "message/rfc822" ) {
02817     msgPart->setMessageBody( (*it).data );
02818     allowedCTEs << DwMime::kCte7bit;
02819     allowedCTEs << DwMime::kCte8bit;
02820   } else {
02821     msgPart->setBodyAndGuessCte((*it).data, allowedCTEs,
02822                                !kmkernel->msgSender()->sendQuotedPrintable());
02823     kdDebug(5006) << "autodetected cte: " << msgPart->cteStr() << endl;
02824   }
02825   int slash = mimeType.find( '/' );
02826   if( slash == -1 )
02827     slash = mimeType.length();
02828   msgPart->setTypeStr( mimeType.left( slash ).latin1() );
02829   msgPart->setSubtypeStr( mimeType.mid( slash + 1 ).latin1() );
02830   msgPart->setContentDisposition(QCString("attachment;\n\tfilename")
02831     + ( RFC2231encoded ? "*=" + encName : "=\"" + encName + '"' ) );
02832 
02833   mMapAtmLoadData.remove(it);
02834 
02835   msgPart->setCharset(partCharset);
02836 
02837   // show message part dialog, if not configured away (default):
02838   KConfigGroup composer(KMKernel::config(), "Composer");
02839   if ( GlobalSettings::self()->showMessagePartDialogOnAttach() ) {
02840     const KCursorSaver saver( QCursor::ArrowCursor );
02841     KMMsgPartDialogCompat dlg(mMainWidget);
02842     int encodings = 0;
02843     for ( QValueListConstIterator<int> it = allowedCTEs.begin() ;
02844           it != allowedCTEs.end() ; ++it )
02845       switch ( *it ) {
02846       case DwMime::kCteBase64: encodings |= KMMsgPartDialog::Base64; break;
02847       case DwMime::kCteQp: encodings |= KMMsgPartDialog::QuotedPrintable; break;
02848       case DwMime::kCte7bit: encodings |= KMMsgPartDialog::SevenBit; break;
02849       case DwMime::kCte8bit: encodings |= KMMsgPartDialog::EightBit; break;
02850       default: ;
02851       }
02852     dlg.setShownEncodings( encodings );
02853     dlg.setMsgPart(msgPart);
02854     if (!dlg.exec()) {
02855       delete msgPart;
02856       msgPart = 0;
02857       if (attachURLfound)
02858         emit attachmentAdded(attachURL, false);
02859       return;
02860     }
02861   }
02862   mAtmModified = true;
02863   if (msgPart->typeStr().lower() != "text") msgPart->setCharset(QCString());
02864 
02865   // add the new attachment to the list
02866   addAttach(msgPart);
02867 
02868   if (attachURLfound)
02869     emit attachmentAdded(attachURL, true);
02870 }
02871 
02872 
02873 //-----------------------------------------------------------------------------
02874 void KMComposeWin::slotInsertFile()
02875 {
02876   KFileDialog fdlg(QString::null, QString::null, this, 0, true);
02877   fdlg.setOperationMode( KFileDialog::Opening );
02878   fdlg.okButton()->setText(i18n("&Insert"));
02879   fdlg.setCaption(i18n("Insert File"));
02880   fdlg.toolBar()->insertCombo(KMMsgBase::supportedEncodings(false), 4711,
02881     false, 0, 0, 0);
02882   KComboBox *combo = fdlg.toolBar()->getCombo(4711);
02883   for (int i = 0; i < combo->count(); i++)
02884     if (KGlobal::charsets()->codecForName(KGlobal::charsets()->
02885       encodingForName(combo->text(i)))
02886       == QTextCodec::codecForLocale()) combo->setCurrentItem(i);
02887   if (!fdlg.exec()) return;
02888 
02889   KURL u = fdlg.selectedURL();
02890   mRecentAction->addURL(u);
02891   // Prevent race condition updating list when multiple composers are open
02892   {
02893     KConfig *config = KMKernel::config();
02894     KConfigGroupSaver saver( config, "Composer" );
02895     QString encoding = KGlobal::charsets()->encodingForName(combo->currentText()).latin1();
02896     QStringList urls = config->readListEntry( "recent-urls" );
02897     QStringList encodings = config->readListEntry( "recent-encodings" );
02898     // Prevent config file from growing without bound
02899     // Would be nicer to get this constant from KRecentFilesAction
02900     uint mMaxRecentFiles = 30;
02901     while (urls.count() > mMaxRecentFiles)
02902       urls.erase( urls.fromLast() );
02903     while (encodings.count() > mMaxRecentFiles)
02904       encodings.erase( encodings.fromLast() );
02905     // sanity check
02906     if (urls.count() != encodings.count()) {
02907       urls.clear();
02908       encodings.clear();
02909     }
02910     urls.prepend( u.prettyURL() );
02911     encodings.prepend( encoding );
02912     config->writeEntry( "recent-urls", urls );
02913     config->writeEntry( "recent-encodings", encodings );
02914     mRecentAction->saveEntries( config );
02915   }
02916   slotInsertRecentFile(u);
02917 }
02918 
02919 
02920 //-----------------------------------------------------------------------------
02921 void KMComposeWin::slotInsertRecentFile(const KURL& u)
02922 {
02923   if (u.fileName().isEmpty()) return;
02924 
02925   KIO::Job *job = KIO::get(u);
02926   atmLoadData ld;
02927   ld.url = u;
02928   ld.data = QByteArray();
02929   ld.insert = true;
02930   // Get the encoding previously used when inserting this file
02931   {
02932     KConfig *config = KMKernel::config();
02933     KConfigGroupSaver saver( config, "Composer" );
02934     QStringList urls = config->readListEntry( "recent-urls" );
02935     QStringList encodings = config->readListEntry( "recent-encodings" );
02936     int index = urls.findIndex( u.prettyURL() );
02937     if (index != -1) {
02938       QString encoding = encodings[ index ];
02939       ld.encoding = encoding.latin1();
02940     }
02941   }
02942   mMapAtmLoadData.insert(job, ld);
02943   connect(job, SIGNAL(result(KIO::Job *)),
02944           this, SLOT(slotAttachFileResult(KIO::Job *)));
02945   connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)),
02946           this, SLOT(slotAttachFileData(KIO::Job *, const QByteArray &)));
02947 }
02948 
02949 
02950 //-----------------------------------------------------------------------------
02951 void KMComposeWin::slotSetCharset()
02952 {
02953   if (mEncodingAction->currentItem() == 0)
02954   {
02955     mAutoCharset = true;
02956     return;
02957   }
02958   mAutoCharset = false;
02959 
02960   mCharset = KGlobal::charsets()->encodingForName( mEncodingAction->
02961     currentText() ).latin1();
02962 }
02963 
02964 
02965 //-----------------------------------------------------------------------------
02966 void KMComposeWin::slotSelectCryptoModule( bool init )
02967 {
02968   if ( !init ) {
02969     setModified( true );
02970   }
02971   if( canSignEncryptAttachments() ) {
02972     // if the encrypt/sign columns are hidden then show them
02973     if( 0 == mAtmListView->columnWidth( mAtmColEncrypt ) ) {
02974       // set/unset signing/encryption for all attachments according to the
02975       // state of the global sign/encrypt action
02976       if( !mAtmList.isEmpty() ) {
02977         for( KMAtmListViewItem* lvi = static_cast<KMAtmListViewItem*>( mAtmItemList.first() );
02978              lvi;
02979              lvi = static_cast<KMAtmListViewItem*>( mAtmItemList.next() ) ) {
02980           lvi->setSign( mSignAction->isChecked() );
02981           lvi->setEncrypt( mEncryptAction->isChecked() );
02982         }
02983       }
02984       int totalWidth = 0;
02985       // determine the total width of the columns
02986       for( int col=0; col < mAtmColEncrypt; col++ )
02987         totalWidth += mAtmListView->columnWidth( col );
02988       int reducedTotalWidth = totalWidth - mAtmEncryptColWidth
02989                                          - mAtmSignColWidth;
02990       // reduce the width of all columns so that the encrypt and sign column
02991       // fit
02992       int usedWidth = 0;
02993       for( int col=0; col < mAtmColEncrypt-1; col++ ) {
02994         int newWidth = mAtmListView->columnWidth( col ) * reducedTotalWidth
02995                                                        / totalWidth;
02996         mAtmListView->setColumnWidth( col, newWidth );
02997         usedWidth += newWidth;
02998       }
02999       // the last column before the encrypt column gets the remaining space
03000       // (because of rounding errors the width of this column isn't calculated
03001       // the same way as the width of the other columns)
03002       mAtmListView->setColumnWidth( mAtmColEncrypt-1,
03003                                     reducedTotalWidth - usedWidth );
03004       mAtmListView->setColumnWidth( mAtmColEncrypt, mAtmEncryptColWidth );
03005       mAtmListView->setColumnWidth( mAtmColSign,    mAtmSignColWidth );
03006       for( KMAtmListViewItem* lvi = static_cast<KMAtmListViewItem*>( mAtmItemList.first() );
03007            lvi;
03008            lvi = static_cast<KMAtmListViewItem*>( mAtmItemList.next() ) ) {
03009         lvi->enableCryptoCBs( true );
03010       }
03011     }
03012   } else {
03013     // if the encrypt/sign columns are visible then hide them
03014     if( 0 != mAtmListView->columnWidth( mAtmColEncrypt ) ) {
03015       mAtmEncryptColWidth = mAtmListView->columnWidth( mAtmColEncrypt );
03016       mAtmSignColWidth = mAtmListView->columnWidth( mAtmColSign );
03017       int totalWidth = 0;
03018       // determine the total width of the columns
03019       for( int col=0; col < mAtmListView->columns(); col++ )
03020         totalWidth += mAtmListView->columnWidth( col );
03021       int reducedTotalWidth = totalWidth - mAtmEncryptColWidth
03022                                          - mAtmSignColWidth;
03023       // increase the width of all columns so that the visible columns take
03024       // up the whole space
03025       int usedWidth = 0;
03026       for( int col=0; col < mAtmColEncrypt-1; col++ ) {
03027         int newWidth = mAtmListView->columnWidth( col ) * totalWidth
03028                                                        / reducedTotalWidth;
03029         mAtmListView->setColumnWidth( col, newWidth );
03030         usedWidth += newWidth;
03031       }
03032       // the last column before the encrypt column gets the remaining space
03033       // (because of rounding errors the width of this column isn't calculated
03034       // the same way as the width of the other columns)
03035       mAtmListView->setColumnWidth( mAtmColEncrypt-1, totalWidth - usedWidth );
03036       mAtmListView->setColumnWidth( mAtmColEncrypt, 0 );
03037       mAtmListView->setColumnWidth( mAtmColSign,    0 );
03038       for( KMAtmListViewItem* lvi = static_cast<KMAtmListViewItem*>( mAtmItemList.first() );
03039            lvi;
03040            lvi = static_cast<KMAtmListViewItem*>( mAtmItemList.next() ) ) {
03041         lvi->enableCryptoCBs( false );
03042       }
03043     }
03044   }
03045 }
03046 
03047 static void showExportError( QWidget * w, const GpgME::Error & err ) {
03048   assert( err );
03049   const QString msg = i18n("<qt><p>An error occurred while trying to export "
03050                "the key from the backend:</p>"
03051                "<p><b>%1</b></p></qt>")
03052     .arg( QString::fromLocal8Bit( err.asString() ) );
03053   KMessageBox::error( w, msg, i18n("Key Export Failed") );
03054 }
03055 
03056 
03057 //-----------------------------------------------------------------------------
03058 void KMComposeWin::slotInsertMyPublicKey()
03059 {
03060   // get PGP user id for the chosen identity
03061   mFingerprint =
03062     kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() ).pgpEncryptionKey();
03063   if ( !mFingerprint.isEmpty() )
03064     startPublicKeyExport();
03065 }
03066 
03067 void KMComposeWin::startPublicKeyExport() {
03068   if ( mFingerprint.isEmpty() || !Kleo::CryptoBackendFactory::instance()->openpgp() )
03069     return;
03070   Kleo::ExportJob * job = Kleo::CryptoBackendFactory::instance()->openpgp()->publicKeyExportJob( true );
03071   assert( job );
03072 
03073   connect( job, SIGNAL(result(const GpgME::Error&,const QByteArray&)),
03074        this, SLOT(slotPublicKeyExportResult(const GpgME::Error&,const QByteArray&)) );
03075 
03076   const GpgME::Error err = job->start( mFingerprint );
03077   if ( err )
03078     showExportError( this, err );
03079   else
03080     (void)new Kleo::ProgressDialog( job, i18n("Exporting key..."), this );
03081 }
03082 
03083 void KMComposeWin::slotPublicKeyExportResult( const GpgME::Error & err, const QByteArray & keydata ) {
03084   if ( err ) {
03085     showExportError( this, err );
03086     return;
03087   }
03088 
03089   // create message part
03090   KMMessagePart * msgPart = new KMMessagePart();
03091   msgPart->setName( i18n("OpenPGP key 0x%1").arg( mFingerprint ) );
03092   msgPart->setTypeStr("application");
03093   msgPart->setSubtypeStr("pgp-keys");
03094   QValueList<int> dummy;
03095   msgPart->setBodyAndGuessCte(keydata, dummy, false);
03096   msgPart->setContentDisposition( "attachment;\n\tfilename=0x" + QCString( mFingerprint.latin1() ) + ".asc" );
03097 
03098   // add the new attachment to the list
03099   addAttach(msgPart);
03100   rethinkFields(); //work around initial-size bug in Qt-1.32
03101 }
03102 
03103 //-----------------------------------------------------------------------------
03104 void KMComposeWin::slotInsertPublicKey()
03105 {
03106   Kleo::KeySelectionDialog dlg( i18n("Attach Public OpenPGP Key"),
03107                                 i18n("Select the public key which should "
03108                                      "be attached."),
03109                 std::vector<GpgME::Key>(),
03110                 Kleo::KeySelectionDialog::PublicKeys|Kleo::KeySelectionDialog::OpenPGPKeys,
03111                 false /* no multi selection */,
03112                 false /* no remember choice box */,
03113                 this, "attach public key selection dialog" );
03114   if ( dlg.exec() != QDialog::Accepted )
03115     return;
03116 
03117   mFingerprint = dlg.fingerprint();
03118   startPublicKeyExport();
03119 }
03120 
03121 
03122 //-----------------------------------------------------------------------------
03123 void KMComposeWin::slotAttachPopupMenu(QListViewItem *, const QPoint &, int)
03124 {
03125   if (!mAttachMenu)
03126   {
03127      mAttachMenu = new QPopupMenu(this);
03128 
03129      mOpenId = mAttachMenu->insertItem(i18n("to open", "Open"), this,
03130                              SLOT(slotAttachOpen()));
03131      mOpenWithId = mAttachMenu->insertItem(i18n("Open With..."), this,
03132                              SLOT(slotAttachOpenWith()));
03133      mViewId = mAttachMenu->insertItem(i18n("to view", "View"), this,
03134                              SLOT(slotAttachView()));
03135      mEditId = mAttachMenu->insertItem( i18n("Edit"), this, SLOT(slotAttachEdit()) );
03136      mEditWithId = mAttachMenu->insertItem( i18n("Edit With..."), this,
03137                                             SLOT(slotAttachEditWith()) );
03138      mRemoveId = mAttachMenu->insertItem(i18n("Remove"), this, SLOT(slotAttachRemove()));
03139      mSaveAsId = mAttachMenu->insertItem( SmallIconSet("filesaveas"), i18n("Save As..."), this,
03140                                           SLOT( slotAttachSave() ) );
03141      mPropertiesId = mAttachMenu->insertItem( i18n("Properties"), this,
03142                                               SLOT( slotAttachProperties() ) );
03143      mAttachMenu->insertSeparator();
03144      mAttachMenu->insertItem(i18n("Add Attachment..."), this, SLOT(slotAttachFile()));
03145   }
03146 
03147   int selectedCount = 0;
03148   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ++it ) {
03149     if ( (*it)->isSelected() ) {
03150       ++selectedCount;
03151     }
03152   }
03153 
03154   mAttachMenu->setItemEnabled( mOpenId, selectedCount > 0 );
03155   mAttachMenu->setItemEnabled( mOpenWithId, selectedCount > 0 );
03156   mAttachMenu->setItemEnabled( mViewId, selectedCount > 0 );
03157   mAttachMenu->setItemEnabled( mEditId, selectedCount == 1 );
03158   mAttachMenu->setItemEnabled( mEditWithId, selectedCount == 1 );
03159   mAttachMenu->setItemEnabled( mRemoveId, selectedCount > 0 );
03160   mAttachMenu->setItemEnabled( mSaveAsId, selectedCount == 1 );
03161   mAttachMenu->setItemEnabled( mPropertiesId, selectedCount == 1 );
03162 
03163   mAttachMenu->popup(QCursor::pos());
03164 }
03165 
03166 //-----------------------------------------------------------------------------
03167 int KMComposeWin::currentAttachmentNum()
03168 {
03169   int i = 0;
03170   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ++it, ++i )
03171     if ( *it == mAtmListView->currentItem() )
03172       return i;
03173   return -1;
03174 }
03175 
03176 //-----------------------------------------------------------------------------
03177 void KMComposeWin::slotAttachProperties()
03178 {
03179   int idx = currentAttachmentNum();
03180 
03181   if (idx < 0) return;
03182 
03183   KMMessagePart* msgPart = mAtmList.at(idx);
03184   msgPart->setCharset(mCharset);
03185 
03186   KMMsgPartDialogCompat dlg(mMainWidget);
03187   dlg.setMsgPart(msgPart);
03188   KMAtmListViewItem* listItem = (KMAtmListViewItem*)(mAtmItemList.at(idx));
03189   if( canSignEncryptAttachments() && listItem ) {
03190     dlg.setCanSign(    true );
03191     dlg.setCanEncrypt( true );
03192     dlg.setSigned(    listItem->isSign()    );
03193     dlg.setEncrypted( listItem->isEncrypt() );
03194   } else {
03195     dlg.setCanSign(    false );
03196     dlg.setCanEncrypt( false );
03197   }
03198   if (dlg.exec())
03199   {
03200     mAtmModified = true;
03201     // values may have changed, so recreate the listbox line
03202     if( listItem ) {
03203       msgPartToItem(msgPart, listItem);
03204       if( canSignEncryptAttachments() ) {
03205         listItem->setSign(    dlg.isSigned()    );
03206         listItem->setEncrypt( dlg.isEncrypted() );
03207       }
03208     }
03209   }
03210   if (msgPart->typeStr().lower() != "text") msgPart->setCharset(QCString());
03211 }
03212 
03213 //-----------------------------------------------------------------------------
03214 void KMComposeWin::compressAttach( int idx )
03215 {
03216   if (idx < 0) return;
03217 
03218   unsigned int i;
03219   for ( i = 0; i < mAtmItemList.count(); ++i )
03220       if ( mAtmItemList.at( i )->itemPos() == idx )
03221           break;
03222 
03223   if ( i > mAtmItemList.count() )
03224       return;
03225 
03226   KMMessagePart* msgPart;
03227   msgPart = mAtmList.at( i );
03228   QByteArray array;
03229   QBuffer dev( array );
03230   KZip zip( &dev );
03231   QByteArray decoded = msgPart->bodyDecodedBinary();
03232   if ( ! zip.open( IO_WriteOnly ) ) {
03233     KMessageBox::sorry(0, i18n("KMail could not compress the file.") );
03234     static_cast<KMAtmListViewItem*>( mAtmItemList.at( i ) )->setCompress( false );
03235     return;
03236   }
03237 
03238   zip.setCompression( KZip::DeflateCompression );
03239   if ( ! zip.writeFile( msgPart->name(), "", "", decoded.size(),
03240            decoded.data() ) ) {
03241     KMessageBox::sorry(0, i18n("KMail could not compress the file.") );
03242     static_cast<KMAtmListViewItem*>( mAtmItemList.at( i ) )->setCompress( false );
03243     return;
03244   }
03245   zip.close();
03246   if ( array.size() >= decoded.size() ) {
03247     if ( KMessageBox::questionYesNo( this, i18n("The compressed file is larger "
03248         "than the original. Do you want to keep the original one?" ), QString::null, i18n("Keep"), i18n("Compress") )
03249          == KMessageBox::Yes ) {
03250       static_cast<KMAtmListViewItem*>( mAtmItemList.at( i ) )->setCompress( false );
03251       return;
03252     }
03253   }
03254   static_cast<KMAtmListViewItem*>( mAtmItemList.at( i ) )->setUncompressedCodec(
03255       msgPart->cteStr() );
03256 
03257   msgPart->setCteStr( "base64" );
03258   msgPart->setBodyEncodedBinary( array );
03259   QString name = msgPart->name() + ".zip";
03260 
03261   msgPart->setName( name );
03262 
03263   QCString cDisp = "attachment;";
03264   QCString encoding = KMMsgBase::autoDetectCharset( msgPart->charset(),
03265     KMMessage::preferredCharsets(), name );
03266   kdDebug(5006) << "encoding: " << encoding << endl;
03267   if ( encoding.isEmpty() ) encoding = "utf-8";
03268   kdDebug(5006) << "encoding after: " << encoding << endl;
03269   QCString encName;
03270   if ( GlobalSettings::self()->outlookCompatibleAttachments() )
03271     encName = KMMsgBase::encodeRFC2047String( name, encoding );
03272   else
03273     encName = KMMsgBase::encodeRFC2231String( name, encoding );
03274 
03275   cDisp += "\n\tfilename";
03276   if ( name != QString( encName ) )
03277     cDisp += "*=" + encName;
03278   else
03279     cDisp += "=\"" + encName + '"';
03280   msgPart->setContentDisposition( cDisp );
03281 
03282   static_cast<KMAtmListViewItem*>( mAtmItemList.at( i ) )->setUncompressedMimeType(
03283       msgPart->typeStr(), msgPart->subtypeStr() );
03284   msgPart->setTypeStr( "application" );
03285   msgPart->setSubtypeStr( "x-zip" );
03286 
03287   KMAtmListViewItem* listItem = static_cast<KMAtmListViewItem*>( mAtmItemList.at( i ) );
03288   msgPartToItem( msgPart, listItem, false );
03289 }
03290 
03291 //-----------------------------------------------------------------------------
03292 
03293 void KMComposeWin::uncompressAttach( int idx )
03294 {
03295   if (idx < 0) return;
03296 
03297   unsigned int i;
03298   for ( i = 0; i < mAtmItemList.count(); ++i )
03299       if ( mAtmItemList.at( i )->itemPos() == idx )
03300           break;
03301 
03302   if ( i > mAtmItemList.count() )
03303       return;
03304 
03305   KMMessagePart* msgPart;
03306   msgPart = mAtmList.at( i );
03307 
03308   QBuffer dev( msgPart->bodyDecodedBinary() );
03309   KZip zip( &dev );
03310   QByteArray decoded;
03311 
03312   decoded = msgPart->bodyDecodedBinary();
03313   if ( ! zip.open( IO_ReadOnly ) ) {
03314     KMessageBox::sorry(0, i18n("KMail could not uncompress the file.") );
03315     static_cast<KMAtmListViewItem *>( mAtmItemList.at( i ) )->setCompress( true );
03316     return;
03317   }
03318   const KArchiveDirectory *dir = zip.directory();
03319 
03320   KZipFileEntry *entry;
03321   if ( dir->entries().count() != 1 ) {
03322     KMessageBox::sorry(0, i18n("KMail could not uncompress the file.") );
03323     static_cast<KMAtmListViewItem *>( mAtmItemList.at( i ) )->setCompress( true );
03324     return;
03325   }
03326   entry = (KZipFileEntry*)dir->entry( dir->entries()[0] );
03327 
03328   msgPart->setCteStr(
03329       static_cast<KMAtmListViewItem*>( mAtmItemList.at(i) )->uncompressedCodec() );
03330 
03331   msgPart->setBodyEncodedBinary( entry->data() );
03332   QString name = entry->name();
03333   msgPart->setName( name );
03334 
03335   zip.close();
03336 
03337   QCString cDisp = "attachment;";
03338   QCString encoding = KMMsgBase::autoDetectCharset( msgPart->charset(),
03339     KMMessage::preferredCharsets(), name );
03340   if ( encoding.isEmpty() ) encoding = "utf-8";
03341 
03342   QCString encName;
03343   if ( GlobalSettings::self()->outlookCompatibleAttachments() )
03344     encName = KMMsgBase::encodeRFC2047String( name, encoding );
03345   else
03346     encName = KMMsgBase::encodeRFC2231String( name, encoding );
03347 
03348   cDisp += "\n\tfilename";
03349   if ( name != QString( encName ) )
03350     cDisp += "*=" + encName;
03351   else
03352     cDisp += "=\"" + encName + '"';
03353   msgPart->setContentDisposition( cDisp );
03354 
03355   QCString type, subtype;
03356   static_cast<KMAtmListViewItem*>( mAtmItemList.at( i ) )->uncompressedMimeType( type,
03357         subtype );
03358 
03359   msgPart->setTypeStr( type );
03360   msgPart->setSubtypeStr( subtype );
03361 
03362   KMAtmListViewItem* listItem = static_cast<KMAtmListViewItem*>(mAtmItemList.at( i ));
03363   msgPartToItem( msgPart, listItem, false );
03364 }
03365 
03366 
03367 //-----------------------------------------------------------------------------
03368 void KMComposeWin::slotAttachView()
03369 {
03370   int i = 0;
03371   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ++it, ++i ) {
03372     if ( (*it)->isSelected() ) {
03373       viewAttach( i );
03374     }
03375   }
03376 }
03377 //-----------------------------------------------------------------------------
03378 void KMComposeWin::slotAttachOpen()
03379 {
03380   int i = 0;
03381   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ++it, ++i ) {
03382     if ( (*it)->isSelected() ) {
03383       openAttach( i, false );
03384     }
03385   }
03386 }
03387 
03388 //-----------------------------------------------------------------------------
03389 void KMComposeWin::slotAttachOpenWith()
03390 {
03391   int i = 0;
03392   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ++it, ++i ) {
03393     if ( (*it)->isSelected() ) {
03394       openAttach( i, true );
03395     }
03396   }
03397 }
03398 
03399 void KMComposeWin::slotAttachEdit()
03400 {
03401   int i = 0;
03402   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ++it, ++i ) {
03403     if ( (*it)->isSelected() ) {
03404       editAttach( i, false );
03405     }
03406   }
03407 }
03408 
03409 void KMComposeWin::slotAttachEditWith()
03410 {
03411   int i = 0;
03412   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ++it, ++i ) {
03413     if ( (*it)->isSelected() ) {
03414       editAttach( i, true );
03415     }
03416   }
03417 }
03418 
03419 //-----------------------------------------------------------------------------
03420 bool KMComposeWin::inlineSigningEncryptionSelected() {
03421   if ( !mSignAction->isChecked() && !mEncryptAction->isChecked() )
03422     return false;
03423   return cryptoMessageFormat() == Kleo::InlineOpenPGPFormat;
03424 }
03425 
03426 //-----------------------------------------------------------------------------
03427 void KMComposeWin::viewAttach( int index )
03428 {
03429   QString pname;
03430   KMMessagePart* msgPart;
03431   msgPart = mAtmList.at(index);
03432   pname = msgPart->name().stripWhiteSpace();
03433   if (pname.isEmpty()) pname=msgPart->contentDescription();
03434   if (pname.isEmpty()) pname="unnamed";
03435 
03436   KTempFile* atmTempFile = new KTempFile();
03437   mAtmTempList.append( atmTempFile );
03438   atmTempFile->setAutoDelete( true );
03439   KPIM::kByteArrayToFile(msgPart->bodyDecodedBinary(), atmTempFile->name(), false, false,
03440     false);
03441   KMReaderMainWin *win = new KMReaderMainWin(msgPart, false,
03442     atmTempFile->name(), pname, mCharset );
03443   win->show();
03444 }
03445 
03446 //-----------------------------------------------------------------------------
03447 void KMComposeWin::openAttach( int index, bool with )
03448 {
03449   KMMessagePart* msgPart = mAtmList.at(index);
03450   const QString contentTypeStr =
03451     ( msgPart->typeStr() + '/' + msgPart->subtypeStr() ).lower();
03452 
03453   KMimeType::Ptr mimetype;
03454   mimetype = KMimeType::mimeType( contentTypeStr );
03455 
03456   KTempFile* atmTempFile = new KTempFile();
03457   mAtmTempList.append( atmTempFile );
03458   const bool autoDelete = true;
03459   atmTempFile->setAutoDelete( autoDelete );
03460 
03461   KURL url;
03462   url.setPath( atmTempFile->name() );
03463 
03464   KPIM::kByteArrayToFile( msgPart->bodyDecodedBinary(), atmTempFile->name(), false, false,
03465     false );
03466   if ( ::chmod( QFile::encodeName( atmTempFile->name() ), S_IRUSR ) != 0) {
03467     QFile::remove(url.path());
03468     return;
03469   }
03470 
03471   KService::Ptr offer =
03472     KServiceTypeProfile::preferredService( mimetype->name(), "Application" );
03473 
03474   if ( with || !offer || mimetype->name() == "application/octet-stream" ) {
03475     if ( ( !KRun::displayOpenWithDialog( url, autoDelete ) ) && autoDelete ) {
03476       QFile::remove(url.path());
03477     }
03478   }
03479   else {
03480     if ( ( !KRun::run( *offer, url, autoDelete ) ) && autoDelete ) {
03481         QFile::remove( url.path() );
03482     }
03483   }
03484 }
03485 
03486 void KMComposeWin::editAttach(int index, bool openWith)
03487 {
03488   KMMessagePart* msgPart = mAtmList.at(index);
03489   const QString contentTypeStr =
03490     ( msgPart->typeStr() + '/' + msgPart->subtypeStr() ).lower();
03491 
03492   KTempFile* atmTempFile = new KTempFile();
03493   mAtmTempList.append( atmTempFile );
03494   atmTempFile->setAutoDelete( true );
03495   atmTempFile->file()->writeBlock( msgPart->bodyDecodedBinary() );
03496   atmTempFile->file()->flush();
03497 
03498 
03499   KMail::EditorWatcher *watcher =
03500           new KMail::EditorWatcher( KURL( atmTempFile->name() ), contentTypeStr, openWith,
03501                                     this, this );
03502   connect( watcher, SIGNAL(editDone(KMail::EditorWatcher*)), SLOT(slotEditDone(KMail::EditorWatcher*)) );
03503   if ( watcher->start() ) {
03504     mEditorMap.insert( watcher, msgPart );
03505     mEditorTempFiles.insert( watcher, atmTempFile );
03506   }
03507 }
03508 
03509 //-----------------------------------------------------------------------------
03510 void KMComposeWin::slotAttachSave()
03511 {
03512   KMMessagePart* msgPart;
03513   QString fileName, pname;
03514   int idx = currentAttachmentNum();
03515 
03516   if (idx < 0) return;
03517 
03518   msgPart = mAtmList.at(idx);
03519   pname = msgPart->name();
03520   if (pname.isEmpty()) pname="unnamed";
03521 
03522   KURL url = KFileDialog::getSaveURL(QString::null, QString::null, 0, i18n("Save Attachment As"));
03523 
03524   if( url.isEmpty() )
03525     return;
03526 
03527   kmkernel->byteArrayToRemoteFile(msgPart->bodyDecodedBinary(), url);
03528 }
03529 
03530 
03531 //-----------------------------------------------------------------------------
03532 void KMComposeWin::slotAttachRemove()
03533 {
03534   bool attachmentRemoved = false;
03535   int i = 0;
03536   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ) {
03537     if ( (*it)->isSelected() ) {
03538       removeAttach( i );
03539       attachmentRemoved = true;
03540     }
03541     else {
03542       ++it;
03543       ++i;
03544     }
03545   }
03546 
03547   if ( attachmentRemoved ) {
03548     setModified( true );
03549     slotUpdateAttachActions();
03550   }
03551 }
03552 
03553 //-----------------------------------------------------------------------------
03554 void KMComposeWin::slotFind()
03555 {
03556   mEditor->search();
03557 }
03558 
03559 void KMComposeWin::slotSearchAgain()
03560 {
03561   mEditor->repeatSearch();
03562 }
03563 
03564 //-----------------------------------------------------------------------------
03565 void KMComposeWin::slotReplace()
03566 {
03567   mEditor->replace();
03568 }
03569 
03570 //-----------------------------------------------------------------------------
03571 void KMComposeWin::slotUpdateFont()
03572 {
03573   kdDebug() << "KMComposeWin::slotUpdateFont " << endl;
03574   if ( ! mFixedFontAction ) {
03575     return;
03576   }
03577   mEditor->setFont( mFixedFontAction->isChecked() ? mFixedFont : mBodyFont );
03578 }
03579 
03580 QString KMComposeWin::quotePrefixName() const
03581 {
03582     if ( !msg() )
03583         return QString::null;
03584 
03585     int languageNr = GlobalSettings::self()->replyCurrentLanguage();
03586     ReplyPhrases replyPhrases( QString::number(languageNr) );
03587     replyPhrases.readConfig();
03588     QString quotePrefix = msg()->formatString(
03589                  replyPhrases.indentPrefix() );
03590 
03591     quotePrefix = msg()->formatString(quotePrefix);
03592     return quotePrefix;
03593 }
03594 
03595 void KMComposeWin::slotPasteClipboardAsQuotation()
03596 {
03597     if( mEditor->hasFocus() && msg() )
03598     {
03599         QString s = QApplication::clipboard()->text();
03600         if (!s.isEmpty())
03601             mEditor->insert(addQuotesToText(s));
03602     }
03603 }
03604 
03605 void KMComposeWin::slotPasteClipboardAsAttachment()
03606 {
03607   KURL url( QApplication::clipboard()->text( QClipboard::Clipboard ) );
03608   if ( url.isValid() ) {
03609     addAttach(QApplication::clipboard()->text( QClipboard::Clipboard ) );
03610     return;
03611   }
03612 
03613   QMimeSource *mimeSource = QApplication::clipboard()->data();
03614   if ( QImageDrag::canDecode(mimeSource) ) {
03615     slotAttachPNGImageData(mimeSource->encodedData("image/png"));
03616   }
03617   else {
03618     bool ok;
03619     QString attName = KInputDialog::getText( "KMail", i18n("Name of the attachment:"), QString::null, &ok, this );
03620     if ( !ok )
03621       return;
03622     KMMessagePart *msgPart = new KMMessagePart;
03623     msgPart->setName(attName);
03624     QValueList<int> dummy;
03625     msgPart->setBodyAndGuessCte(QCString(QApplication::clipboard()->text().latin1()), dummy,
03626                                 kmkernel->msgSender()->sendQuotedPrintable());
03627     addAttach(msgPart);
03628   }
03629 }
03630 
03631 void KMComposeWin::slotAddQuotes()
03632 {
03633     if( mEditor->hasFocus() && msg() )
03634     {
03635         // TODO: I think this is backwards.
03636         // i.e, if no region is marked then add quotes to every line
03637         // else add quotes only on the lines that are marked.
03638 
03639         if ( mEditor->hasMarkedText() ) {
03640             QString s = mEditor->markedText();
03641             if(!s.isEmpty())
03642                 mEditor->insert(addQuotesToText(s));
03643         } else {
03644             int l =  mEditor->currentLine();
03645             int c =  mEditor->currentColumn();
03646             QString s =  mEditor->textLine(l);
03647             s.prepend(quotePrefixName());
03648             mEditor->insertLine(s,l);
03649             mEditor->removeLine(l+1);
03650             mEditor->setCursorPosition(l,c+2);
03651         }
03652     }
03653 }
03654 
03655 QString KMComposeWin::addQuotesToText(const QString &inputText)
03656 {
03657     QString answer = QString( inputText );
03658     QString indentStr = quotePrefixName();
03659     answer.replace( '\n', '\n' + indentStr);
03660     answer.prepend( indentStr );
03661     answer += '\n';
03662     return KMMessage::smartQuote( answer, GlobalSettings::self()->lineWrapWidth() );
03663 }
03664 
03665 QString KMComposeWin::removeQuotesFromText(const QString &inputText)
03666 {
03667     QString s = inputText;
03668 
03669     // remove first leading quote
03670     QString quotePrefix = '^' + quotePrefixName();
03671     QRegExp rx(quotePrefix);
03672     s.remove(rx);
03673 
03674     // now remove all remaining leading quotes
03675     quotePrefix = '\n' + quotePrefixName();
03676     rx = quotePrefix;
03677     s.replace(rx, "\n");
03678 
03679     return s;
03680 }
03681 
03682 void KMComposeWin::slotRemoveQuotes()
03683 {
03684     if( mEditor->hasFocus() && msg() )
03685     {
03686         // TODO: I think this is backwards.
03687         // i.e, if no region is marked then remove quotes from every line
03688         // else remove quotes only on the lines that are marked.
03689 
03690         if ( mEditor->hasMarkedText() ) {
03691             QString s = mEditor->markedText();
03692             mEditor->insert(removeQuotesFromText(s));
03693         } else {
03694             int l = mEditor->currentLine();
03695             int c = mEditor->currentColumn();
03696             QString s = mEditor->textLine(l);
03697             mEditor->insertLine(removeQuotesFromText(s),l);
03698             mEditor->removeLine(l+1);
03699             mEditor->setCursorPosition(l,c-2);
03700         }
03701     }
03702 }
03703 
03704 //-----------------------------------------------------------------------------
03705 void KMComposeWin::slotUndo()
03706 {
03707   QWidget* fw = focusWidget();
03708   if (!fw) return;
03709 
03710   if ( ::qt_cast<KEdit*>(fw) )
03711       static_cast<QTextEdit*>(fw)->undo();
03712   else if (::qt_cast<QLineEdit*>(fw))
03713       static_cast<QLineEdit*>(fw)->undo();
03714 }
03715 
03716 void KMComposeWin::slotRedo()
03717 {
03718   QWidget* fw = focusWidget();
03719   if (!fw) return;
03720 
03721   if (::qt_cast<KEdit*>(fw))
03722       static_cast<KEdit*>(fw)->redo();
03723   else if (::qt_cast<QLineEdit*>(fw))
03724       static_cast<QLineEdit*>(fw)->redo();
03725 }
03726 
03727 //-----------------------------------------------------------------------------
03728 void KMComposeWin::slotCut()
03729 {
03730   QWidget* fw = focusWidget();
03731   if (!fw) return;
03732 
03733   if (::qt_cast<KEdit*>(fw))
03734       static_cast<KEdit*>(fw)->cut();
03735   else if (::qt_cast<QLineEdit*>(fw))
03736       static_cast<QLineEdit*>(fw)->cut();
03737 }
03738 
03739 
03740 //-----------------------------------------------------------------------------
03741 void KMComposeWin::slotCopy()
03742 {
03743   QWidget* fw = focusWidget();
03744   if (!fw) return;
03745 
03746 #ifdef KeyPress
03747 #undef KeyPress
03748 #endif
03749 
03750   QKeyEvent k(QEvent::KeyPress, Key_C, 0, ControlButton);
03751   kapp->notify(fw, &k);
03752 }
03753 
03754 
03755 //-----------------------------------------------------------------------------
03756 void KMComposeWin::slotPasteClipboard()
03757 {
03758   paste( QClipboard::Clipboard );
03759 }
03760 
03761 void KMComposeWin::paste( QClipboard::Mode mode )
03762 {
03763   QWidget* fw = focusWidget();
03764   if (!fw) return;
03765 
03766   QMimeSource *mimeSource = QApplication::clipboard()->data( mode );
03767   if ( mimeSource->provides("image/png") )  {
03768     slotAttachPNGImageData(mimeSource->encodedData("image/png"));
03769   } else if ( KURLDrag::canDecode( mimeSource ) ) {
03770         KURL::List urlList;
03771         if( KURLDrag::decode( mimeSource, urlList ) ) {
03772             const QString asText = i18n("Add as Text");
03773             const QString asAttachment = i18n("Add as Attachment");
03774             const QString text = i18n("Please select whether you want to insert the content as text into the editor, "
03775                     "or append the referenced file as an attachment.");
03776             const QString caption = i18n("Paste as text or attachment?");
03777 
03778             int id = KMessageBox::questionYesNoCancel( this, text, caption,
03779                     KGuiItem( asText ), KGuiItem( asAttachment) );
03780             switch ( id) {
03781               case KMessageBox::Yes:
03782                 for ( KURL::List::Iterator it = urlList.begin();
03783                      it != urlList.end(); ++it ) {
03784                   mEditor->insert( (*it).url() );
03785                 }
03786                 break;
03787               case KMessageBox::No:
03788                 for ( KURL::List::Iterator it = urlList.begin();
03789                      it != urlList.end(); ++it ) {
03790                   addAttach( *it );
03791                 }
03792                 break;
03793             }
03794         }
03795   } else if ( QTextDrag::canDecode( mimeSource ) ) {
03796       QString s;
03797       if ( QTextDrag::decode( mimeSource, s ) )
03798           mEditor->insert( s );
03799   }
03800 }
03801 
03802 
03803 //-----------------------------------------------------------------------------
03804 void KMComposeWin::slotMarkAll()
03805 {
03806   QWidget* fw = focusWidget();
03807   if (!fw) return;
03808 
03809   if (::qt_cast<QLineEdit*>(fw))
03810       static_cast<QLineEdit*>(fw)->selectAll();
03811   else if (::qt_cast<KEdit*>(fw))
03812       static_cast<KEdit*>(fw)->selectAll();
03813 }
03814 
03815 
03816 //-----------------------------------------------------------------------------
03817 void KMComposeWin::slotClose()
03818 {
03819   close(false);
03820 }
03821 
03822 
03823 //-----------------------------------------------------------------------------
03824 void KMComposeWin::slotNewComposer()
03825 {
03826   KMComposeWin* win;
03827   KMMessage* msg = new KMMessage;
03828 
03829   msg->initHeader();
03830   win = new KMComposeWin(msg);
03831   win->show();
03832 }
03833 
03834 
03835 //-----------------------------------------------------------------------------
03836 void KMComposeWin::slotNewMailReader()
03837 {
03838   KMMainWin *kmmwin = new KMMainWin(0);
03839   kmmwin->show();
03840   //d->resize(d->size());
03841 }
03842 
03843 
03844 //-----------------------------------------------------------------------------
03845 void KMComposeWin::slotUpdWinTitle(const QString& text)
03846 {
03847   QString s( text );
03848   // Remove characters that show badly in most window decorations:
03849   // newlines tend to become boxes.
03850   if (text.isEmpty())
03851     setCaption("("+i18n("unnamed")+")");
03852   else setCaption( s.replace( QChar('\n'), ' ' ) );
03853 }
03854 
03855 
03856 //-----------------------------------------------------------------------------
03857 void KMComposeWin::slotEncryptToggled(bool on)
03858 {
03859   setEncryption( on, true /* set by the user */ );
03860   slotUpdateSignatureAndEncrypionStateIndicators();
03861 }
03862 
03863 
03864 //-----------------------------------------------------------------------------
03865 void KMComposeWin::setEncryption( bool encrypt, bool setByUser )
03866 {
03867   bool wasModified = isModified();
03868   if ( setByUser )
03869     setModified( true );
03870   if ( !mEncryptAction->isEnabled() )
03871     encrypt = false;
03872   // check if the user wants to encrypt messages to himself and if he defined
03873   // an encryption key for the current identity
03874   else if ( encrypt && encryptToSelf() && !mLastIdentityHasEncryptionKey ) {
03875     if ( setByUser ) {
03876       KMessageBox::sorry( this,
03877                           i18n("<qt><p>You have requested that messages be "
03878                    "encrypted to yourself, but the currently selected "
03879                    "identity does not define an (OpenPGP or S/MIME) "
03880                    "encryption key to use for this.</p>"
03881                                "<p>Please select the key(s) to use "
03882                                "in the identity configuration.</p>"
03883                                "</qt>"),
03884                           i18n("Undefined Encryption Key") );
03885       setModified( wasModified );
03886     }
03887     encrypt = false;
03888   }
03889 
03890   // make sure the mEncryptAction is in the right state
03891   mEncryptAction->setChecked( encrypt );
03892 
03893   // show the appropriate icon
03894   if ( encrypt )
03895     mEncryptAction->setIcon("encrypted");
03896   else
03897     mEncryptAction->setIcon("decrypted");
03898 
03899   // mark the attachments for (no) encryption
03900   if ( canSignEncryptAttachments() ) {
03901     for ( KMAtmListViewItem* entry =
03902             static_cast<KMAtmListViewItem*>( mAtmItemList.first() );
03903           entry;
03904           entry = static_cast<KMAtmListViewItem*>( mAtmItemList.next() ) )
03905       entry->setEncrypt( encrypt );
03906   }
03907 }
03908 
03909 
03910 //-----------------------------------------------------------------------------
03911 void KMComposeWin::slotSignToggled(bool on)
03912 {
03913   setSigning( on, true /* set by the user */ );
03914   slotUpdateSignatureAndEncrypionStateIndicators();
03915 }
03916 
03917 
03918 //-----------------------------------------------------------------------------
03919 void KMComposeWin::setSigning( bool sign, bool setByUser )
03920 {
03921   bool wasModified = isModified();
03922   if ( setByUser )
03923     setModified( true );
03924   if ( !mSignAction->isEnabled() )
03925     sign = false;
03926 
03927   // check if the user defined a signing key for the current identity
03928   if ( sign && !mLastIdentityHasSigningKey ) {
03929     if ( setByUser ) {
03930       KMessageBox::sorry( this,
03931                           i18n("<qt><p>In order to be able to sign "
03932                                "this message you first have to "
03933                                "define the (OpenPGP or S/MIME) signing key "
03934                    "to use.</p>"
03935                                "<p>Please select the key to use "
03936                                "in the identity configuration.</p>"
03937                                "</qt>"),
03938                           i18n("Undefined Signing Key") );
03939       setModified( wasModified );
03940     }
03941     sign = false;
03942   }
03943 
03944   // make sure the mSignAction is in the right state
03945   mSignAction->setChecked( sign );
03946 
03947   // mark the attachments for (no) signing
03948   if ( canSignEncryptAttachments() ) {
03949     for ( KMAtmListViewItem* entry =
03950             static_cast<KMAtmListViewItem*>( mAtmItemList.first() );
03951           entry;
03952           entry = static_cast<KMAtmListViewItem*>( mAtmItemList.next() ) )
03953       entry->setSign( sign );
03954   }
03955 }
03956 
03957 
03958 //-----------------------------------------------------------------------------
03959 void KMComposeWin::slotWordWrapToggled(bool on)
03960 {
03961   if (on)
03962   {
03963     mEditor->setWordWrap( QTextEdit::FixedColumnWidth );
03964     mEditor->setWrapColumnOrWidth( GlobalSettings::self()->lineWrapWidth() );
03965   }
03966   else
03967   {
03968     mEditor->setWordWrap( QTextEdit::WidgetWidth );
03969   }
03970 }
03971 
03972 
03973 void KMComposeWin::disableWordWrap()
03974 {
03975     mEditor->setWordWrap( QTextEdit::NoWrap );
03976 }
03977 
03978 void KMComposeWin::disableRecipientNumberCheck()
03979 {
03980   mCheckForRecipients = false;
03981 }
03982 
03983 //-----------------------------------------------------------------------------
03984 void KMComposeWin::slotPrint()
03985 {
03986   mMessageWasModified = isModified();
03987   connect( this, SIGNAL( applyChangesDone( bool ) ),
03988            this, SLOT( slotContinuePrint( bool ) ) );
03989   applyChanges( true );
03990 }
03991 
03992 void KMComposeWin::slotContinuePrint( bool rc )
03993 {
03994   disconnect( this, SIGNAL( applyChangesDone( bool ) ),
03995               this, SLOT( slotContinuePrint( bool ) ) );
03996 
03997   if( rc ) {
03998     if ( mComposedMessages.isEmpty() ) {
03999       kdDebug(5006) << "Composing the message failed." << endl;
04000       return;
04001     }
04002     KMCommand *command = new KMPrintCommand( this, mComposedMessages.first() );
04003     command->start();
04004     setModified( mMessageWasModified );
04005   }
04006 }
04007 
04008 //----------------------------------------------------------------------------
04009 bool KMComposeWin::validateAddresses( QWidget * parent, const QString & addresses )
04010 {
04011   QString brokenAddress;
04012   KPIM::EmailParseResult errorCode = KMMessage::isValidEmailAddressList( KMMessage::expandAliases( addresses ), brokenAddress );
04013   if ( !( errorCode == KPIM::AddressOk || errorCode == KPIM::AddressEmpty ) ) {
04014     QString errorMsg( "<qt><p><b>" + brokenAddress +
04015                       "</b></p><p>" + KPIM::emailParseResultToString( errorCode ) +
04016                       "</p></qt>" );
04017     KMessageBox::sorry( parent, errorMsg, i18n("Invalid Email Address") );
04018     return false;
04019   }
04020   return true;
04021 }
04022 
04023 //----------------------------------------------------------------------------
04024 void KMComposeWin::doSend( KMail::MessageSender::SendMethod method,
04025                            KMComposeWin::SaveIn saveIn )
04026 {
04027   if ( method != KMail::MessageSender::SendLater && kmkernel->isOffline() ) {
04028     KMessageBox::information( this,
04029                               i18n("KMail is currently in offline mode,"
04030                                    "your messages will be kept in the outbox until you go online."),
04031                               i18n("Online/Offline"), "kmailIsOffline" );
04032     mSendMethod = KMail::MessageSender::SendLater;
04033   } else {
04034     mSendMethod = method;
04035   }
04036   mSaveIn = saveIn;
04037 
04038   if ( saveIn == KMComposeWin::None ) {
04039     if ( KPIM::getFirstEmailAddress( from() ).isEmpty() ) {
04040       if ( !( mShowHeaders & HDR_FROM ) ) {
04041         mShowHeaders |= HDR_FROM;
04042         rethinkFields( false );
04043       }
04044       mEdtFrom->setFocus();
04045       KMessageBox::sorry( this,
04046                           i18n("You must enter your email address in the "
04047                                "From: field. You should also set your email "
04048                                "address for all identities, so that you do "
04049                                "not have to enter it for each message.") );
04050       return;
04051     }
04052     if ( to().isEmpty() )
04053     {
04054         if (  cc().isEmpty() && bcc().isEmpty()) {
04055           if ( mEdtTo ) mEdtTo->setFocus();
04056           KMessageBox::information( this,
04057                                 i18n("You must specify at least one receiver,"
04058                                      "either in the To: field or as CC or as BCC.") );
04059           return;
04060         }
04061         else {
04062                 if ( mEdtTo ) mEdtTo->setFocus();
04063                 int rc =
04064                             KMessageBox::questionYesNo( this,
04065                                                         i18n("To field is missing."
04066                                                               "Send message anyway?"),
04067                                                         i18n("No To: specified") );
04068                 if ( rc == KMessageBox::No ){
04069                    return;
04070                 }
04071         }
04072     }
04073 
04074     // Validate the To:, CC: and BCC fields
04075     if ( !validateAddresses( this, to().stripWhiteSpace() ) ) {
04076       return;
04077     }
04078 
04079     if ( !validateAddresses( this, cc().stripWhiteSpace() ) ) {
04080       return;
04081     }
04082 
04083     if ( !validateAddresses( this, bcc().stripWhiteSpace() ) ) {
04084       return;
04085     }
04086 
04087     if (subject().isEmpty())
04088     {
04089         mEdtSubject->setFocus();
04090         int rc =
04091           KMessageBox::questionYesNo( this,
04092                                       i18n("You did not specify a subject. "
04093                                            "Send message anyway?"),
04094                                       i18n("No Subject Specified"),
04095                                       i18n("S&end as Is"),
04096                                       i18n("&Specify the Subject"),
04097                                       "no_subject_specified" );
04098         if( rc == KMessageBox::No )
04099         {
04100            return;
04101         }
04102     }
04103 
04104     if ( userForgotAttachment() )
04105       return;
04106   }
04107 
04108   KCursorSaver busy(KBusyPtr::busy());
04109   mMsg->setDateToday();
04110 
04111   // If a user sets up their outgoing messages preferences wrong and then
04112   // sends mail that gets 'stuck' in their outbox, they should be able to
04113   // rectify the problem by editing their outgoing preferences and
04114   // resending.
04115   // Hence this following conditional
04116   QString hf = mMsg->headerField("X-KMail-Transport");
04117   if ((mTransport->currentText() != mTransport->text(0)) ||
04118       (!hf.isEmpty() && (hf != mTransport->text(0))))
04119     mMsg->setHeaderField("X-KMail-Transport", mTransport->currentText());
04120 
04121   mDisableBreaking = ( saveIn != KMComposeWin::None );
04122 
04123   const bool neverEncrypt = ( mDisableBreaking && GlobalSettings::self()->neverEncryptDrafts() )
04124                            || mSigningAndEncryptionExplicitlyDisabled;
04125   connect( this, SIGNAL( applyChangesDone( bool ) ),
04126            SLOT( slotContinueDoSend( bool ) ) );
04127 
04128   if ( mEditor->textFormat() == Qt::RichText )
04129     mMsg->setHeaderField( "X-KMail-Markup", "true" );
04130   else
04131     mMsg->removeHeaderField( "X-KMail-Markup" );
04132   if ( mEditor->textFormat() == Qt::RichText && inlineSigningEncryptionSelected() ) {
04133     QString keepBtnText = mEncryptAction->isChecked() ?
04134       mSignAction->isChecked() ? i18n( "&Keep markup, do not sign/encrypt" )
04135                                : i18n( "&Keep markup, do not encrypt" )
04136       : i18n( "&Keep markup, do not sign" );
04137     QString yesBtnText = mEncryptAction->isChecked() ?
04138       mSignAction->isChecked() ? i18n("Sign/Encrypt (delete markup)")
04139       : i18n( "Encrypt (delete markup)" )
04140       : i18n( "Sign (delete markup)" );
04141     int ret = KMessageBox::warningYesNoCancel(this,
04142                                       i18n("<qt><p>Inline signing/encrypting of HTML messages is not possible;</p>"
04143                                            "<p>do you want to delete your markup?</p></qt>"),
04144                                            i18n("Sign/Encrypt Message?"),
04145                                            KGuiItem( yesBtnText ),
04146                                            KGuiItem( keepBtnText ) );
04147     if ( KMessageBox::Cancel == ret )
04148       return;
04149     if ( KMessageBox::No == ret ) {
04150       mEncryptAction->setChecked(false);
04151       mSignAction->setChecked(false);
04152     }
04153     else {
04154       toggleMarkup(false);
04155     }
04156   }
04157 
04158   if (neverEncrypt && saveIn != KMComposeWin::None ) {
04159       // we can't use the state of the mail itself, to remember the
04160       // signing and encryption state, so let's add a header instead
04161     mMsg->setHeaderField( "X-KMail-SignatureActionEnabled", mSignAction->isChecked()? "true":"false" );
04162     mMsg->setHeaderField( "X-KMail-EncryptActionEnabled", mEncryptAction->isChecked()? "true":"false"  );
04163     mMsg->setHeaderField( "X-KMail-CryptoMessageFormat", QString::number( cryptoMessageFormat() ) );
04164   } else {
04165     mMsg->removeHeaderField( "X-KMail-SignatureActionEnabled" );
04166     mMsg->removeHeaderField( "X-KMail-EncryptActionEnabled" );
04167     mMsg->removeHeaderField( "X-KMail-CryptoMessageFormat" );
04168   }
04169 
04170 
04171   kdDebug(5006) << "KMComposeWin::doSend() - calling applyChanges()"
04172                 << endl;
04173   applyChanges( neverEncrypt );
04174 }
04175 
04176 bool KMComposeWin::saveDraftOrTemplate( const QString &folderName,
04177                                         KMMessage *msg )
04178 {
04179   KMFolder *theFolder = 0, *imapTheFolder = 0;
04180   // get the draftsFolder
04181   if ( !folderName.isEmpty() ) {
04182     theFolder = kmkernel->folderMgr()->findIdString( folderName );
04183     if ( theFolder == 0 )
04184       // This is *NOT* supposed to be "imapDraftsFolder", because a
04185       // dIMAP folder works like a normal folder
04186       theFolder = kmkernel->dimapFolderMgr()->findIdString( folderName );
04187     if ( theFolder == 0 )
04188       imapTheFolder = kmkernel->imapFolderMgr()->findIdString( folderName );
04189     if ( !theFolder && !imapTheFolder ) {
04190       const KPIM::Identity & id = kmkernel->identityManager()
04191         ->identityForUoidOrDefault( msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt() );
04192       KMessageBox::information( 0,
04193                                 i18n("The custom drafts or templates folder for "
04194                                      "identify \"%1\" does not exist (anymore); "
04195                                      "therefore, the default drafts or templates "
04196                                      "folder will be used.")
04197                                 .arg( id.identityName() ) );
04198     }
04199   }
04200   if ( imapTheFolder && imapTheFolder->noContent() )
04201     imapTheFolder = 0;
04202 
04203   bool didOpen = false;
04204   if ( theFolder == 0 ) {
04205     theFolder = ( mSaveIn==KMComposeWin::Drafts ?
04206                   kmkernel->draftsFolder() : kmkernel->templatesFolder() );
04207   } else {
04208     //XXX this looks really, really fishy
04209     theFolder->open( "composer" );
04210     didOpen = true;
04211   }
04212   kdDebug(5006) << k_funcinfo << "theFolder=" << theFolder->name() << endl;
04213   if ( imapTheFolder )
04214     kdDebug(5006) << k_funcinfo << "imapTheFolder=" << imapTheFolder->name() << endl;
04215 
04216   bool sentOk = !( theFolder->addMsg( msg ) );
04217 
04218   // Ensure the message is correctly and fully parsed
04219   theFolder->unGetMsg( theFolder->count() - 1 );
04220   msg = theFolder->getMsg( theFolder->count() - 1 );
04221   // Does that assignment needs to be propagated out to the caller?
04222   // Assuming the send is OK, the iterator is set to 0 immediately afterwards.
04223   if ( imapTheFolder ) {
04224     // move the message to the imap-folder and highlight it
04225     imapTheFolder->moveMsg( msg );
04226     (static_cast<KMFolderImap*>( imapTheFolder->storage() ))->getFolder();
04227   }
04228 
04229   if ( didOpen )
04230     theFolder->close( "composer" );
04231   return sentOk;
04232 }
04233 
04234 void KMComposeWin::slotContinueDoSend( bool sentOk )
04235 {
04236   kdDebug(5006) << "KMComposeWin::slotContinueDoSend( " << sentOk << " )"
04237                 << endl;
04238   disconnect( this, SIGNAL( applyChangesDone( bool ) ),
04239               this, SLOT( slotContinueDoSend( bool ) ) );
04240 
04241   if ( !sentOk ) {
04242     mDisableBreaking = false;
04243     return;
04244   }
04245 
04246   for ( QValueVector<KMMessage*>::iterator it = mComposedMessages.begin() ; it != mComposedMessages.end() ; ++it ) {
04247 
04248     // remove fields that contain no data (e.g. an empty Cc: or Bcc:)
04249     (*it)->cleanupHeader();
04250 
04251     // needed for imap
04252     (*it)->setComplete( true );
04253 
04254     if ( mSaveIn==KMComposeWin::Drafts ) {
04255       sentOk = saveDraftOrTemplate( (*it)->drafts(), (*it) );
04256     } else if ( mSaveIn==KMComposeWin::Templates ) {
04257       sentOk = saveDraftOrTemplate( (*it)->templates(), (*it) );
04258     } else {
04259       (*it)->setTo( KMMessage::expandAliases( to() ));
04260       (*it)->setCc( KMMessage::expandAliases( cc() ));
04261       if( !mComposer->originalBCC().isEmpty() )
04262     (*it)->setBcc( KMMessage::expandAliases( mComposer->originalBCC() ));
04263       QString recips = (*it)->headerField( "X-KMail-Recipients" );
04264       if( !recips.isEmpty() ) {
04265     (*it)->setHeaderField( "X-KMail-Recipients", KMMessage::expandAliases( recips ), KMMessage::Address );
04266       }
04267       (*it)->cleanupHeader();
04268       sentOk = kmkernel->msgSender()->send((*it), mSendMethod);
04269     }
04270 
04271     if (!sentOk)
04272       return;
04273 
04274     *it = 0; // don't kill it later...
04275   }
04276 
04277   RecentAddresses::self( KMKernel::config() )->add( bcc() );
04278   RecentAddresses::self( KMKernel::config() )->add( cc() );
04279   RecentAddresses::self( KMKernel::config() )->add( to() );
04280 
04281   setModified( false );
04282   mAutoDeleteMsg = false;
04283   mFolder = 0;
04284   cleanupAutoSave();
04285   close();
04286   return;
04287 }
04288 
04289 bool KMComposeWin::checkTransport() const
04290 {
04291   if ( KMail::TransportManager::transportNames().isEmpty() ) {
04292     KMessageBox::information( mMainWidget,
04293                               i18n("Please create an account for sending and try again.") );
04294     return false;
04295   }
04296   return true;
04297 
04298 }
04299 
04300 //----------------------------------------------------------------------------
04301 void KMComposeWin::slotSendLater()
04302 {
04303   if ( !checkTransport() )
04304     return;
04305   if ( !checkRecipientNumber() )
04306     return;
04307   if ( mEditor->checkExternalEditorFinished() )
04308     doSend( KMail::MessageSender::SendLater );
04309 }
04310 
04311 
04312 //----------------------------------------------------------------------------
04313 void KMComposeWin::slotSaveDraft() {
04314   if ( mEditor->checkExternalEditorFinished() )
04315     doSend( KMail::MessageSender::SendLater, KMComposeWin::Drafts );
04316 }
04317 
04318 //----------------------------------------------------------------------------
04319 void KMComposeWin::slotSaveTemplate() {
04320   if ( mEditor->checkExternalEditorFinished() )
04321     doSend( KMail::MessageSender::SendLater, KMComposeWin::Templates );
04322 }
04323 
04324 //----------------------------------------------------------------------------
04325 void KMComposeWin::slotSendNowVia( int item )
04326 {
04327   QStringList availTransports= KMail::TransportManager::transportNames();
04328   QString customTransport = availTransports[ item ];
04329 
04330   mTransport->setCurrentText( customTransport );
04331   slotSendNow();
04332 }
04333 
04334 //----------------------------------------------------------------------------
04335 void KMComposeWin::slotSendLaterVia( int item )
04336 {
04337   QStringList availTransports= KMail::TransportManager::transportNames();
04338   QString customTransport = availTransports[ item ];
04339 
04340   mTransport->setCurrentText( customTransport );
04341   slotSendLater();
04342 }
04343 
04344 
04345 //----------------------------------------------------------------------------
04346 void KMComposeWin::slotSendNow() {
04347   if ( !mEditor->checkExternalEditorFinished() )
04348     return;
04349   if ( !checkTransport() )
04350     return;
04351   if ( !checkRecipientNumber() )
04352     return;
04353   if ( GlobalSettings::self()->confirmBeforeSend() )
04354   {
04355     int rc = KMessageBox::warningYesNoCancel( mMainWidget,
04356                                         i18n("About to send email..."),
04357                                         i18n("Send Confirmation"),
04358                                         i18n("&Send Now"),
04359                                         i18n("Send &Later") );
04360 
04361     if ( rc == KMessageBox::Yes )
04362       doSend( KMail::MessageSender::SendImmediate );
04363     else if ( rc == KMessageBox::No )
04364       doSend( KMail::MessageSender::SendLater );
04365   }
04366   else
04367     doSend( KMail::MessageSender::SendImmediate );
04368 }
04369 
04370 
04371 //----------------------------------------------------------------------------
04372 bool KMComposeWin::checkRecipientNumber() const
04373 {
04374   int thresHold = GlobalSettings::self()->recipientThreshold();
04375   if ( mCheckForRecipients &&
04376        GlobalSettings::self()->tooManyRecipients() &&
04377        mRecipientsEditor->recipients().count() > thresHold ) {
04378     if ( KMessageBox::questionYesNo( mMainWidget,
04379                                i18n("You are trying to send the mail to more than %1 recipients. Send message anyway?").arg(thresHold),
04380                                i18n("Too many receipients"),
04381                                i18n("&Send as Is"),
04382                                i18n("&Edit Recipients")) == KMessageBox::No ) {
04383       return false;
04384     }
04385   }
04386   return true;
04387 }
04388 
04389 
04390 //----------------------------------------------------------------------------
04391 void KMComposeWin::slotAppendSignature()
04392 {
04393     insertSignature();
04394 }
04395 
04396 //----------------------------------------------------------------------------
04397 void KMComposeWin::slotPrependSignature()
04398 {
04399     insertSignature( false );
04400 }
04401 
04402 //----------------------------------------------------------------------------
04403 void KMComposeWin::slotInsertSignatureAtCursor()
04404 {
04405     insertSignature( false, mEditor->currentLine() );
04406 }
04407 
04408 //----------------------------------------------------------------------------
04409 void KMComposeWin::insertSignature( bool append, int pos )
04410 {
04411    bool mod = mEditor->isModified();
04412 
04413    const KPIM::Identity &ident =
04414      kmkernel->identityManager()->
04415      identityForUoidOrDefault( mIdentity->currentIdentity() );
04416 
04417    mOldSigText = GlobalSettings::self()->prependSignature()? ident.signature().rawText() : ident.signatureText();
04418 
04419    if( !mOldSigText.isEmpty() )
04420    {
04421       mEditor->sync();
04422       if ( append ) {
04423          mEditor->setText( mEditor->text() + mOldSigText );
04424     } else {
04425        mOldSigText = "\n\n"+mOldSigText+"\n";
04426        mEditor->insertAt(mOldSigText, pos, 0);
04427     }
04428     mEditor->update();
04429     mEditor->setModified(mod);
04430 
04431     if (  mPreserveUserCursorPosition ) {
04432       mEditor->setCursorPositionFromStart( (unsigned int) mMsg->getCursorPos() );
04433       // Only keep the cursor from the mMsg *once* based on the
04434       // preserve-cursor-position setting; this handles the case where
04435       // the message comes from a template with a specific cursor
04436       // position set and the signature is appended automatically.
04437       mPreserveUserCursorPosition = false;
04438     } else {
04439       // for append and prepend, move the cursor to 0,0, for insertAt,
04440       // keep it in the same row, but move to first column
04441       mEditor->setCursorPosition( pos, 0 );
04442       if ( !append && pos == 0 )
04443         mEditor->setContentsPos( 0, 0 );
04444     }
04445     mEditor->sync();
04446   }
04447 }
04448 
04449 //-----------------------------------------------------------------------------
04450 void KMComposeWin::slotHelp()
04451 {
04452   kapp->invokeHelp();
04453 }
04454 
04455 //-----------------------------------------------------------------------------
04456 void KMComposeWin::slotCleanSpace()
04457 {
04458   // Originally we simply used the KEdit::cleanWhiteSpace() method,
04459   // but that code doesn't handle quoted-lines or signatures, so instead
04460   // we now simply use regexp's to squeeze sequences of tabs and spaces
04461   // into a single space, and make sure all our lines are single-spaced.
04462   //
04463   // Yes, extra space in a quote string is squeezed.
04464   // Signatures are respected (i.e. not cleaned).
04465 
04466   QString s;
04467   if ( mEditor->hasMarkedText() ) {
04468     s = mEditor->markedText();
04469     if( s.isEmpty() )
04470       return;
04471   } else {
04472     s = mEditor->text();
04473   }
04474 
04475   // Remove the signature for now.
04476   QString sig;
04477   bool restore = false;
04478   const KPIM::Identity & ident =
04479     kmkernel->identityManager()->identityForUoid( mId );
04480   if ( !ident.isNull() ) {
04481     sig = ident.signatureText();
04482     if( !sig.isEmpty() ) {
04483       if( s.endsWith( sig ) ) {
04484         s.truncate( s.length() - sig.length() );
04485         restore = true;
04486       }
04487     }
04488   }
04489 
04490   // Squeeze tabs and spaces
04491   QRegExp squeeze( "[\t ]+" );
04492   s.replace( squeeze, QChar( ' ' ) );
04493 
04494   // Remove trailing whitespace
04495   QRegExp trailing( "\\s+$" );
04496   s.replace( trailing, QChar( '\n' ) );
04497 
04498   // Single space lines
04499   QRegExp singleSpace( "[\n]{2,}" );
04500   s.replace( singleSpace, QChar( '\n' ) );
04501 
04502   // Restore the signature
04503   if ( restore )
04504     s.append( sig );
04505 
04506   // Put the new text in place.
04507   // The lines below do not clear the undo history, but unfortuately cause
04508   // the side-effect that you need to press Ctrl-Z twice (first Ctrl-Z will
04509   // show cleared text area) to get back the original, pre-cleaned text.
04510   // If you use mEditor->setText( s ) then the undo history is cleared so
04511   // that isn't a good solution either.
04512   // TODO: is Qt4 better at handling the undo history??
04513   if ( !mEditor->hasMarkedText() )
04514     mEditor->clear();
04515   mEditor->insert( s );
04516 }
04517 
04518 //-----------------------------------------------------------------------------
04519 void KMComposeWin::slotToggleMarkup()
04520 {
04521  if ( markupAction->isChecked() ) {
04522     mHtmlMarkup = true;
04523     toolBar("htmlToolBar")->show();
04524    // markup will be toggled as soon as markup is actually used
04525    fontChanged( mEditor->currentFont() ); // set buttons in correct position
04526    mSaveFont = mEditor->currentFont();
04527  }
04528  else
04529    toggleMarkup(false);
04530 
04531 }
04532 //-----------------------------------------------------------------------------
04533 void KMComposeWin::toggleMarkup(bool markup)
04534 {
04535   if ( markup ) {
04536     if ( !mUseHTMLEditor ) {
04537       kdDebug(5006) << "setting RichText editor" << endl;
04538       mUseHTMLEditor = true; // set it directly to true. setColor hits another toggleMarkup
04539       mHtmlMarkup = true;
04540 
04541       // set all highlighted text caused by spelling back to black
04542       int paraFrom, indexFrom, paraTo, indexTo;
04543       mEditor->getSelection ( &paraFrom, &indexFrom, &paraTo, &indexTo);
04544       mEditor->selectAll();
04545       // save the buttonstates because setColor calls fontChanged
04546       bool _bold = textBoldAction->isChecked();
04547       bool _italic = textItalicAction->isChecked();
04548       mEditor->setColor(QColor(0,0,0));
04549       textBoldAction->setChecked(_bold);
04550       textItalicAction->setChecked(_italic);
04551       mEditor->setSelection ( paraFrom, indexFrom, paraTo, indexTo);
04552 
04553       mEditor->setTextFormat(Qt::RichText);
04554       mEditor->setModified(true);
04555       markupAction->setChecked(true);
04556       toolBar( "htmlToolBar" )->show();
04557       mEditor->deleteAutoSpellChecking();
04558       mAutoSpellCheckingAction->setChecked(false);
04559       slotAutoSpellCheckingToggled(false);
04560     }
04561   } else { // markup is to be turned off
04562     kdDebug(5006) << "setting PlainText editor" << endl;
04563     mHtmlMarkup = false;
04564     toolBar("htmlToolBar")->hide();
04565     if ( mUseHTMLEditor ) { // it was turned on
04566       mUseHTMLEditor = false;
04567       mEditor->setTextFormat(Qt::PlainText);
04568       QString text = mEditor->text();
04569       mEditor->setText(text); // otherwise the text still looks formatted
04570       mEditor->setModified(true);
04571       slotAutoSpellCheckingToggled(true);
04572     }
04573   }
04574 }
04575 
04576 void KMComposeWin::htmlToolBarVisibilityChanged( bool visible )
04577 {
04578   // disable markup if the user hides the HTML toolbar
04579   if ( !visible ) {
04580     markupAction->setChecked( false );
04581     toggleMarkup( false );
04582   }
04583 }
04584 
04585 void KMComposeWin::slotSubjectTextSpellChecked()
04586 {
04587   mSubjectTextWasSpellChecked = true;
04588 }
04589 
04590 //-----------------------------------------------------------------------------
04591 void KMComposeWin::slotAutoSpellCheckingToggled( bool on )
04592 {
04593   if ( mEditor->autoSpellChecking(on) == -1 ) {
04594     mAutoSpellCheckingAction->setChecked(false); // set it to false again
04595   }
04596 
04597   QString temp;
04598   if ( on )
04599     temp = i18n( "Spellcheck: on" );
04600   else
04601     temp = i18n( "Spellcheck: off" );
04602   statusBar()->changeItem( temp, 3 );
04603 }
04604 //-----------------------------------------------------------------------------
04605 void KMComposeWin::slotSpellcheck()
04606 {
04607   if (mSpellCheckInProgress) return;
04608   mSubjectTextWasSpellChecked = false;
04609   mSpellCheckInProgress=true;
04610   /*
04611     connect (mEditor, SIGNAL (spellcheck_progress (unsigned)),
04612     this, SLOT (spell_progress (unsigned)));
04613     */
04614 
04615   mEditor->spellcheck();
04616 }
04617 //-----------------------------------------------------------------------------
04618 void KMComposeWin::slotUpdateSignatureActions()
04619 {
04620   //Check if an identity has signature or not and turn on/off actions in the
04621   //edit menu accordingly.
04622   const KPIM::Identity & ident =
04623     kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() );
04624   QString sig = ident.signatureText();
04625 
04626   if ( sig.isEmpty() ) {
04627      mAppendSignatureAction->setEnabled( false );
04628      mPrependSignatureAction->setEnabled( false );
04629      mInsertSignatureAction->setEnabled( false );
04630   }
04631   else {
04632       mAppendSignatureAction->setEnabled( true );
04633       mPrependSignatureAction->setEnabled( true );
04634       mInsertSignatureAction->setEnabled( true );
04635   }
04636 }
04637 
04638 void KMComposeWin::polish()
04639 {
04640   // Ensure the html toolbar is appropriately shown/hidden
04641   markupAction->setChecked(mHtmlMarkup);
04642   if (mHtmlMarkup)
04643     toolBar("htmlToolBar")->show();
04644   else
04645     toolBar("htmlToolBar")->hide();
04646   KMail::Composer::polish();
04647 }
04648 
04649 //-----------------------------------------------------------------------------
04650 void KMComposeWin::slotSpellcheckDone(int result)
04651 {
04652   kdDebug(5006) << "spell check complete: result = " << result << endl;
04653   mSpellCheckInProgress=false;
04654 
04655   switch( result )
04656   {
04657     case KS_CANCEL:
04658       statusBar()->changeItem(i18n(" Spell check canceled."),0);
04659       break;
04660     case KS_STOP:
04661       statusBar()->changeItem(i18n(" Spell check stopped."),0);
04662       break;
04663     default:
04664       statusBar()->changeItem(i18n(" Spell check complete."),0);
04665       break;
04666   }
04667   QTimer::singleShot( 2000, this, SLOT(slotSpellcheckDoneClearStatus()) );
04668 }
04669 
04670 void KMComposeWin::slotSpellcheckDoneClearStatus()
04671 {
04672   statusBar()->changeItem("", 0);
04673 }
04674 
04675 
04676 //-----------------------------------------------------------------------------
04677 void KMComposeWin::slotIdentityChanged( uint uoid )
04678 {
04679   const KPIM::Identity & ident =
04680     kmkernel->identityManager()->identityForUoid( uoid );
04681   if( ident.isNull() ) return;
04682 
04683   //Turn on/off signature actions if identity has no signature.
04684   slotUpdateSignatureActions();
04685 
04686   if( !ident.fullEmailAddr().isNull() )
04687     mEdtFrom->setText(ident.fullEmailAddr());
04688   // make sure the From field is shown if it does not contain a valid email address
04689   if ( KPIM::getFirstEmailAddress( from() ).isEmpty() )
04690     mShowHeaders |= HDR_FROM;
04691   if ( mEdtReplyTo ) mEdtReplyTo->setText(ident.replyToAddr());
04692 
04693   if ( mRecipientsEditor ) {
04694     // remove BCC of old identity and add BCC of new identity (if they differ)
04695     const KPIM::Identity & oldIdentity =
04696       kmkernel->identityManager()->identityForUoidOrDefault( mId );
04697     if ( oldIdentity.bcc() != ident.bcc() ) {
04698       mRecipientsEditor->removeRecipient( oldIdentity.bcc(), Recipient::Bcc );
04699       mRecipientsEditor->addRecipient( ident.bcc(), Recipient::Bcc );
04700       mRecipientsEditor->setFocusBottom();
04701     }
04702   }
04703 
04704   // don't overwrite the BCC field under certain circomstances
04705   // NOT edited and preset BCC from the identity
04706   if( mEdtBcc && !mEdtBcc->edited() && !ident.bcc().isEmpty() ) {
04707     // BCC NOT empty AND contains a diff adress then the preset BCC
04708     // of the new identity
04709     if( !mEdtBcc->text().isEmpty() && mEdtBcc->text() != ident.bcc() && !mEdtBcc->edited() ) {
04710       mEdtBcc->setText( ident.bcc() );
04711     } else {
04712       // user type into the editbox an address that != to the preset bcc
04713       // of the identity, we assume that since the user typed it
04714       // they want to keep it
04715       if ( mEdtBcc->text() != ident.bcc() && !mEdtBcc->text().isEmpty() ) {
04716         QString temp_string( mEdtBcc->text() + QString::fromLatin1(",") + ident.bcc() );
04717         mEdtBcc->setText( temp_string );
04718       } else {
04719         // if the user typed the same address as the preset BCC
04720         // from the identity we will overwrite it to avoid duplicates.
04721         mEdtBcc->setText( ident.bcc() );
04722       }
04723     }
04724   }
04725   // user edited the bcc box and has a preset bcc in the identity
04726   // we will append whatever the user typed to the preset address
04727   // allowing the user to keep all addresses
04728   if( mEdtBcc && mEdtBcc->edited() && !ident.bcc().isEmpty() ) {
04729     if( !mEdtBcc->text().isEmpty() ) {
04730       QString temp_string ( mEdtBcc->text() + QString::fromLatin1(",") + ident.bcc() );
04731       mEdtBcc->setText( temp_string );
04732     } else {
04733       mEdtBcc->setText( ident.bcc() );
04734     }
04735   }
04736   // user typed nothing and the identity does not have a preset bcc
04737   // we then reset the value to get rid of any previous
04738   // values if the user changed identity mid way through.
04739   if( mEdtBcc && !mEdtBcc->edited() && ident.bcc().isEmpty() ) {
04740     mEdtBcc->setText( ident.bcc() );
04741   }
04742   // make sure the BCC field is shown because else it's ignored
04743   if ( !ident.bcc().isEmpty() ) {
04744     mShowHeaders |= HDR_BCC;
04745   }
04746 
04747   if ( ident.organization().isEmpty() )
04748     mMsg->removeHeaderField("Organization");
04749   else
04750     mMsg->setHeaderField("Organization", ident.organization());
04751 
04752   if (!ident.isXFaceEnabled() || ident.xface().isEmpty())
04753     mMsg->removeHeaderField("X-Face");
04754   else
04755   {
04756     QString xface = ident.xface();
04757     if (!xface.isEmpty())
04758     {
04759       int numNL = ( xface.length() - 1 ) / 70;
04760       for ( int i = numNL; i > 0; --i )
04761         xface.insert( i*70, "\n\t" );
04762       mMsg->setHeaderField("X-Face", xface);
04763     }
04764   }
04765 
04766   if ( !mBtnTransport->isChecked() ) {
04767     QString transp = ident.transport();
04768     if ( transp.isEmpty() )
04769     {
04770       mMsg->removeHeaderField("X-KMail-Transport");
04771       transp = GlobalSettings::self()->defaultTransport();
04772     }
04773     else
04774       mMsg->setHeaderField("X-KMail-Transport", transp);
04775     setTransport( transp );
04776   }
04777 
04778   mDictionaryCombo->setCurrentByDictionary( ident.dictionary() );
04779 
04780   if ( !mBtnFcc->isChecked() && !mPreventFccOverwrite ) {
04781     setFcc( ident.fcc() );
04782   }
04783 
04784   QString edtText = mEditor->text();
04785 
04786   if ( mOldSigText.isEmpty() ) {
04787     const KPIM::Identity &id =
04788       kmkernel->
04789       identityManager()->
04790       identityForUoidOrDefault( mMsg->headerField( "X-KMail-Identity" ).
04791                                 stripWhiteSpace().toUInt() );
04792     mOldSigText = GlobalSettings::self()->prependSignature() ? id.signature().rawText() : id.signatureText();
04793   }
04794 
04795 
04796   if ( !GlobalSettings::prependSignature() ) {
04797     // try to truncate the old sig
04798     // First remove any trailing whitespace
04799     while ( !edtText.isEmpty() && edtText[edtText.length()-1].isSpace() )
04800       edtText.truncate( edtText.length() - 1 );
04801     // From the sig too, just in case
04802     while ( !mOldSigText.isEmpty() && mOldSigText[mOldSigText.length()-1].isSpace() )
04803       mOldSigText.truncate( mOldSigText.length() - 1 );
04804 
04805     if ( edtText.endsWith( mOldSigText ) )
04806       edtText.truncate( edtText.length() - mOldSigText.length() );
04807 
04808     // now append the new sig
04809     mOldSigText = ident.signatureText();
04810     if( ( !mOldSigText.isEmpty() ) &&
04811         ( GlobalSettings::self()->autoTextSignature() == "auto" ) ) {
04812       edtText.append( mOldSigText );
04813     }
04814     mEditor->setText( edtText );
04815   } else {
04816     const int pos = edtText.find( mOldSigText );
04817     if ( pos >= 0 && !mOldSigText.isEmpty() ) {
04818       const int oldLength = mOldSigText.length();
04819       mOldSigText = "\n\n"+ ident.signature().rawText() + "\n"; // see insertSignature()
04820       edtText = edtText.replace( pos, oldLength, mOldSigText );
04821       mEditor->setText( edtText );
04822     } else {
04823       insertSignature( false );
04824     }
04825   }
04826 
04827   // disable certain actions if there is no PGP user identity set
04828   // for this profile
04829   bool bNewIdentityHasSigningKey = !ident.pgpSigningKey().isEmpty() || !ident.smimeSigningKey().isEmpty();
04830   bool bNewIdentityHasEncryptionKey = !ident.pgpSigningKey().isEmpty() || !ident.smimeSigningKey().isEmpty();
04831   mAttachMPK->setEnabled( Kleo::CryptoBackendFactory::instance()->openpgp() &&
04832               !ident.pgpEncryptionKey().isEmpty() );
04833   // save the state of the sign and encrypt button
04834   if ( !bNewIdentityHasEncryptionKey && mLastIdentityHasEncryptionKey ) {
04835     mLastEncryptActionState = mEncryptAction->isChecked();
04836     setEncryption( false );
04837   }
04838   if ( !bNewIdentityHasSigningKey && mLastIdentityHasSigningKey ) {
04839     mLastSignActionState = mSignAction->isChecked();
04840     setSigning( false );
04841   }
04842   // restore the last state of the sign and encrypt button
04843   if ( bNewIdentityHasEncryptionKey && !mLastIdentityHasEncryptionKey )
04844       setEncryption( mLastEncryptActionState );
04845   if ( bNewIdentityHasSigningKey && !mLastIdentityHasSigningKey )
04846     setSigning( mLastSignActionState );
04847 
04848   mLastIdentityHasSigningKey = bNewIdentityHasSigningKey;
04849   mLastIdentityHasEncryptionKey = bNewIdentityHasEncryptionKey;
04850 
04851   setModified( true );
04852   mId = uoid;
04853 
04854   // make sure the From and BCC fields are shown if necessary
04855   rethinkFields( false );
04856 }
04857 
04858 //-----------------------------------------------------------------------------
04859 void KMComposeWin::slotSpellcheckConfig()
04860 {
04861   KDialogBase dlg(KDialogBase::Plain, i18n("Spellchecker"),
04862                   KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok,
04863                   this, 0, true, true );
04864   KWin kwin;
04865   QTabDialog qtd (this, "tabdialog", true);
04866   KSpellConfig mKSpellConfig (&qtd);
04867   mKSpellConfig.layout()->setMargin( KDialog::marginHint() );
04868 
04869   qtd.addTab (&mKSpellConfig, i18n("Spellchecker"));
04870   qtd.setCancelButton ();
04871 
04872   kwin.setIcons (qtd.winId(), kapp->icon(), kapp->miniIcon());
04873   qtd.setCancelButton(KStdGuiItem::cancel().text());
04874   qtd.setOkButton(KStdGuiItem::ok().text());
04875 
04876   if (qtd.exec())
04877     mKSpellConfig.writeGlobalSettings();
04878 }
04879 
04880 //-----------------------------------------------------------------------------
04881 void KMComposeWin::slotStatusMessage(const QString &message)
04882 {
04883     statusBar()->changeItem( message, 0 );
04884 }
04885 
04886 void KMComposeWin::slotEditToolbars()
04887 {
04888   saveMainWindowSettings(KMKernel::config(), "Composer");
04889   KEditToolbar dlg(guiFactory(), this);
04890 
04891   connect( &dlg, SIGNAL(newToolbarConfig()),
04892            SLOT(slotUpdateToolbars()) );
04893 
04894   dlg.exec();
04895 }
04896 
04897 void KMComposeWin::slotUpdateToolbars()
04898 {
04899   createGUI("kmcomposerui.rc");
04900   applyMainWindowSettings(KMKernel::config(), "Composer");
04901 }
04902 
04903 void KMComposeWin::slotEditKeys()
04904 {
04905   KKeyDialog::configure( actionCollection(),
04906                          false /*don't allow one-letter shortcuts*/
04907                          );
04908 }
04909 
04910 void KMComposeWin::setReplyFocus( bool hasMessage )
04911 {
04912   mEditor->setFocus();
04913   if ( hasMessage ) {
04914     if( mMsg->getCursorPos() ) {
04915       mEditor->setCursorPositionFromStart( (unsigned int) mMsg->getCursorPos() );
04916     } else {
04917       mEditor->setCursorPosition( 1, 0 );
04918     }
04919   }
04920 }
04921 
04922 void KMComposeWin::setFocusToSubject()
04923 {
04924   mEdtSubject->setFocus();
04925 }
04926 
04927 int KMComposeWin::autoSaveInterval() const
04928 {
04929   return GlobalSettings::self()->autosaveInterval() * 1000 * 60;
04930 }
04931 
04932 void KMComposeWin::initAutoSave()
04933 {
04934   kdDebug(5006) << k_funcinfo << endl;
04935   // make sure the autosave folder exists
04936   KMFolderMaildir::createMaildirFolders( KMKernel::localDataPath() + "autosave" );
04937   if ( mAutoSaveFilename.isEmpty() ) {
04938     mAutoSaveFilename = KMFolderMaildir::constructValidFileName();
04939   }
04940 
04941   updateAutoSave();
04942 }
04943 
04944 void KMComposeWin::updateAutoSave()
04945 {
04946   if ( autoSaveInterval() == 0 ) {
04947     delete mAutoSaveTimer; mAutoSaveTimer = 0;
04948   }
04949   else {
04950     if ( !mAutoSaveTimer ) {
04951       mAutoSaveTimer = new QTimer( this, "mAutoSaveTimer" );
04952       connect( mAutoSaveTimer, SIGNAL( timeout() ),
04953                this, SLOT( autoSaveMessage() ) );
04954     }
04955     mAutoSaveTimer->start( autoSaveInterval() );
04956   }
04957 }
04958 
04959 void KMComposeWin::setAutoSaveFilename( const QString & filename )
04960 {
04961   if ( !mAutoSaveFilename.isEmpty() )
04962     KMFolderMaildir::removeFile( KMKernel::localDataPath() + "autosave",
04963                                  mAutoSaveFilename );
04964   mAutoSaveFilename = filename;
04965 }
04966 
04967 void KMComposeWin::cleanupAutoSave()
04968 {
04969   delete mAutoSaveTimer; mAutoSaveTimer = 0;
04970   if ( !mAutoSaveFilename.isEmpty() ) {
04971     kdDebug(5006) << k_funcinfo << "deleting autosave file "
04972                   << mAutoSaveFilename << endl;
04973     KMFolderMaildir::removeFile( KMKernel::localDataPath() + "autosave",
04974                                  mAutoSaveFilename );
04975     mAutoSaveFilename = QString();
04976   }
04977 }
04978 
04979 void KMComposeWin::slotCompletionModeChanged( KGlobalSettings::Completion mode)
04980 {
04981   GlobalSettings::self()->setCompletionMode( (int) mode );
04982 
04983   // sync all the lineedits to the same completion mode
04984   mEdtFrom->setCompletionMode( mode );
04985   mEdtReplyTo->setCompletionMode( mode );
04986   if ( mClassicalRecipients ) {
04987     mEdtTo->setCompletionMode( mode );
04988     mEdtCc->setCompletionMode( mode );
04989     mEdtBcc->setCompletionMode( mode );
04990   }else
04991     mRecipientsEditor->setCompletionMode( mode );
04992 }
04993 
04994 void KMComposeWin::slotConfigChanged()
04995 {
04996   readConfig( true /*reload*/);
04997   updateAutoSave();
04998   rethinkFields();
04999   slotWordWrapToggled( mWordWrapAction->isChecked() );
05000 }
05001 
05002 /*
05003 * checks if the drafts-folder has been deleted
05004 * that is not nice so we set the system-drafts-folder
05005 */
05006 void KMComposeWin::slotFolderRemoved(KMFolder* folder)
05007 {
05008   // TODO: need to handle templates here?
05009   if ( (mFolder) && (folder->idString() == mFolder->idString()) )
05010   {
05011     mFolder = kmkernel->draftsFolder();
05012     kdDebug(5006) << "restoring drafts to " << mFolder->idString() << endl;
05013   }
05014   if (mMsg) mMsg->setParent(0);
05015 }
05016 
05017 
05018 void KMComposeWin::editorFocusChanged(bool gained)
05019 {
05020   mPasteQuotation->setEnabled(gained);
05021 }
05022 
05023 void KMComposeWin::slotSetAlwaysSend( bool bAlways )
05024 {
05025     mAlwaysSend = bAlways;
05026 }
05027 
05028 void KMComposeWin::slotListAction( const QString& style )
05029 {
05030     toggleMarkup(true);
05031     if ( style == i18n( "Standard" ) )
05032        mEditor->setParagType( QStyleSheetItem::DisplayBlock, QStyleSheetItem::ListDisc );
05033     else if ( style == i18n( "Bulleted List (Disc)" ) )
05034        mEditor->setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListDisc );
05035     else if ( style == i18n( "Bulleted List (Circle)" ) )
05036        mEditor->setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListCircle );
05037     else if ( style == i18n( "Bulleted List (Square)" ) )
05038        mEditor->setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListSquare );
05039     else if ( style == i18n( "Ordered List (Decimal)" ))
05040        mEditor->setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListDecimal );
05041     else if ( style == i18n( "Ordered List (Alpha lower)" ) )
05042        mEditor->setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListLowerAlpha );
05043     else if ( style == i18n( "Ordered List (Alpha upper)" ) )
05044        mEditor->setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListUpperAlpha );
05045     mEditor->viewport()->setFocus();
05046 }
05047 
05048 void KMComposeWin::slotFontAction( const QString& font)
05049 {
05050     toggleMarkup(true);
05051     mEditor->QTextEdit::setFamily( font );
05052     mEditor->viewport()->setFocus();
05053 }
05054 
05055 void KMComposeWin::slotSizeAction( int size )
05056 {
05057     toggleMarkup(true);
05058     mEditor->setPointSize( size );
05059     mEditor->viewport()->setFocus();
05060 }
05061 
05062 void KMComposeWin::slotAlignLeft()
05063 {
05064     toggleMarkup(true);
05065     mEditor->QTextEdit::setAlignment( AlignLeft );
05066 }
05067 
05068 void KMComposeWin::slotAlignCenter()
05069 {
05070     toggleMarkup(true);
05071     mEditor->QTextEdit::setAlignment( AlignHCenter );
05072 }
05073 
05074 void KMComposeWin::slotAlignRight()
05075 {
05076     toggleMarkup(true);
05077     mEditor->QTextEdit::setAlignment( AlignRight );
05078 }
05079 
05080 void KMComposeWin::slotTextBold()
05081 {
05082     toggleMarkup(true);
05083     mEditor->QTextEdit::setBold( textBoldAction->isChecked() );
05084 }
05085 
05086 void KMComposeWin::slotTextItalic()
05087 {
05088     toggleMarkup(true);
05089     mEditor->QTextEdit::setItalic( textItalicAction->isChecked() );
05090 }
05091 
05092 void KMComposeWin::slotTextUnder()
05093 {
05094     toggleMarkup(true);
05095     mEditor->QTextEdit::setUnderline( textUnderAction->isChecked() );
05096 }
05097 
05098 void KMComposeWin::slotFormatReset()
05099 {
05100   mEditor->setColor(mForeColor);
05101   mEditor->setCurrentFont( mSaveFont ); // fontChanged is called now
05102 }
05103 void KMComposeWin::slotTextColor()
05104 {
05105   QColor color = mEditor->color();
05106 
05107   if ( KColorDialog::getColor( color, this ) ) {
05108     toggleMarkup(true);
05109     mEditor->setColor( color );
05110   }
05111 }
05112 
05113 void KMComposeWin::fontChanged( const QFont &f )
05114 {
05115   QFont fontTemp = f;
05116   fontTemp.setBold( true );
05117   fontTemp.setItalic( true );
05118   QFontInfo fontInfo( fontTemp );
05119 
05120   if ( fontInfo.bold() ) {
05121     textBoldAction->setChecked( f.bold() );
05122     textBoldAction->setEnabled( true ) ;
05123   } else {
05124     textBoldAction->setEnabled( false );
05125   }
05126 
05127   if ( fontInfo.italic() ) {
05128     textItalicAction->setChecked( f.italic() );
05129     textItalicAction->setEnabled( true ) ;
05130   } else {
05131     textItalicAction->setEnabled( false );
05132   }
05133 
05134   textUnderAction->setChecked( f.underline() );
05135 
05136   fontAction->setFont( f.family() );
05137   fontSizeAction->setFontSize( f.pointSize() );
05138 }
05139 
05140 void KMComposeWin::alignmentChanged( int a )
05141 {
05142     //toggleMarkup();
05143     alignLeftAction->setChecked( ( a == AlignAuto ) || ( a & AlignLeft ) );
05144     alignCenterAction->setChecked( ( a & AlignHCenter ) );
05145     alignRightAction->setChecked( ( a & AlignRight ) );
05146 }
05147 
05148 namespace {
05149   class KToggleActionResetter {
05150     KToggleAction * mAction;
05151     bool mOn;
05152   public:
05153     KToggleActionResetter( KToggleAction * action, bool on )
05154       : mAction( action ),  mOn( on ) {}
05155     ~KToggleActionResetter() {
05156       if ( mAction )
05157         mAction->setChecked( mOn );
05158     }
05159     void disable() { mAction = 0; }
05160   };
05161 }
05162 
05163 void KMComposeWin::slotEncryptChiasmusToggled( bool on ) {
05164   mEncryptWithChiasmus = false;
05165 
05166   if ( !on )
05167     return;
05168 
05169   KToggleActionResetter resetter( mEncryptChiasmusAction, false );
05170 
05171   const Kleo::CryptoBackend::Protocol * chiasmus =
05172     Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
05173 
05174   if ( !chiasmus ) {
05175     const QString msg = Kleo::CryptoBackendFactory::instance()->knowsAboutProtocol( "Chiasmus" )
05176       ? i18n( "Please configure a Crypto Backend to use for "
05177               "Chiasmus encryption first.\n"
05178               "You can do this in the Crypto Backends tab of "
05179               "the configure dialog's Security page." )
05180       : i18n( "It looks as though libkleopatra was compiled without "
05181               "Chiasmus support. You might want to recompile "
05182               "libkleopatra with --enable-chiasmus.");
05183     KMessageBox::information( this, msg, i18n("No Chiasmus Backend Configured" ) );
05184     return;
05185   }
05186 
05187   STD_NAMESPACE_PREFIX auto_ptr<Kleo::SpecialJob> job( chiasmus->specialJob( "x-obtain-keys", QMap<QString,QVariant>() ) );
05188   if ( !job.get() ) {
05189     const QString msg = i18n( "Chiasmus backend does not offer the "
05190                               "\"x-obtain-keys\" function. Please report this bug." );
05191     KMessageBox::error( this, msg, i18n( "Chiasmus Backend Error" ) );
05192     return;
05193   }
05194 
05195   if ( job->exec() ) {
05196     job->showErrorDialog( this, i18n( "Chiasmus Backend Error" ) );
05197     return;
05198   }
05199 
05200   const QVariant result = job->property( "result" );
05201   if ( result.type() != QVariant::StringList ) {
05202     const QString msg = i18n( "Unexpected return value from Chiasmus backend: "
05203                               "The \"x-obtain-keys\" function did not return a "
05204                               "string list. Please report this bug." );
05205     KMessageBox::error( this, msg, i18n( "Chiasmus Backend Error" ) );
05206     return;
05207   }
05208 
05209   const QStringList keys = result.toStringList();
05210   if ( keys.empty() ) {
05211     const QString msg = i18n( "No keys have been found. Please check that a "
05212                               "valid key path has been set in the Chiasmus "
05213                               "configuration." );
05214     KMessageBox::information( this, msg, i18n( "No Chiasmus Keys Found" ) );
05215     return;
05216   }
05217 
05218   ChiasmusKeySelector selectorDlg( this, i18n( "Chiasmus Encryption Key Selection" ),
05219                                    keys, GlobalSettings::chiasmusKey(),
05220                                    GlobalSettings::chiasmusOptions() );
05221   if ( selectorDlg.exec() != QDialog::Accepted )
05222     return;
05223 
05224   GlobalSettings::setChiasmusOptions( selectorDlg.options() );
05225   GlobalSettings::setChiasmusKey( selectorDlg.key() );
05226   assert( !GlobalSettings::chiasmusKey().isEmpty() );
05227   mEncryptWithChiasmus = true;
05228   resetter.disable();
05229 }
05230 
05231 void KMComposeWin::slotEditDone(KMail::EditorWatcher * watcher)
05232 {
05233   kdDebug(5006) << k_funcinfo << endl;
05234   KMMessagePart *part = mEditorMap[ watcher ];
05235   KTempFile *tf = mEditorTempFiles[ watcher ];
05236   mEditorMap.remove( watcher );
05237   mEditorTempFiles.remove( watcher );
05238   if ( !watcher->fileChanged() )
05239     return;
05240 
05241   tf->file()->reset();
05242   QByteArray data = tf->file()->readAll();
05243   part->setBodyEncodedBinary( data );
05244 }
05245 
05246 
05247 void KMComposeWin::slotUpdateSignatureAndEncrypionStateIndicators()
05248 {
05249     const bool showIndicatorsAlways = false; // FIXME config option?
05250     mSignatureStateIndicator->setText( mSignAction->isChecked()? i18n("Message will be signed") : i18n("Message will not be signed") );
05251     mEncryptionStateIndicator->setText( mEncryptAction->isChecked()? i18n("Message will be encrypted") : i18n("Message will not be encrypted") );
05252     if ( !showIndicatorsAlways ) {
05253       mSignatureStateIndicator->setShown( mSignAction->isChecked() );
05254       mEncryptionStateIndicator->setShown( mEncryptAction->isChecked() );
05255     }
05256 }
05257 
05258 void KMComposeWin::slotAttachmentDragStarted()
05259 {
05260   kdDebug(5006) << k_funcinfo << endl;
05261   int idx = 0;
05262   QStringList filenames;
05263   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ++it, ++idx ) {
05264     if ( (*it)->isSelected() ) {
05265       KMMessagePart* msgPart = mAtmList.at(idx);
05266       KTempDir * tempDir = new KTempDir(); // will be deleted on composer close
05267       tempDir->setAutoDelete( true );
05268       mTempDirs.insert( tempDir );
05269       const QString fileName = tempDir->name() + "/" + msgPart->name();
05270       KPIM::kByteArrayToFile(msgPart->bodyDecodedBinary(),
05271                              fileName,
05272                              false, false, false);
05273       KURL url;
05274       url.setPath( fileName );
05275       filenames << url.path();
05276     }
05277   }
05278   if ( filenames.isEmpty() ) return;
05279 
05280   QUriDrag *drag  = new QUriDrag( mAtmListView );
05281   drag->setFileNames( filenames );
05282   drag->dragCopy();
05283 }
05284 
05285 void KMComposeWin::recipientEditorSizeHintChanged()
05286 {
05287   QTimer::singleShot( 1, this, SLOT(setMaximumHeaderSize()) );
05288 }
05289 
05290 void KMComposeWin::setMaximumHeaderSize()
05291 {
05292   mHeadersArea->setMaximumHeight( mHeadersArea->sizeHint().height() );
05293 }
05294 
KDE Home | KDE Accessibility Home | Description of Access Keys