kmail

configuredialog.cpp

00001 /*   -*- mode: C++; c-file-style: "gnu" -*-
00002  *   kmail: KDE mail client
00003  *   This file: Copyright (C) 2000 Espen Sand, espen@kde.org
00004  *              Copyright (C) 2001-2003 Marc Mutz, mutz@kde.org
00005  *   Contains code segments and ideas from earlier kmail dialog code.
00006  *
00007  *   This program is free software; you can redistribute it and/or modify
00008  *   it under the terms of the GNU General Public License as published by
00009  *   the Free Software Foundation; either version 2 of the License, or
00010  *   (at your option) any later version.
00011  *
00012  *   This program is distributed in the hope that it will be useful,
00013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *   GNU General Public License for more details.
00016  *
00017  *   You should have received a copy of the GNU General Public License
00018  *   along with this program; if not, write to the Free Software
00019  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 // This must be first
00024 #include <config.h>
00025 
00026 // my headers:
00027 #include "configuredialog.h"
00028 #include "configuredialog_p.h"
00029 
00030 #include "globalsettings.h"
00031 #include "replyphrases.h"
00032 #include "templatesconfiguration_kfg.h"
00033 
00034 // other KMail headers:
00035 #include "kmkernel.h"
00036 #include "simplestringlisteditor.h"
00037 #include "accountdialog.h"
00038 using KMail::AccountDialog;
00039 #include "colorlistbox.h"
00040 #include "kmacctseldlg.h"
00041 #include "messagesender.h"
00042 #include "kmtransport.h"
00043 #include "kmfoldermgr.h"
00044 #include <libkpimidentities/identitymanager.h>
00045 #include "identitylistview.h"
00046 using KMail::IdentityListView;
00047 using KMail::IdentityListViewItem;
00048 #include "kcursorsaver.h"
00049 #include "accountmanager.h"
00050 #include <composercryptoconfiguration.h>
00051 #include <warningconfiguration.h>
00052 #include <smimeconfiguration.h>
00053 #include "templatesconfiguration.h"
00054 #include "customtemplates.h"
00055 #include "folderrequester.h"
00056 using KMail::FolderRequester;
00057 #include "accountcombobox.h"
00058 #include "imapaccountbase.h"
00059 using KMail::ImapAccountBase;
00060 #include "folderstorage.h"
00061 #include "kmfolder.h"
00062 #include "kmmainwidget.h"
00063 #include "recentaddresses.h"
00064 using KRecentAddress::RecentAddresses;
00065 #include "completionordereditor.h"
00066 #include "ldapclient.h"
00067 #include "index.h"
00068 
00069 using KMail::IdentityListView;
00070 using KMail::IdentityListViewItem;
00071 #include "identitydialog.h"
00072 using KMail::IdentityDialog;
00073 
00074 // other kdenetwork headers:
00075 #include <libkpimidentities/identity.h>
00076 #include <kmime_util.h>
00077 using KMime::DateFormatter;
00078 #include <kleo/cryptoconfig.h>
00079 #include <kleo/cryptobackendfactory.h>
00080 #include <ui/backendconfigwidget.h>
00081 #include <ui/keyrequester.h>
00082 #include <ui/keyselectiondialog.h>
00083 
00084 // other KDE headers:
00085 #include <klocale.h>
00086 #include <kapplication.h>
00087 #include <kcharsets.h>
00088 #include <kasciistringtools.h>
00089 #include <kdebug.h>
00090 #include <knuminput.h>
00091 #include <kfontdialog.h>
00092 #include <kmessagebox.h>
00093 #include <kurlrequester.h>
00094 #include <kseparator.h>
00095 #include <kiconloader.h>
00096 #include <kstandarddirs.h>
00097 #include <kwin.h>
00098 #include <knotifydialog.h>
00099 #include <kconfig.h>
00100 #include <kactivelabel.h>
00101 #include <kcmultidialog.h>
00102 #include <kcombobox.h>
00103 
00104 // Qt headers:
00105 #include <qvalidator.h>
00106 #include <qwhatsthis.h>
00107 #include <qvgroupbox.h>
00108 #include <qvbox.h>
00109 #include <qvbuttongroup.h>
00110 #include <qhbuttongroup.h>
00111 #include <qtooltip.h>
00112 #include <qlabel.h>
00113 #include <qtextcodec.h>
00114 #include <qheader.h>
00115 #include <qpopupmenu.h>
00116 #include <qradiobutton.h>
00117 #include <qlayout.h>
00118 #include <qcheckbox.h>
00119 #include <qwidgetstack.h>
00120 
00121 // other headers:
00122 #include <assert.h>
00123 #include <stdlib.h>
00124 
00125 #ifndef _PATH_SENDMAIL
00126 #define _PATH_SENDMAIL  "/usr/sbin/sendmail"
00127 #endif
00128 
00129 #ifdef DIM
00130 #undef DIM
00131 #endif
00132 #define DIM(x) sizeof(x) / sizeof(*x)
00133 
00134 namespace {
00135 
00136   struct EnumConfigEntryItem {
00137     const char * key; // config key value, as appears in config file
00138     const char * desc; // description, to be i18n()ized
00139   };
00140   struct EnumConfigEntry {
00141     const char * group;
00142     const char * key;
00143     const char * desc;
00144     const EnumConfigEntryItem * items;
00145     int numItems;
00146     int defaultItem;
00147   };
00148   struct BoolConfigEntry {
00149     const char * group;
00150     const char * key;
00151     const char * desc;
00152     bool defaultValue;
00153   };
00154 
00155   static const char * lockedDownWarning =
00156     I18N_NOOP("<qt><p>This setting has been fixed by your administrator.</p>"
00157               "<p>If you think this is an error, please contact him.</p></qt>");
00158 
00159   void checkLockDown( QWidget * w, const KConfigBase & c, const char * key ) {
00160     if ( c.entryIsImmutable( key ) ) {
00161       w->setEnabled( false );
00162       QToolTip::add( w, i18n( lockedDownWarning ) );
00163     } else {
00164       QToolTip::remove( w );
00165     }
00166   }
00167 
00168   void populateButtonGroup( QButtonGroup * g, const EnumConfigEntry & e ) {
00169     g->setTitle( i18n( e.desc ) );
00170     g->layout()->setSpacing( KDialog::spacingHint() );
00171     for ( int i = 0 ; i < e.numItems ; ++i )
00172       g->insert( new QRadioButton( i18n( e.items[i].desc ), g ), i );
00173   }
00174 
00175   void populateCheckBox( QCheckBox * b, const BoolConfigEntry & e ) {
00176     b->setText( i18n( e.desc ) );
00177   }
00178 
00179   void loadWidget( QCheckBox * b, const KConfigBase & c, const BoolConfigEntry & e ) {
00180     Q_ASSERT( c.group() == e.group );
00181     checkLockDown( b, c, e.key );
00182     b->setChecked( c.readBoolEntry( e.key, e.defaultValue ) );
00183   }
00184 
00185   void loadWidget( QButtonGroup * g, const KConfigBase & c, const EnumConfigEntry & e ) {
00186     Q_ASSERT( c.group() == e.group );
00187     Q_ASSERT( g->count() == e.numItems );
00188     checkLockDown( g, c, e.key );
00189     const QString s = c.readEntry( e.key, e.items[e.defaultItem].key );
00190     for ( int i = 0 ; i < e.numItems ; ++i )
00191       if ( s == e.items[i].key ) {
00192         g->setButton( i );
00193         return;
00194       }
00195     g->setButton( e.defaultItem );
00196   }
00197 
00198   void saveCheckBox( QCheckBox * b, KConfigBase & c, const BoolConfigEntry & e ) {
00199     Q_ASSERT( c.group() == e.group );
00200     c.writeEntry( e.key, b->isChecked() );
00201   }
00202 
00203   void saveButtonGroup( QButtonGroup * g, KConfigBase & c, const EnumConfigEntry & e ) {
00204     Q_ASSERT( c.group() == e.group );
00205     Q_ASSERT( g->count() == e.numItems );
00206     c.writeEntry( e.key, e.items[ g->id( g->selected() ) ].key );
00207   }
00208 
00209   template <typename T_Widget, typename T_Entry>
00210   inline void loadProfile( T_Widget * g, const KConfigBase & c, const T_Entry & e ) {
00211     if ( c.hasKey( e.key ) )
00212       loadWidget( g, c, e );
00213   }
00214 }
00215 
00216 
00217 ConfigureDialog::ConfigureDialog( QWidget *parent, const char *name, bool modal )
00218   : KCMultiDialog( KDialogBase::IconList, KGuiItem( i18n( "&Load Profile..." ) ),
00219                    KGuiItem(), User2, i18n( "Configure" ), parent, name, modal )
00220   , mProfileDialog( 0 )
00221 {
00222   KWin::setIcons( winId(), kapp->icon(), kapp->miniIcon() );
00223   showButton( User1, true );
00224 
00225   addModule ( "kmail_config_identity", false );
00226   addModule ( "kmail_config_accounts", false );
00227   addModule ( "kmail_config_appearance", false );
00228   addModule ( "kmail_config_composer", false );
00229   addModule ( "kmail_config_security", false );
00230   addModule ( "kmail_config_misc", false );
00231 
00232   // We store the size of the dialog on hide, because otherwise
00233   // the KCMultiDialog starts with the size of the first kcm, not
00234   // the largest one. This way at least after the first showing of
00235   // the largest kcm the size is kept.
00236   KConfigGroup geometry( KMKernel::config(), "Geometry" );
00237   int width = geometry.readNumEntry( "ConfigureDialogWidth" );
00238   int height = geometry.readNumEntry( "ConfigureDialogHeight" );
00239   if ( width != 0 && height != 0 ) {
00240      setMinimumSize( width, height );
00241   }
00242 
00243 }
00244 
00245 void ConfigureDialog::hideEvent( QHideEvent *ev ) {
00246   KConfigGroup geometry( KMKernel::config(), "Geometry" );
00247   geometry.writeEntry( "ConfigureDialogWidth", width() );
00248   geometry.writeEntry( "ConfigureDialogHeight",height() );
00249   KDialogBase::hideEvent( ev );
00250 }
00251 
00252 ConfigureDialog::~ConfigureDialog() {
00253 }
00254 
00255 void ConfigureDialog::slotApply() {
00256   KCMultiDialog::slotApply();
00257   GlobalSettings::self()->writeConfig();
00258   emit configChanged();
00259 }
00260 
00261 void ConfigureDialog::slotOk() {
00262   KCMultiDialog::slotOk();
00263   GlobalSettings::self()->writeConfig();
00264   emit configChanged();
00265 }
00266 
00267 void ConfigureDialog::slotUser2() {
00268   if ( mProfileDialog ) {
00269     mProfileDialog->raise();
00270     return;
00271   }
00272   mProfileDialog = new ProfileDialog( this, "mProfileDialog" );
00273   connect( mProfileDialog, SIGNAL(profileSelected(KConfig*)),
00274                 this, SIGNAL(installProfile(KConfig*)) );
00275   mProfileDialog->show();
00276 }
00277 
00278 // *************************************************************
00279 // *                                                           *
00280 // *                      IdentityPage                         *
00281 // *                                                           *
00282 // *************************************************************
00283 QString IdentityPage::helpAnchor() const {
00284   return QString::fromLatin1("configure-identity");
00285 }
00286 
00287 IdentityPage::IdentityPage( QWidget * parent, const char * name )
00288   : ConfigModule( parent, name ),
00289     mIdentityDialog( 0 )
00290 {
00291   QHBoxLayout * hlay = new QHBoxLayout( this, 0, KDialog::spacingHint() );
00292 
00293   mIdentityList = new IdentityListView( this );
00294   connect( mIdentityList, SIGNAL(selectionChanged()),
00295            SLOT(slotIdentitySelectionChanged()) );
00296   connect( mIdentityList, SIGNAL(itemRenamed(QListViewItem*,const QString&,int)),
00297            SLOT(slotRenameIdentity(QListViewItem*,const QString&,int)) );
00298   connect( mIdentityList, SIGNAL(doubleClicked(QListViewItem*,const QPoint&,int)),
00299            SLOT(slotModifyIdentity()) );
00300   connect( mIdentityList, SIGNAL(contextMenu(KListView*,QListViewItem*,const QPoint&)),
00301            SLOT(slotContextMenu(KListView*,QListViewItem*,const QPoint&)) );
00302   // ### connect dragged(...), ...
00303 
00304   hlay->addWidget( mIdentityList, 1 );
00305 
00306   QVBoxLayout * vlay = new QVBoxLayout( hlay ); // inherits spacing
00307 
00308   QPushButton * button = new QPushButton( i18n("&Add..."), this );
00309   mModifyButton = new QPushButton( i18n("&Modify..."), this );
00310   mRenameButton = new QPushButton( i18n("&Rename"), this );
00311   mRemoveButton = new QPushButton( i18n("Remo&ve"), this );
00312   mSetAsDefaultButton = new QPushButton( i18n("Set as &Default"), this );
00313   button->setAutoDefault( false );
00314   mModifyButton->setAutoDefault( false );
00315   mModifyButton->setEnabled( false );
00316   mRenameButton->setAutoDefault( false );
00317   mRenameButton->setEnabled( false );
00318   mRemoveButton->setAutoDefault( false );
00319   mRemoveButton->setEnabled( false );
00320   mSetAsDefaultButton->setAutoDefault( false );
00321   mSetAsDefaultButton->setEnabled( false );
00322   connect( button, SIGNAL(clicked()),
00323            this, SLOT(slotNewIdentity()) );
00324   connect( mModifyButton, SIGNAL(clicked()),
00325            this, SLOT(slotModifyIdentity()) );
00326   connect( mRenameButton, SIGNAL(clicked()),
00327            this, SLOT(slotRenameIdentity()) );
00328   connect( mRemoveButton, SIGNAL(clicked()),
00329            this, SLOT(slotRemoveIdentity()) );
00330   connect( mSetAsDefaultButton, SIGNAL(clicked()),
00331            this, SLOT(slotSetAsDefault()) );
00332   vlay->addWidget( button );
00333   vlay->addWidget( mModifyButton );
00334   vlay->addWidget( mRenameButton );
00335   vlay->addWidget( mRemoveButton );
00336   vlay->addWidget( mSetAsDefaultButton );
00337   vlay->addStretch( 1 );
00338   load();
00339 }
00340 
00341 void IdentityPage::load()
00342 {
00343   KPIM::IdentityManager * im = kmkernel->identityManager();
00344   mOldNumberOfIdentities = im->shadowIdentities().count();
00345   // Fill the list:
00346   mIdentityList->clear();
00347   QListViewItem * item = 0;
00348   for ( KPIM::IdentityManager::Iterator it = im->modifyBegin() ; it != im->modifyEnd() ; ++it )
00349     item = new IdentityListViewItem( mIdentityList, item, *it  );
00350   mIdentityList->setSelected( mIdentityList->currentItem(), true );
00351 }
00352 
00353 void IdentityPage::save() {
00354   assert( !mIdentityDialog );
00355 
00356   kmkernel->identityManager()->sort();
00357   kmkernel->identityManager()->commit();
00358 
00359   if( mOldNumberOfIdentities < 2 && mIdentityList->childCount() > 1 ) {
00360     // have more than one identity, so better show the combo in the
00361     // composer now:
00362     KConfigGroup composer( KMKernel::config(), "Composer" );
00363     int showHeaders = composer.readNumEntry( "headers", HDR_STANDARD );
00364     showHeaders |= HDR_IDENTITY;
00365     composer.writeEntry( "headers", showHeaders );
00366   }
00367   // and now the reverse
00368   if( mOldNumberOfIdentities > 1 && mIdentityList->childCount() < 2 ) {
00369     // have only one identity, so remove the combo in the composer:
00370     KConfigGroup composer( KMKernel::config(), "Composer" );
00371     int showHeaders = composer.readNumEntry( "headers", HDR_STANDARD );
00372     showHeaders &= ~HDR_IDENTITY;
00373     composer.writeEntry( "headers", showHeaders );
00374   }
00375 }
00376 
00377 void IdentityPage::slotNewIdentity()
00378 {
00379   assert( !mIdentityDialog );
00380 
00381   KPIM::IdentityManager * im = kmkernel->identityManager();
00382   NewIdentityDialog dialog( im->shadowIdentities(), this, "new", true );
00383 
00384   if( dialog.exec() == QDialog::Accepted ) {
00385     QString identityName = dialog.identityName().stripWhiteSpace();
00386     assert( !identityName.isEmpty() );
00387 
00388     //
00389     // Construct a new Identity:
00390     //
00391     switch ( dialog.duplicateMode() ) {
00392     case NewIdentityDialog::ExistingEntry:
00393       {
00394         KPIM::Identity & dupThis = im->modifyIdentityForName( dialog.duplicateIdentity() );
00395         im->newFromExisting( dupThis, identityName );
00396         break;
00397       }
00398     case NewIdentityDialog::ControlCenter:
00399       im->newFromControlCenter( identityName );
00400       break;
00401     case NewIdentityDialog::Empty:
00402       im->newFromScratch( identityName );
00403     default: ;
00404     }
00405 
00406     //
00407     // Insert into listview:
00408     //
00409     KPIM::Identity & newIdent = im->modifyIdentityForName( identityName );
00410     QListViewItem * item = mIdentityList->selectedItem();
00411     if ( item )
00412       item = item->itemAbove();
00413     mIdentityList->setSelected( new IdentityListViewItem( mIdentityList,
00414                                                           /*after*/ item,
00415                                                           newIdent ), true );
00416     slotModifyIdentity();
00417   }
00418 }
00419 
00420 void IdentityPage::slotModifyIdentity() {
00421   assert( !mIdentityDialog );
00422 
00423   IdentityListViewItem * item =
00424     dynamic_cast<IdentityListViewItem*>( mIdentityList->selectedItem() );
00425   if ( !item ) return;
00426 
00427   mIdentityDialog = new IdentityDialog( this );
00428   mIdentityDialog->setIdentity( item->identity() );
00429 
00430   // Hmm, an unmodal dialog would be nicer, but a modal one is easier ;-)
00431   if ( mIdentityDialog->exec() == QDialog::Accepted ) {
00432     mIdentityDialog->updateIdentity( item->identity() );
00433     item->redisplay();
00434     emit changed(true);
00435   }
00436 
00437   delete mIdentityDialog;
00438   mIdentityDialog = 0;
00439 }
00440 
00441 void IdentityPage::slotRemoveIdentity()
00442 {
00443   assert( !mIdentityDialog );
00444 
00445   KPIM::IdentityManager * im = kmkernel->identityManager();
00446   kdFatal( im->shadowIdentities().count() < 2 )
00447     << "Attempted to remove the last identity!" << endl;
00448 
00449   IdentityListViewItem * item =
00450     dynamic_cast<IdentityListViewItem*>( mIdentityList->selectedItem() );
00451   if ( !item ) return;
00452 
00453   QString msg = i18n("<qt>Do you really want to remove the identity named "
00454                      "<b>%1</b>?</qt>").arg( item->identity().identityName() );
00455   if( KMessageBox::warningContinueCancel( this, msg, i18n("Remove Identity"),
00456    KGuiItem(i18n("&Remove"),"editdelete") ) == KMessageBox::Continue )
00457     if ( im->removeIdentity( item->identity().identityName() ) ) {
00458       delete item;
00459       mIdentityList->setSelected( mIdentityList->currentItem(), true );
00460       refreshList();
00461     }
00462 }
00463 
00464 void IdentityPage::slotRenameIdentity() {
00465   assert( !mIdentityDialog );
00466 
00467   QListViewItem * item = mIdentityList->selectedItem();
00468   if ( !item ) return;
00469 
00470   mIdentityList->rename( item, 0 );
00471 }
00472 
00473 void IdentityPage::slotRenameIdentity( QListViewItem * i,
00474                                        const QString & s, int col ) {
00475   assert( col == 0 );
00476   Q_UNUSED( col );
00477 
00478   IdentityListViewItem * item = dynamic_cast<IdentityListViewItem*>( i );
00479   if ( !item ) return;
00480 
00481   QString newName = s.stripWhiteSpace();
00482   if ( !newName.isEmpty() &&
00483        !kmkernel->identityManager()->shadowIdentities().contains( newName ) ) {
00484     KPIM::Identity & ident = item->identity();
00485     ident.setIdentityName( newName );
00486     emit changed(true);
00487   }
00488   item->redisplay();
00489 }
00490 
00491 void IdentityPage::slotContextMenu( KListView *, QListViewItem * i,
00492                                     const QPoint & pos ) {
00493   IdentityListViewItem * item = dynamic_cast<IdentityListViewItem*>( i );
00494 
00495   QPopupMenu * menu = new QPopupMenu( this );
00496   menu->insertItem( i18n("Add..."), this, SLOT(slotNewIdentity()) );
00497   if ( item ) {
00498     menu->insertItem( i18n("Modify..."), this, SLOT(slotModifyIdentity()) );
00499     if ( mIdentityList->childCount() > 1 )
00500       menu->insertItem( i18n("Remove"), this, SLOT(slotRemoveIdentity()) );
00501     if ( !item->identity().isDefault() )
00502       menu->insertItem( i18n("Set as Default"), this, SLOT(slotSetAsDefault()) );
00503   }
00504   menu->exec( pos );
00505   delete menu;
00506 }
00507 
00508 
00509 void IdentityPage::slotSetAsDefault() {
00510   assert( !mIdentityDialog );
00511 
00512   IdentityListViewItem * item =
00513     dynamic_cast<IdentityListViewItem*>( mIdentityList->selectedItem() );
00514   if ( !item ) return;
00515 
00516   KPIM::IdentityManager * im = kmkernel->identityManager();
00517   im->setAsDefault( item->identity().identityName() );
00518   refreshList();
00519 }
00520 
00521 void IdentityPage::refreshList() {
00522   for ( QListViewItemIterator it( mIdentityList ) ; it.current() ; ++it ) {
00523     IdentityListViewItem * item =
00524       dynamic_cast<IdentityListViewItem*>(it.current());
00525     if ( item )
00526       item->redisplay();
00527   }
00528   emit changed(true);
00529 }
00530 
00531 void IdentityPage::slotIdentitySelectionChanged()
00532 {
00533   IdentityListViewItem *item =
00534     dynamic_cast<IdentityListViewItem*>( mIdentityList->selectedItem() );
00535 
00536   mRemoveButton->setEnabled( item && mIdentityList->childCount() > 1 );
00537   mModifyButton->setEnabled( item );
00538   mRenameButton->setEnabled( item );
00539   mSetAsDefaultButton->setEnabled( item && !item->identity().isDefault() );
00540 }
00541 
00542 void IdentityPage::slotUpdateTransportCombo( const QStringList & sl )
00543 {
00544   if ( mIdentityDialog ) mIdentityDialog->slotUpdateTransportCombo( sl );
00545 }
00546 
00547 
00548 
00549 // *************************************************************
00550 // *                                                           *
00551 // *                       AccountsPage                         *
00552 // *                                                           *
00553 // *************************************************************
00554 QString AccountsPage::helpAnchor() const {
00555   return QString::fromLatin1("configure-accounts");
00556 }
00557 
00558 AccountsPage::AccountsPage( QWidget * parent, const char * name )
00559   : ConfigModuleWithTabs( parent, name )
00560 {
00561   //
00562   // "Receiving" tab:
00563   //
00564   mReceivingTab = new ReceivingTab();
00565   addTab( mReceivingTab, i18n( "&Receiving" ) );
00566   connect( mReceivingTab, SIGNAL(accountListChanged(const QStringList &)),
00567            this, SIGNAL(accountListChanged(const QStringList &)) );
00568 
00569   //
00570   // "Sending" tab:
00571   //
00572   mSendingTab = new SendingTab();
00573   addTab( mSendingTab, i18n( "&Sending" ) );
00574   connect( mSendingTab, SIGNAL(transportListChanged(const QStringList&)),
00575            this, SIGNAL(transportListChanged(const QStringList&)) );
00576 
00577   load();
00578 }
00579 
00580 QString AccountsPage::SendingTab::helpAnchor() const {
00581   return QString::fromLatin1("configure-accounts-sending");
00582 }
00583 
00584 AccountsPageSendingTab::AccountsPageSendingTab( QWidget * parent, const char * name )
00585   : ConfigModuleTab( parent, name )
00586 {
00587   mTransportInfoList.setAutoDelete( true );
00588   // temp. vars:
00589   QVBoxLayout *vlay;
00590   QVBoxLayout *btn_vlay;
00591   QHBoxLayout *hlay;
00592   QGridLayout *glay;
00593   QPushButton *button;
00594   QGroupBox   *group;
00595 
00596   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
00597   // label: zero stretch ### FIXME more
00598   vlay->addWidget( new QLabel( i18n("Outgoing accounts (add at least one):"), this ) );
00599 
00600   // hbox layout: stretch 10, spacing inherited from vlay
00601   hlay = new QHBoxLayout();
00602   vlay->addLayout( hlay, 10 ); // high stretch b/c of the groupbox's sizeHint
00603 
00604   // transport list: left widget in hlay; stretch 1
00605   // ### FIXME: allow inline renaming of the account:
00606   mTransportList = new ListView( this, "transportList", 5 );
00607   mTransportList->addColumn( i18n("Name") );
00608   mTransportList->addColumn( i18n("Type") );
00609   mTransportList->setAllColumnsShowFocus( true );
00610   mTransportList->setSorting( -1 );
00611   connect( mTransportList, SIGNAL(selectionChanged()),
00612            this, SLOT(slotTransportSelected()) );
00613   connect( mTransportList, SIGNAL(doubleClicked( QListViewItem *)),
00614            this, SLOT(slotModifySelectedTransport()) );
00615   hlay->addWidget( mTransportList, 1 );
00616 
00617   // a vbox layout for the buttons: zero stretch, spacing inherited from hlay
00618   btn_vlay = new QVBoxLayout( hlay );
00619 
00620   // "add..." button: stretch 0
00621   button = new QPushButton( i18n("A&dd..."), this );
00622   button->setAutoDefault( false );
00623   connect( button, SIGNAL(clicked()),
00624            this, SLOT(slotAddTransport()) );
00625   btn_vlay->addWidget( button );
00626 
00627   // "modify..." button: stretch 0
00628   mModifyTransportButton = new QPushButton( i18n("&Modify..."), this );
00629   mModifyTransportButton->setAutoDefault( false );
00630   mModifyTransportButton->setEnabled( false ); // b/c no item is selected yet
00631   connect( mModifyTransportButton, SIGNAL(clicked()),
00632            this, SLOT(slotModifySelectedTransport()) );
00633   btn_vlay->addWidget( mModifyTransportButton );
00634 
00635   // "remove" button: stretch 0
00636   mRemoveTransportButton = new QPushButton( i18n("R&emove"), this );
00637   mRemoveTransportButton->setAutoDefault( false );
00638   mRemoveTransportButton->setEnabled( false ); // b/c no item is selected yet
00639   connect( mRemoveTransportButton, SIGNAL(clicked()),
00640            this, SLOT(slotRemoveSelectedTransport()) );
00641   btn_vlay->addWidget( mRemoveTransportButton );
00642 
00643   mSetDefaultTransportButton = new QPushButton( i18n("Set Default"), this );
00644   mSetDefaultTransportButton->setAutoDefault( false );
00645   mSetDefaultTransportButton->setEnabled( false );
00646   connect ( mSetDefaultTransportButton, SIGNAL(clicked()),
00647             this, SLOT(slotSetDefaultTransport()) );
00648   btn_vlay->addWidget( mSetDefaultTransportButton );
00649   btn_vlay->addStretch( 1 ); // spacer
00650 
00651   // "Common options" groupbox:
00652   group = new QGroupBox( 0, Qt::Vertical,
00653                          i18n("Common Options"), this );
00654   vlay->addWidget(group);
00655 
00656   // a grid layout for the contents of the "common options" group box
00657   glay = new QGridLayout( group->layout(), 5, 3, KDialog::spacingHint() );
00658   glay->setColStretch( 2, 10 );
00659 
00660   // "confirm before send" check box:
00661   mConfirmSendCheck = new QCheckBox( i18n("Confirm &before send"), group );
00662   glay->addMultiCellWidget( mConfirmSendCheck, 0, 0, 0, 1 );
00663   connect( mConfirmSendCheck, SIGNAL( stateChanged( int ) ),
00664            this, SLOT( slotEmitChanged( void ) ) );
00665 
00666   // "send on check" combo:
00667   mSendOnCheckCombo = new QComboBox( false, group );
00668   mSendOnCheckCombo->insertStringList( QStringList()
00669                                       << i18n("Never Automatically")
00670                                       << i18n("On Manual Mail Checks")
00671                                       << i18n("On All Mail Checks") );
00672   glay->addWidget( mSendOnCheckCombo, 1, 1 );
00673   connect( mSendOnCheckCombo, SIGNAL( activated( int ) ),
00674            this, SLOT( slotEmitChanged( void ) ) );
00675 
00676   // "default send method" combo:
00677   mSendMethodCombo = new QComboBox( false, group );
00678   mSendMethodCombo->insertStringList( QStringList()
00679                                       << i18n("Send Now")
00680                                       << i18n("Send Later") );
00681   glay->addWidget( mSendMethodCombo, 2, 1 );
00682   connect( mSendMethodCombo, SIGNAL( activated( int ) ),
00683            this, SLOT( slotEmitChanged( void ) ) );
00684 
00685 
00686   // "message property" combo:
00687   // ### FIXME: remove completely?
00688   mMessagePropertyCombo = new QComboBox( false, group );
00689   mMessagePropertyCombo->insertStringList( QStringList()
00690                      << i18n("Allow 8-bit")
00691                      << i18n("MIME Compliant (Quoted Printable)") );
00692   glay->addWidget( mMessagePropertyCombo, 3, 1 );
00693   connect( mMessagePropertyCombo, SIGNAL( activated( int ) ),
00694            this, SLOT( slotEmitChanged( void ) ) );
00695 
00696   // "default domain" input field:
00697   mDefaultDomainEdit = new KLineEdit( group );
00698   glay->addMultiCellWidget( mDefaultDomainEdit, 4, 4, 1, 2 );
00699   connect( mDefaultDomainEdit, SIGNAL( textChanged( const QString& ) ),
00700            this, SLOT( slotEmitChanged( void ) ) );
00701 
00702   // labels:
00703   QLabel *l =  new QLabel( mSendOnCheckCombo, /*buddy*/
00704                             i18n("Send &messages in outbox folder:"), group );
00705   glay->addWidget( l, 1, 0 );
00706 
00707   QString msg = i18n( GlobalSettings::self()->sendOnCheckItem()->whatsThis().utf8() );
00708   QWhatsThis::add( l, msg );
00709   QWhatsThis::add( mSendOnCheckCombo, msg );
00710 
00711   glay->addWidget( new QLabel( mSendMethodCombo, /*buddy*/
00712                                i18n("Defa&ult send method:"), group ), 2, 0 );
00713   glay->addWidget( new QLabel( mMessagePropertyCombo, /*buddy*/
00714                                i18n("Message &property:"), group ), 3, 0 );
00715   l = new QLabel( mDefaultDomainEdit, /*buddy*/
00716                           i18n("Defaul&t domain:"), group );
00717   glay->addWidget( l, 4, 0 );
00718 
00719   // and now: add QWhatsThis:
00720   msg = i18n( "<qt><p>The default domain is used to complete email "
00721               "addresses that only consist of the user's name."
00722               "</p></qt>" );
00723   QWhatsThis::add( l, msg );
00724   QWhatsThis::add( mDefaultDomainEdit, msg );
00725 }
00726 
00727 
00728 void AccountsPage::SendingTab::slotTransportSelected()
00729 {
00730   QListViewItem *cur = mTransportList->selectedItem();
00731   mModifyTransportButton->setEnabled( cur );
00732   mRemoveTransportButton->setEnabled( cur );
00733   mSetDefaultTransportButton->setEnabled( cur );
00734 }
00735 
00736 // adds a number to @p name to make the name unique
00737 static inline QString uniqueName( const QStringList & list,
00738                                   const QString & name )
00739 {
00740   int suffix = 1;
00741   QString result = name;
00742   while ( list.find( result ) != list.end() ) {
00743     result = i18n("%1: name; %2: number appended to it to make it unique "
00744                   "among a list of names", "%1 %2")
00745       .arg( name ).arg( suffix );
00746     suffix++;
00747   }
00748   return result;
00749 }
00750 
00751 void AccountsPage::SendingTab::slotSetDefaultTransport()
00752 {
00753   QListViewItem *item = mTransportList->selectedItem();
00754   if ( !item ) return;
00755 
00756   KMTransportInfo ti;
00757 
00758   QListViewItemIterator it( mTransportList );
00759   for ( ; it.current(); ++it ) {
00760   ti.readConfig( KMTransportInfo::findTransport( it.current()->text(0) ));
00761   if ( ti.type != "sendmail" ) {
00762     it.current()->setText( 1, "smtp" );
00763   } else {
00764     it.current()->setText( 1, "sendmail" );
00765     }
00766   }
00767 
00768   if ( item->text(1) != "sendmail" ) {
00769     item->setText( 1, i18n( "smtp (Default)" ));
00770   } else {
00771     item->setText( 1, i18n( "sendmail (Default)" ));
00772   }
00773   GlobalSettings::self()->setDefaultTransport( item->text(0) );
00774 
00775 }
00776 
00777 void AccountsPage::SendingTab::slotAddTransport()
00778 {
00779   int transportType;
00780 
00781   { // limit scope of selDialog
00782     KMTransportSelDlg selDialog( this );
00783     if ( selDialog.exec() != QDialog::Accepted ) return;
00784     transportType = selDialog.selected();
00785   }
00786 
00787   KMTransportInfo *transportInfo = new KMTransportInfo();
00788   switch ( transportType ) {
00789   case 0: // smtp
00790     transportInfo->type = QString::fromLatin1("smtp");
00791     break;
00792   case 1: // sendmail
00793     transportInfo->type = QString::fromLatin1("sendmail");
00794     transportInfo->name = i18n("Sendmail");
00795     transportInfo->host = _PATH_SENDMAIL; // ### FIXME: use const, not #define
00796     break;
00797   default:
00798     assert( 0 );
00799   }
00800 
00801   KMTransportDialog dialog( i18n("Add Transport"), transportInfo, this );
00802 
00803   // create list of names:
00804   // ### move behind dialog.exec()?
00805   QStringList transportNames;
00806   QPtrListIterator<KMTransportInfo> it( mTransportInfoList );
00807   for ( it.toFirst() ; it.current() ; ++it )
00808     transportNames << (*it)->name;
00809 
00810   if( dialog.exec() != QDialog::Accepted ) {
00811     delete transportInfo;
00812     return;
00813   }
00814 
00815   // disambiguate the name by appending a number:
00816   // ### FIXME: don't allow this error to happen in the first place!
00817   transportInfo->name = uniqueName( transportNames, transportInfo->name );
00818   // append to names and transportinfo lists:
00819   transportNames << transportInfo->name;
00820   mTransportInfoList.append( transportInfo );
00821 
00822   // append to listview:
00823   // ### FIXME: insert before the selected item, append on empty selection
00824   QListViewItem *lastItem = mTransportList->firstChild();
00825   QString typeDisplayName;
00826   if ( lastItem ) {
00827     typeDisplayName = transportInfo->type;
00828   } else {
00829     typeDisplayName = i18n("%1: type of transport. Result used in "
00830                            "Configure->Accounts->Sending listview, \"type\" "
00831                            "column, first row, to indicate that this is the "
00832                            "default transport", "%1 (Default)")
00833       .arg( transportInfo->type );
00834     GlobalSettings::self()->setDefaultTransport( transportInfo->name );
00835   }
00836   (void) new QListViewItem( mTransportList, lastItem, transportInfo->name,
00837                             typeDisplayName );
00838 
00839   // notify anyone who cares:
00840   emit transportListChanged( transportNames );
00841   emit changed( true );
00842 }
00843 
00844 void AccountsPage::SendingTab::slotModifySelectedTransport()
00845 {
00846   QListViewItem *item = mTransportList->selectedItem();
00847   if ( !item ) return;
00848 
00849   const QString& originalTransport = item->text(0);
00850 
00851   QPtrListIterator<KMTransportInfo> it( mTransportInfoList );
00852   for ( it.toFirst() ; it.current() ; ++it )
00853     if ( (*it)->name == item->text(0) ) break;
00854   if ( !it.current() ) return;
00855 
00856   KMTransportDialog dialog( i18n("Modify Transport"), (*it), this );
00857 
00858   if ( dialog.exec() != QDialog::Accepted ) return;
00859 
00860   // create the list of names of transports, but leave out the current
00861   // item:
00862   QStringList transportNames;
00863   QPtrListIterator<KMTransportInfo> jt( mTransportInfoList );
00864   int entryLocation = -1;
00865   for ( jt.toFirst() ; jt.current() ; ++jt )
00866     if ( jt != it )
00867       transportNames << (*jt)->name;
00868     else
00869       entryLocation = transportNames.count();
00870   assert( entryLocation >= 0 );
00871 
00872   // make the new name unique by appending a high enough number:
00873   (*it)->name = uniqueName( transportNames, (*it)->name );
00874   // change the list item to the new name
00875   item->setText( 0, (*it)->name );
00876   // and insert the new name at the position of the old in the list of
00877   // strings; then broadcast the new list:
00878   transportNames.insert( transportNames.at( entryLocation ), (*it)->name );
00879   const QString& newTransportName = (*it)->name;
00880 
00881   QStringList changedIdents;
00882   KPIM::IdentityManager * im = kmkernel->identityManager();
00883   for ( KPIM::IdentityManager::Iterator it = im->modifyBegin(); it != im->modifyEnd(); ++it ) {
00884     if ( originalTransport == (*it).transport() ) {
00885       (*it).setTransport( newTransportName );
00886       changedIdents += (*it).identityName();
00887     }
00888   }
00889 
00890   if ( !changedIdents.isEmpty() ) {
00891     QString information = i18n( "This identity has been changed to use the modified transport:",
00892                           "These %n identities have been changed to use the modified transport:",
00893                           changedIdents.count() );
00894     KMessageBox::informationList( this, information, changedIdents );
00895   }
00896 
00897   emit transportListChanged( transportNames );
00898   emit changed( true );
00899 }
00900 
00901 void AccountsPage::SendingTab::slotRemoveSelectedTransport()
00902 {
00903   QListViewItem *item = mTransportList->selectedItem();
00904   if ( !item ) return;
00905 
00906   bool selectedTransportWasDefault = false;
00907   if ( item->text( 0 ) == GlobalSettings::self()->defaultTransport() ) {
00908       selectedTransportWasDefault = true;
00909   }
00910   QStringList changedIdents;
00911   KPIM::IdentityManager * im = kmkernel->identityManager();
00912   for ( KPIM::IdentityManager::Iterator it = im->modifyBegin(); it != im->modifyEnd(); ++it ) {
00913     if ( item->text( 0 ) == (*it).transport() ) {
00914       (*it).setTransport( QString::null );
00915       changedIdents += (*it).identityName();
00916     }
00917   }
00918 
00919   // if the deleted transport is the currently used transport reset it to default
00920   const QString& currentTransport = GlobalSettings::self()->currentTransport();
00921   if ( item->text( 0 ) == currentTransport ) {
00922     GlobalSettings::self()->setCurrentTransport( QString::null );
00923   }
00924 
00925   if ( !changedIdents.isEmpty() ) {
00926     QString information = i18n( "This identity has been changed to use the default transport:",
00927                           "These %n identities have been changed to use the default transport:",
00928                           changedIdents.count() );
00929     KMessageBox::informationList( this, information, changedIdents );
00930   }
00931 
00932   QPtrListIterator<KMTransportInfo> it( mTransportInfoList );
00933   for ( it.toFirst() ; it.current() ; ++it )
00934     if ( (*it)->name == item->text(0) ) break;
00935   if ( !it.current() ) return;
00936 
00937   KMTransportInfo ti;
00938 
00939   if( selectedTransportWasDefault )
00940   {
00941     QListViewItem *newCurrent = item->itemBelow();
00942     if ( !newCurrent ) newCurrent = item->itemAbove();
00943     //mTransportList->removeItem( item );
00944     if ( newCurrent ) {
00945       mTransportList->setCurrentItem( newCurrent );
00946       mTransportList->setSelected( newCurrent, true );
00947       GlobalSettings::self()->setDefaultTransport( newCurrent->text(0) );
00948       ti.readConfig( KMTransportInfo::findTransport( newCurrent->text(0) ));
00949       if ( ti.type != "sendmail" ) {
00950         newCurrent->setText( 1, i18n("smtp (Default)") );
00951       } else {
00952         newCurrent->setText( 1, i18n("sendmail (Default)" ));
00953       }
00954     } else {
00955       GlobalSettings::self()->setDefaultTransport( QString::null );
00956     }
00957   }
00958   delete item;
00959   mTransportInfoList.remove( it );
00960 
00961   QStringList transportNames;
00962   for ( it.toFirst() ; it.current() ; ++it )
00963     transportNames << (*it)->name;
00964   emit transportListChanged( transportNames );
00965   emit changed( true );
00966 }
00967 
00968 void AccountsPage::SendingTab::doLoadFromGlobalSettings() {
00969   mSendOnCheckCombo->setCurrentItem( GlobalSettings::self()->sendOnCheck() );
00970 }
00971 
00972 void AccountsPage::SendingTab::doLoadOther() {
00973   KConfigGroup general( KMKernel::config(), "General");
00974   KConfigGroup composer( KMKernel::config(), "Composer");
00975 
00976   int numTransports = general.readNumEntry("transports", 0);
00977 
00978   QListViewItem *top = 0;
00979   mTransportInfoList.clear();
00980   mTransportList->clear();
00981   QStringList transportNames;
00982   for ( int i = 1 ; i <= numTransports ; i++ ) {
00983     KMTransportInfo *ti = new KMTransportInfo();
00984     ti->readConfig(i);
00985     mTransportInfoList.append( ti );
00986     transportNames << ti->name;
00987     top = new QListViewItem( mTransportList, top, ti->name, ti->type );
00988   }
00989   emit transportListChanged( transportNames );
00990 
00991   const QString &defaultTransport = GlobalSettings::self()->defaultTransport();
00992 
00993   QListViewItemIterator it( mTransportList );
00994   for ( ; it.current(); ++it ) {
00995     if ( it.current()->text(0) == defaultTransport ) {
00996       if ( it.current()->text(1) != "sendmail" ) {
00997         it.current()->setText( 1, i18n( "smtp (Default)" ));
00998       } else {
00999         it.current()->setText( 1, i18n( "sendmail (Default)" ));
01000       }
01001     } else {
01002       if ( it.current()->text(1) != "sendmail" ) {
01003         it.current()->setText( 1, "smtp" );
01004       } else {
01005         it.current()->setText( 1, "sendmail" );
01006       }
01007     }
01008   }
01009 
01010   mSendMethodCombo->setCurrentItem(
01011                 kmkernel->msgSender()->sendImmediate() ? 0 : 1 );
01012   mMessagePropertyCombo->setCurrentItem(
01013                 kmkernel->msgSender()->sendQuotedPrintable() ? 1 : 0 );
01014 
01015   mConfirmSendCheck->setChecked( composer.readBoolEntry( "confirm-before-send",
01016                                                          false ) );
01017   QString str = general.readEntry( "Default domain" );
01018   if( str.isEmpty() )
01019   {
01020     //### FIXME: Use the global convenience function instead of the homebrewed
01021     //           solution once we can rely on HEAD kdelibs.
01022     //str = KGlobal::hostname(); ???????
01023     char buffer[256];
01024     if ( !gethostname( buffer, 255 ) )
01025       // buffer need not be NUL-terminated if it has full length
01026       buffer[255] = 0;
01027     else
01028       buffer[0] = 0;
01029     str = QString::fromLatin1( *buffer ? buffer : "localhost" );
01030   }
01031   mDefaultDomainEdit->setText( str );
01032 }
01033 
01034 void AccountsPage::SendingTab::save() {
01035   KConfigGroup general( KMKernel::config(), "General" );
01036   KConfigGroup composer( KMKernel::config(), "Composer" );
01037 
01038   // Save transports:
01039   general.writeEntry( "transports", mTransportInfoList.count() );
01040   QPtrListIterator<KMTransportInfo> it( mTransportInfoList );
01041   for ( int i = 1 ; it.current() ; ++it, ++i )
01042     (*it)->writeConfig(i);
01043 
01044   // Save common options:
01045   GlobalSettings::self()->setSendOnCheck( mSendOnCheckCombo->currentItem() );
01046   kmkernel->msgSender()->setSendImmediate(
01047                              mSendMethodCombo->currentItem() == 0 );
01048   kmkernel->msgSender()->setSendQuotedPrintable(
01049                              mMessagePropertyCombo->currentItem() == 1 );
01050   kmkernel->msgSender()->writeConfig( false ); // don't sync
01051   composer.writeEntry("confirm-before-send", mConfirmSendCheck->isChecked() );
01052   general.writeEntry( "Default domain", mDefaultDomainEdit->text() );
01053 }
01054 
01055 QString AccountsPage::ReceivingTab::helpAnchor() const {
01056   return QString::fromLatin1("configure-accounts-receiving");
01057 }
01058 
01059 AccountsPageReceivingTab::AccountsPageReceivingTab( QWidget * parent, const char * name )
01060   : ConfigModuleTab ( parent, name )
01061 {
01062   // temp. vars:
01063   QVBoxLayout *vlay;
01064   QVBoxLayout *btn_vlay;
01065   QHBoxLayout *hlay;
01066   QPushButton *button;
01067   QGroupBox   *group;
01068 
01069   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
01070 
01071   // label: zero stretch
01072   vlay->addWidget( new QLabel( i18n("Incoming accounts (add at least one):"), this ) );
01073 
01074   // hbox layout: stretch 10, spacing inherited from vlay
01075   hlay = new QHBoxLayout();
01076   vlay->addLayout( hlay, 10 ); // high stretch to suppress groupbox's growing
01077 
01078   // account list: left widget in hlay; stretch 1
01079   mAccountList = new ListView( this, "accountList", 5 );
01080   mAccountList->addColumn( i18n("Name") );
01081   mAccountList->addColumn( i18n("Type") );
01082   mAccountList->addColumn( i18n("Folder") );
01083   mAccountList->setAllColumnsShowFocus( true );
01084   mAccountList->setSorting( -1 );
01085   connect( mAccountList, SIGNAL(selectionChanged()),
01086            this, SLOT(slotAccountSelected()) );
01087   connect( mAccountList, SIGNAL(doubleClicked( QListViewItem *)),
01088            this, SLOT(slotModifySelectedAccount()) );
01089   hlay->addWidget( mAccountList, 1 );
01090 
01091   // a vbox layout for the buttons: zero stretch, spacing inherited from hlay
01092   btn_vlay = new QVBoxLayout( hlay );
01093 
01094   // "add..." button: stretch 0
01095   button = new QPushButton( i18n("A&dd..."), this );
01096   button->setAutoDefault( false );
01097   connect( button, SIGNAL(clicked()),
01098            this, SLOT(slotAddAccount()) );
01099   btn_vlay->addWidget( button );
01100 
01101   // "modify..." button: stretch 0
01102   mModifyAccountButton = new QPushButton( i18n("&Modify..."), this );
01103   mModifyAccountButton->setAutoDefault( false );
01104   mModifyAccountButton->setEnabled( false ); // b/c no item is selected yet
01105   connect( mModifyAccountButton, SIGNAL(clicked()),
01106            this, SLOT(slotModifySelectedAccount()) );
01107   btn_vlay->addWidget( mModifyAccountButton );
01108 
01109   // "remove..." button: stretch 0
01110   mRemoveAccountButton = new QPushButton( i18n("R&emove"), this );
01111   mRemoveAccountButton->setAutoDefault( false );
01112   mRemoveAccountButton->setEnabled( false ); // b/c no item is selected yet
01113   connect( mRemoveAccountButton, SIGNAL(clicked()),
01114            this, SLOT(slotRemoveSelectedAccount()) );
01115   btn_vlay->addWidget( mRemoveAccountButton );
01116   btn_vlay->addStretch( 1 ); // spacer
01117 
01118   mCheckmailStartupCheck = new QCheckBox( i18n("Chec&k mail on startup"), this );
01119   vlay->addWidget( mCheckmailStartupCheck );
01120   connect( mCheckmailStartupCheck, SIGNAL( stateChanged( int ) ),
01121            this, SLOT( slotEmitChanged( void ) ) );
01122 
01123   // "New Mail Notification" group box: stretch 0
01124   group = new QVGroupBox( i18n("New Mail Notification"), this );
01125   vlay->addWidget( group );
01126   group->layout()->setSpacing( KDialog::spacingHint() );
01127 
01128   // "beep on new mail" check box:
01129   mBeepNewMailCheck = new QCheckBox(i18n("&Beep"), group );
01130   mBeepNewMailCheck->setSizePolicy( QSizePolicy( QSizePolicy::MinimumExpanding,
01131                                                  QSizePolicy::Fixed ) );
01132   connect( mBeepNewMailCheck, SIGNAL( stateChanged( int ) ),
01133            this, SLOT( slotEmitChanged( void ) ) );
01134 
01135   // "Detailed new mail notification" check box
01136   mVerboseNotificationCheck =
01137     new QCheckBox( i18n( "Deta&iled new mail notification" ), group );
01138   mVerboseNotificationCheck->setSizePolicy( QSizePolicy( QSizePolicy::MinimumExpanding,
01139                                                          QSizePolicy::Fixed ) );
01140   QToolTip::add( mVerboseNotificationCheck,
01141                  i18n( "Show for each folder the number of newly arrived "
01142                        "messages" ) );
01143   QWhatsThis::add( mVerboseNotificationCheck,
01144     GlobalSettings::self()->verboseNewMailNotificationItem()->whatsThis() );
01145   connect( mVerboseNotificationCheck, SIGNAL( stateChanged( int ) ),
01146            this, SLOT( slotEmitChanged() ) );
01147 
01148   // "Other Actions" button:
01149   mOtherNewMailActionsButton = new QPushButton( i18n("Other Actio&ns"), group );
01150   mOtherNewMailActionsButton->setSizePolicy( QSizePolicy( QSizePolicy::Fixed,
01151                                                           QSizePolicy::Fixed ) );
01152   connect( mOtherNewMailActionsButton, SIGNAL(clicked()),
01153            this, SLOT(slotEditNotifications()) );
01154 }
01155 
01156 AccountsPageReceivingTab::~AccountsPageReceivingTab()
01157 {
01158   // When hitting Cancel or closing the dialog with the window-manager-button,
01159   // we have a number of things to clean up:
01160 
01161   // The newly created accounts
01162   QValueList< QGuardedPtr<KMAccount> >::Iterator it;
01163   for (it = mNewAccounts.begin(); it != mNewAccounts.end(); ++it ) {
01164     delete (*it);
01165   }
01166   mNewAccounts.clear();
01167 
01168   // The modified accounts
01169   QValueList<ModifiedAccountsType*>::Iterator j;
01170   for ( j = mModifiedAccounts.begin() ; j != mModifiedAccounts.end() ; ++j ) {
01171     delete (*j)->newAccount;
01172     delete (*j);
01173   }
01174   mModifiedAccounts.clear();
01175 
01176 
01177 }
01178 
01179 void AccountsPage::ReceivingTab::slotAccountSelected()
01180 {
01181   QListViewItem * item = mAccountList->selectedItem();
01182   mModifyAccountButton->setEnabled( item );
01183   mRemoveAccountButton->setEnabled( item );
01184 }
01185 
01186 QStringList AccountsPage::ReceivingTab::occupiedNames()
01187 {
01188   QStringList accountNames = kmkernel->acctMgr()->getAccounts();
01189 
01190   QValueList<ModifiedAccountsType*>::Iterator k;
01191   for (k = mModifiedAccounts.begin(); k != mModifiedAccounts.end(); ++k )
01192     if ((*k)->oldAccount)
01193       accountNames.remove( (*k)->oldAccount->name() );
01194 
01195   QValueList< QGuardedPtr<KMAccount> >::Iterator l;
01196   for (l = mAccountsToDelete.begin(); l != mAccountsToDelete.end(); ++l )
01197     if (*l)
01198       accountNames.remove( (*l)->name() );
01199 
01200   QValueList< QGuardedPtr<KMAccount> >::Iterator it;
01201   for (it = mNewAccounts.begin(); it != mNewAccounts.end(); ++it )
01202     if (*it)
01203       accountNames += (*it)->name();
01204 
01205   QValueList<ModifiedAccountsType*>::Iterator j;
01206   for (j = mModifiedAccounts.begin(); j != mModifiedAccounts.end(); ++j )
01207     accountNames += (*j)->newAccount->name();
01208 
01209   return accountNames;
01210 }
01211 
01212 void AccountsPage::ReceivingTab::slotAddAccount() {
01213   KMAcctSelDlg accountSelectorDialog( this );
01214   if( accountSelectorDialog.exec() != QDialog::Accepted ) return;
01215 
01216   const char *accountType = 0;
01217   switch ( accountSelectorDialog.selected() ) {
01218     case 0: accountType = "local";      break;
01219     case 1: accountType = "pop";        break;
01220     case 2: accountType = "imap";       break;
01221     case 3: accountType = "cachedimap"; break;
01222     case 4: accountType = "maildir";    break;
01223 
01224     default:
01225       // ### FIXME: How should this happen???
01226       // replace with assert.
01227       KMessageBox::sorry( this, i18n("Unknown account type selected") );
01228       return;
01229   }
01230 
01231   KMAccount *account
01232     = kmkernel->acctMgr()->create( QString::fromLatin1( accountType ) );
01233   if ( !account ) {
01234     // ### FIXME: Give the user more information. Is this error
01235     // recoverable?
01236     KMessageBox::sorry( this, i18n("Unable to create account") );
01237     return;
01238   }
01239 
01240   account->init(); // fill the account fields with good default values
01241 
01242   AccountDialog dialog( i18n("Add Account"), account, this );
01243 
01244   QStringList accountNames = occupiedNames();
01245 
01246   if( dialog.exec() != QDialog::Accepted ) {
01247     delete account;
01248     return;
01249   }
01250 
01251   account->deinstallTimer();
01252   account->setName( uniqueName( accountNames, account->name() ) );
01253 
01254   QListViewItem *after = mAccountList->firstChild();
01255   while ( after && after->nextSibling() )
01256     after = after->nextSibling();
01257 
01258   QListViewItem *listItem =
01259     new QListViewItem( mAccountList, after, account->name(), account->type() );
01260   if( account->folder() )
01261     listItem->setText( 2, account->folder()->label() );
01262 
01263   mNewAccounts.append( account );
01264   emit changed( true );
01265 }
01266 
01267 
01268 
01269 void AccountsPage::ReceivingTab::slotModifySelectedAccount()
01270 {
01271   QListViewItem *listItem = mAccountList->selectedItem();
01272   if( !listItem ) return;
01273 
01274   KMAccount *account = 0;
01275   QValueList<ModifiedAccountsType*>::Iterator j;
01276   for (j = mModifiedAccounts.begin(); j != mModifiedAccounts.end(); ++j )
01277     if ( (*j)->newAccount->name() == listItem->text(0) ) {
01278       account = (*j)->newAccount;
01279       break;
01280     }
01281 
01282   if ( !account ) {
01283     QValueList< QGuardedPtr<KMAccount> >::Iterator it;
01284     for ( it = mNewAccounts.begin() ; it != mNewAccounts.end() ; ++it )
01285       if ( (*it)->name() == listItem->text(0) ) {
01286         account = *it;
01287         break;
01288       }
01289 
01290     if ( !account ) {
01291       account = kmkernel->acctMgr()->findByName( listItem->text(0) );
01292       if( !account ) {
01293         // ### FIXME: How should this happen? See above.
01294         KMessageBox::sorry( this, i18n("Unable to locate account") );
01295         return;
01296       }
01297       if ( account->type() == "imap" || account->type() == "cachedimap" )
01298       {
01299         ImapAccountBase* ai = static_cast<ImapAccountBase*>( account );
01300         if ( ai->namespaces().isEmpty() || ai->namespaceToDelimiter().isEmpty() )
01301         {
01302           // connect to server - the namespaces are fetched automatically
01303           kdDebug(5006) << "slotModifySelectedAccount - connect" << endl;
01304           ai->makeConnection();
01305         }
01306       }
01307 
01308       ModifiedAccountsType *mod = new ModifiedAccountsType;
01309       mod->oldAccount = account;
01310       mod->newAccount = kmkernel->acctMgr()->create( account->type(),
01311                                                    account->name() );
01312       mod->newAccount->pseudoAssign( account );
01313       mModifiedAccounts.append( mod );
01314       account = mod->newAccount;
01315     }
01316   }
01317 
01318   QStringList accountNames = occupiedNames();
01319   accountNames.remove( account->name() );
01320 
01321   AccountDialog dialog( i18n("Modify Account"), account, this );
01322 
01323   if( dialog.exec() != QDialog::Accepted ) return;
01324 
01325   account->setName( uniqueName( accountNames, account->name() ) );
01326 
01327   listItem->setText( 0, account->name() );
01328   listItem->setText( 1, account->type() );
01329   if( account->folder() )
01330     listItem->setText( 2, account->folder()->label() );
01331 
01332   emit changed( true );
01333 }
01334 
01335 
01336 
01337 void AccountsPage::ReceivingTab::slotRemoveSelectedAccount() {
01338   QListViewItem *listItem = mAccountList->selectedItem();
01339   if( !listItem ) return;
01340 
01341   KMAccount *acct = 0;
01342   QValueList<ModifiedAccountsType*>::Iterator j;
01343   for ( j = mModifiedAccounts.begin() ; j != mModifiedAccounts.end() ; ++j )
01344     if ( (*j)->newAccount->name() == listItem->text(0) ) {
01345       acct = (*j)->oldAccount;
01346       mAccountsToDelete.append( acct );
01347       mModifiedAccounts.remove( j );
01348       break;
01349     }
01350   if ( !acct ) {
01351     QValueList< QGuardedPtr<KMAccount> >::Iterator it;
01352     for ( it = mNewAccounts.begin() ; it != mNewAccounts.end() ; ++it )
01353       if ( (*it)->name() == listItem->text(0) ) {
01354         acct = *it;
01355         mNewAccounts.remove( it );
01356         break;
01357       }
01358   }
01359   if ( !acct ) {
01360     acct = kmkernel->acctMgr()->findByName( listItem->text(0) );
01361     if ( acct )
01362       mAccountsToDelete.append( acct );
01363   }
01364   if ( !acct ) {
01365     // ### FIXME: see above
01366     KMessageBox::sorry( this, i18n("<qt>Unable to locate account <b>%1</b>.</qt>")
01367                         .arg(listItem->text(0)) );
01368     return;
01369   }
01370 
01371   QListViewItem * item = listItem->itemBelow();
01372   if ( !item ) item = listItem->itemAbove();
01373   delete listItem;
01374 
01375   if ( item )
01376     mAccountList->setSelected( item, true );
01377 
01378   emit changed( true );
01379 }
01380 
01381 void AccountsPage::ReceivingTab::slotEditNotifications()
01382 {
01383   if(kmkernel->xmlGuiInstance())
01384     KNotifyDialog::configure(this, 0, kmkernel->xmlGuiInstance()->aboutData());
01385   else
01386     KNotifyDialog::configure(this);
01387 }
01388 
01389 void AccountsPage::ReceivingTab::doLoadFromGlobalSettings() {
01390   mVerboseNotificationCheck->setChecked( GlobalSettings::self()->verboseNewMailNotification() );
01391 }
01392 
01393 void AccountsPage::ReceivingTab::doLoadOther() {
01394   KConfigGroup general( KMKernel::config(), "General" );
01395 
01396   mAccountList->clear();
01397   QListViewItem *top = 0;
01398 
01399   for( KMAccount *a = kmkernel->acctMgr()->first(); a!=0;
01400        a = kmkernel->acctMgr()->next() ) {
01401     QListViewItem *listItem =
01402       new QListViewItem( mAccountList, top, a->name(), a->type() );
01403     if( a->folder() )
01404       listItem->setText( 2, a->folder()->label() );
01405     top = listItem;
01406   }
01407   QListViewItem *listItem = mAccountList->firstChild();
01408   if ( listItem ) {
01409     mAccountList->setCurrentItem( listItem );
01410     mAccountList->setSelected( listItem, true );
01411   }
01412 
01413   mBeepNewMailCheck->setChecked( general.readBoolEntry("beep-on-mail", false ) );
01414   mCheckmailStartupCheck->setChecked( general.readBoolEntry("checkmail-startup", false) );
01415   QTimer::singleShot( 0, this, SLOT( slotTweakAccountList() ) );
01416 }
01417 
01418 void AccountsPage::ReceivingTab::slotTweakAccountList()
01419 {
01420   // Force the contentsWidth of mAccountList to be recalculated so that items can be
01421   // selected in the normal way. It would be best if this were not necessary.
01422   mAccountList->resizeContents( mAccountList->visibleWidth(), mAccountList->contentsHeight() );
01423 }
01424 
01425 void AccountsPage::ReceivingTab::save() {
01426   // Add accounts marked as new
01427   QValueList< QGuardedPtr<KMAccount> >::Iterator it;
01428   for (it = mNewAccounts.begin(); it != mNewAccounts.end(); ++it ) {
01429     kmkernel->acctMgr()->add( *it ); // calls installTimer too
01430   }
01431 
01432   // Update accounts that have been modified
01433   QValueList<ModifiedAccountsType*>::Iterator j;
01434   for ( j = mModifiedAccounts.begin() ; j != mModifiedAccounts.end() ; ++j ) {
01435     (*j)->oldAccount->pseudoAssign( (*j)->newAccount );
01436     delete (*j)->newAccount;
01437     delete (*j);
01438   }
01439   mModifiedAccounts.clear();
01440 
01441   // Delete accounts marked for deletion
01442   for ( it = mAccountsToDelete.begin() ;
01443         it != mAccountsToDelete.end() ; ++it ) {
01444     kmkernel->acctMgr()->writeConfig( true );
01445     if ( (*it) && !kmkernel->acctMgr()->remove(*it) )
01446       KMessageBox::sorry( this, i18n("<qt>Unable to locate account <b>%1</b>.</qt>")
01447                           .arg( (*it)->name() ) );
01448   }
01449   mAccountsToDelete.clear();
01450 
01451   // Incoming mail
01452   kmkernel->acctMgr()->writeConfig( false );
01453   kmkernel->cleanupImapFolders();
01454 
01455   // Save Mail notification settings
01456   KConfigGroup general( KMKernel::config(), "General" );
01457   general.writeEntry( "beep-on-mail", mBeepNewMailCheck->isChecked() );
01458   GlobalSettings::self()->setVerboseNewMailNotification( mVerboseNotificationCheck->isChecked() );
01459 
01460   general.writeEntry( "checkmail-startup", mCheckmailStartupCheck->isChecked() );
01461 
01462   // Sync new IMAP accounts ASAP:
01463   for (it = mNewAccounts.begin(); it != mNewAccounts.end(); ++it ) {
01464     KMAccount *macc = (*it);
01465     ImapAccountBase *acc = dynamic_cast<ImapAccountBase*> (macc);
01466     if ( acc ) {
01467       AccountUpdater *au = new AccountUpdater( acc );
01468       au->update();
01469     }
01470   }
01471   mNewAccounts.clear();
01472 
01473 }
01474 
01475 // *************************************************************
01476 // *                                                           *
01477 // *                     AppearancePage                        *
01478 // *                                                           *
01479 // *************************************************************
01480 QString AppearancePage::helpAnchor() const {
01481   return QString::fromLatin1("configure-appearance");
01482 }
01483 
01484 AppearancePage::AppearancePage( QWidget * parent, const char * name )
01485   : ConfigModuleWithTabs( parent, name )
01486 {
01487   //
01488   // "Fonts" tab:
01489   //
01490   mFontsTab = new FontsTab();
01491   addTab( mFontsTab, i18n("&Fonts") );
01492 
01493   //
01494   // "Colors" tab:
01495   //
01496   mColorsTab = new ColorsTab();
01497   addTab( mColorsTab, i18n("Color&s") );
01498 
01499   //
01500   // "Layout" tab:
01501   //
01502   mLayoutTab = new LayoutTab();
01503   addTab( mLayoutTab, i18n("La&yout") );
01504 
01505   //
01506   // "Headers" tab:
01507   //
01508   mHeadersTab = new HeadersTab();
01509   addTab( mHeadersTab, i18n("M&essage List") );
01510 
01511   //
01512   // "Reader window" tab:
01513   //
01514   mReaderTab = new ReaderTab();
01515   addTab( mReaderTab, i18n("Message W&indow") );
01516 
01517   //
01518   // "System Tray" tab:
01519   //
01520   mSystemTrayTab = new SystemTrayTab();
01521   addTab( mSystemTrayTab, i18n("System &Tray") );
01522 
01523   load();
01524 }
01525 
01526 
01527 QString AppearancePage::FontsTab::helpAnchor() const {
01528   return QString::fromLatin1("configure-appearance-fonts");
01529 }
01530 
01531 static const struct {
01532   const char * configName;
01533   const char * displayName;
01534   bool   enableFamilyAndSize;
01535   bool   onlyFixed;
01536 } fontNames[] = {
01537   { "body-font", I18N_NOOP("Message Body"), true, false },
01538   { "list-font", I18N_NOOP("Message List"), true, false },
01539   { "list-new-font", I18N_NOOP("Message List - New Messages"), true, false },
01540   { "list-unread-font", I18N_NOOP("Message List - Unread Messages"), true, false },
01541   { "list-important-font", I18N_NOOP("Message List - Important Messages"), true, false },
01542   { "list-todo-font", I18N_NOOP("Message List - Todo Messages"), true, false },
01543   { "list-date-font", I18N_NOOP("Message List - Date Field"), true, false },
01544   { "folder-font", I18N_NOOP("Folder List"), true, false },
01545   { "quote1-font", I18N_NOOP("Quoted Text - First Level"), false, false },
01546   { "quote2-font", I18N_NOOP("Quoted Text - Second Level"), false, false },
01547   { "quote3-font", I18N_NOOP("Quoted Text - Third Level"), false, false },
01548   { "fixed-font", I18N_NOOP("Fixed Width Font"), true, true },
01549   { "composer-font", I18N_NOOP("Composer"), true, false },
01550   { "print-font",  I18N_NOOP("Printing Output"), true, false },
01551 };
01552 static const int numFontNames = sizeof fontNames / sizeof *fontNames;
01553 
01554 AppearancePageFontsTab::AppearancePageFontsTab( QWidget * parent, const char * name )
01555   : ConfigModuleTab( parent, name ), mActiveFontIndex( -1 )
01556 {
01557   assert( numFontNames == sizeof mFont / sizeof *mFont );
01558   // tmp. vars:
01559   QVBoxLayout *vlay;
01560   QHBoxLayout *hlay;
01561   QLabel      *label;
01562 
01563   // "Use custom fonts" checkbox, followed by <hr>
01564   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
01565   mCustomFontCheck = new QCheckBox( i18n("&Use custom fonts"), this );
01566   vlay->addWidget( mCustomFontCheck );
01567   vlay->addWidget( new KSeparator( KSeparator::HLine, this ) );
01568   connect ( mCustomFontCheck, SIGNAL( stateChanged( int ) ),
01569             this, SLOT( slotEmitChanged( void ) ) );
01570 
01571   // "font location" combo box and label:
01572   hlay = new QHBoxLayout( vlay ); // inherites spacing
01573   mFontLocationCombo = new QComboBox( false, this );
01574   mFontLocationCombo->setEnabled( false ); // !mCustomFontCheck->isChecked()
01575 
01576   QStringList fontDescriptions;
01577   for ( int i = 0 ; i < numFontNames ; i++ )
01578     fontDescriptions << i18n( fontNames[i].displayName );
01579   mFontLocationCombo->insertStringList( fontDescriptions );
01580 
01581   label = new QLabel( mFontLocationCombo, i18n("Apply &to:"), this );
01582   label->setEnabled( false ); // since !mCustomFontCheck->isChecked()
01583   hlay->addWidget( label );
01584 
01585   hlay->addWidget( mFontLocationCombo );
01586   hlay->addStretch( 10 );
01587   vlay->addSpacing( KDialog::spacingHint() );
01588   mFontChooser = new KFontChooser( this, "font", false, QStringList(),
01589                                    false, 4 );
01590   mFontChooser->setEnabled( false ); // since !mCustomFontCheck->isChecked()
01591   vlay->addWidget( mFontChooser );
01592   connect ( mFontChooser, SIGNAL( fontSelected( const QFont& ) ),
01593             this, SLOT( slotEmitChanged( void ) ) );
01594 
01595 
01596   // {en,dis}able widgets depending on the state of mCustomFontCheck:
01597   connect( mCustomFontCheck, SIGNAL(toggled(bool)),
01598            label, SLOT(setEnabled(bool)) );
01599   connect( mCustomFontCheck, SIGNAL(toggled(bool)),
01600            mFontLocationCombo, SLOT(setEnabled(bool)) );
01601   connect( mCustomFontCheck, SIGNAL(toggled(bool)),
01602            mFontChooser, SLOT(setEnabled(bool)) );
01603   // load the right font settings into mFontChooser:
01604   connect( mFontLocationCombo, SIGNAL(activated(int) ),
01605            this, SLOT(slotFontSelectorChanged(int)) );
01606 }
01607 
01608 
01609 void AppearancePage::FontsTab::slotFontSelectorChanged( int index )
01610 {
01611   kdDebug(5006) << "slotFontSelectorChanged() called" << endl;
01612   if( index < 0 || index >= mFontLocationCombo->count() )
01613     return; // Should never happen, but it is better to check.
01614 
01615   // Save current fontselector setting before we install the new:
01616   if( mActiveFontIndex == 0 ) {
01617     mFont[0] = mFontChooser->font();
01618     // hardcode the family and size of "message body" dependant fonts:
01619     for ( int i = 0 ; i < numFontNames ; i++ )
01620       if ( !fontNames[i].enableFamilyAndSize ) {
01621         // ### shall we copy the font and set the save and re-set
01622         // {regular,italic,bold,bold italic} property or should we
01623         // copy only family and pointSize?
01624         mFont[i].setFamily( mFont[0].family() );
01625         mFont[i].setPointSize/*Float?*/( mFont[0].pointSize/*Float?*/() );
01626       }
01627   } else if ( mActiveFontIndex > 0 )
01628     mFont[ mActiveFontIndex ] = mFontChooser->font();
01629   mActiveFontIndex = index;
01630 
01631   // Disonnect so the "Apply" button is not activated by the change
01632   disconnect ( mFontChooser, SIGNAL( fontSelected( const QFont& ) ),
01633             this, SLOT( slotEmitChanged( void ) ) );
01634 
01635   // Display the new setting:
01636   mFontChooser->setFont( mFont[index], fontNames[index].onlyFixed );
01637 
01638   connect ( mFontChooser, SIGNAL( fontSelected( const QFont& ) ),
01639             this, SLOT( slotEmitChanged( void ) ) );
01640 
01641   // Disable Family and Size list if we have selected a quote font:
01642   mFontChooser->enableColumn( KFontChooser::FamilyList|KFontChooser::SizeList,
01643                               fontNames[ index ].enableFamilyAndSize );
01644 }
01645 
01646 void AppearancePage::FontsTab::doLoadOther() {
01647   KConfigGroup fonts( KMKernel::config(), "Fonts" );
01648 
01649   mFont[0] = KGlobalSettings::generalFont();
01650   QFont fixedFont = KGlobalSettings::fixedFont();
01651   for ( int i = 0 ; i < numFontNames ; i++ )
01652     mFont[i] = fonts.readFontEntry( fontNames[i].configName,
01653       (fontNames[i].onlyFixed) ? &fixedFont : &mFont[0] );
01654 
01655   mCustomFontCheck->setChecked( !fonts.readBoolEntry( "defaultFonts", true ) );
01656   mFontLocationCombo->setCurrentItem( 0 );
01657   slotFontSelectorChanged( 0 );
01658 }
01659 
01660 void AppearancePage::FontsTab::installProfile( KConfig * profile ) {
01661   KConfigGroup fonts( profile, "Fonts" );
01662 
01663   // read fonts that are defined in the profile:
01664   bool needChange = false;
01665   for ( int i = 0 ; i < numFontNames ; i++ )
01666     if ( fonts.hasKey( fontNames[i].configName ) ) {
01667       needChange = true;
01668       mFont[i] = fonts.readFontEntry( fontNames[i].configName );
01669       kdDebug(5006) << "got font \"" << fontNames[i].configName
01670                 << "\" thusly: \"" << mFont[i].toString() << "\"" << endl;
01671     }
01672   if ( needChange && mFontLocationCombo->currentItem() > 0 )
01673     mFontChooser->setFont( mFont[ mFontLocationCombo->currentItem() ],
01674       fontNames[ mFontLocationCombo->currentItem() ].onlyFixed );
01675 
01676   if ( fonts.hasKey( "defaultFonts" ) )
01677     mCustomFontCheck->setChecked( !fonts.readBoolEntry( "defaultFonts" ) );
01678 }
01679 
01680 void AppearancePage::FontsTab::save() {
01681   KConfigGroup fonts( KMKernel::config(), "Fonts" );
01682 
01683   // read the current font (might have been modified)
01684   if ( mActiveFontIndex >= 0 )
01685     mFont[ mActiveFontIndex ] = mFontChooser->font();
01686 
01687   bool customFonts = mCustomFontCheck->isChecked();
01688   fonts.writeEntry( "defaultFonts", !customFonts );
01689   for ( int i = 0 ; i < numFontNames ; i++ )
01690     if ( customFonts || fonts.hasKey( fontNames[i].configName ) )
01691       // Don't write font info when we use default fonts, but write
01692       // if it's already there:
01693       fonts.writeEntry( fontNames[i].configName, mFont[i] );
01694 }
01695 
01696 QString AppearancePage::ColorsTab::helpAnchor() const {
01697   return QString::fromLatin1("configure-appearance-colors");
01698 }
01699 
01700 
01701 static const struct {
01702   const char * configName;
01703   const char * displayName;
01704 } colorNames[] = { // adjust setup() if you change this:
01705   { "BackgroundColor", I18N_NOOP("Composer Background") },
01706   { "AltBackgroundColor", I18N_NOOP("Alternative Background Color") },
01707   { "ForegroundColor", I18N_NOOP("Normal Text") },
01708   { "QuotedText1", I18N_NOOP("Quoted Text - First Level") },
01709   { "QuotedText2", I18N_NOOP("Quoted Text - Second Level") },
01710   { "QuotedText3", I18N_NOOP("Quoted Text - Third Level") },
01711   { "LinkColor", I18N_NOOP("Link") },
01712   { "FollowedColor", I18N_NOOP("Followed Link") },
01713   { "MisspelledColor", I18N_NOOP("Misspelled Words") },
01714   { "NewMessage", I18N_NOOP("New Message") },
01715   { "UnreadMessage", I18N_NOOP("Unread Message") },
01716   { "FlagMessage", I18N_NOOP("Important Message") },
01717   { "TodoMessage", I18N_NOOP("Todo Message") },
01718   { "PGPMessageEncr", I18N_NOOP("OpenPGP Message - Encrypted") },
01719   { "PGPMessageOkKeyOk", I18N_NOOP("OpenPGP Message - Valid Signature with Trusted Key") },
01720   { "PGPMessageOkKeyBad", I18N_NOOP("OpenPGP Message - Valid Signature with Untrusted Key") },
01721   { "PGPMessageWarn", I18N_NOOP("OpenPGP Message - Unchecked Signature") },
01722   { "PGPMessageErr", I18N_NOOP("OpenPGP Message - Bad Signature") },
01723   { "HTMLWarningColor", I18N_NOOP("Border Around Warning Prepending HTML Messages") },
01724   { "CloseToQuotaColor", I18N_NOOP("Folder Name and Size When Close to Quota") },
01725   { "ColorbarBackgroundPlain", I18N_NOOP("HTML Status Bar Background - No HTML Message") },
01726   { "ColorbarForegroundPlain", I18N_NOOP("HTML Status Bar Foreground - No HTML Message") },
01727   { "ColorbarBackgroundHTML",  I18N_NOOP("HTML Status Bar Background - HTML Message") },
01728   { "ColorbarForegroundHTML",  I18N_NOOP("HTML Status Bar Foreground - HTML Message") },
01729 };
01730 static const int numColorNames = sizeof colorNames / sizeof *colorNames;
01731 
01732 AppearancePageColorsTab::AppearancePageColorsTab( QWidget * parent, const char * name )
01733   : ConfigModuleTab( parent, name )
01734 {
01735   // tmp. vars:
01736   QVBoxLayout *vlay;
01737 
01738   // "use custom colors" check box
01739   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
01740   mCustomColorCheck = new QCheckBox( i18n("&Use custom colors"), this );
01741   vlay->addWidget( mCustomColorCheck );
01742   connect( mCustomColorCheck, SIGNAL( stateChanged( int ) ),
01743            this, SLOT( slotEmitChanged( void ) ) );
01744 
01745   // color list box:
01746   mColorList = new ColorListBox( this );
01747   mColorList->setEnabled( false ); // since !mCustomColorCheck->isChecked()
01748   QStringList modeList;
01749   for ( int i = 0 ; i < numColorNames ; i++ )
01750     mColorList->insertItem( new ColorListItem( i18n( colorNames[i].displayName ) ) );
01751   vlay->addWidget( mColorList, 1 );
01752 
01753   // "recycle colors" check box:
01754   mRecycleColorCheck =
01755     new QCheckBox( i18n("Recycle colors on deep &quoting"), this );
01756   mRecycleColorCheck->setEnabled( false );
01757   vlay->addWidget( mRecycleColorCheck );
01758   connect( mRecycleColorCheck, SIGNAL( stateChanged( int ) ),
01759            this, SLOT( slotEmitChanged( void ) ) );
01760 
01761   // close to quota threshold
01762   QHBoxLayout *hbox = new QHBoxLayout(vlay);
01763   QLabel *l = new QLabel( i18n("Close to quota threshold"), this );
01764   hbox->addWidget( l );
01765   l->setEnabled( false );
01766   mCloseToQuotaThreshold = new QSpinBox( 0, 100, 1, this );
01767   connect( mCloseToQuotaThreshold, SIGNAL( valueChanged( int ) ),
01768            this, SLOT( slotEmitChanged( void ) ) );
01769   mCloseToQuotaThreshold->setEnabled( false );
01770   mCloseToQuotaThreshold->setSuffix( i18n("%"));
01771   hbox->addWidget( mCloseToQuotaThreshold );
01772   hbox->addWidget( new QWidget(this), 2 );
01773 
01774   // {en,dir}able widgets depending on the state of mCustomColorCheck:
01775   connect( mCustomColorCheck, SIGNAL(toggled(bool)),
01776            mColorList, SLOT(setEnabled(bool)) );
01777   connect( mCustomColorCheck, SIGNAL(toggled(bool)),
01778            mRecycleColorCheck, SLOT(setEnabled(bool)) );
01779   connect( mCustomColorCheck, SIGNAL(toggled(bool)),
01780            l, SLOT(setEnabled(bool)) );
01781   connect( mCustomColorCheck, SIGNAL(toggled(bool)),
01782        mCloseToQuotaThreshold, SLOT(setEnabled(bool)) );
01783 
01784   connect( mCustomColorCheck, SIGNAL( stateChanged( int ) ),
01785            this, SLOT( slotEmitChanged( void ) ) );
01786 }
01787 
01788 void AppearancePage::ColorsTab::doLoadOther() {
01789   KConfigGroup reader( KMKernel::config(), "Reader" );
01790 
01791   mCustomColorCheck->setChecked( !reader.readBoolEntry( "defaultColors", true ) );
01792   mRecycleColorCheck->setChecked( reader.readBoolEntry( "RecycleQuoteColors", false ) );
01793   mCloseToQuotaThreshold->setValue( GlobalSettings::closeToQuotaThreshold() );
01794 
01795   static const QColor defaultColor[ numColorNames ] = {
01796     kapp->palette().active().base(), // bg
01797     KGlobalSettings::alternateBackgroundColor(), // alt bg
01798     kapp->palette().active().text(), // fg
01799     QColor( 0x00, 0x80, 0x00 ), // quoted l1
01800     QColor( 0x00, 0x70, 0x00 ), // quoted l2
01801     QColor( 0x00, 0x60, 0x00 ), // quoted l3
01802     KGlobalSettings::linkColor(), // link
01803     KGlobalSettings::visitedLinkColor(), // visited link
01804     Qt::red, // misspelled words
01805     Qt::red, // new msg
01806     Qt::blue, // unread mgs
01807     QColor( 0x00, 0x7F, 0x00 ), // important msg
01808     Qt::blue, // todo mgs
01809     QColor( 0x00, 0x80, 0xFF ), // light blue // pgp encrypted
01810     QColor( 0x40, 0xFF, 0x40 ), // light green // pgp ok, trusted key
01811     QColor( 0xFF, 0xFF, 0x40 ), // light yellow // pgp ok, untrusted key
01812     QColor( 0xFF, 0xFF, 0x40 ), // light yellow // pgp unchk
01813     Qt::red, // pgp bad
01814     QColor( 0xFF, 0x40, 0x40 ), // warning text color: light red
01815     Qt::red, // close to quota
01816     Qt::lightGray, // colorbar plain bg
01817     Qt::black,     // colorbar plain fg
01818     Qt::black,     // colorbar html  bg
01819     Qt::white,     // colorbar html  fg
01820   };
01821 
01822   for ( int i = 0 ; i < numColorNames ; i++ ) {
01823     mColorList->setColor( i,
01824       reader.readColorEntry( colorNames[i].configName, &defaultColor[i] ) );
01825   }
01826   connect( mColorList, SIGNAL( changed( ) ),
01827            this, SLOT( slotEmitChanged( void ) ) );
01828 }
01829 
01830 void AppearancePage::ColorsTab::installProfile( KConfig * profile ) {
01831   KConfigGroup reader( profile, "Reader" );
01832 
01833   if ( reader.hasKey( "defaultColors" ) )
01834     mCustomColorCheck->setChecked( !reader.readBoolEntry( "defaultColors" ) );
01835   if ( reader.hasKey( "RecycleQuoteColors" ) )
01836     mRecycleColorCheck->setChecked( reader.readBoolEntry( "RecycleQuoteColors" ) );
01837 
01838   for ( int i = 0 ; i < numColorNames ; i++ )
01839     if ( reader.hasKey( colorNames[i].configName ) )
01840       mColorList->setColor( i, reader.readColorEntry( colorNames[i].configName ) );
01841 }
01842 
01843 void AppearancePage::ColorsTab::save() {
01844   KConfigGroup reader( KMKernel::config(), "Reader" );
01845 
01846   bool customColors = mCustomColorCheck->isChecked();
01847   reader.writeEntry( "defaultColors", !customColors );
01848 
01849   for ( int i = 0 ; i < numColorNames ; i++ )
01850     // Don't write color info when we use default colors, but write
01851     // if it's already there:
01852     if ( customColors || reader.hasKey( colorNames[i].configName ) )
01853       reader.writeEntry( colorNames[i].configName, mColorList->color(i) );
01854 
01855   reader.writeEntry( "RecycleQuoteColors", mRecycleColorCheck->isChecked() );
01856   GlobalSettings::setCloseToQuotaThreshold( mCloseToQuotaThreshold->value() );
01857 }
01858 
01859 QString AppearancePage::LayoutTab::helpAnchor() const {
01860   return QString::fromLatin1("configure-appearance-layout");
01861 }
01862 
01863 static const EnumConfigEntryItem folderListModes[] = {
01864   { "long", I18N_NOOP("Lon&g folder list") },
01865   { "short", I18N_NOOP("Shor&t folder list" ) }
01866 };
01867 static const EnumConfigEntry folderListMode = {
01868   "Geometry", "FolderList", I18N_NOOP("Folder List"),
01869   folderListModes, DIM(folderListModes), 0
01870 };
01871 
01872 
01873 static const EnumConfigEntryItem mimeTreeLocations[] = {
01874   { "top", I18N_NOOP("Abo&ve the message pane") },
01875   { "bottom", I18N_NOOP("&Below the message pane") }
01876 };
01877 static const EnumConfigEntry mimeTreeLocation = {
01878   "Reader", "MimeTreeLocation", I18N_NOOP("Message Structure Viewer Placement"),
01879   mimeTreeLocations, DIM(mimeTreeLocations), 1
01880 };
01881 
01882 static const EnumConfigEntryItem mimeTreeModes[] = {
01883   { "never", I18N_NOOP("Show &never") },
01884   { "smart", I18N_NOOP("Show only for non-plaintext &messages") },
01885   { "always", I18N_NOOP("Show alway&s") }
01886 };
01887 static const EnumConfigEntry mimeTreeMode = {
01888   "Reader", "MimeTreeMode", I18N_NOOP("Message Structure Viewer"),
01889   mimeTreeModes, DIM(mimeTreeModes), 1
01890 };
01891 
01892 
01893 static const EnumConfigEntryItem readerWindowModes[] = {
01894   { "hide", I18N_NOOP("&Do not show a message preview pane") },
01895   { "below", I18N_NOOP("Show the message preview pane belo&w the message list") },
01896   { "right", I18N_NOOP("Show the message preview pane ne&xt to the message list") }
01897 };
01898 static const EnumConfigEntry readerWindowMode = {
01899   "Geometry", "readerWindowMode", I18N_NOOP("Message Preview Pane"),
01900   readerWindowModes, DIM(readerWindowModes), 1
01901 };
01902 
01903 AppearancePageLayoutTab::AppearancePageLayoutTab( QWidget * parent, const char * name )
01904   : ConfigModuleTab( parent, name )
01905 {
01906   // tmp. vars:
01907   QVBoxLayout * vlay;
01908 
01909   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
01910 
01911   // "folder list" radio buttons:
01912   populateButtonGroup( mFolderListGroup = new QHButtonGroup( this ), folderListMode );
01913   vlay->addWidget( mFolderListGroup );
01914   connect( mFolderListGroup, SIGNAL ( clicked( int ) ),
01915            this, SLOT( slotEmitChanged() ) );
01916 
01917   mFavoriteFolderViewCB = new QCheckBox( i18n("Show favorite folder view"), this );
01918   connect( mFavoriteFolderViewCB, SIGNAL(toggled(bool)), SLOT(slotEmitChanged()) );
01919   vlay->addWidget( mFavoriteFolderViewCB );
01920 
01921   // "show reader window" radio buttons:
01922   populateButtonGroup( mReaderWindowModeGroup = new QVButtonGroup( this ), readerWindowMode );
01923   vlay->addWidget( mReaderWindowModeGroup );
01924   connect( mReaderWindowModeGroup, SIGNAL ( clicked( int ) ),
01925            this, SLOT( slotEmitChanged() ) );
01926 
01927   // "Show MIME Tree" radio buttons:
01928   populateButtonGroup( mMIMETreeModeGroup = new QVButtonGroup( this ), mimeTreeMode );
01929   vlay->addWidget( mMIMETreeModeGroup );
01930   connect( mMIMETreeModeGroup, SIGNAL ( clicked( int ) ),
01931            this, SLOT( slotEmitChanged() ) );
01932 
01933   // "MIME Tree Location" radio buttons:
01934   populateButtonGroup( mMIMETreeLocationGroup = new QHButtonGroup( this ), mimeTreeLocation );
01935   vlay->addWidget( mMIMETreeLocationGroup );
01936   connect( mMIMETreeLocationGroup, SIGNAL ( clicked( int ) ),
01937            this, SLOT( slotEmitChanged() ) );
01938 
01939   vlay->addStretch( 10 ); // spacer
01940 }
01941 
01942 void AppearancePage::LayoutTab::doLoadOther() {
01943   const KConfigGroup reader( KMKernel::config(), "Reader" );
01944   const KConfigGroup geometry( KMKernel::config(), "Geometry" );
01945 
01946   loadWidget( mFolderListGroup, geometry, folderListMode );
01947   loadWidget( mMIMETreeLocationGroup, reader, mimeTreeLocation );
01948   loadWidget( mMIMETreeModeGroup, reader, mimeTreeMode );
01949   loadWidget( mReaderWindowModeGroup, geometry, readerWindowMode );
01950   mFavoriteFolderViewCB->setChecked( GlobalSettings::self()->enableFavoriteFolderView() );
01951 }
01952 
01953 void AppearancePage::LayoutTab::installProfile( KConfig * profile ) {
01954   const KConfigGroup reader( profile, "Reader" );
01955   const KConfigGroup geometry( profile, "Geometry" );
01956 
01957   loadProfile( mFolderListGroup, geometry, folderListMode );
01958   loadProfile( mMIMETreeLocationGroup, reader, mimeTreeLocation );
01959   loadProfile( mMIMETreeModeGroup, reader, mimeTreeMode );
01960   loadProfile( mReaderWindowModeGroup, geometry, readerWindowMode );
01961 }
01962 
01963 void AppearancePage::LayoutTab::save() {
01964   KConfigGroup reader( KMKernel::config(), "Reader" );
01965   KConfigGroup geometry( KMKernel::config(), "Geometry" );
01966 
01967   saveButtonGroup( mFolderListGroup, geometry, folderListMode );
01968   saveButtonGroup( mMIMETreeLocationGroup, reader, mimeTreeLocation );
01969   saveButtonGroup( mMIMETreeModeGroup, reader, mimeTreeMode );
01970   saveButtonGroup( mReaderWindowModeGroup, geometry, readerWindowMode );
01971   GlobalSettings::self()->setEnableFavoriteFolderView( mFavoriteFolderViewCB->isChecked() );
01972 }
01973 
01974 //
01975 // Appearance Message List
01976 //
01977 
01978 QString AppearancePage::HeadersTab::helpAnchor() const {
01979   return QString::fromLatin1("configure-appearance-headers");
01980 }
01981 
01982 static const struct {
01983   const char * displayName;
01984   DateFormatter::FormatType dateDisplay;
01985 } dateDisplayConfig[] = {
01986   { I18N_NOOP("Sta&ndard format (%1)"), KMime::DateFormatter::CTime },
01987   { I18N_NOOP("Locali&zed format (%1)"), KMime::DateFormatter::Localized },
01988   { I18N_NOOP("Fancy for&mat (%1)"), KMime::DateFormatter::Fancy },
01989   { I18N_NOOP("C&ustom format (Shift+F1 for help):"),
01990     KMime::DateFormatter::Custom }
01991 };
01992 static const int numDateDisplayConfig =
01993   sizeof dateDisplayConfig / sizeof *dateDisplayConfig;
01994 
01995 AppearancePageHeadersTab::AppearancePageHeadersTab( QWidget * parent, const char * name )
01996   : ConfigModuleTab( parent, name ),
01997     mCustomDateFormatEdit( 0 )
01998 {
01999   // tmp. vars:
02000   QButtonGroup * group;
02001   QRadioButton * radio;
02002 
02003   QVBoxLayout * vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
02004 
02005   // "General Options" group:
02006   group = new QVButtonGroup( i18n( "General Options" ), this );
02007   group->layout()->setSpacing( KDialog::spacingHint() );
02008 
02009   mMessageSizeCheck = new QCheckBox( i18n("Display messa&ge sizes"), group );
02010 
02011   mCryptoIconsCheck = new QCheckBox( i18n( "Show crypto &icons" ), group );
02012 
02013   mAttachmentCheck = new QCheckBox( i18n("Show attachment icon"), group );
02014 
02015   mNestedMessagesCheck =
02016     new QCheckBox( i18n("&Threaded message list"), group );
02017 
02018   connect( mMessageSizeCheck, SIGNAL( stateChanged( int ) ),
02019            this, SLOT( slotEmitChanged( void ) ) );
02020   connect( mAttachmentCheck, SIGNAL( stateChanged( int ) ),
02021            this, SLOT( slotEmitChanged( void ) ) );
02022   connect( mCryptoIconsCheck, SIGNAL( stateChanged( int ) ),
02023            this, SLOT( slotEmitChanged( void ) ) );
02024   connect( mNestedMessagesCheck, SIGNAL( stateChanged( int ) ),
02025            this, SLOT( slotEmitChanged( void ) ) );
02026 
02027 
02028   vlay->addWidget( group );
02029 
02030   // "Message Header Threading Options" group:
02031   mNestingPolicy =
02032     new QVButtonGroup( i18n("Threaded Message List Options"), this );
02033   mNestingPolicy->layout()->setSpacing( KDialog::spacingHint() );
02034 
02035   mNestingPolicy->insert(
02036     new QRadioButton( i18n("Always &keep threads open"),
02037                       mNestingPolicy ), 0 );
02038   mNestingPolicy->insert(
02039     new QRadioButton( i18n("Threads default to o&pen"),
02040                       mNestingPolicy ), 1 );
02041   mNestingPolicy->insert(
02042     new QRadioButton( i18n("Threads default to closed"),
02043                       mNestingPolicy ), 2 );
02044   mNestingPolicy->insert(
02045     new QRadioButton( i18n("Open threads that contain ne&w, unread "
02046                            "or important messages and open watched threads."),
02047                       mNestingPolicy ), 3 );
02048 
02049   vlay->addWidget( mNestingPolicy );
02050 
02051   connect( mNestingPolicy, SIGNAL( clicked( int ) ),
02052            this, SLOT( slotEmitChanged( void ) ) );
02053 
02054   // "Date Display" group:
02055   mDateDisplay = new QVButtonGroup( i18n("Date Display"), this );
02056   mDateDisplay->layout()->setSpacing( KDialog::spacingHint() );
02057 
02058   for ( int i = 0 ; i < numDateDisplayConfig ; i++ ) {
02059     QString buttonLabel = i18n(dateDisplayConfig[i].displayName);
02060     if ( buttonLabel.contains("%1") )
02061       buttonLabel = buttonLabel.arg( DateFormatter::formatCurrentDate( dateDisplayConfig[i].dateDisplay ) );
02062     radio = new QRadioButton( buttonLabel, mDateDisplay );
02063     mDateDisplay->insert( radio, i );
02064     if ( dateDisplayConfig[i].dateDisplay == DateFormatter::Custom ) {
02065       mCustomDateFormatEdit = new KLineEdit( mDateDisplay );
02066       mCustomDateFormatEdit->setEnabled( false );
02067       connect( radio, SIGNAL(toggled(bool)),
02068                mCustomDateFormatEdit, SLOT(setEnabled(bool)) );
02069       connect( mCustomDateFormatEdit, SIGNAL(textChanged(const QString&)),
02070                this, SLOT(slotEmitChanged(void)) );
02071       QString customDateWhatsThis =
02072         i18n("<qt><p><strong>These expressions may be used for the date:"
02073              "</strong></p>"
02074              "<ul>"
02075              "<li>d - the day as a number without a leading zero (1-31)</li>"
02076              "<li>dd - the day as a number with a leading zero (01-31)</li>"
02077              "<li>ddd - the abbreviated day name (Mon - Sun)</li>"
02078              "<li>dddd - the long day name (Monday - Sunday)</li>"
02079              "<li>M - the month as a number without a leading zero (1-12)</li>"
02080              "<li>MM - the month as a number with a leading zero (01-12)</li>"
02081              "<li>MMM - the abbreviated month name (Jan - Dec)</li>"
02082              "<li>MMMM - the long month name (January - December)</li>"
02083              "<li>yy - the year as a two digit number (00-99)</li>"
02084              "<li>yyyy - the year as a four digit number (0000-9999)</li>"
02085              "</ul>"
02086              "<p><strong>These expressions may be used for the time:"
02087              "</string></p> "
02088              "<ul>"
02089              "<li>h - the hour without a leading zero (0-23 or 1-12 if AM/PM display)</li>"
02090              "<li>hh - the hour with a leading zero (00-23 or 01-12 if AM/PM display)</li>"
02091              "<li>m - the minutes without a leading zero (0-59)</li>"
02092              "<li>mm - the minutes with a leading zero (00-59)</li>"
02093              "<li>s - the seconds without a leading zero (0-59)</li>"
02094              "<li>ss - the seconds with a leading zero (00-59)</li>"
02095              "<li>z - the milliseconds without leading zeroes (0-999)</li>"
02096              "<li>zzz - the milliseconds with leading zeroes (000-999)</li>"
02097              "<li>AP - switch to AM/PM display. AP will be replaced by either \"AM\" or \"PM\".</li>"
02098              "<li>ap - switch to AM/PM display. ap will be replaced by either \"am\" or \"pm\".</li>"
02099              "<li>Z - time zone in numeric form (-0500)</li>"
02100              "</ul>"
02101              "<p><strong>All other input characters will be ignored."
02102              "</strong></p></qt>");
02103       QWhatsThis::add( mCustomDateFormatEdit, customDateWhatsThis );
02104       QWhatsThis::add( radio, customDateWhatsThis );
02105     }
02106   } // end for loop populating mDateDisplay
02107 
02108   vlay->addWidget( mDateDisplay );
02109   connect( mDateDisplay, SIGNAL( clicked( int ) ),
02110            this, SLOT( slotEmitChanged( void ) ) );
02111 
02112 
02113   vlay->addStretch( 10 ); // spacer
02114 }
02115 
02116 void AppearancePage::HeadersTab::doLoadOther() {
02117   KConfigGroup general( KMKernel::config(), "General" );
02118   KConfigGroup geometry( KMKernel::config(), "Geometry" );
02119 
02120   // "General Options":
02121   mNestedMessagesCheck->setChecked( geometry.readBoolEntry( "nestedMessages", false ) );
02122   mMessageSizeCheck->setChecked( general.readBoolEntry( "showMessageSize", false ) );
02123   mCryptoIconsCheck->setChecked( general.readBoolEntry( "showCryptoIcons", false ) );
02124   mAttachmentCheck->setChecked( general.readBoolEntry( "showAttachmentIcon", true ) );
02125 
02126   // "Message Header Threading Options":
02127   int num = geometry.readNumEntry( "nestingPolicy", 3 );
02128   if ( num < 0 || num > 3 ) num = 3;
02129   mNestingPolicy->setButton( num );
02130 
02131   // "Date Display":
02132   setDateDisplay( general.readNumEntry( "dateFormat", DateFormatter::Fancy ),
02133                   general.readEntry( "customDateFormat" ) );
02134 }
02135 
02136 void AppearancePage::HeadersTab::setDateDisplay( int num, const QString & format ) {
02137   DateFormatter::FormatType dateDisplay =
02138     static_cast<DateFormatter::FormatType>( num );
02139 
02140   // special case: needs text for the line edit:
02141   if ( dateDisplay == DateFormatter::Custom )
02142     mCustomDateFormatEdit->setText( format );
02143 
02144   for ( int i = 0 ; i < numDateDisplayConfig ; i++ )
02145     if ( dateDisplay == dateDisplayConfig[i].dateDisplay ) {
02146       mDateDisplay->setButton( i );
02147       return;
02148     }
02149   // fell through since none found:
02150   mDateDisplay->setButton( numDateDisplayConfig - 2 ); // default
02151 }
02152 
02153 void AppearancePage::HeadersTab::installProfile( KConfig * profile ) {
02154   KConfigGroup general( profile, "General" );
02155   KConfigGroup geometry( profile, "Geometry" );
02156 
02157   if ( geometry.hasKey( "nestedMessages" ) )
02158     mNestedMessagesCheck->setChecked( geometry.readBoolEntry( "nestedMessages" ) );
02159   if ( general.hasKey( "showMessageSize" ) )
02160     mMessageSizeCheck->setChecked( general.readBoolEntry( "showMessageSize" ) );
02161 
02162   if( general.hasKey( "showCryptoIcons" ) )
02163     mCryptoIconsCheck->setChecked( general.readBoolEntry( "showCryptoIcons" ) );
02164   if ( general.hasKey( "showAttachmentIcon" ) )
02165     mAttachmentCheck->setChecked( general.readBoolEntry( "showAttachmentIcon" ) );
02166 
02167   if ( geometry.hasKey( "nestingPolicy" ) ) {
02168     int num = geometry.readNumEntry( "nestingPolicy" );
02169     if ( num < 0 || num > 3 ) num = 3;
02170     mNestingPolicy->setButton( num );
02171   }
02172 
02173   if ( general.hasKey( "dateFormat" ) )
02174     setDateDisplay( general.readNumEntry( "dateFormat" ),
02175                    general.readEntry( "customDateFormat" ) );
02176 }
02177 
02178 void AppearancePage::HeadersTab::save() {
02179   KConfigGroup general( KMKernel::config(), "General" );
02180   KConfigGroup geometry( KMKernel::config(), "Geometry" );
02181 
02182   if ( geometry.readBoolEntry( "nestedMessages", false )
02183        != mNestedMessagesCheck->isChecked() ) {
02184     int result = KMessageBox::warningContinueCancel( this,
02185                    i18n("Changing the global threading setting will override "
02186                         "all folder specific values."),
02187                    QString::null, KStdGuiItem::cont(), "threadOverride" );
02188     if ( result == KMessageBox::Continue ) {
02189       geometry.writeEntry( "nestedMessages", mNestedMessagesCheck->isChecked() );
02190       // remove all threadMessagesOverride keys from all [Folder-*] groups:
02191       QStringList groups = KMKernel::config()->groupList().grep( QRegExp("^Folder-") );
02192       kdDebug(5006) << "groups.count() == " << groups.count() << endl;
02193       for ( QStringList::const_iterator it = groups.begin() ; it != groups.end() ; ++it ) {
02194         KConfigGroup group( KMKernel::config(), *it );
02195         group.deleteEntry( "threadMessagesOverride" );
02196       }
02197     }
02198   }
02199 
02200   geometry.writeEntry( "nestingPolicy",
02201                        mNestingPolicy->id( mNestingPolicy->selected() ) );
02202   general.writeEntry( "showMessageSize", mMessageSizeCheck->isChecked() );
02203   general.writeEntry( "showCryptoIcons", mCryptoIconsCheck->isChecked() );
02204   general.writeEntry( "showAttachmentIcon", mAttachmentCheck->isChecked() );
02205 
02206   int dateDisplayID = mDateDisplay->id( mDateDisplay->selected() );
02207   // check bounds:
02208   assert( dateDisplayID >= 0 ); assert( dateDisplayID < numDateDisplayConfig );
02209   general.writeEntry( "dateFormat",
02210                       dateDisplayConfig[ dateDisplayID ].dateDisplay );
02211   general.writeEntry( "customDateFormat", mCustomDateFormatEdit->text() );
02212 }
02213 
02214 
02215 //
02216 // Message Window
02217 //
02218 
02219 
02220 static const BoolConfigEntry closeAfterReplyOrForward = {
02221   "Reader", "CloseAfterReplyOrForward", I18N_NOOP("Close message window after replying or forwarding"), false
02222 };
02223 
02224 static const BoolConfigEntry showColorbarMode = {
02225   "Reader", "showColorbar", I18N_NOOP("Show HTML stat&us bar"), false
02226 };
02227 
02228 static const BoolConfigEntry showSpamStatusMode = {
02229   "Reader", "showSpamStatus", I18N_NOOP("Show s&pam status in fancy headers"), true
02230 };
02231 
02232 static const BoolConfigEntry showEmoticons = {
02233   "Reader", "ShowEmoticons", I18N_NOOP("Replace smileys by emoticons"), true
02234 };
02235 
02236 static const BoolConfigEntry shrinkQuotes = {
02237   "Reader", "ShrinkQuotes", I18N_NOOP("Use smaller font for quoted text"), false
02238 };
02239 
02240 static const BoolConfigEntry showExpandQuotesMark= {
02241   "Reader", "ShowExpandQuotesMark", I18N_NOOP("Show expand/collapse quote marks"), false
02242 };
02243 
02244 
02245 QString AppearancePage::ReaderTab::helpAnchor() const {
02246   return QString::fromLatin1("configure-appearance-reader");
02247 }
02248 
02249 AppearancePageReaderTab::AppearancePageReaderTab( QWidget * parent,
02250                                                   const char * name )
02251   : ConfigModuleTab( parent, name )
02252 {
02253   QVBoxLayout *vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
02254 
02255   // "close message window after replying or forwarding" checkbox
02256   populateCheckBox( mCloseAfterReplyOrForwardCheck = new QCheckBox( this ),
02257                     closeAfterReplyOrForward );
02258   QToolTip::add( mCloseAfterReplyOrForwardCheck,
02259                  i18n( "Close the standalone message window after replying or forwarding the message" ) );
02260   vlay->addWidget( mCloseAfterReplyOrForwardCheck );
02261   connect( mCloseAfterReplyOrForwardCheck, SIGNAL ( stateChanged( int ) ),
02262            this, SLOT( slotEmitChanged() ) );
02263 
02264   // "show colorbar" check box:
02265   populateCheckBox( mShowColorbarCheck = new QCheckBox( this ), showColorbarMode );
02266   vlay->addWidget( mShowColorbarCheck );
02267   connect( mShowColorbarCheck, SIGNAL ( stateChanged( int ) ),
02268            this, SLOT( slotEmitChanged() ) );
02269 
02270   // "show spam status" check box;
02271   populateCheckBox( mShowSpamStatusCheck = new QCheckBox( this ), showSpamStatusMode );
02272   vlay->addWidget( mShowSpamStatusCheck );
02273   connect( mShowSpamStatusCheck, SIGNAL ( stateChanged( int ) ),
02274            this, SLOT( slotEmitChanged() ) );
02275 
02276   // "replace smileys by emoticons" check box;
02277   populateCheckBox( mShowEmoticonsCheck = new QCheckBox( this ), showEmoticons );
02278   vlay->addWidget( mShowEmoticonsCheck );
02279   connect( mShowEmoticonsCheck, SIGNAL ( stateChanged( int ) ),
02280            this, SLOT( slotEmitChanged() ) );
02281 
02282   // "Use smaller font for quoted text" check box
02283   mShrinkQuotesCheck = new QCheckBox( i18n( shrinkQuotes.desc ), this,
02284                                       "kcfg_ShrinkQuotes" );
02285   vlay->addWidget( mShrinkQuotesCheck );
02286   connect( mShrinkQuotesCheck, SIGNAL( stateChanged( int ) ),
02287            this, SLOT( slotEmitChanged() ) );
02288 
02289   // "Show expand/collaps quote marks" check box;
02290   QHBoxLayout *hlay= new QHBoxLayout( vlay ); // inherits spacing
02291   populateCheckBox( mShowExpandQuotesMark= new QCheckBox( this ), showExpandQuotesMark);
02292   hlay->addWidget( mShowExpandQuotesMark);
02293   connect( mShowExpandQuotesMark, SIGNAL ( stateChanged( int ) ),
02294            this, SLOT( slotEmitChanged() ) );
02295 
02296   hlay->addStretch( 1 );
02297   mCollapseQuoteLevelSpin = new KIntSpinBox( 0/*min*/,10/*max*/,1/*step*/,
02298       3/*init*/,10/*base*/,this );
02299 
02300   QLabel *label = new QLabel( mCollapseQuoteLevelSpin,
02301            GlobalSettings::self()->collapseQuoteLevelSpinItem()->label(), this );
02302 
02303   hlay->addWidget( label );
02304 
02305   mCollapseQuoteLevelSpin->setEnabled( false ); //since !mShowExpandQuotesMark->isCheckec()
02306   connect(  mCollapseQuoteLevelSpin, SIGNAL( valueChanged( int ) ),
02307       this, SLOT( slotEmitChanged( void ) ) );
02308   hlay->addWidget( mCollapseQuoteLevelSpin);
02309 
02310   connect( mShowExpandQuotesMark, SIGNAL( toggled( bool ) ),
02311       mCollapseQuoteLevelSpin, SLOT( setEnabled( bool ) ) );
02312 
02313   // Fallback Character Encoding
02314   hlay = new QHBoxLayout( vlay ); // inherits spacing
02315   mCharsetCombo = new QComboBox( this );
02316   mCharsetCombo->insertStringList( KMMsgBase::supportedEncodings( false ) );
02317 
02318   connect( mCharsetCombo, SIGNAL( activated( int ) ),
02319            this, SLOT( slotEmitChanged( void ) ) );
02320 
02321   QString fallbackCharsetWhatsThis =
02322     i18n( GlobalSettings::self()->fallbackCharacterEncodingItem()->whatsThis().utf8() );
02323   QWhatsThis::add( mCharsetCombo, fallbackCharsetWhatsThis );
02324 
02325   label = new QLabel( i18n("Fallback ch&aracter encoding:"), this );
02326   label->setBuddy( mCharsetCombo );
02327 
02328   hlay->addWidget( label );
02329   hlay->addWidget( mCharsetCombo );
02330 
02331   // Override Character Encoding
02332   QHBoxLayout *hlay2 = new QHBoxLayout( vlay ); // inherits spacing
02333   mOverrideCharsetCombo = new QComboBox( this );
02334   QStringList encodings = KMMsgBase::supportedEncodings( false );
02335   encodings.prepend( i18n( "Auto" ) );
02336   mOverrideCharsetCombo->insertStringList( encodings );
02337   mOverrideCharsetCombo->setCurrentItem(0);
02338 
02339   connect( mOverrideCharsetCombo, SIGNAL( activated( int ) ),
02340            this, SLOT( slotEmitChanged( void ) ) );
02341 
02342   QString overrideCharsetWhatsThis =
02343     i18n( GlobalSettings::self()->overrideCharacterEncodingItem()->whatsThis().utf8() );
02344   QWhatsThis::add( mOverrideCharsetCombo, overrideCharsetWhatsThis );
02345 
02346   label = new QLabel( i18n("&Override character encoding:"), this );
02347   label->setBuddy( mOverrideCharsetCombo );
02348 
02349   hlay2->addWidget( label );
02350   hlay2->addWidget( mOverrideCharsetCombo );
02351 
02352   vlay->addStretch( 100 ); // spacer
02353 }
02354 
02355 
02356 void AppearancePage::ReaderTab::readCurrentFallbackCodec()
02357 {
02358   QStringList encodings = KMMsgBase::supportedEncodings( false );
02359   QStringList::ConstIterator it( encodings.begin() );
02360   QStringList::ConstIterator end( encodings.end() );
02361   QString currentEncoding = GlobalSettings::self()->fallbackCharacterEncoding();
02362   currentEncoding = currentEncoding.replace( "iso ", "iso-", false );
02364   int i = 0;
02365   int indexOfLatin9 = 0;
02366   bool found = false;
02367   for( ; it != end; ++it)
02368   {
02369     const QString encoding = KGlobal::charsets()->encodingForName(*it);
02370     if ( encoding == "iso-8859-15" )
02371         indexOfLatin9 = i;
02372     if( encoding == currentEncoding )
02373     {
02374       mCharsetCombo->setCurrentItem( i );
02375       found = true;
02376       break;
02377     }
02378     i++;
02379   }
02380   if ( !found ) // nothing matched, use latin9
02381     mCharsetCombo->setCurrentItem( indexOfLatin9 );
02382 }
02383 
02384 void AppearancePage::ReaderTab::readCurrentOverrideCodec()
02385 {
02386   const QString &currentOverrideEncoding = GlobalSettings::self()->overrideCharacterEncoding();
02387   if ( currentOverrideEncoding.isEmpty() ) {
02388     mOverrideCharsetCombo->setCurrentItem( 0 );
02389     return;
02390   }
02391   QStringList encodings = KMMsgBase::supportedEncodings( false );
02392   encodings.prepend( i18n( "Auto" ) );
02393   QStringList::Iterator it( encodings.begin() );
02394   QStringList::Iterator end( encodings.end() );
02395   uint i = 0;
02396   for( ; it != end; ++it)
02397   {
02398     if( KGlobal::charsets()->encodingForName(*it) == currentOverrideEncoding )
02399     {
02400       mOverrideCharsetCombo->setCurrentItem( i );
02401       break;
02402     }
02403     i++;
02404   }
02405   if ( i == encodings.size() ) {
02406     // the current value of overrideCharacterEncoding is an unknown encoding => reset to Auto
02407     kdWarning(5006) << "Unknown override character encoding \"" << currentOverrideEncoding
02408                     << "\". Resetting to Auto." << endl;
02409     mOverrideCharsetCombo->setCurrentItem( 0 );
02410     GlobalSettings::self()->setOverrideCharacterEncoding( QString::null );
02411   }
02412 }
02413 
02414 void AppearancePage::ReaderTab::doLoadFromGlobalSettings()
02415 {
02416   mCloseAfterReplyOrForwardCheck->setChecked( GlobalSettings::self()->closeAfterReplyOrForward() );
02417   mShowEmoticonsCheck->setChecked( GlobalSettings::self()->showEmoticons() );
02418   mShrinkQuotesCheck->setChecked( GlobalSettings::self()->shrinkQuotes() );
02419   mShowExpandQuotesMark->setChecked( GlobalSettings::self()->showExpandQuotesMark() );
02420   mCollapseQuoteLevelSpin->setValue( GlobalSettings::self()->collapseQuoteLevelSpin() );
02421   readCurrentFallbackCodec();
02422   readCurrentOverrideCodec();
02423 }
02424 
02425 void AppearancePage::ReaderTab::doLoadOther()
02426 {
02427   const KConfigGroup reader( KMKernel::config(), "Reader" );
02428   loadWidget( mShowColorbarCheck, reader, showColorbarMode );
02429   loadWidget( mShowSpamStatusCheck, reader, showSpamStatusMode );
02430 }
02431 
02432 
02433 void AppearancePage::ReaderTab::save() {
02434   KConfigGroup reader( KMKernel::config(), "Reader" );
02435   saveCheckBox( mShowColorbarCheck, reader, showColorbarMode );
02436   saveCheckBox( mShowSpamStatusCheck, reader, showSpamStatusMode );
02437   GlobalSettings::self()->setCloseAfterReplyOrForward( mCloseAfterReplyOrForwardCheck->isChecked() );
02438   GlobalSettings::self()->setShowEmoticons( mShowEmoticonsCheck->isChecked() );
02439   GlobalSettings::self()->setShrinkQuotes( mShrinkQuotesCheck->isChecked() );
02440   GlobalSettings::self()->setShowExpandQuotesMark( mShowExpandQuotesMark->isChecked() );
02441 
02442   GlobalSettings::self()->setCollapseQuoteLevelSpin( mCollapseQuoteLevelSpin->value() );
02443   GlobalSettings::self()->setFallbackCharacterEncoding(
02444       KGlobal::charsets()->encodingForName( mCharsetCombo->currentText() ) );
02445   GlobalSettings::self()->setOverrideCharacterEncoding(
02446       mOverrideCharsetCombo->currentItem() == 0 ?
02447         QString() :
02448         KGlobal::charsets()->encodingForName( mOverrideCharsetCombo->currentText() ) );
02449 }
02450 
02451 
02452 void AppearancePage::ReaderTab::installProfile( KConfig * /* profile */ ) {
02453   const KConfigGroup reader( KMKernel::config(), "Reader" );
02454   loadProfile( mCloseAfterReplyOrForwardCheck, reader, closeAfterReplyOrForward );
02455   loadProfile( mShowColorbarCheck, reader, showColorbarMode );
02456   loadProfile( mShowSpamStatusCheck, reader, showSpamStatusMode );
02457   loadProfile( mShowEmoticonsCheck, reader, showEmoticons );
02458   loadProfile( mShrinkQuotesCheck, reader, shrinkQuotes );
02459   loadProfile( mShowExpandQuotesMark, reader, showExpandQuotesMark);
02460 }
02461 
02462 
02463 QString AppearancePage::SystemTrayTab::helpAnchor() const {
02464   return QString::fromLatin1("configure-appearance-systemtray");
02465 }
02466 
02467 AppearancePageSystemTrayTab::AppearancePageSystemTrayTab( QWidget * parent,
02468                                                           const char * name )
02469   : ConfigModuleTab( parent, name )
02470 {
02471   QVBoxLayout * vlay = new QVBoxLayout( this, KDialog::marginHint(),
02472                                         KDialog::spacingHint() );
02473 
02474   // "Enable system tray applet" check box
02475   mSystemTrayCheck = new QCheckBox( i18n("Enable system tray icon"), this );
02476   vlay->addWidget( mSystemTrayCheck );
02477   connect( mSystemTrayCheck, SIGNAL( stateChanged( int ) ),
02478            this, SLOT( slotEmitChanged( void ) ) );
02479 
02480   // System tray modes
02481   mSystemTrayGroup = new QVButtonGroup( i18n("System Tray Mode"), this );
02482   mSystemTrayGroup->layout()->setSpacing( KDialog::spacingHint() );
02483   vlay->addWidget( mSystemTrayGroup );
02484   connect( mSystemTrayGroup, SIGNAL( clicked( int ) ),
02485            this, SLOT( slotEmitChanged( void ) ) );
02486   connect( mSystemTrayCheck, SIGNAL( toggled( bool ) ),
02487            mSystemTrayGroup, SLOT( setEnabled( bool ) ) );
02488 
02489   mSystemTrayGroup->insert( new QRadioButton( i18n("Always show KMail in system tray"), mSystemTrayGroup ),
02490                             GlobalSettings::EnumSystemTrayPolicy::ShowAlways );
02491 
02492   mSystemTrayGroup->insert( new QRadioButton( i18n("Only show KMail in system tray if there are unread messages"), mSystemTrayGroup ),
02493                             GlobalSettings::EnumSystemTrayPolicy::ShowOnUnread );
02494 
02495   vlay->addStretch( 10 ); // spacer
02496 }
02497 
02498 void AppearancePage::SystemTrayTab::doLoadFromGlobalSettings() {
02499   mSystemTrayCheck->setChecked( GlobalSettings::self()->systemTrayEnabled() );
02500   mSystemTrayGroup->setButton( GlobalSettings::self()->systemTrayPolicy() );
02501   mSystemTrayGroup->setEnabled( mSystemTrayCheck->isChecked() );
02502 }
02503 
02504 void AppearancePage::SystemTrayTab::installProfile( KConfig * profile ) {
02505   KConfigGroup general( profile, "General" );
02506 
02507   if ( general.hasKey( "SystemTrayEnabled" ) ) {
02508     mSystemTrayCheck->setChecked( general.readBoolEntry( "SystemTrayEnabled" ) );
02509   }
02510   if ( general.hasKey( "SystemTrayPolicy" ) ) {
02511     mSystemTrayGroup->setButton( general.readNumEntry( "SystemTrayPolicy" ) );
02512   }
02513   mSystemTrayGroup->setEnabled( mSystemTrayCheck->isChecked() );
02514 }
02515 
02516 void AppearancePage::SystemTrayTab::save() {
02517   GlobalSettings::self()->setSystemTrayEnabled( mSystemTrayCheck->isChecked() );
02518   GlobalSettings::self()->setSystemTrayPolicy( mSystemTrayGroup->id( mSystemTrayGroup->selected() ) );
02519 }
02520 
02521 
02522 // *************************************************************
02523 // *                                                           *
02524 // *                      ComposerPage                         *
02525 // *                                                           *
02526 // *************************************************************
02527 
02528 QString ComposerPage::helpAnchor() const {
02529   return QString::fromLatin1("configure-composer");
02530 }
02531 
02532 ComposerPage::ComposerPage( QWidget * parent, const char * name )
02533   : ConfigModuleWithTabs( parent, name )
02534 {
02535   //
02536   // "General" tab:
02537   //
02538   mGeneralTab = new GeneralTab();
02539   addTab( mGeneralTab, i18n("&General") );
02540   addConfig( GlobalSettings::self(), mGeneralTab );
02541 
02542   //
02543   // "Phrases" tab:
02544   //
02545   // mPhrasesTab = new PhrasesTab();
02546   // addTab( mPhrasesTab, i18n("&Phrases") );
02547 
02548   //
02549   // "Templates" tab:
02550   //
02551   mTemplatesTab = new TemplatesTab();
02552   addTab( mTemplatesTab, i18n("&Templates") );
02553 
02554   //
02555   // "Custom Templates" tab:
02556   //
02557   mCustomTemplatesTab = new CustomTemplatesTab();
02558   addTab( mCustomTemplatesTab, i18n("&Custom Templates") );
02559 
02560   //
02561   // "Subject" tab:
02562   //
02563   mSubjectTab = new SubjectTab();
02564   addTab( mSubjectTab, i18n("&Subject") );
02565   addConfig( GlobalSettings::self(), mSubjectTab );
02566 
02567   //
02568   // "Charset" tab:
02569   //
02570   mCharsetTab = new CharsetTab();
02571   addTab( mCharsetTab, i18n("Cha&rset") );
02572 
02573   //
02574   // "Headers" tab:
02575   //
02576   mHeadersTab = new HeadersTab();
02577   addTab( mHeadersTab, i18n("H&eaders") );
02578 
02579   //
02580   // "Attachments" tab:
02581   //
02582   mAttachmentsTab = new AttachmentsTab();
02583   addTab( mAttachmentsTab, i18n("Config->Composer->Attachments", "A&ttachments") );
02584   load();
02585 }
02586 
02587 QString ComposerPage::GeneralTab::helpAnchor() const {
02588   return QString::fromLatin1("configure-composer-general");
02589 }
02590 
02591 ComposerPageGeneralTab::ComposerPageGeneralTab( QWidget * parent, const char * name )
02592   : ConfigModuleTab( parent, name )
02593 {
02594   // tmp. vars:
02595   QVBoxLayout *vlay;
02596   QHBoxLayout *hlay;
02597   QGroupBox   *group;
02598   QLabel      *label;
02599   QHBox       *hbox;
02600   QString      msg;
02601 
02602   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
02603 
02604   // some check buttons...
02605   mAutoAppSignFileCheck = new QCheckBox(
02606            GlobalSettings::self()->autoTextSignatureItem()->label(),
02607            this );
02608   vlay->addWidget( mAutoAppSignFileCheck );
02609   connect( mAutoAppSignFileCheck, SIGNAL( stateChanged(int) ),
02610            this, SLOT( slotEmitChanged( void ) ) );
02611 
02612   mTopQuoteCheck =
02613     new QCheckBox( GlobalSettings::self()->prependSignatureItem()->label(), this );
02614   vlay->addWidget( mTopQuoteCheck);
02615   connect( mTopQuoteCheck, SIGNAL( stateChanged(int) ),
02616            this, SLOT( slotEmitChanged( void ) ) );
02617 
02618   mSmartQuoteCheck = new QCheckBox(
02619            GlobalSettings::self()->smartQuoteItem()->label(),
02620            this, "kcfg_SmartQuote" );
02621   vlay->addWidget( mSmartQuoteCheck );
02622   connect( mSmartQuoteCheck, SIGNAL( stateChanged(int) ),
02623            this, SLOT( slotEmitChanged( void ) ) );
02624 
02625   mAutoRequestMDNCheck = new QCheckBox(
02626            GlobalSettings::self()->requestMDNItem()->label(),
02627            this, "kcfg_RequestMDN" );
02628   vlay->addWidget( mAutoRequestMDNCheck );
02629   connect( mAutoRequestMDNCheck, SIGNAL( stateChanged(int) ),
02630            this, SLOT( slotEmitChanged( void ) ) );
02631 
02632   mShowRecentAddressesInComposer = new QCheckBox(
02633            GlobalSettings::self()->showRecentAddressesInComposerItem()->label(),
02634            this, "kcfg_ShowRecentAddressesInComposer" );
02635   vlay->addWidget( mShowRecentAddressesInComposer );
02636   connect( mShowRecentAddressesInComposer, SIGNAL( stateChanged(int) ),
02637            this, SLOT( slotEmitChanged( void ) ) );
02638 
02639   // a checkbox for "word wrap" and a spinbox for the column in
02640   // which to wrap:
02641   hlay = new QHBoxLayout( vlay ); // inherits spacing
02642   mWordWrapCheck = new QCheckBox(
02643            GlobalSettings::self()->wordWrapItem()->label(),
02644            this, "kcfg_WordWrap" );
02645   hlay->addWidget( mWordWrapCheck );
02646   connect( mWordWrapCheck, SIGNAL( stateChanged(int) ),
02647            this, SLOT( slotEmitChanged( void ) ) );
02648 
02649   mWrapColumnSpin = new KIntSpinBox( 30/*min*/, 78/*max*/, 1/*step*/,
02650            78/*init*/, 10 /*base*/, this, "kcfg_LineWrapWidth" );
02651   mWrapColumnSpin->setEnabled( false ); // since !mWordWrapCheck->isChecked()
02652   connect( mWrapColumnSpin, SIGNAL( valueChanged(int) ),
02653            this, SLOT( slotEmitChanged( void ) ) );
02654 
02655   hlay->addWidget( mWrapColumnSpin );
02656   hlay->addStretch( 1 );
02657   // only enable the spinbox if the checkbox is checked:
02658   connect( mWordWrapCheck, SIGNAL(toggled(bool)),
02659            mWrapColumnSpin, SLOT(setEnabled(bool)) );
02660 
02661   // a checkbox for "too many recipient warning" and a spinbox for the recipient threshold
02662   hlay = new QHBoxLayout( vlay ); // inherits spacing
02663   mRecipientCheck = new QCheckBox(
02664            GlobalSettings::self()->tooManyRecipientsItem()->label(),
02665            this, "kcfg_TooManyRecipients" );
02666   hlay->addWidget( mRecipientCheck );
02667   connect( mRecipientCheck, SIGNAL( stateChanged(int) ),
02668            this, SLOT( slotEmitChanged( void ) ) );
02669 
02670   QString recipientCheckWhatsthis =
02671     i18n( GlobalSettings::self()->tooManyRecipientsItem()->whatsThis().utf8() );
02672   QWhatsThis::add( mRecipientCheck, recipientCheckWhatsthis );
02673   QToolTip::add( mRecipientCheck,
02674                  i18n( "Warn if too many recipients are specified" ) );
02675 
02676   mRecipientSpin = new KIntSpinBox( 1/*min*/, 100/*max*/, 1/*step*/,
02677            5/*init*/, 10 /*base*/, this, "kcfg_RecipientThreshold" );
02678   mRecipientSpin->setEnabled( false );
02679   connect( mRecipientSpin, SIGNAL( valueChanged(int) ),
02680            this, SLOT( slotEmitChanged( void ) ) );
02681 
02682   QString recipientWhatsthis =
02683     i18n( GlobalSettings::self()->recipientThresholdItem()->whatsThis().utf8() );
02684   QWhatsThis::add( mRecipientSpin, recipientWhatsthis );
02685   QToolTip::add( mRecipientSpin,
02686                  i18n( "Warn if more than this many recipients are specified" ) );
02687 
02688 
02689   hlay->addWidget( mRecipientSpin );
02690   hlay->addStretch( 1 );
02691   // only enable the spinbox if the checkbox is checked:
02692   connect( mRecipientCheck, SIGNAL(toggled(bool)),
02693            mRecipientSpin, SLOT(setEnabled(bool)) );
02694 
02695 
02696   hlay = new QHBoxLayout( vlay ); // inherits spacing
02697   mAutoSave = new KIntSpinBox( 0, 60, 1, 1, 10, this, "kcfg_AutosaveInterval" );
02698   label = new QLabel( mAutoSave,
02699            GlobalSettings::self()->autosaveIntervalItem()->label(), this );
02700   hlay->addWidget( label );
02701   hlay->addWidget( mAutoSave );
02702   mAutoSave->setSpecialValueText( i18n("No autosave") );
02703   mAutoSave->setSuffix( i18n(" min") );
02704   hlay->addStretch( 1 );
02705   connect( mAutoSave, SIGNAL( valueChanged(int) ),
02706            this, SLOT( slotEmitChanged( void ) ) );
02707 
02708   hlay = new QHBoxLayout( vlay ); // inherits spacing
02709   mForwardTypeCombo = new KComboBox( false, this );
02710   label = new QLabel( mForwardTypeCombo,
02711                       i18n( "Default Forwarding Type:" ),
02712                       this );
02713   mForwardTypeCombo->insertStringList( QStringList()
02714                                        << i18n( "Inline" )
02715                                        << i18n( "As Attachment" ) );
02716   hlay->addWidget( label );
02717   hlay->addWidget( mForwardTypeCombo );
02718   hlay->addStretch( 1 );
02719   connect( mForwardTypeCombo, SIGNAL(activated(int)),
02720            this, SLOT( slotEmitChanged( void ) ) );
02721 
02722   hlay = new QHBoxLayout( vlay ); // inherits spacing
02723   QPushButton *completionOrderBtn = new QPushButton( i18n( "Configure Completion Order" ), this );
02724   connect( completionOrderBtn, SIGNAL( clicked() ),
02725            this, SLOT( slotConfigureCompletionOrder() ) );
02726   hlay->addWidget( completionOrderBtn );
02727   hlay->addItem( new QSpacerItem(0, 0) );
02728 
02729   // recent addresses
02730   hlay = new QHBoxLayout( vlay ); // inherits spacing
02731   QPushButton *recentAddressesBtn = new QPushButton( i18n( "Edit Recent Addresses..." ), this );
02732   connect( recentAddressesBtn, SIGNAL( clicked() ),
02733            this, SLOT( slotConfigureRecentAddresses() ) );
02734   hlay->addWidget( recentAddressesBtn );
02735   hlay->addItem( new QSpacerItem(0, 0) );
02736 
02737   // The "external editor" group:
02738   group = new QVGroupBox( i18n("External Editor"), this );
02739   group->layout()->setSpacing( KDialog::spacingHint() );
02740 
02741   mExternalEditorCheck = new QCheckBox(
02742            GlobalSettings::self()->useExternalEditorItem()->label(),
02743            group, "kcfg_UseExternalEditor" );
02744   connect( mExternalEditorCheck, SIGNAL( toggled( bool ) ),
02745            this, SLOT( slotEmitChanged( void ) ) );
02746 
02747   hbox = new QHBox( group );
02748   label = new QLabel( GlobalSettings::self()->externalEditorItem()->label(),
02749                    hbox );
02750   mEditorRequester = new KURLRequester( hbox, "kcfg_ExternalEditor" );
02751   connect( mEditorRequester, SIGNAL( urlSelected(const QString&) ),
02752            this, SLOT( slotEmitChanged( void ) ) );
02753   connect( mEditorRequester, SIGNAL( textChanged(const QString&) ),
02754            this, SLOT( slotEmitChanged( void ) ) );
02755 
02756   hbox->setStretchFactor( mEditorRequester, 1 );
02757   label->setBuddy( mEditorRequester );
02758   label->setEnabled( false ); // since !mExternalEditorCheck->isChecked()
02759   // ### FIXME: allow only executables (x-bit when available..)
02760   mEditorRequester->setFilter( "application/x-executable "
02761                                "application/x-shellscript "
02762                                "application/x-desktop" );
02763   mEditorRequester->setEnabled( false ); // !mExternalEditorCheck->isChecked()
02764   connect( mExternalEditorCheck, SIGNAL(toggled(bool)),
02765            label, SLOT(setEnabled(bool)) );
02766   connect( mExternalEditorCheck, SIGNAL(toggled(bool)),
02767            mEditorRequester, SLOT(setEnabled(bool)) );
02768 
02769   label = new QLabel( i18n("<b>%f</b> will be replaced with the "
02770                            "filename to edit."), group );
02771   label->setEnabled( false ); // see above
02772   connect( mExternalEditorCheck, SIGNAL(toggled(bool)),
02773            label, SLOT(setEnabled(bool)) );
02774 
02775   vlay->addWidget( group );
02776   vlay->addStretch( 100 );
02777 }
02778 
02779 void ComposerPage::GeneralTab::doLoadFromGlobalSettings() {
02780   // various check boxes:
02781 
02782   mAutoAppSignFileCheck->setChecked(
02783            GlobalSettings::self()->autoTextSignature()=="auto" );
02784   mTopQuoteCheck->setChecked( GlobalSettings::self()->prependSignature() );
02785   mSmartQuoteCheck->setChecked( GlobalSettings::self()->smartQuote() );
02786   mAutoRequestMDNCheck->setChecked( GlobalSettings::self()->requestMDN() );
02787   mWordWrapCheck->setChecked( GlobalSettings::self()->wordWrap() );
02788 
02789   mWrapColumnSpin->setValue( GlobalSettings::self()->lineWrapWidth() );
02790   mRecipientCheck->setChecked( GlobalSettings::self()->tooManyRecipients() );
02791   mRecipientSpin->setValue( GlobalSettings::self()->recipientThreshold() );
02792   mAutoSave->setValue( GlobalSettings::self()->autosaveInterval() );
02793   if ( GlobalSettings::self()->forwardingInlineByDefault() )
02794     mForwardTypeCombo->setCurrentItem( 0 );
02795   else
02796     mForwardTypeCombo->setCurrentItem( 1 );
02797 
02798   // editor group:
02799   mExternalEditorCheck->setChecked( GlobalSettings::self()->useExternalEditor() );
02800   mEditorRequester->setURL( GlobalSettings::self()->externalEditor() );
02801 }
02802 
02803 void ComposerPage::GeneralTab::installProfile( KConfig * profile ) {
02804   KConfigGroup composer( profile, "Composer" );
02805   KConfigGroup general( profile, "General" );
02806 
02807   if ( composer.hasKey( "signature" ) ) {
02808     bool state = composer.readBoolEntry("signature");
02809     mAutoAppSignFileCheck->setChecked( state );
02810   }
02811   if ( composer.hasKey( "prepend-signature" ) )
02812     mTopQuoteCheck->setChecked( composer.readBoolEntry( "prepend-signature" ) );
02813   if ( composer.hasKey( "smart-quote" ) )
02814     mSmartQuoteCheck->setChecked( composer.readBoolEntry( "smart-quote" ) );
02815   if ( composer.hasKey( "request-mdn" ) )
02816     mAutoRequestMDNCheck->setChecked( composer.readBoolEntry( "request-mdn" ) );
02817   if ( composer.hasKey( "word-wrap" ) )
02818     mWordWrapCheck->setChecked( composer.readBoolEntry( "word-wrap" ) );
02819   if ( composer.hasKey( "break-at" ) )
02820     mWrapColumnSpin->setValue( composer.readNumEntry( "break-at" ) );
02821   if ( composer.hasKey( "too-many-recipients" ) )
02822     mRecipientCheck->setChecked( composer.readBoolEntry( "too-many-recipients" ) );
02823   if ( composer.hasKey( "recipient-threshold" ) )
02824     mRecipientSpin->setValue( composer.readNumEntry( "recipient-threshold" ) );
02825   if ( composer.hasKey( "autosave" ) )
02826     mAutoSave->setValue( composer.readNumEntry( "autosave" ) );
02827 
02828   if ( general.hasKey( "use-external-editor" )
02829        && general.hasKey( "external-editor" ) ) {
02830     mExternalEditorCheck->setChecked( general.readBoolEntry( "use-external-editor" ) );
02831     mEditorRequester->setURL( general.readPathEntry( "external-editor" ) );
02832   }
02833 }
02834 
02835 void ComposerPage::GeneralTab::save() {
02836   GlobalSettings::self()->setAutoTextSignature(
02837          mAutoAppSignFileCheck->isChecked() ? "auto" : "manual" );
02838   GlobalSettings::self()->setPrependSignature( mTopQuoteCheck->isChecked());
02839   GlobalSettings::self()->setSmartQuote( mSmartQuoteCheck->isChecked() );
02840   GlobalSettings::self()->setRequestMDN( mAutoRequestMDNCheck->isChecked() );
02841   GlobalSettings::self()->setWordWrap( mWordWrapCheck->isChecked() );
02842 
02843   GlobalSettings::self()->setLineWrapWidth( mWrapColumnSpin->value() );
02844   GlobalSettings::self()->setTooManyRecipients( mRecipientCheck->isChecked() );
02845   GlobalSettings::self()->setRecipientThreshold( mRecipientSpin->value() );
02846   GlobalSettings::self()->setAutosaveInterval( mAutoSave->value() );
02847   GlobalSettings::self()->setForwardingInlineByDefault( mForwardTypeCombo->currentItem() == 0 );
02848 
02849   // editor group:
02850   GlobalSettings::self()->setUseExternalEditor( mExternalEditorCheck->isChecked() );
02851   GlobalSettings::self()->setExternalEditor( mEditorRequester->url() );
02852 }
02853 
02854 void ComposerPage::GeneralTab::slotConfigureRecentAddresses( )
02855 {
02856   KRecentAddress::RecentAddressDialog dlg( this );
02857   dlg.setAddresses( RecentAddresses::self( KMKernel::config() )->addresses() );
02858   if ( dlg.exec() ) {
02859     RecentAddresses::self( KMKernel::config() )->clear();
02860     const QStringList &addrList = dlg.addresses();
02861     QStringList::ConstIterator it;
02862     for ( it = addrList.constBegin(); it != addrList.constEnd(); ++it )
02863       RecentAddresses::self( KMKernel::config() )->add( *it );
02864   }
02865 }
02866 
02867 void ComposerPage::GeneralTab::slotConfigureCompletionOrder( )
02868 {
02869   KPIM::LdapSearch search;
02870   KPIM::CompletionOrderEditor editor( &search, this );
02871   editor.exec();
02872 }
02873 
02874 QString ComposerPage::PhrasesTab::helpAnchor() const {
02875   return QString::fromLatin1("configure-composer-phrases");
02876 }
02877 
02878 ComposerPagePhrasesTab::ComposerPagePhrasesTab( QWidget * parent, const char * name )
02879   : ConfigModuleTab( parent, name )
02880 {
02881   // tmp. vars:
02882   QGridLayout *glay;
02883   QPushButton *button;
02884 
02885   glay = new QGridLayout( this, 7, 3, KDialog::spacingHint() );
02886   glay->setMargin( KDialog::marginHint() );
02887   glay->setColStretch( 1, 1 );
02888   glay->setColStretch( 2, 1 );
02889   glay->setRowStretch( 7, 1 );
02890 
02891   // row 0: help text
02892   glay->addMultiCellWidget( new QLabel( i18n("<qt>The following placeholders are "
02893                                              "supported in the reply phrases:<br>"
02894                                              "<b>%D</b>: date, <b>%S</b>: subject,<br>"
02895                                              "<b>%e</b>: sender's address, <b>%F</b>: sender's name, <b>%f</b>: sender's initials,<br>"
02896                                              "<b>%T</b>: recipient's name, <b>%t</b>: recipient's name and address,<br>"
02897                                              "<b>%C</b>: carbon copy names, <b>%c</b>: carbon copy names and addresses,<br>"
02898                                              "<b>%%</b>: percent sign, <b>%_</b>: space, "
02899                                              "<b>%L</b>: linebreak</qt>"), this ),
02900                             0, 0, 0, 2 ); // row 0; cols 0..2
02901 
02902   // row 1: label and language combo box:
02903   mPhraseLanguageCombo = new LanguageComboBox( false, this );
02904   glay->addWidget( new QLabel( mPhraseLanguageCombo,
02905                                i18n("Lang&uage:"), this ), 1, 0 );
02906   glay->addMultiCellWidget( mPhraseLanguageCombo, 1, 1, 1, 2 );
02907   connect( mPhraseLanguageCombo, SIGNAL(activated(const QString&)),
02908            this, SLOT(slotLanguageChanged(const QString&)) );
02909 
02910   // row 2: "add..." and "remove" push buttons:
02911   button = new QPushButton( i18n("A&dd..."), this );
02912   button->setAutoDefault( false );
02913   glay->addWidget( button, 2, 1 );
02914   mRemoveButton = new QPushButton( i18n("Re&move"), this );
02915   mRemoveButton->setAutoDefault( false );
02916   mRemoveButton->setEnabled( false ); // combo doesn't contain anything...
02917   glay->addWidget( mRemoveButton, 2, 2 );
02918   connect( button, SIGNAL(clicked()),
02919            this, SLOT(slotNewLanguage()) );
02920   connect( mRemoveButton, SIGNAL(clicked()),
02921            this, SLOT(slotRemoveLanguage()) );
02922 
02923   // row 3: "reply to sender" line edit and label:
02924   mPhraseReplyEdit = new KLineEdit( this );
02925   connect( mPhraseReplyEdit, SIGNAL( textChanged( const QString& ) ),
02926            this, SLOT( slotEmitChanged( void ) ) );
02927   glay->addWidget( new QLabel( mPhraseReplyEdit,
02928                                i18n("Reply to se&nder:"), this ), 3, 0 );
02929   glay->addMultiCellWidget( mPhraseReplyEdit, 3, 3, 1, 2 ); // cols 1..2
02930 
02931   // row 4: "reply to all" line edit and label:
02932   mPhraseReplyAllEdit = new KLineEdit( this );
02933   connect( mPhraseReplyAllEdit, SIGNAL( textChanged( const QString& ) ),
02934            this, SLOT( slotEmitChanged( void ) ) );
02935   glay->addWidget( new QLabel( mPhraseReplyAllEdit,
02936                                i18n("Repl&y to all:"), this ), 4, 0 );
02937   glay->addMultiCellWidget( mPhraseReplyAllEdit, 4, 4, 1, 2 ); // cols 1..2
02938 
02939   // row 5: "forward" line edit and label:
02940   mPhraseForwardEdit = new KLineEdit( this );
02941   connect( mPhraseForwardEdit, SIGNAL( textChanged( const QString& ) ),
02942            this, SLOT( slotEmitChanged( void ) ) );
02943   glay->addWidget( new QLabel( mPhraseForwardEdit,
02944                                i18n("&Forward:"), this ), 5, 0 );
02945   glay->addMultiCellWidget( mPhraseForwardEdit, 5, 5, 1, 2 ); // cols 1..2
02946 
02947   // row 6: "quote indicator" line edit and label:
02948   mPhraseIndentPrefixEdit = new KLineEdit( this );
02949   connect( mPhraseIndentPrefixEdit, SIGNAL( textChanged( const QString& ) ),
02950            this, SLOT( slotEmitChanged( void ) ) );
02951   glay->addWidget( new QLabel( mPhraseIndentPrefixEdit,
02952                                i18n("&Quote indicator:"), this ), 6, 0 );
02953   glay->addMultiCellWidget( mPhraseIndentPrefixEdit, 6, 6, 1, 2 );
02954 
02955   // row 7: spacer
02956 }
02957 
02958 
02959 void ComposerPage::PhrasesTab::setLanguageItemInformation( int index ) {
02960   assert( 0 <= index && index < (int)mLanguageList.count() );
02961 
02962   LanguageItem &l = *mLanguageList.at( index );
02963 
02964   mPhraseReplyEdit->setText( l.mReply );
02965   mPhraseReplyAllEdit->setText( l.mReplyAll );
02966   mPhraseForwardEdit->setText( l.mForward );
02967   mPhraseIndentPrefixEdit->setText( l.mIndentPrefix );
02968 }
02969 
02970 void ComposerPage::PhrasesTab::saveActiveLanguageItem() {
02971   int index = mActiveLanguageItem;
02972   if (index == -1) return;
02973   assert( 0 <= index && index < (int)mLanguageList.count() );
02974 
02975   LanguageItem &l = *mLanguageList.at( index );
02976 
02977   l.mReply = mPhraseReplyEdit->text();
02978   l.mReplyAll = mPhraseReplyAllEdit->text();
02979   l.mForward = mPhraseForwardEdit->text();
02980   l.mIndentPrefix = mPhraseIndentPrefixEdit->text();
02981 }
02982 
02983 void ComposerPage::PhrasesTab::slotNewLanguage()
02984 {
02985   NewLanguageDialog dialog( mLanguageList, parentWidget(), "New", true );
02986   if ( dialog.exec() == QDialog::Accepted ) slotAddNewLanguage( dialog.language() );
02987 }
02988 
02989 void ComposerPage::PhrasesTab::slotAddNewLanguage( const QString& lang )
02990 {
02991   mPhraseLanguageCombo->setCurrentItem(
02992     mPhraseLanguageCombo->insertLanguage( lang ) );
02993   KLocale locale("kmail");
02994   locale.setLanguage( lang );
02995   mLanguageList.append(
02996      LanguageItem( lang,
02997                    locale.translate("On %D, you wrote:"),
02998                    locale.translate("On %D, %F wrote:"),
02999                    locale.translate("Forwarded Message"),
03000                    locale.translate(">%_") ) );
03001   mRemoveButton->setEnabled( true );
03002   slotLanguageChanged( QString::null );
03003 }
03004 
03005 void ComposerPage::PhrasesTab::slotRemoveLanguage()
03006 {
03007   assert( mPhraseLanguageCombo->count() > 1 );
03008   int index = mPhraseLanguageCombo->currentItem();
03009   assert( 0 <= index && index < (int)mLanguageList.count() );
03010 
03011   // remove current item from internal list and combobox:
03012   mLanguageList.remove( mLanguageList.at( index ) );
03013   mPhraseLanguageCombo->removeItem( index );
03014 
03015   if ( index >= (int)mLanguageList.count() ) index--;
03016 
03017   mActiveLanguageItem = index;
03018   setLanguageItemInformation( index );
03019   mRemoveButton->setEnabled( mLanguageList.count() > 1 );
03020   emit changed( true );
03021 }
03022 
03023 void ComposerPage::PhrasesTab::slotLanguageChanged( const QString& )
03024 {
03025   int index = mPhraseLanguageCombo->currentItem();
03026   assert( index < (int)mLanguageList.count() );
03027   saveActiveLanguageItem();
03028   mActiveLanguageItem = index;
03029   setLanguageItemInformation( index );
03030   emit changed( true );
03031 }
03032 
03033 
03034 void ComposerPage::PhrasesTab::doLoadFromGlobalSettings() {
03035   mLanguageList.clear();
03036   mPhraseLanguageCombo->clear();
03037   mActiveLanguageItem = -1;
03038 
03039   int numLang = GlobalSettings::self()->replyLanguagesCount();
03040   int currentNr = GlobalSettings::self()->replyCurrentLanguage();
03041 
03042   // build mLanguageList and mPhraseLanguageCombo:
03043   for ( int i = 0 ; i < numLang ; i++ ) {
03044     ReplyPhrases replyPhrases( QString::number(i) );
03045     replyPhrases.readConfig();
03046     QString lang = replyPhrases.language();
03047     mLanguageList.append(
03048          LanguageItem( lang,
03049                        replyPhrases.phraseReplySender(),
03050                        replyPhrases.phraseReplyAll(),
03051                        replyPhrases.phraseForward(),
03052                        replyPhrases.indentPrefix() ) );
03053     mPhraseLanguageCombo->insertLanguage( lang );
03054   }
03055 
03056   if ( currentNr >= numLang || currentNr < 0 )
03057     currentNr = 0;
03058 
03059   if ( numLang == 0 ) {
03060     slotAddNewLanguage( KGlobal::locale()->language() );
03061   }
03062 
03063   mPhraseLanguageCombo->setCurrentItem( currentNr );
03064   mActiveLanguageItem = currentNr;
03065   setLanguageItemInformation( currentNr );
03066   mRemoveButton->setEnabled( mLanguageList.count() > 1 );
03067 }
03068 
03069 void ComposerPage::PhrasesTab::save() {
03070   GlobalSettings::self()->setReplyLanguagesCount( mLanguageList.count() );
03071   GlobalSettings::self()->setReplyCurrentLanguage( mPhraseLanguageCombo->currentItem() );
03072 
03073   saveActiveLanguageItem();
03074   LanguageItemList::Iterator it = mLanguageList.begin();
03075   for ( int i = 0 ; it != mLanguageList.end() ; ++it, ++i ) {
03076     ReplyPhrases replyPhrases( QString::number(i) );
03077     replyPhrases.setLanguage( (*it).mLanguage );
03078     replyPhrases.setPhraseReplySender( (*it).mReply );
03079     replyPhrases.setPhraseReplyAll( (*it).mReplyAll );
03080     replyPhrases.setPhraseForward( (*it).mForward );
03081     replyPhrases.setIndentPrefix( (*it).mIndentPrefix );
03082     replyPhrases.writeConfig();
03083   }
03084 }
03085 
03086 QString ComposerPage::TemplatesTab::helpAnchor() const {
03087   return QString::fromLatin1("configure-composer-templates");
03088 }
03089 
03090 ComposerPageTemplatesTab::ComposerPageTemplatesTab( QWidget * parent, const char * name )
03091   : ConfigModuleTab ( parent, name )
03092 {
03093   QVBoxLayout* vlay = new QVBoxLayout( this, 0, KDialog::spacingHint() );
03094 
03095   mWidget = new TemplatesConfiguration( this );
03096   vlay->addWidget( mWidget );
03097 
03098   connect( mWidget, SIGNAL( changed() ),
03099            this, SLOT( slotEmitChanged( void ) ) );
03100 }
03101 
03102 void ComposerPage::TemplatesTab::doLoadFromGlobalSettings() {
03103     mWidget->loadFromGlobal();
03104 }
03105 
03106 void ComposerPage::TemplatesTab::save() {
03107     mWidget->saveToGlobal();
03108 }
03109 
03110 QString ComposerPage::CustomTemplatesTab::helpAnchor() const {
03111   return QString::fromLatin1("configure-composer-custom-templates");
03112 }
03113 
03114 ComposerPageCustomTemplatesTab::ComposerPageCustomTemplatesTab( QWidget * parent, const char * name )
03115   : ConfigModuleTab ( parent, name )
03116 {
03117   QVBoxLayout* vlay = new QVBoxLayout( this, 0, KDialog::spacingHint() );
03118 
03119   mWidget = new CustomTemplates( this );
03120   vlay->addWidget( mWidget );
03121 
03122   connect( mWidget, SIGNAL( changed() ),
03123            this, SLOT( slotEmitChanged( void ) ) );
03124 }
03125 
03126 void ComposerPage::CustomTemplatesTab::doLoadFromGlobalSettings() {
03127     mWidget->load();
03128 }
03129 
03130 void ComposerPage::CustomTemplatesTab::save() {
03131     mWidget->save();
03132 }
03133 
03134 QString ComposerPage::SubjectTab::helpAnchor() const {
03135   return QString::fromLatin1("configure-composer-subject");
03136 }
03137 
03138 ComposerPageSubjectTab::ComposerPageSubjectTab( QWidget * parent, const char * name )
03139   : ConfigModuleTab( parent, name )
03140 {
03141   // tmp. vars:
03142   QVBoxLayout *vlay;
03143   QGroupBox   *group;
03144   QLabel      *label;
03145 
03146 
03147   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
03148 
03149   group = new QVGroupBox( i18n("Repl&y Subject Prefixes"), this );
03150   group->layout()->setSpacing( KDialog::spacingHint() );
03151 
03152   // row 0: help text:
03153   label = new QLabel( i18n("Recognize any sequence of the following prefixes\n"
03154                            "(entries are case-insensitive regular expressions):"), group );
03155   label->setAlignment( AlignLeft|WordBreak );
03156 
03157   // row 1, string list editor:
03158   SimpleStringListEditor::ButtonCode buttonCode =
03159     static_cast<SimpleStringListEditor::ButtonCode>( SimpleStringListEditor::Add | SimpleStringListEditor::Remove | SimpleStringListEditor::Modify );
03160   mReplyListEditor =
03161     new SimpleStringListEditor( group, 0, buttonCode,
03162                                 i18n("A&dd..."), i18n("Re&move"),
03163                                 i18n("Mod&ify..."),
03164                                 i18n("Enter new reply prefix:") );
03165   connect( mReplyListEditor, SIGNAL( changed( void ) ),
03166            this, SLOT( slotEmitChanged( void ) ) );
03167 
03168   // row 2: "replace [...]" check box:
03169   mReplaceReplyPrefixCheck = new QCheckBox(
03170      GlobalSettings::self()->replaceReplyPrefixItem()->label(),
03171      group, "kcfg_ReplaceReplyPrefix" );
03172   connect( mReplaceReplyPrefixCheck, SIGNAL( stateChanged( int ) ),
03173            this, SLOT( slotEmitChanged( void ) ) );
03174 
03175   vlay->addWidget( group );
03176 
03177 
03178   group = new QVGroupBox( i18n("For&ward Subject Prefixes"), this );
03179   group->layout()->setSpacing( KDialog::marginHint() );
03180 
03181   // row 0: help text:
03182   label= new QLabel( i18n("Recognize any sequence of the following prefixes\n"
03183                           "(entries are case-insensitive regular expressions):"), group );
03184   label->setAlignment( AlignLeft|WordBreak );
03185 
03186   // row 1: string list editor
03187   mForwardListEditor =
03188     new SimpleStringListEditor( group, 0, buttonCode,
03189                                 i18n("Add..."),
03190                                 i18n("Remo&ve"),
03191                                 i18n("Modify..."),
03192                                 i18n("Enter new forward prefix:") );
03193   connect( mForwardListEditor, SIGNAL( changed( void ) ),
03194            this, SLOT( slotEmitChanged( void ) ) );
03195 
03196   // row 3: "replace [...]" check box:
03197   mReplaceForwardPrefixCheck = new QCheckBox(
03198        GlobalSettings::self()->replaceForwardPrefixItem()->label(),
03199        group, "kcfg_ReplaceForwardPrefix" );
03200   connect( mReplaceForwardPrefixCheck, SIGNAL( stateChanged( int ) ),
03201            this, SLOT( slotEmitChanged( void ) ) );
03202 
03203   vlay->addWidget( group );
03204 }
03205 
03206 void ComposerPage::SubjectTab::doLoadFromGlobalSettings() {
03207   mReplyListEditor->setStringList( GlobalSettings::self()->replyPrefixes() );
03208   mReplaceReplyPrefixCheck->setChecked( GlobalSettings::self()->replaceReplyPrefix() );
03209   mForwardListEditor->setStringList( GlobalSettings::self()->forwardPrefixes() );
03210   mReplaceForwardPrefixCheck->setChecked( GlobalSettings::self()->replaceForwardPrefix() );
03211 }
03212 
03213 void ComposerPage::SubjectTab::save() {
03214   GlobalSettings::self()->setReplyPrefixes( mReplyListEditor->stringList() );
03215   GlobalSettings::self()->setForwardPrefixes( mForwardListEditor->stringList() );
03216 }
03217 
03218 QString ComposerPage::CharsetTab::helpAnchor() const {
03219   return QString::fromLatin1("configure-composer-charset");
03220 }
03221 
03222 ComposerPageCharsetTab::ComposerPageCharsetTab( QWidget * parent, const char * name )
03223   : ConfigModuleTab( parent, name )
03224 {
03225   // tmp. vars:
03226   QVBoxLayout *vlay;
03227   QLabel      *label;
03228 
03229   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
03230 
03231   label = new QLabel( i18n("This list is checked for every outgoing message "
03232                            "from the top to the bottom for a charset that "
03233                            "contains all required characters."), this );
03234   label->setAlignment( WordBreak);
03235   vlay->addWidget( label );
03236 
03237   mCharsetListEditor =
03238     new SimpleStringListEditor( this, 0, SimpleStringListEditor::All,
03239                                 i18n("A&dd..."), i18n("Remo&ve"),
03240                                 i18n("&Modify..."), i18n("Enter charset:") );
03241   connect( mCharsetListEditor, SIGNAL( changed( void ) ),
03242            this, SLOT( slotEmitChanged( void ) ) );
03243 
03244   vlay->addWidget( mCharsetListEditor, 1 );
03245 
03246   mKeepReplyCharsetCheck = new QCheckBox( i18n("&Keep original charset when "
03247                                                 "replying or forwarding (if "
03248                                                 "possible)"), this );
03249   connect( mKeepReplyCharsetCheck, SIGNAL ( stateChanged( int ) ),
03250            this, SLOT( slotEmitChanged( void ) ) );
03251   vlay->addWidget( mKeepReplyCharsetCheck );
03252 
03253   connect( mCharsetListEditor, SIGNAL(aboutToAdd(QString&)),
03254            this, SLOT(slotVerifyCharset(QString&)) );
03255 }
03256 
03257 void ComposerPage::CharsetTab::slotVerifyCharset( QString & charset ) {
03258   if ( charset.isEmpty() ) return;
03259 
03260   // KCharsets::codecForName("us-ascii") returns "iso-8859-1" (cf. Bug #49812)
03261   // therefore we have to treat this case specially
03262   if ( charset.lower() == QString::fromLatin1("us-ascii") ) {
03263     charset = QString::fromLatin1("us-ascii");
03264     return;
03265   }
03266 
03267   if ( charset.lower() == QString::fromLatin1("locale") ) {
03268     charset =  QString::fromLatin1("%1 (locale)")
03269       .arg( QCString( kmkernel->networkCodec()->mimeName() ).lower() );
03270     return;
03271   }
03272 
03273   bool ok = false;
03274   QTextCodec *codec = KGlobal::charsets()->codecForName( charset, ok );
03275   if ( ok && codec ) {
03276     charset = QString::fromLatin1( codec->mimeName() ).lower();
03277     return;
03278   }
03279 
03280   KMessageBox::sorry( this, i18n("This charset is not supported.") );
03281   charset = QString::null;
03282 }
03283 
03284 void ComposerPage::CharsetTab::doLoadOther() {
03285   KConfigGroup composer( KMKernel::config(), "Composer" );
03286 
03287   QStringList charsets = composer.readListEntry( "pref-charsets" );
03288   for ( QStringList::Iterator it = charsets.begin() ;
03289         it != charsets.end() ; ++it )
03290     if ( (*it) == QString::fromLatin1("locale") ) {
03291       QCString cset = kmkernel->networkCodec()->mimeName();
03292       KPIM::kAsciiToLower( cset.data() );
03293       (*it) = QString("%1 (locale)").arg( cset );
03294     }
03295 
03296   mCharsetListEditor->setStringList( charsets );
03297   mKeepReplyCharsetCheck->setChecked( !composer.readBoolEntry( "force-reply-charset", false ) );
03298 }
03299 
03300 void ComposerPage::CharsetTab::save() {
03301   KConfigGroup composer( KMKernel::config(), "Composer" );
03302 
03303   QStringList charsetList = mCharsetListEditor->stringList();
03304   QStringList::Iterator it = charsetList.begin();
03305   for ( ; it != charsetList.end() ; ++it )
03306     if ( (*it).endsWith("(locale)") )
03307       (*it) = "locale";
03308   composer.writeEntry( "pref-charsets", charsetList );
03309   composer.writeEntry( "force-reply-charset",
03310                        !mKeepReplyCharsetCheck->isChecked() );
03311 }
03312 
03313 QString ComposerPage::HeadersTab::helpAnchor() const {
03314   return QString::fromLatin1("configure-composer-headers");
03315 }
03316 
03317 ComposerPageHeadersTab::ComposerPageHeadersTab( QWidget * parent, const char * name )
03318   : ConfigModuleTab( parent, name )
03319 {
03320   // tmp. vars:
03321   QVBoxLayout *vlay;
03322   QHBoxLayout *hlay;
03323   QGridLayout *glay;
03324   QLabel      *label;
03325   QPushButton *button;
03326 
03327   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
03328 
03329   // "Use custom Message-Id suffix" checkbox:
03330   mCreateOwnMessageIdCheck =
03331     new QCheckBox( i18n("&Use custom message-id suffix"), this );
03332   connect( mCreateOwnMessageIdCheck, SIGNAL ( stateChanged( int ) ),
03333            this, SLOT( slotEmitChanged( void ) ) );
03334   vlay->addWidget( mCreateOwnMessageIdCheck );
03335 
03336   // "Message-Id suffix" line edit and label:
03337   hlay = new QHBoxLayout( vlay ); // inherits spacing
03338   mMessageIdSuffixEdit = new KLineEdit( this );
03339   // only ASCII letters, digits, plus, minus and dots are allowed
03340   mMessageIdSuffixValidator =
03341     new QRegExpValidator( QRegExp( "[a-zA-Z0-9+-]+(?:\\.[a-zA-Z0-9+-]+)*" ), this );
03342   mMessageIdSuffixEdit->setValidator( mMessageIdSuffixValidator );
03343   label = new QLabel( mMessageIdSuffixEdit,
03344                       i18n("Custom message-&id suffix:"), this );
03345   label->setEnabled( false ); // since !mCreateOwnMessageIdCheck->isChecked()
03346   mMessageIdSuffixEdit->setEnabled( false );
03347   hlay->addWidget( label );
03348   hlay->addWidget( mMessageIdSuffixEdit, 1 );
03349   connect( mCreateOwnMessageIdCheck, SIGNAL(toggled(bool) ),
03350            label, SLOT(setEnabled(bool)) );
03351   connect( mCreateOwnMessageIdCheck, SIGNAL(toggled(bool) ),
03352            mMessageIdSuffixEdit, SLOT(setEnabled(bool)) );
03353   connect( mMessageIdSuffixEdit, SIGNAL( textChanged( const QString& ) ),
03354            this, SLOT( slotEmitChanged( void ) ) );
03355 
03356   // horizontal rule and "custom header fields" label:
03357   vlay->addWidget( new KSeparator( KSeparator::HLine, this ) );
03358   vlay->addWidget( new QLabel( i18n("Define custom mime header fields:"), this) );
03359 
03360   // "custom header fields" listbox:
03361   glay = new QGridLayout( vlay, 5, 3 ); // inherits spacing
03362   glay->setRowStretch( 2, 1 );
03363   glay->setColStretch( 1, 1 );
03364   mTagList = new ListView( this, "tagList" );
03365   mTagList->addColumn( i18n("Name") );
03366   mTagList->addColumn( i18n("Value") );
03367   mTagList->setAllColumnsShowFocus( true );
03368   mTagList->setSorting( -1 );
03369   connect( mTagList, SIGNAL(selectionChanged()),
03370            this, SLOT(slotMimeHeaderSelectionChanged()) );
03371   glay->addMultiCellWidget( mTagList, 0, 2, 0, 1 );
03372 
03373   // "new" and "remove" buttons:
03374   button = new QPushButton( i18n("Ne&w"), this );
03375   connect( button, SIGNAL(clicked()), this, SLOT(slotNewMimeHeader()) );
03376   button->setAutoDefault( false );
03377   glay->addWidget( button, 0, 2 );
03378   mRemoveHeaderButton = new QPushButton( i18n("Re&move"), this );
03379   connect( mRemoveHeaderButton, SIGNAL(clicked()),
03380            this, SLOT(slotRemoveMimeHeader()) );
03381   button->setAutoDefault( false );
03382   glay->addWidget( mRemoveHeaderButton, 1, 2 );
03383 
03384   // "name" and "value" line edits and labels:
03385   mTagNameEdit = new KLineEdit( this );
03386   mTagNameEdit->setEnabled( false );
03387   mTagNameLabel = new QLabel( mTagNameEdit, i18n("&Name:"), this );
03388   mTagNameLabel->setEnabled( false );
03389   glay->addWidget( mTagNameLabel, 3, 0 );
03390   glay->addWidget( mTagNameEdit, 3, 1 );
03391   connect( mTagNameEdit, SIGNAL(textChanged(const QString&)),
03392            this, SLOT(slotMimeHeaderNameChanged(const QString&)) );
03393 
03394   mTagValueEdit = new KLineEdit( this );
03395   mTagValueEdit->setEnabled( false );
03396   mTagValueLabel = new QLabel( mTagValueEdit, i18n("&Value:"), this );
03397   mTagValueLabel->setEnabled( false );
03398   glay->addWidget( mTagValueLabel, 4, 0 );
03399   glay->addWidget( mTagValueEdit, 4, 1 );
03400   connect( mTagValueEdit, SIGNAL(textChanged(const QString&)),
03401            this, SLOT(slotMimeHeaderValueChanged(const QString&)) );
03402 }
03403 
03404 void ComposerPage::HeadersTab::slotMimeHeaderSelectionChanged()
03405 {
03406   QListViewItem * item = mTagList->selectedItem();
03407 
03408   if ( item ) {
03409     mTagNameEdit->setText( item->text( 0 ) );
03410     mTagValueEdit->setText( item->text( 1 ) );
03411   } else {
03412     mTagNameEdit->clear();
03413     mTagValueEdit->clear();
03414   }
03415   mRemoveHeaderButton->setEnabled( item );
03416   mTagNameEdit->setEnabled( item );
03417   mTagValueEdit->setEnabled( item );
03418   mTagNameLabel->setEnabled( item );
03419   mTagValueLabel->setEnabled( item );
03420 }
03421 
03422 
03423 void ComposerPage::HeadersTab::slotMimeHeaderNameChanged( const QString & text ) {
03424   // is called on ::setup(), when clearing the line edits. So be
03425   // prepared to not find a selection:
03426   QListViewItem * item = mTagList->selectedItem();
03427   if ( item )
03428     item->setText( 0, text );
03429   emit changed( true );
03430 }
03431 
03432 
03433 void ComposerPage::HeadersTab::slotMimeHeaderValueChanged( const QString & text ) {
03434   // is called on ::setup(), when clearing the line edits. So be
03435   // prepared to not find a selection:
03436   QListViewItem * item = mTagList->selectedItem();
03437   if ( item )
03438     item->setText( 1, text );
03439   emit changed( true );
03440 }
03441 
03442 
03443 void ComposerPage::HeadersTab::slotNewMimeHeader()
03444 {
03445   QListViewItem *listItem = new QListViewItem( mTagList );
03446   mTagList->setCurrentItem( listItem );
03447   mTagList->setSelected( listItem, true );
03448   emit changed( true );
03449 }
03450 
03451 
03452 void ComposerPage::HeadersTab::slotRemoveMimeHeader()
03453 {
03454   // calling this w/o selection is a programming error:
03455   QListViewItem * item = mTagList->selectedItem();
03456   if ( !item ) {
03457     kdDebug(5006) << "==================================================\n"
03458                   << "Error: Remove button was pressed although no custom header was selected\n"
03459                   << "==================================================\n";
03460     return;
03461   }
03462 
03463   QListViewItem * below = item->nextSibling();
03464   delete item;
03465 
03466   if ( below )
03467     mTagList->setSelected( below, true );
03468   else if ( mTagList->lastItem() )
03469     mTagList->setSelected( mTagList->lastItem(), true );
03470   emit changed( true );
03471 }
03472 
03473 void ComposerPage::HeadersTab::doLoadOther() {
03474   KConfigGroup general( KMKernel::config(), "General" );
03475 
03476   QString suffix = general.readEntry( "myMessageIdSuffix" );
03477   mMessageIdSuffixEdit->setText( suffix );
03478   bool state = ( !suffix.isEmpty() &&
03479             general.readBoolEntry( "useCustomMessageIdSuffix", false ) );
03480   mCreateOwnMessageIdCheck->setChecked( state );
03481 
03482   mTagList->clear();
03483   mTagNameEdit->clear();
03484   mTagValueEdit->clear();
03485 
03486   QListViewItem * item = 0;
03487 
03488   int count = general.readNumEntry( "mime-header-count", 0 );
03489   for( int i = 0 ; i < count ; i++ ) {
03490     KConfigGroup config( KMKernel::config(),
03491                          QCString("Mime #") + QCString().setNum(i) );
03492     QString name  = config.readEntry( "name" );
03493     QString value = config.readEntry( "value" );
03494     if( !name.isEmpty() )
03495       item = new QListViewItem( mTagList, item, name, value );
03496   }
03497   if ( mTagList->childCount() ) {
03498     mTagList->setCurrentItem( mTagList->firstChild() );
03499     mTagList->setSelected( mTagList->firstChild(), true );
03500   }
03501   else {
03502     // disable the "Remove" button
03503     mRemoveHeaderButton->setEnabled( false );
03504   }
03505 }
03506 
03507 void ComposerPage::HeadersTab::save() {
03508   KConfigGroup general( KMKernel::config(), "General" );
03509 
03510   general.writeEntry( "useCustomMessageIdSuffix",
03511                       mCreateOwnMessageIdCheck->isChecked() );
03512   general.writeEntry( "myMessageIdSuffix",
03513                       mMessageIdSuffixEdit->text() );
03514 
03515   int numValidEntries = 0;
03516   QListViewItem * item = mTagList->firstChild();
03517   for ( ; item ; item = item->itemBelow() )
03518     if( !item->text(0).isEmpty() ) {
03519       KConfigGroup config( KMKernel::config(), QCString("Mime #")
03520                              + QCString().setNum( numValidEntries ) );
03521       config.writeEntry( "name",  item->text( 0 ) );
03522       config.writeEntry( "value", item->text( 1 ) );
03523       numValidEntries++;
03524     }
03525   general.writeEntry( "mime-header-count", numValidEntries );
03526 }
03527 
03528 QString ComposerPage::AttachmentsTab::helpAnchor() const {
03529   return QString::fromLatin1("configure-composer-attachments");
03530 }
03531 
03532 ComposerPageAttachmentsTab::ComposerPageAttachmentsTab( QWidget * parent,
03533                                                         const char * name )
03534   : ConfigModuleTab( parent, name ) {
03535   // tmp. vars:
03536   QVBoxLayout *vlay;
03537   QLabel      *label;
03538 
03539   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
03540 
03541   // "Outlook compatible attachment naming" check box
03542   mOutlookCompatibleCheck =
03543     new QCheckBox( i18n( "Outlook-compatible attachment naming" ), this );
03544   mOutlookCompatibleCheck->setChecked( false );
03545   QToolTip::add( mOutlookCompatibleCheck, i18n(
03546     "Turn this option on to make Outlook(tm) understand attachment names "
03547     "containing non-English characters" ) );
03548   connect( mOutlookCompatibleCheck, SIGNAL( stateChanged( int ) ),
03549            this, SLOT( slotEmitChanged( void ) ) );
03550   connect( mOutlookCompatibleCheck, SIGNAL( clicked() ),
03551            this, SLOT( slotOutlookCompatibleClicked() ) );
03552   vlay->addWidget( mOutlookCompatibleCheck );
03553   vlay->addSpacing( 5 );
03554 
03555   // "Enable detection of missing attachments" check box
03556   mMissingAttachmentDetectionCheck =
03557     new QCheckBox( i18n("E&nable detection of missing attachments"), this );
03558   mMissingAttachmentDetectionCheck->setChecked( true );
03559   connect( mMissingAttachmentDetectionCheck, SIGNAL( stateChanged( int ) ),
03560            this, SLOT( slotEmitChanged( void ) ) );
03561   vlay->addWidget( mMissingAttachmentDetectionCheck );
03562 
03563   // "Attachment key words" label and string list editor
03564   label = new QLabel( i18n("Recognize any of the following key words as "
03565                            "intention to attach a file:"), this );
03566   label->setAlignment( AlignLeft|WordBreak );
03567   vlay->addWidget( label );
03568 
03569   SimpleStringListEditor::ButtonCode buttonCode =
03570     static_cast<SimpleStringListEditor::ButtonCode>( SimpleStringListEditor::Add | SimpleStringListEditor::Remove | SimpleStringListEditor::Modify );
03571   mAttachWordsListEditor =
03572     new SimpleStringListEditor( this, 0, buttonCode,
03573                                 i18n("A&dd..."), i18n("Re&move"),
03574                                 i18n("Mod&ify..."),
03575                                 i18n("Enter new key word:") );
03576   connect( mAttachWordsListEditor, SIGNAL( changed( void ) ),
03577            this, SLOT( slotEmitChanged( void ) ) );
03578   vlay->addWidget( mAttachWordsListEditor );
03579 
03580   connect( mMissingAttachmentDetectionCheck, SIGNAL(toggled(bool) ),
03581            label, SLOT(setEnabled(bool)) );
03582   connect( mMissingAttachmentDetectionCheck, SIGNAL(toggled(bool) ),
03583            mAttachWordsListEditor, SLOT(setEnabled(bool)) );
03584 }
03585 
03586 void ComposerPage::AttachmentsTab::doLoadFromGlobalSettings() {
03587   mOutlookCompatibleCheck->setChecked(
03588     GlobalSettings::self()->outlookCompatibleAttachments() );
03589   mMissingAttachmentDetectionCheck->setChecked(
03590     GlobalSettings::self()->showForgottenAttachmentWarning() );
03591   QStringList attachWordsList = GlobalSettings::self()->attachmentKeywords();
03592   if ( attachWordsList.isEmpty() ) {
03593     // default value
03594     attachWordsList << QString::fromLatin1("attachment")
03595                     << QString::fromLatin1("attached");
03596     if ( QString::fromLatin1("attachment") != i18n("attachment") )
03597       attachWordsList << i18n("attachment");
03598     if ( QString::fromLatin1("attached") != i18n("attached") )
03599       attachWordsList << i18n("attached");
03600   }
03601 
03602   mAttachWordsListEditor->setStringList( attachWordsList );
03603 }
03604 
03605 void ComposerPage::AttachmentsTab::save() {
03606   GlobalSettings::self()->setOutlookCompatibleAttachments(
03607     mOutlookCompatibleCheck->isChecked() );
03608   GlobalSettings::self()->setShowForgottenAttachmentWarning(
03609     mMissingAttachmentDetectionCheck->isChecked() );
03610   GlobalSettings::self()->setAttachmentKeywords(
03611     mAttachWordsListEditor->stringList() );
03612 }
03613 
03614 void ComposerPageAttachmentsTab::slotOutlookCompatibleClicked()
03615 {
03616   if (mOutlookCompatibleCheck->isChecked()) {
03617     KMessageBox::information(0,i18n("You have chosen to "
03618     "encode attachment names containing non-English characters in a way that "
03619     "is understood by Outlook(tm) and other mail clients that do not "
03620     "support standard-compliant encoded attachment names.\n"
03621     "Note that KMail may create non-standard compliant messages, "
03622     "and consequently it is possible that your messages will not be "
03623     "understood by standard-compliant mail clients; so, unless you have no "
03624     "other choice, you should not enable this option." ) );
03625   }
03626 }
03627 
03628 // *************************************************************
03629 // *                                                           *
03630 // *                      SecurityPage                         *
03631 // *                                                           *
03632 // *************************************************************
03633 QString SecurityPage::helpAnchor() const {
03634   return QString::fromLatin1("configure-security");
03635 }
03636 
03637 SecurityPage::SecurityPage( QWidget * parent, const char * name )
03638   : ConfigModuleWithTabs( parent, name )
03639 {
03640   //
03641   // "Reading" tab:
03642   //
03643   mGeneralTab = new GeneralTab(); //  @TODO: rename
03644   addTab( mGeneralTab, i18n("&Reading") );
03645 
03646   //
03647   // "Composing" tab:
03648   //
03649   mComposerCryptoTab = new ComposerCryptoTab();
03650   addTab( mComposerCryptoTab, i18n("Composing") );
03651 
03652   //
03653   // "Warnings" tab:
03654   //
03655   mWarningTab = new WarningTab();
03656   addTab( mWarningTab, i18n("Warnings") );
03657 
03658   //
03659   // "S/MIME Validation" tab:
03660   //
03661   mSMimeTab = new SMimeTab();
03662   addTab( mSMimeTab, i18n("S/MIME &Validation") );
03663 
03664   //
03665   // "Crypto Backends" tab:
03666   //
03667   mCryptPlugTab = new CryptPlugTab();
03668   addTab( mCryptPlugTab, i18n("Crypto Backe&nds") );
03669   load();
03670 }
03671 
03672 
03673 void SecurityPage::installProfile( KConfig * profile ) {
03674   mGeneralTab->installProfile( profile );
03675   mComposerCryptoTab->installProfile( profile );
03676   mWarningTab->installProfile( profile );
03677   mSMimeTab->installProfile( profile );
03678 }
03679 
03680 QString SecurityPage::GeneralTab::helpAnchor() const {
03681   return QString::fromLatin1("configure-security-reading");
03682 }
03683 
03684 SecurityPageGeneralTab::SecurityPageGeneralTab( QWidget * parent, const char * name )
03685   : ConfigModuleTab ( parent, name )
03686 {
03687   // tmp. vars:
03688   QVBoxLayout  *vlay;
03689   QHBox        *hbox;
03690   QGroupBox    *group;
03691   QRadioButton *radio;
03692   KActiveLabel *label;
03693   QWidget      *w;
03694   QString       msg;
03695 
03696   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
03697 
03698   // QWhat'sThis texts
03699   QString htmlWhatsThis = i18n( "<qt><p>Messages sometimes come in both formats. "
03700               "This option controls whether you want the HTML part or the plain "
03701               "text part to be displayed.</p>"
03702               "<p>Displaying the HTML part makes the message look better, "
03703               "but at the same time increases the risk of security holes "
03704               "being exploited.</p>"
03705               "<p>Displaying the plain text part loses much of the message's "
03706               "formatting, but makes it almost <em>impossible</em> "
03707               "to exploit security holes in the HTML renderer (Konqueror).</p>"
03708               "<p>The option below guards against one common misuse of HTML "
03709               "messages, but it cannot guard against security issues that were "
03710               "not known at the time this version of KMail was written.</p>"
03711               "<p>It is therefore advisable to <em>not</em> prefer HTML to "
03712               "plain text.</p>"
03713               "<p><b>Note:</b> You can set this option on a per-folder basis "
03714               "from the <i>Folder</i> menu of KMail's main window.</p></qt>" );
03715 
03716   QString externalWhatsThis = i18n( "<qt><p>Some mail advertisements are in HTML "
03717               "and contain references to, for example, images that the advertisers"
03718               " employ to find out that you have read their message "
03719               "(&quot;web bugs&quot;).</p>"
03720               "<p>There is no valid reason to load images off the Internet like "
03721               "this, since the sender can always attach the required images "
03722               "directly to the message.</p>"
03723               "<p>To guard from such a misuse of the HTML displaying feature "
03724               "of KMail, this option is <em>disabled</em> by default.</p>"
03725               "<p>However, if you wish to, for example, view images in HTML "
03726               "messages that were not attached to it, you can enable this "
03727               "option, but you should be aware of the possible problem.</p></qt>" );
03728 
03729   QString receiptWhatsThis = i18n( "<qt><h3>Message Disposition "
03730               "Notification Policy</h3>"
03731               "<p>MDNs are a generalization of what is commonly called <b>read "
03732               "receipt</b>. The message author requests a disposition "
03733               "notification to be sent and the receiver's mail program "
03734               "generates a reply from which the author can learn what "
03735               "happened to his message. Common disposition types include "
03736               "<b>displayed</b> (i.e. read), <b>deleted</b> and <b>dispatched</b> "
03737               "(e.g. forwarded).</p>"
03738               "<p>The following options are available to control KMail's "
03739               "sending of MDNs:</p>"
03740               "<ul>"
03741               "<li><em>Ignore</em>: Ignores any request for disposition "
03742               "notifications. No MDN will ever be sent automatically "
03743               "(recommended).</li>"
03744               "<li><em>Ask</em>: Answers requests only after asking the user "
03745               "for permission. This way, you can send MDNs for selected "
03746               "messages while denying or ignoring them for others.</li>"
03747               "<li><em>Deny</em>: Always sends a <b>denied</b> notification. This "
03748               "is only <em>slightly</em> better than always sending MDNs. "
03749               "The author will still know that the messages has been acted "
03750               "upon, he just cannot tell whether it was deleted or read etc.</li>"
03751               "<li><em>Always send</em>: Always sends the requested "
03752               "disposition notification. That means that the author of the "
03753               "message gets to know when the message was acted upon and, "
03754               "in addition, what happened to it (displayed, deleted, "
03755               "etc.). This option is strongly discouraged, but since it "
03756               "makes much sense e.g. for customer relationship management, "
03757               "it has been made available.</li>"
03758               "</ul></qt>" );
03759 
03760 
03761   // "HTML Messages" group box:
03762   group = new QVGroupBox( i18n( "HTML Messages" ), this );
03763   group->layout()->setSpacing( KDialog::spacingHint() );
03764 
03765   mHtmlMailCheck = new QCheckBox( i18n("Prefer H&TML to plain text"), group );
03766   QWhatsThis::add( mHtmlMailCheck, htmlWhatsThis );
03767   connect( mHtmlMailCheck, SIGNAL( stateChanged( int ) ),
03768            this, SLOT( slotEmitChanged( void ) ) );
03769   mExternalReferences = new QCheckBox( i18n("Allow messages to load e&xternal "
03770                                             "references from the Internet" ), group );
03771   QWhatsThis::add( mExternalReferences, externalWhatsThis );
03772   connect( mExternalReferences, SIGNAL( stateChanged( int ) ),
03773            this, SLOT( slotEmitChanged( void ) ) );
03774   label = new KActiveLabel( i18n("<b>WARNING:</b> Allowing HTML in email may "
03775                            "increase the risk that your system will be "
03776                            "compromised by present and anticipated security "
03777                            "exploits. <a href=\"whatsthis:%1\">More about "
03778                            "HTML mails...</a> <a href=\"whatsthis:%2\">More "
03779                            "about external references...</a>")
03780                            .arg(htmlWhatsThis).arg(externalWhatsThis),
03781                            group );
03782 
03783   vlay->addWidget( group );
03784 
03785   // encrypted messages group
03786   group = new QVGroupBox( i18n("Encrypted Messages"), this );
03787   group->layout()->setSpacing( KDialog::spacingHint() );
03788   mAlwaysDecrypt = new QCheckBox( i18n( "Attempt decryption of encrypted messages when viewing" ), group );
03789   connect( mAlwaysDecrypt, SIGNAL(stateChanged(int)), this, SLOT(slotEmitChanged()) );
03790   vlay->addWidget( group );
03791 
03792   // "Message Disposition Notification" groupbox:
03793   group = new QVGroupBox( i18n("Message Disposition Notifications"), this );
03794   group->layout()->setSpacing( KDialog::spacingHint() );
03795 
03796 
03797   // "ignore", "ask", "deny", "always send" radiobutton line:
03798   mMDNGroup = new QButtonGroup( group );
03799   mMDNGroup->hide();
03800   connect( mMDNGroup, SIGNAL( clicked( int ) ),
03801            this, SLOT( slotEmitChanged( void ) ) );
03802   hbox = new QHBox( group );
03803   hbox->setSpacing( KDialog::spacingHint() );
03804 
03805   (void)new QLabel( i18n("Send policy:"), hbox );
03806 
03807   radio = new QRadioButton( i18n("&Ignore"), hbox );
03808   mMDNGroup->insert( radio );
03809 
03810   radio = new QRadioButton( i18n("As&k"), hbox );
03811   mMDNGroup->insert( radio );
03812 
03813   radio = new QRadioButton( i18n("&Deny"), hbox );
03814   mMDNGroup->insert( radio );
03815 
03816   radio = new QRadioButton( i18n("Al&ways send"), hbox );
03817   mMDNGroup->insert( radio );
03818 
03819   for ( int i = 0 ; i < mMDNGroup->count() ; ++i )
03820       QWhatsThis::add( mMDNGroup->find( i ), receiptWhatsThis );
03821 
03822   w = new QWidget( hbox ); // spacer
03823   hbox->setStretchFactor( w, 1 );
03824 
03825   // "Original Message quote" radiobutton line:
03826   mOrigQuoteGroup = new QButtonGroup( group );
03827   mOrigQuoteGroup->hide();
03828   connect( mOrigQuoteGroup, SIGNAL( clicked( int ) ),
03829            this, SLOT( slotEmitChanged( void ) ) );
03830 
03831   hbox = new QHBox( group );
03832   hbox->setSpacing( KDialog::spacingHint() );
03833 
03834   (void)new QLabel( i18n("Quote original message:"), hbox );
03835 
03836   radio = new QRadioButton( i18n("Nothin&g"), hbox );
03837   mOrigQuoteGroup->insert( radio );
03838 
03839   radio = new QRadioButton( i18n("&Full message"), hbox );
03840   mOrigQuoteGroup->insert( radio );
03841 
03842   radio = new QRadioButton( i18n("Onl&y headers"), hbox );
03843   mOrigQuoteGroup->insert( radio );
03844 
03845   w = new QWidget( hbox );
03846   hbox->setStretchFactor( w, 1 );
03847 
03848   mNoMDNsWhenEncryptedCheck = new QCheckBox( i18n("Do not send MDNs in response to encrypted messages"), group );
03849   connect( mNoMDNsWhenEncryptedCheck, SIGNAL(toggled(bool)), SLOT(slotEmitChanged()) );
03850 
03851   // Warning label:
03852   label = new KActiveLabel( i18n("<b>WARNING:</b> Unconditionally returning "
03853                            "confirmations undermines your privacy. "
03854                            "<a href=\"whatsthis:%1\">More...</a>")
03855                              .arg(receiptWhatsThis),
03856                            group );
03857 
03858   vlay->addWidget( group );
03859 
03860   // "Attached keys" group box:
03861   group = new QVGroupBox( i18n( "Certificate && Key Bundle Attachments" ), this );
03862   group->layout()->setSpacing( KDialog::spacingHint() );
03863 
03864   mAutomaticallyImportAttachedKeysCheck = new QCheckBox( i18n("Automatically import keys and certificates"), group );
03865   connect( mAutomaticallyImportAttachedKeysCheck, SIGNAL(toggled(bool)), SLOT(slotEmitChanged()) );
03866 
03867   vlay->addWidget( group );
03868 
03869 
03870 
03871   vlay->addStretch( 10 ); // spacer
03872 }
03873 
03874 void SecurityPage::GeneralTab::doLoadOther() {
03875   const KConfigGroup reader( KMKernel::config(), "Reader" );
03876 
03877   mHtmlMailCheck->setChecked( reader.readBoolEntry( "htmlMail", false ) );
03878   mExternalReferences->setChecked( reader.readBoolEntry( "htmlLoadExternal", false ) );
03879   mAutomaticallyImportAttachedKeysCheck->setChecked( reader.readBoolEntry( "AutoImportKeys", false ) );
03880 
03881   mAlwaysDecrypt->setChecked( GlobalSettings::self()->alwaysDecrypt() );
03882 
03883   const KConfigGroup mdn( KMKernel::config(), "MDN" );
03884 
03885   int num = mdn.readNumEntry( "default-policy", 0 );
03886   if ( num < 0 || num >= mMDNGroup->count() ) num = 0;
03887   mMDNGroup->setButton( num );
03888   num = mdn.readNumEntry( "quote-message", 0 );
03889   if ( num < 0 || num >= mOrigQuoteGroup->count() ) num = 0;
03890   mOrigQuoteGroup->setButton( num );
03891   mNoMDNsWhenEncryptedCheck->setChecked(mdn.readBoolEntry( "not-send-when-encrypted", true ));
03892 }
03893 
03894 void SecurityPage::GeneralTab::installProfile( KConfig * profile ) {
03895   const KConfigGroup reader( profile, "Reader" );
03896   const KConfigGroup mdn( profile, "MDN" );
03897 
03898   if ( reader.hasKey( "htmlMail" ) )
03899     mHtmlMailCheck->setChecked( reader.readBoolEntry( "htmlMail" ) );
03900   if ( reader.hasKey( "htmlLoadExternal" ) )
03901     mExternalReferences->setChecked( reader.readBoolEntry( "htmlLoadExternal" ) );
03902   if ( reader.hasKey( "AutoImportKeys" ) )
03903     mAutomaticallyImportAttachedKeysCheck->setChecked( reader.readBoolEntry( "AutoImportKeys" ) );
03904 
03905   if ( mdn.hasKey( "default-policy" ) ) {
03906       int num = mdn.readNumEntry( "default-policy" );
03907       if ( num < 0 || num >= mMDNGroup->count() ) num = 0;
03908       mMDNGroup->setButton( num );
03909   }
03910   if ( mdn.hasKey( "quote-message" ) ) {
03911       int num = mdn.readNumEntry( "quote-message" );
03912       if ( num < 0 || num >= mOrigQuoteGroup->count() ) num = 0;
03913       mOrigQuoteGroup->setButton( num );
03914   }
03915   if ( mdn.hasKey( "not-send-when-encrypted" ) )
03916       mNoMDNsWhenEncryptedCheck->setChecked(mdn.readBoolEntry( "not-send-when-encrypted" ));
03917 }
03918 
03919 void SecurityPage::GeneralTab::save() {
03920   KConfigGroup reader( KMKernel::config(), "Reader" );
03921   KConfigGroup mdn( KMKernel::config(), "MDN" );
03922 
03923   if (reader.readBoolEntry( "htmlMail", false ) != mHtmlMailCheck->isChecked())
03924   {
03925     if (KMessageBox::warningContinueCancel(this, i18n("Changing the global "
03926       "HTML setting will override all folder specific values."), QString::null,
03927       KStdGuiItem::cont(), "htmlMailOverride") == KMessageBox::Continue)
03928     {
03929       reader.writeEntry( "htmlMail", mHtmlMailCheck->isChecked() );
03930       QStringList names;
03931       QValueList<QGuardedPtr<KMFolder> > folders;
03932       kmkernel->folderMgr()->createFolderList(&names, &folders);
03933       kmkernel->imapFolderMgr()->createFolderList(&names, &folders);
03934       kmkernel->dimapFolderMgr()->createFolderList(&names, &folders);
03935       kmkernel->searchFolderMgr()->createFolderList(&names, &folders);
03936       for (QValueList<QGuardedPtr<KMFolder> >::iterator it = folders.begin();
03937         it != folders.end(); ++it)
03938       {
03939         if (*it)
03940         {
03941           KConfigGroupSaver saver(KMKernel::config(),
03942             "Folder-" + (*it)->idString());
03943           KMKernel::config()->writeEntry("htmlMailOverride", false);
03944         }
03945       }
03946     }
03947   }
03948   reader.writeEntry( "htmlLoadExternal", mExternalReferences->isChecked() );
03949   reader.writeEntry( "AutoImportKeys", mAutomaticallyImportAttachedKeysCheck->isChecked() );
03950   mdn.writeEntry( "default-policy", mMDNGroup->id( mMDNGroup->selected() ) );
03951   mdn.writeEntry( "quote-message", mOrigQuoteGroup->id( mOrigQuoteGroup->selected() ) );
03952   mdn.writeEntry( "not-send-when-encrypted", mNoMDNsWhenEncryptedCheck->isChecked() );
03953   GlobalSettings::self()->setAlwaysDecrypt( mAlwaysDecrypt->isChecked() );
03954 }
03955 
03956 
03957 QString SecurityPage::ComposerCryptoTab::helpAnchor() const {
03958   return QString::fromLatin1("configure-security-composing");
03959 }
03960 
03961 SecurityPageComposerCryptoTab::SecurityPageComposerCryptoTab( QWidget * parent, const char * name )
03962   : ConfigModuleTab ( parent, name )
03963 {
03964   // the margins are inside mWidget itself
03965   QVBoxLayout* vlay = new QVBoxLayout( this, 0, 0 );
03966 
03967   mWidget = new ComposerCryptoConfiguration( this );
03968   connect( mWidget->mAutoSignature, SIGNAL( toggled(bool) ), this, SLOT( slotEmitChanged() ) );
03969   connect( mWidget->mEncToSelf, SIGNAL( toggled(bool) ), this, SLOT( slotEmitChanged() ) );
03970   connect( mWidget->mShowEncryptionResult, SIGNAL( toggled(bool) ), this, SLOT( slotEmitChanged() ) );
03971   connect( mWidget->mShowKeyApprovalDlg, SIGNAL( toggled(bool) ), this, SLOT( slotEmitChanged() ) );
03972   connect( mWidget->mAutoEncrypt, SIGNAL( toggled(bool) ), this, SLOT( slotEmitChanged() ) );
03973   connect( mWidget->mNeverEncryptWhenSavingInDrafts, SIGNAL( toggled(bool) ), this, SLOT( slotEmitChanged() ) );
03974   connect( mWidget->mStoreEncrypted, SIGNAL( toggled(bool) ), this, SLOT( slotEmitChanged() ) );
03975   vlay->addWidget( mWidget );
03976 }
03977 
03978 void SecurityPage::ComposerCryptoTab::doLoadOther() {
03979   const KConfigGroup composer( KMKernel::config(), "Composer" );
03980 
03981   // If you change default values, sync messagecomposer.cpp too
03982 
03983   mWidget->mAutoSignature->setChecked( composer.readBoolEntry( "pgp-auto-sign", false ) );
03984 
03985   mWidget->mEncToSelf->setChecked( composer.readBoolEntry( "crypto-encrypt-to-self", true ) );
03986   mWidget->mShowEncryptionResult->setChecked( false ); //composer.readBoolEntry( "crypto-show-encryption-result", true ) );
03987   mWidget->mShowEncryptionResult->hide();
03988   mWidget->mShowKeyApprovalDlg->setChecked( composer.readBoolEntry( "crypto-show-keys-for-approval", true ) );
03989 
03990   mWidget->mAutoEncrypt->setChecked( composer.readBoolEntry( "pgp-auto-encrypt", false ) );
03991   mWidget->mNeverEncryptWhenSavingInDrafts->setChecked( composer.readBoolEntry( "never-encrypt-drafts", true ) );
03992 
03993   mWidget->mStoreEncrypted->setChecked( composer.readBoolEntry( "crypto-store-encrypted", true ) );
03994 }
03995 
03996 void SecurityPage::ComposerCryptoTab::installProfile( KConfig * profile ) {
03997   const KConfigGroup composer( profile, "Composer" );
03998 
03999   if ( composer.hasKey( "pgp-auto-sign" ) )
04000     mWidget->mAutoSignature->setChecked( composer.readBoolEntry( "pgp-auto-sign" ) );
04001 
04002   if ( composer.hasKey( "crypto-encrypt-to-self" ) )
04003     mWidget->mEncToSelf->setChecked( composer.readBoolEntry( "crypto-encrypt-to-self" ) );
04004   if ( composer.hasKey( "crypto-show-encryption-result" ) )
04005     mWidget->mShowEncryptionResult->setChecked( composer.readBoolEntry( "crypto-show-encryption-result" ) );
04006   if ( composer.hasKey( "crypto-show-keys-for-approval" ) )
04007     mWidget->mShowKeyApprovalDlg->setChecked( composer.readBoolEntry( "crypto-show-keys-for-approval" ) );
04008   if ( composer.hasKey( "pgp-auto-encrypt" ) )
04009     mWidget->mAutoEncrypt->setChecked( composer.readBoolEntry( "pgp-auto-encrypt" ) );
04010   if ( composer.hasKey( "never-encrypt-drafts" ) )
04011     mWidget->mNeverEncryptWhenSavingInDrafts->setChecked( composer.readBoolEntry( "never-encrypt-drafts" ) );
04012 
04013   if ( composer.hasKey( "crypto-store-encrypted" ) )
04014     mWidget->mStoreEncrypted->setChecked( composer.readBoolEntry( "crypto-store-encrypted" ) );
04015 }
04016 
04017 void SecurityPage::ComposerCryptoTab::save() {
04018   KConfigGroup composer( KMKernel::config(), "Composer" );
04019 
04020   composer.writeEntry( "pgp-auto-sign", mWidget->mAutoSignature->isChecked() );
04021 
04022   composer.writeEntry( "crypto-encrypt-to-self", mWidget->mEncToSelf->isChecked() );
04023   composer.writeEntry( "crypto-show-encryption-result", mWidget->mShowEncryptionResult->isChecked() );
04024   composer.writeEntry( "crypto-show-keys-for-approval", mWidget->mShowKeyApprovalDlg->isChecked() );
04025 
04026   composer.writeEntry( "pgp-auto-encrypt", mWidget->mAutoEncrypt->isChecked() );
04027   composer.writeEntry( "never-encrypt-drafts", mWidget->mNeverEncryptWhenSavingInDrafts->isChecked() );
04028 
04029   composer.writeEntry( "crypto-store-encrypted", mWidget->mStoreEncrypted->isChecked() );
04030 }
04031 
04032 QString SecurityPage::WarningTab::helpAnchor() const {
04033   return QString::fromLatin1("configure-security-warnings");
04034 }
04035 
04036 SecurityPageWarningTab::SecurityPageWarningTab( QWidget * parent, const char * name )
04037   : ConfigModuleTab( parent, name )
04038 {
04039   // the margins are inside mWidget itself
04040   QVBoxLayout* vlay = new QVBoxLayout( this, 0, 0 );
04041 
04042   mWidget = new WarningConfiguration( this );
04043   vlay->addWidget( mWidget );
04044 
04045   connect( mWidget->warnGroupBox, SIGNAL(toggled(bool)), SLOT(slotEmitChanged()) );
04046   connect( mWidget->mWarnUnsigned, SIGNAL(toggled(bool)), SLOT(slotEmitChanged()) );
04047   connect( mWidget->warnUnencryptedCB, SIGNAL(toggled(bool)), SLOT(slotEmitChanged()) );
04048   connect( mWidget->warnReceiverNotInCertificateCB, SIGNAL(toggled(bool)), SLOT(slotEmitChanged()) );
04049   connect( mWidget->mWarnSignKeyExpiresSB, SIGNAL( valueChanged( int ) ), SLOT( slotEmitChanged() ) );
04050   connect( mWidget->mWarnSignChainCertExpiresSB, SIGNAL( valueChanged( int ) ), SLOT( slotEmitChanged() ) );
04051   connect( mWidget->mWarnSignRootCertExpiresSB, SIGNAL( valueChanged( int ) ), SLOT( slotEmitChanged() ) );
04052 
04053   connect( mWidget->mWarnEncrKeyExpiresSB, SIGNAL( valueChanged( int ) ), SLOT( slotEmitChanged() ) );
04054   connect( mWidget->mWarnEncrChainCertExpiresSB, SIGNAL( valueChanged( int ) ), SLOT( slotEmitChanged() ) );
04055   connect( mWidget->mWarnEncrRootCertExpiresSB, SIGNAL( valueChanged( int ) ), SLOT( slotEmitChanged() ) );
04056 
04057   connect( mWidget->enableAllWarningsPB, SIGNAL(clicked()),
04058            SLOT(slotReenableAllWarningsClicked()) );
04059 }
04060 
04061 void SecurityPage::WarningTab::doLoadOther() {
04062   const KConfigGroup composer( KMKernel::config(), "Composer" );
04063 
04064   mWidget->warnUnencryptedCB->setChecked( composer.readBoolEntry( "crypto-warning-unencrypted", false ) );
04065   mWidget->mWarnUnsigned->setChecked( composer.readBoolEntry( "crypto-warning-unsigned", false ) );
04066   mWidget->warnReceiverNotInCertificateCB->setChecked( composer.readBoolEntry( "crypto-warn-recv-not-in-cert", true ) );
04067 
04068   // The "-int" part of the key name is because there used to be a separate boolean
04069   // config entry for enabling/disabling. This is done with the single bool value now.
04070   mWidget->warnGroupBox->setChecked( composer.readBoolEntry( "crypto-warn-when-near-expire", true ) );
04071 
04072   mWidget->mWarnSignKeyExpiresSB->setValue( composer.readNumEntry( "crypto-warn-sign-key-near-expire-int", 14 ) );
04073   mWidget->mWarnSignChainCertExpiresSB->setValue( composer.readNumEntry( "crypto-warn-sign-chaincert-near-expire-int", 14 ) );
04074   mWidget->mWarnSignRootCertExpiresSB->setValue( composer.readNumEntry( "crypto-warn-sign-root-near-expire-int", 14 ) );
04075 
04076   mWidget->mWarnEncrKeyExpiresSB->setValue( composer.readNumEntry( "crypto-warn-encr-key-near-expire-int", 14 ) );
04077   mWidget->mWarnEncrChainCertExpiresSB->setValue( composer.readNumEntry( "crypto-warn-encr-chaincert-near-expire-int", 14 ) );
04078   mWidget->mWarnEncrRootCertExpiresSB->setValue( composer.readNumEntry( "crypto-warn-encr-root-near-expire-int", 14 ) );
04079 
04080   mWidget->enableAllWarningsPB->setEnabled( true );
04081 }
04082 
04083 void SecurityPage::WarningTab::installProfile( KConfig * profile ) {
04084   const KConfigGroup composer( profile, "Composer" );
04085 
04086   if ( composer.hasKey( "crypto-warning-unencrypted" ) )
04087     mWidget->warnUnencryptedCB->setChecked( composer.readBoolEntry( "crypto-warning-unencrypted" ) );
04088   if ( composer.hasKey( "crypto-warning-unsigned" ) )
04089     mWidget->mWarnUnsigned->setChecked( composer.readBoolEntry( "crypto-warning-unsigned" ) );
04090   if ( composer.hasKey( "crypto-warn-recv-not-in-cert" ) )
04091     mWidget->warnReceiverNotInCertificateCB->setChecked( composer.readBoolEntry( "crypto-warn-recv-not-in-cert" ) );
04092 
04093   if ( composer.hasKey( "crypto-warn-when-near-expire" ) )
04094     mWidget->warnGroupBox->setChecked( composer.readBoolEntry( "crypto-warn-when-near-expire" ) );
04095 
04096   if ( composer.hasKey( "crypto-warn-sign-key-near-expire-int" ) )
04097     mWidget->mWarnSignKeyExpiresSB->setValue( composer.readNumEntry( "crypto-warn-sign-key-near-expire-int" ) );
04098   if ( composer.hasKey( "crypto-warn-sign-chaincert-near-expire-int" ) )
04099     mWidget->mWarnSignChainCertExpiresSB->setValue( composer.readNumEntry( "crypto-warn-sign-chaincert-near-expire-int" ) );
04100   if ( composer.hasKey( "crypto-warn-sign-root-near-expire-int" ) )
04101     mWidget->mWarnSignRootCertExpiresSB->setValue( composer.readNumEntry( "crypto-warn-sign-root-near-expire-int" ) );
04102 
04103   if ( composer.hasKey( "crypto-warn-encr-key-near-expire-int" ) )
04104     mWidget->mWarnEncrKeyExpiresSB->setValue( composer.readNumEntry( "crypto-warn-encr-key-near-expire-int" ) );
04105   if ( composer.hasKey( "crypto-warn-encr-chaincert-near-expire-int" ) )
04106     mWidget->mWarnEncrChainCertExpiresSB->setValue( composer.readNumEntry( "crypto-warn-encr-chaincert-near-expire-int" ) );
04107   if ( composer.hasKey( "crypto-warn-encr-root-near-expire-int" ) )
04108     mWidget->mWarnEncrRootCertExpiresSB->setValue( composer.readNumEntry( "crypto-warn-encr-root-near-expire-int" ) );
04109 }
04110 
04111 void SecurityPage::WarningTab::save() {
04112   KConfigGroup composer( KMKernel::config(), "Composer" );
04113 
04114   composer.writeEntry( "crypto-warn-recv-not-in-cert", mWidget->warnReceiverNotInCertificateCB->isChecked() );
04115   composer.writeEntry( "crypto-warning-unencrypted", mWidget->warnUnencryptedCB->isChecked() );
04116   composer.writeEntry( "crypto-warning-unsigned", mWidget->mWarnUnsigned->isChecked() );
04117 
04118   composer.writeEntry( "crypto-warn-when-near-expire", mWidget->warnGroupBox->isChecked() );
04119   composer.writeEntry( "crypto-warn-sign-key-near-expire-int",
04120                        mWidget->mWarnSignKeyExpiresSB->value() );
04121   composer.writeEntry( "crypto-warn-sign-chaincert-near-expire-int",
04122                        mWidget->mWarnSignChainCertExpiresSB->value() );
04123   composer.writeEntry( "crypto-warn-sign-root-near-expire-int",
04124                        mWidget->mWarnSignRootCertExpiresSB->value() );
04125 
04126   composer.writeEntry( "crypto-warn-encr-key-near-expire-int",
04127                        mWidget->mWarnEncrKeyExpiresSB->value() );
04128   composer.writeEntry( "crypto-warn-encr-chaincert-near-expire-int",
04129                        mWidget->mWarnEncrChainCertExpiresSB->value() );
04130   composer.writeEntry( "crypto-warn-encr-root-near-expire-int",
04131                        mWidget->mWarnEncrRootCertExpiresSB->value() );
04132 }
04133 
04134 void SecurityPage::WarningTab::slotReenableAllWarningsClicked() {
04135   KMessageBox::enableAllMessages();
04136   mWidget->enableAllWarningsPB->setEnabled( false );
04137 }
04138 
04140 
04141 QString SecurityPage::SMimeTab::helpAnchor() const {
04142   return QString::fromLatin1("configure-security-smime-validation");
04143 }
04144 
04145 SecurityPageSMimeTab::SecurityPageSMimeTab( QWidget * parent, const char * name )
04146   : ConfigModuleTab( parent, name )
04147 {
04148   // the margins are inside mWidget itself
04149   QVBoxLayout* vlay = new QVBoxLayout( this, 0, 0 );
04150 
04151   mWidget = new SMimeConfiguration( this );
04152   vlay->addWidget( mWidget );
04153 
04154   // Button-group for exclusive radiobuttons
04155   QButtonGroup* bg = new QButtonGroup( mWidget );
04156   bg->hide();
04157   bg->insert( mWidget->CRLRB );
04158   bg->insert( mWidget->OCSPRB );
04159 
04160   // Settings for the keyrequester custom widget
04161   mWidget->OCSPResponderSignature->setAllowedKeys(
04162      Kleo::KeySelectionDialog::SMIMEKeys
04163      | Kleo::KeySelectionDialog::TrustedKeys
04164      | Kleo::KeySelectionDialog::ValidKeys
04165      | Kleo::KeySelectionDialog::SigningKeys
04166      | Kleo::KeySelectionDialog::PublicKeys );
04167   mWidget->OCSPResponderSignature->setMultipleKeysEnabled( false );
04168 
04169   mConfig = Kleo::CryptoBackendFactory::instance()->config();
04170 
04171   connect( mWidget->CRLRB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
04172   connect( mWidget->OCSPRB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
04173   connect( mWidget->OCSPResponderURL, SIGNAL( textChanged( const QString& ) ), this, SLOT( slotEmitChanged() ) );
04174   connect( mWidget->OCSPResponderSignature, SIGNAL( changed() ), this, SLOT( slotEmitChanged() ) );
04175   connect( mWidget->doNotCheckCertPolicyCB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
04176   connect( mWidget->neverConsultCB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
04177   connect( mWidget->fetchMissingCB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
04178 
04179   connect( mWidget->ignoreServiceURLCB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
04180   connect( mWidget->ignoreHTTPDPCB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
04181   connect( mWidget->disableHTTPCB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
04182   connect( mWidget->honorHTTPProxyRB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
04183   connect( mWidget->useCustomHTTPProxyRB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
04184   connect( mWidget->customHTTPProxy, SIGNAL( textChanged( const QString& ) ), this, SLOT( slotEmitChanged() ) );
04185   connect( mWidget->ignoreLDAPDPCB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
04186   connect( mWidget->disableLDAPCB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
04187   connect( mWidget->customLDAPProxy, SIGNAL( textChanged( const QString& ) ), this, SLOT( slotEmitChanged() ) );
04188 
04189   connect( mWidget->disableHTTPCB, SIGNAL( toggled( bool ) ),
04190            this, SLOT( slotUpdateHTTPActions() ) );
04191   connect( mWidget->ignoreHTTPDPCB, SIGNAL( toggled( bool ) ),
04192            this, SLOT( slotUpdateHTTPActions() ) );
04193 
04194   // Button-group for exclusive radiobuttons
04195   QButtonGroup* bgHTTPProxy = new QButtonGroup( mWidget );
04196   bgHTTPProxy->hide();
04197   bgHTTPProxy->insert( mWidget->honorHTTPProxyRB );
04198   bgHTTPProxy->insert( mWidget->useCustomHTTPProxyRB );
04199 
04200   if ( !connectDCOPSignal( 0, "KPIM::CryptoConfig", "changed()",
04201                            "load()", false ) )
04202     kdError(5650) << "SecurityPageSMimeTab: connection to CryptoConfig's changed() failed" << endl;
04203 
04204 }
04205 
04206 SecurityPageSMimeTab::~SecurityPageSMimeTab()
04207 {
04208 }
04209 
04210 static void disableDirmngrWidget( QWidget* w ) {
04211   w->setEnabled( false );
04212   QWhatsThis::remove( w );
04213   QWhatsThis::add( w, i18n( "This option requires dirmngr >= 0.9.0" ) );
04214 }
04215 
04216 static void initializeDirmngrCheckbox( QCheckBox* cb, Kleo::CryptoConfigEntry* entry ) {
04217   if ( entry )
04218     cb->setChecked( entry->boolValue() );
04219   else
04220     disableDirmngrWidget( cb );
04221 }
04222 
04223 struct SMIMECryptoConfigEntries {
04224   SMIMECryptoConfigEntries( Kleo::CryptoConfig* config )
04225     : mConfig( config ) {
04226 
04227     // Checkboxes
04228     mCheckUsingOCSPConfigEntry = configEntry( "gpgsm", "Security", "enable-ocsp", Kleo::CryptoConfigEntry::ArgType_None, false );
04229     mEnableOCSPsendingConfigEntry = configEntry( "dirmngr", "OCSP", "allow-ocsp", Kleo::CryptoConfigEntry::ArgType_None, false );
04230     mDoNotCheckCertPolicyConfigEntry = configEntry( "gpgsm", "Security", "disable-policy-checks", Kleo::CryptoConfigEntry::ArgType_None, false );
04231     mNeverConsultConfigEntry = configEntry( "gpgsm", "Security", "disable-crl-checks", Kleo::CryptoConfigEntry::ArgType_None, false );
04232     mFetchMissingConfigEntry = configEntry( "gpgsm", "Security", "auto-issuer-key-retrieve", Kleo::CryptoConfigEntry::ArgType_None, false );
04233     // dirmngr-0.9.0 options
04234     mIgnoreServiceURLEntry = configEntry( "dirmngr", "OCSP", "ignore-ocsp-service-url", Kleo::CryptoConfigEntry::ArgType_None, false );
04235     mIgnoreHTTPDPEntry = configEntry( "dirmngr", "HTTP", "ignore-http-dp", Kleo::CryptoConfigEntry::ArgType_None, false );
04236     mDisableHTTPEntry = configEntry( "dirmngr", "HTTP", "disable-http", Kleo::CryptoConfigEntry::ArgType_None, false );
04237     mHonorHTTPProxy = configEntry( "dirmngr", "HTTP", "honor-http-proxy", Kleo::CryptoConfigEntry::ArgType_None, false );
04238 
04239     mIgnoreLDAPDPEntry = configEntry( "dirmngr", "LDAP", "ignore-ldap-dp", Kleo::CryptoConfigEntry::ArgType_None, false );
04240     mDisableLDAPEntry = configEntry( "dirmngr", "LDAP", "disable-ldap", Kleo::CryptoConfigEntry::ArgType_None, false );
04241     // Other widgets
04242     mOCSPResponderURLConfigEntry = configEntry( "dirmngr", "OCSP", "ocsp-responder", Kleo::CryptoConfigEntry::ArgType_String, false );
04243     mOCSPResponderSignature = configEntry( "dirmngr", "OCSP", "ocsp-signer", Kleo::CryptoConfigEntry::ArgType_String, false );
04244     mCustomHTTPProxy = configEntry( "dirmngr", "HTTP", "http-proxy", Kleo::CryptoConfigEntry::ArgType_String, false );
04245     mCustomLDAPProxy = configEntry( "dirmngr", "LDAP", "ldap-proxy", Kleo::CryptoConfigEntry::ArgType_String, false );
04246   }
04247 
04248   Kleo::CryptoConfigEntry* configEntry( const char* componentName,
04249                                         const char* groupName,
04250                                         const char* entryName,
04251                                         int argType,
04252                                         bool isList );
04253 
04254   // Checkboxes
04255   Kleo::CryptoConfigEntry* mCheckUsingOCSPConfigEntry;
04256   Kleo::CryptoConfigEntry* mEnableOCSPsendingConfigEntry;
04257   Kleo::CryptoConfigEntry* mDoNotCheckCertPolicyConfigEntry;
04258   Kleo::CryptoConfigEntry* mNeverConsultConfigEntry;
04259   Kleo::CryptoConfigEntry* mFetchMissingConfigEntry;
04260   Kleo::CryptoConfigEntry* mIgnoreServiceURLEntry;
04261   Kleo::CryptoConfigEntry* mIgnoreHTTPDPEntry;
04262   Kleo::CryptoConfigEntry* mDisableHTTPEntry;
04263   Kleo::CryptoConfigEntry* mHonorHTTPProxy;
04264   Kleo::CryptoConfigEntry* mIgnoreLDAPDPEntry;
04265   Kleo::CryptoConfigEntry* mDisableLDAPEntry;
04266   // Other widgets
04267   Kleo::CryptoConfigEntry* mOCSPResponderURLConfigEntry;
04268   Kleo::CryptoConfigEntry* mOCSPResponderSignature;
04269   Kleo::CryptoConfigEntry* mCustomHTTPProxy;
04270   Kleo::CryptoConfigEntry* mCustomLDAPProxy;
04271 
04272   Kleo::CryptoConfig* mConfig;
04273 };
04274 
04275 void SecurityPage::SMimeTab::doLoadOther() {
04276   if ( !mConfig ) {
04277     setEnabled( false );
04278     return;
04279   }
04280 
04281   // Force re-parsing gpgconf data, in case e.g. kleopatra or "configure backend" was used
04282   // (which ends up calling us via dcop)
04283   mConfig->clear();
04284 
04285   // Create config entries
04286   // Don't keep them around, they'll get deleted by clear(), which could be
04287   // done by the "configure backend" button even before we save().
04288   SMIMECryptoConfigEntries e( mConfig );
04289 
04290   // Initialize GUI items from the config entries
04291 
04292   if ( e.mCheckUsingOCSPConfigEntry ) {
04293     bool b = e.mCheckUsingOCSPConfigEntry->boolValue();
04294     mWidget->OCSPRB->setChecked( b );
04295     mWidget->CRLRB->setChecked( !b );
04296     mWidget->OCSPGroupBox->setEnabled( b );
04297   } else {
04298     mWidget->OCSPGroupBox->setEnabled( false );
04299   }
04300   if ( e.mDoNotCheckCertPolicyConfigEntry )
04301     mWidget->doNotCheckCertPolicyCB->setChecked( e.mDoNotCheckCertPolicyConfigEntry->boolValue() );
04302   if ( e.mNeverConsultConfigEntry )
04303     mWidget->neverConsultCB->setChecked( e.mNeverConsultConfigEntry->boolValue() );
04304   if ( e.mFetchMissingConfigEntry )
04305     mWidget->fetchMissingCB->setChecked( e.mFetchMissingConfigEntry->boolValue() );
04306 
04307   if ( e.mOCSPResponderURLConfigEntry )
04308     mWidget->OCSPResponderURL->setText( e.mOCSPResponderURLConfigEntry->stringValue() );
04309   if ( e.mOCSPResponderSignature ) {
04310     mWidget->OCSPResponderSignature->setFingerprint( e.mOCSPResponderSignature->stringValue() );
04311   }
04312 
04313   // dirmngr-0.9.0 options
04314   initializeDirmngrCheckbox( mWidget->ignoreServiceURLCB, e.mIgnoreServiceURLEntry );
04315   initializeDirmngrCheckbox( mWidget->ignoreHTTPDPCB, e.mIgnoreHTTPDPEntry );
04316   initializeDirmngrCheckbox( mWidget->disableHTTPCB, e.mDisableHTTPEntry );
04317   initializeDirmngrCheckbox( mWidget->ignoreLDAPDPCB, e.mIgnoreLDAPDPEntry );
04318   initializeDirmngrCheckbox( mWidget->disableLDAPCB, e.mDisableLDAPEntry );
04319   if ( e.mCustomHTTPProxy ) {
04320     QString systemProxy = QString::fromLocal8Bit( getenv( "http_proxy" ) );
04321     if ( systemProxy.isEmpty() )
04322       systemProxy = i18n( "no proxy" );
04323     mWidget->systemHTTPProxy->setText( i18n( "(Current system setting: %1)" ).arg( systemProxy ) );
04324     bool honor = e.mHonorHTTPProxy && e.mHonorHTTPProxy->boolValue();
04325     mWidget->honorHTTPProxyRB->setChecked( honor );
04326     mWidget->useCustomHTTPProxyRB->setChecked( !honor );
04327     mWidget->customHTTPProxy->setText( e.mCustomHTTPProxy->stringValue() );
04328   } else {
04329     disableDirmngrWidget( mWidget->honorHTTPProxyRB );
04330     disableDirmngrWidget( mWidget->useCustomHTTPProxyRB );
04331     disableDirmngrWidget( mWidget->systemHTTPProxy );
04332     disableDirmngrWidget( mWidget->customHTTPProxy );
04333   }
04334   if ( e.mCustomLDAPProxy )
04335     mWidget->customLDAPProxy->setText( e.mCustomLDAPProxy->stringValue() );
04336   else {
04337     disableDirmngrWidget( mWidget->customLDAPProxy );
04338     disableDirmngrWidget( mWidget->customLDAPLabel );
04339   }
04340   slotUpdateHTTPActions();
04341 }
04342 
04343 void SecurityPage::SMimeTab::slotUpdateHTTPActions() {
04344   mWidget->ignoreHTTPDPCB->setEnabled( !mWidget->disableHTTPCB->isChecked() );
04345 
04346   // The proxy settings only make sense when "Ignore HTTP CRL DPs of certificate" is checked.
04347   bool enableProxySettings = !mWidget->disableHTTPCB->isChecked()
04348                           && mWidget->ignoreHTTPDPCB->isChecked();
04349   mWidget->systemHTTPProxy->setEnabled( enableProxySettings );
04350   mWidget->useCustomHTTPProxyRB->setEnabled( enableProxySettings );
04351   mWidget->honorHTTPProxyRB->setEnabled( enableProxySettings );
04352   mWidget->customHTTPProxy->setEnabled( enableProxySettings );
04353 }
04354 
04355 void SecurityPage::SMimeTab::installProfile( KConfig * ) {
04356 }
04357 
04358 static void saveCheckBoxToKleoEntry( QCheckBox* cb, Kleo::CryptoConfigEntry* entry ) {
04359   const bool b = cb->isChecked();
04360   if ( entry && entry->boolValue() != b )
04361     entry->setBoolValue( b );
04362 }
04363 
04364 void SecurityPage::SMimeTab::save() {
04365   if ( !mConfig ) {
04366     return;
04367   }
04368   // Create config entries
04369   // Don't keep them around, they'll get deleted by clear(), which could be done by the
04370   // "configure backend" button.
04371   SMIMECryptoConfigEntries e( mConfig );
04372 
04373   bool b = mWidget->OCSPRB->isChecked();
04374   if ( e.mCheckUsingOCSPConfigEntry && e.mCheckUsingOCSPConfigEntry->boolValue() != b )
04375     e.mCheckUsingOCSPConfigEntry->setBoolValue( b );
04376   // Set allow-ocsp together with enable-ocsp
04377   if ( e.mEnableOCSPsendingConfigEntry && e.mEnableOCSPsendingConfigEntry->boolValue() != b )
04378     e.mEnableOCSPsendingConfigEntry->setBoolValue( b );
04379 
04380   saveCheckBoxToKleoEntry( mWidget->doNotCheckCertPolicyCB, e.mDoNotCheckCertPolicyConfigEntry );
04381   saveCheckBoxToKleoEntry( mWidget->neverConsultCB, e.mNeverConsultConfigEntry );
04382   saveCheckBoxToKleoEntry( mWidget->fetchMissingCB, e.mFetchMissingConfigEntry );
04383 
04384   QString txt = mWidget->OCSPResponderURL->text();
04385   if ( e.mOCSPResponderURLConfigEntry && e.mOCSPResponderURLConfigEntry->stringValue() != txt )
04386     e.mOCSPResponderURLConfigEntry->setStringValue( txt );
04387 
04388   txt = mWidget->OCSPResponderSignature->fingerprint();
04389   if ( e.mOCSPResponderSignature && e.mOCSPResponderSignature->stringValue() != txt ) {
04390     e.mOCSPResponderSignature->setStringValue( txt );
04391   }
04392 
04393   //dirmngr-0.9.0 options
04394   saveCheckBoxToKleoEntry( mWidget->ignoreServiceURLCB, e.mIgnoreServiceURLEntry );
04395   saveCheckBoxToKleoEntry( mWidget->ignoreHTTPDPCB, e.mIgnoreHTTPDPEntry );
04396   saveCheckBoxToKleoEntry( mWidget->disableHTTPCB, e.mDisableHTTPEntry );
04397   saveCheckBoxToKleoEntry( mWidget->ignoreLDAPDPCB, e.mIgnoreLDAPDPEntry );
04398   saveCheckBoxToKleoEntry( mWidget->disableLDAPCB, e.mDisableLDAPEntry );
04399   if ( e.mCustomHTTPProxy ) {
04400     const bool honor = mWidget->honorHTTPProxyRB->isChecked();
04401     if ( e.mHonorHTTPProxy && e.mHonorHTTPProxy->boolValue() != honor )
04402         e.mHonorHTTPProxy->setBoolValue( honor );
04403 
04404     QString chosenProxy = mWidget->customHTTPProxy->text();
04405     if ( chosenProxy != e.mCustomHTTPProxy->stringValue() )
04406       e.mCustomHTTPProxy->setStringValue( chosenProxy );
04407   }
04408   txt = mWidget->customLDAPProxy->text();
04409   if ( e.mCustomLDAPProxy && e.mCustomLDAPProxy->stringValue() != txt )
04410     e.mCustomLDAPProxy->setStringValue( mWidget->customLDAPProxy->text() );
04411 
04412   mConfig->sync( true );
04413 }
04414 
04415 bool SecurityPageSMimeTab::process(const QCString &fun, const QByteArray &data, QCString& replyType, QByteArray &replyData)
04416 {
04417     if ( fun == "load()" ) {
04418         replyType = "void";
04419         load();
04420     } else {
04421         return DCOPObject::process( fun, data, replyType, replyData );
04422     }
04423     return true;
04424 }
04425 
04426 QCStringList SecurityPageSMimeTab::interfaces()
04427 {
04428   QCStringList ifaces = DCOPObject::interfaces();
04429   ifaces += "SecurityPageSMimeTab";
04430   return ifaces;
04431 }
04432 
04433 QCStringList SecurityPageSMimeTab::functions()
04434 {
04435   // Hide our slot, just because it's simpler to do so.
04436   return DCOPObject::functions();
04437 }
04438 
04439 Kleo::CryptoConfigEntry* SMIMECryptoConfigEntries::configEntry( const char* componentName,
04440                                                                 const char* groupName,
04441                                                                 const char* entryName,
04442                                                                 int /*Kleo::CryptoConfigEntry::ArgType*/ argType,
04443                                                                 bool isList )
04444 {
04445     Kleo::CryptoConfigEntry* entry = mConfig->entry( componentName, groupName, entryName );
04446     if ( !entry ) {
04447         kdWarning(5006) << QString( "Backend error: gpgconf doesn't seem to know the entry for %1/%2/%3" ).arg( componentName, groupName, entryName ) << endl;
04448         return 0;
04449     }
04450     if( entry->argType() != argType || entry->isList() != isList ) {
04451         kdWarning(5006) << QString( "Backend error: gpgconf has wrong type for %1/%2/%3: %4 %5" ).arg( componentName, groupName, entryName ).arg( entry->argType() ).arg( entry->isList() ) << endl;
04452         return 0;
04453     }
04454     return entry;
04455 }
04456 
04458 
04459 QString SecurityPage::CryptPlugTab::helpAnchor() const {
04460   return QString::fromLatin1("configure-security-crypto-backends");
04461 }
04462 
04463 SecurityPageCryptPlugTab::SecurityPageCryptPlugTab( QWidget * parent, const char * name )
04464   : ConfigModuleTab( parent, name )
04465 {
04466   QVBoxLayout * vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
04467 
04468   mBackendConfig = Kleo::CryptoBackendFactory::instance()->configWidget( this, "mBackendConfig" );
04469   connect( mBackendConfig, SIGNAL( changed( bool ) ), this, SIGNAL( changed( bool ) ) );
04470 
04471   vlay->addWidget( mBackendConfig );
04472 }
04473 
04474 SecurityPageCryptPlugTab::~SecurityPageCryptPlugTab()
04475 {
04476 
04477 }
04478 
04479 void SecurityPage::CryptPlugTab::doLoadOther() {
04480   mBackendConfig->load();
04481 }
04482 
04483 void SecurityPage::CryptPlugTab::save() {
04484   mBackendConfig->save();
04485 }
04486 
04487 // *************************************************************
04488 // *                                                           *
04489 // *                        MiscPage                           *
04490 // *                                                           *
04491 // *************************************************************
04492 QString MiscPage::helpAnchor() const {
04493   return QString::fromLatin1("configure-misc");
04494 }
04495 
04496 MiscPage::MiscPage( QWidget * parent, const char * name )
04497   : ConfigModuleWithTabs( parent, name )
04498 {
04499   mFolderTab = new FolderTab();
04500   addTab( mFolderTab, i18n("&Folders") );
04501 
04502   mGroupwareTab = new GroupwareTab();
04503   addTab( mGroupwareTab, i18n("&Groupware") );
04504   load();
04505 }
04506 
04507 QString MiscPage::FolderTab::helpAnchor() const {
04508   return QString::fromLatin1("configure-misc-folders");
04509 }
04510 
04511 MiscPageFolderTab::MiscPageFolderTab( QWidget * parent, const char * name )
04512   : ConfigModuleTab( parent, name )
04513 {
04514   // temp. vars:
04515   QVBoxLayout *vlay;
04516   QHBoxLayout *hlay;
04517   QLabel      *label;
04518 
04519   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
04520 
04521   // "confirm before emptying folder" check box: stretch 0
04522   mEmptyFolderConfirmCheck =
04523     new QCheckBox( i18n("Corresponds to Folder->Move All Messages to Trash",
04524                         "Ask for co&nfirmation before moving all messages to "
04525                         "trash"),
04526                    this );
04527   vlay->addWidget( mEmptyFolderConfirmCheck );
04528   connect( mEmptyFolderConfirmCheck, SIGNAL( stateChanged( int ) ),
04529            this, SLOT( slotEmitChanged( void ) ) );
04530   mExcludeImportantFromExpiry =
04531     new QCheckBox( i18n("E&xclude important messages from expiry"), this );
04532   vlay->addWidget( mExcludeImportantFromExpiry );
04533   connect( mExcludeImportantFromExpiry, SIGNAL( stateChanged( int ) ),
04534            this, SLOT( slotEmitChanged( void ) ) );
04535 
04536   // "when trying to find unread messages" combo + label: stretch 0
04537   hlay = new QHBoxLayout( vlay ); // inherits spacing
04538   mLoopOnGotoUnread = new QComboBox( false, this );
04539   label = new QLabel( mLoopOnGotoUnread,
04540            i18n("to be continued with \"do not loop\", \"loop in current folder\", "
04541                 "and \"loop in all folders\".",
04542                 "When trying to find unread messages:"), this );
04543   mLoopOnGotoUnread->insertStringList( QStringList()
04544       << i18n("continuation of \"When trying to find unread messages:\"",
04545               "Do not Loop")
04546       << i18n("continuation of \"When trying to find unread messages:\"",
04547               "Loop in Current Folder")
04548       << i18n("continuation of \"When trying to find unread messages:\"",
04549               "Loop in All Folders"));
04550   hlay->addWidget( label );
04551   hlay->addWidget( mLoopOnGotoUnread, 1 );
04552   connect( mLoopOnGotoUnread, SIGNAL( activated( int ) ),
04553            this, SLOT( slotEmitChanged( void ) ) );
04554 
04555   // when entering a folder
04556   hlay = new QHBoxLayout( vlay ); // inherits spacing
04557   mActionEnterFolder = new QComboBox( false, this );
04558   label = new QLabel( mActionEnterFolder,
04559            i18n("to be continued with \"jump to first new message\", "
04560                 "\"jump to first unread or new message\","
04561                 "and \"jump to last selected message\".",
04562                 "When entering a folder:"), this );
04563   mActionEnterFolder->insertStringList( QStringList()
04564       << i18n("continuation of \"When entering a folder:\"",
04565               "Jump to First New Message")
04566       << i18n("continuation of \"When entering a folder:\"",
04567               "Jump to First Unread or New Message")
04568       << i18n("continuation of \"When entering a folder:\"",
04569               "Jump to Last Selected Message"));
04570   hlay->addWidget( label );
04571   hlay->addWidget( mActionEnterFolder, 1 );
04572   connect( mActionEnterFolder, SIGNAL( activated( int ) ),
04573            this, SLOT( slotEmitChanged( void ) ) );
04574 
04575   hlay = new QHBoxLayout( vlay ); // inherits spacing
04576   mDelayedMarkAsRead = new QCheckBox( i18n("Mar&k selected message as read after"), this );
04577   hlay->addWidget( mDelayedMarkAsRead );
04578   mDelayedMarkTime = new KIntSpinBox( 0 /*min*/, 60 /*max*/, 1/*step*/,
04579                                       0 /*init*/, 10 /*base*/, this);
04580   mDelayedMarkTime->setSuffix( i18n(" sec") );
04581   mDelayedMarkTime->setEnabled( false ); // since mDelayedMarkAsREad is off
04582   hlay->addWidget( mDelayedMarkTime );
04583   hlay->addStretch( 1 );
04584   connect( mDelayedMarkTime, SIGNAL( valueChanged( int ) ),
04585            this, SLOT( slotEmitChanged( void ) ) );
04586   connect( mDelayedMarkAsRead, SIGNAL(toggled(bool)),
04587            mDelayedMarkTime, SLOT(setEnabled(bool)));
04588   connect( mDelayedMarkAsRead, SIGNAL(toggled(bool)),
04589            this , SLOT(slotEmitChanged( void )));
04590 
04591   // "show popup after Drag'n'Drop" checkbox: stretch 0
04592   mShowPopupAfterDnD =
04593     new QCheckBox( i18n("Ask for action after &dragging messages to another folder"), this );
04594   vlay->addWidget( mShowPopupAfterDnD );
04595   connect( mShowPopupAfterDnD, SIGNAL( stateChanged( int ) ),
04596            this, SLOT( slotEmitChanged( void ) ) );
04597 
04598   // "default mailbox format" combo + label: stretch 0
04599   hlay = new QHBoxLayout( vlay ); // inherits spacing
04600   mMailboxPrefCombo = new QComboBox( false, this );
04601   label = new QLabel( mMailboxPrefCombo,
04602                       i18n("to be continued with \"flat files\" and "
04603                            "\"directories\", resp.",
04604                            "By default, &message folders on disk are:"), this );
04605   mMailboxPrefCombo->insertStringList( QStringList()
04606           << i18n("continuation of \"By default, &message folders on disk are\"",
04607                   "Flat Files (\"mbox\" format)")
04608           << i18n("continuation of \"By default, &message folders on disk are\"",
04609                   "Directories (\"maildir\" format)") );
04610   // and now: add QWhatsThis:
04611   QString msg = i18n( "what's this help",
04612                       "<qt><p>This selects which mailbox format will be "
04613                       "the default for local folders:</p>"
04614                       "<p><b>mbox:</b> KMail's mail "
04615                       "folders are represented by a single file each. "
04616                       "Individual messages are separated from each other by a "
04617                       "line starting with \"From \". This saves space on "
04618                       "disk, but may be less robust, e.g. when moving messages "
04619                       "between folders.</p>"
04620                       "<p><b>maildir:</b> KMail's mail folders are "
04621                       "represented by real folders on disk. Individual messages "
04622                       "are separate files. This may waste a bit of space on "
04623                       "disk, but should be more robust, e.g. when moving "
04624                       "messages between folders.</p></qt>");
04625   QWhatsThis::add( mMailboxPrefCombo, msg );
04626   QWhatsThis::add( label, msg );
04627   hlay->addWidget( label );
04628   hlay->addWidget( mMailboxPrefCombo, 1 );
04629   connect( mMailboxPrefCombo, SIGNAL( activated( int ) ),
04630            this, SLOT( slotEmitChanged( void ) ) );
04631 
04632   // "On startup..." option:
04633   hlay = new QHBoxLayout( vlay ); // inherits spacing
04634   mOnStartupOpenFolder = new FolderRequester( this,
04635       kmkernel->getKMMainWidget()->folderTree() );
04636   label = new QLabel( mOnStartupOpenFolder,
04637                       i18n("Open this folder on startup:"), this );
04638   hlay->addWidget( label );
04639   hlay->addWidget( mOnStartupOpenFolder, 1 );
04640   connect( mOnStartupOpenFolder, SIGNAL( folderChanged( KMFolder* ) ),
04641            this, SLOT( slotEmitChanged( void ) ) );
04642 
04643   // "Empty &trash on program exit" option:
04644   hlay = new QHBoxLayout( vlay ); // inherits spacing
04645   mEmptyTrashCheck = new QCheckBox( i18n("Empty local &trash folder on program exit"),
04646                                     this );
04647   hlay->addWidget( mEmptyTrashCheck );
04648   connect( mEmptyTrashCheck, SIGNAL( stateChanged( int ) ),
04649            this, SLOT( slotEmitChanged( void ) ) );
04650 
04651 #ifdef HAVE_INDEXLIB
04652   // indexing enabled option:
04653   mIndexingEnabled = new QCheckBox( i18n("Enable full text &indexing"), this );
04654   vlay->addWidget( mIndexingEnabled );
04655   connect( mIndexingEnabled, SIGNAL( stateChanged( int ) ),
04656            this, SLOT( slotEmitChanged( void ) ) );
04657 #endif
04658 
04659   // "Quota Units"
04660   hlay = new QHBoxLayout( vlay ); // inherits spacing
04661   mQuotaCmbBox = new QComboBox( false, this );
04662   label = new QLabel( mQuotaCmbBox,
04663                       i18n("Quota units: "), this );
04664   mQuotaCmbBox->insertStringList( QStringList()
04665                    << i18n("KB")
04666                    << i18n("MB")
04667                    << i18n("GB") );
04668   hlay->addWidget( label );
04669   hlay->addWidget( mQuotaCmbBox, 1 );
04670   connect( mQuotaCmbBox, SIGNAL( activated( int )  ), this, SLOT( slotEmitChanged( void ) ) );
04671 
04672   vlay->addStretch( 1 );
04673 
04674   // @TODO: Till, move into .kcgc file
04675   msg = i18n( "what's this help",
04676             "<qt><p>When jumping to the next unread message, it may occur "
04677             "that no more unread messages are below the current message.</p>"
04678             "<p><b>Do not loop:</b> The search will stop at the last message in "
04679             "the current folder.</p>"
04680             "<p><b>Loop in current folder:</b> The search will continue at the "
04681             "top of the message list, but not go to another folder.</p>"
04682             "<p><b>Loop in all folders:</b> The search will continue at the top of "
04683             "the message list. If no unread messages are found it will then continue "
04684             "to the next folder.</p>"
04685             "<p>Similarly, when searching for the previous unread message, "
04686             "the search will start from the bottom of the message list and continue to "
04687             "the previous folder depending on which option is selected.</p></qt>" );
04688   QWhatsThis::add( mLoopOnGotoUnread, msg );
04689 
04690 #ifdef HAVE_INDEXLIB
04691  // this is probably overly pessimistic
04692   msg = i18n( "what's this help",
04693           "<qt><p>Full text indexing allows very fast searches on the content "
04694           "of your messages. When enabled, the search dialog will work very fast. "
04695           "Also, the search tool bar will select messages based on content.</p>"
04696           "<p>It takes up a certain amount of disk space "
04697           "(about half the disk space for the messages).</p>"
04698           "<p>After enabling, the index will need to be built, but you can continue to use KMail "
04699           "while this operation is running.</p>"
04700           "</qt>"
04701         );
04702 
04703   QWhatsThis::add( mIndexingEnabled, msg );
04704 #endif
04705 }
04706 
04707 void MiscPage::FolderTab::doLoadFromGlobalSettings() {
04708   mExcludeImportantFromExpiry->setChecked( GlobalSettings::self()->excludeImportantMailFromExpiry() );
04709   // default = "Loop in current folder"
04710   mLoopOnGotoUnread->setCurrentItem( GlobalSettings::self()->loopOnGotoUnread() );
04711   mActionEnterFolder->setCurrentItem( GlobalSettings::self()->actionEnterFolder() );
04712   mDelayedMarkAsRead->setChecked( GlobalSettings::self()->delayedMarkAsRead() );
04713   mDelayedMarkTime->setValue( GlobalSettings::self()->delayedMarkTime() );
04714   mShowPopupAfterDnD->setChecked( GlobalSettings::self()->showPopupAfterDnD() );
04715   mQuotaCmbBox->setCurrentItem( GlobalSettings::self()->quotaUnit() );
04716 }
04717 
04718 void MiscPage::FolderTab::doLoadOther() {
04719   KConfigGroup general( KMKernel::config(), "General" );
04720 
04721   mEmptyTrashCheck->setChecked( general.readBoolEntry( "empty-trash-on-exit", true ) );
04722   mOnStartupOpenFolder->setFolder( general.readEntry( "startupFolder",
04723                                                   kmkernel->inboxFolder()->idString() ) );
04724   mEmptyFolderConfirmCheck->setChecked( general.readBoolEntry( "confirm-before-empty", true ) );
04725 
04726   int num = general.readNumEntry("default-mailbox-format", 1 );
04727   if ( num < 0 || num > 1 ) num = 1;
04728   mMailboxPrefCombo->setCurrentItem( num );
04729 
04730 #ifdef HAVE_INDEXLIB
04731   mIndexingEnabled->setChecked( kmkernel->msgIndex() && kmkernel->msgIndex()->isEnabled() );
04732 #endif
04733 }
04734 
04735 void MiscPage::FolderTab::save() {
04736   KConfigGroup general( KMKernel::config(), "General" );
04737 
04738   general.writeEntry( "empty-trash-on-exit", mEmptyTrashCheck->isChecked() );
04739   general.writeEntry( "confirm-before-empty", mEmptyFolderConfirmCheck->isChecked() );
04740   general.writeEntry( "default-mailbox-format", mMailboxPrefCombo->currentItem() );
04741   general.writeEntry( "startupFolder", mOnStartupOpenFolder->folder() ?
04742                                   mOnStartupOpenFolder->folder()->idString() : QString::null );
04743 
04744   GlobalSettings::self()->setDelayedMarkAsRead( mDelayedMarkAsRead->isChecked() );
04745   GlobalSettings::self()->setDelayedMarkTime( mDelayedMarkTime->value() );
04746   GlobalSettings::self()->setActionEnterFolder( mActionEnterFolder->currentItem() );
04747   GlobalSettings::self()->setLoopOnGotoUnread( mLoopOnGotoUnread->currentItem() );
04748   GlobalSettings::self()->setShowPopupAfterDnD( mShowPopupAfterDnD->isChecked() );
04749   GlobalSettings::self()->setExcludeImportantMailFromExpiry(
04750         mExcludeImportantFromExpiry->isChecked() );
04751   GlobalSettings::self()->setQuotaUnit( mQuotaCmbBox->currentItem() );
04752 #ifdef HAVE_INDEXLIB
04753   if ( kmkernel->msgIndex() ) kmkernel->msgIndex()->setEnabled( mIndexingEnabled->isChecked() );
04754 #endif
04755 }
04756 
04757 QString MiscPage::GroupwareTab::helpAnchor() const {
04758   return QString::fromLatin1("configure-misc-groupware");
04759 }
04760 
04761 MiscPageGroupwareTab::MiscPageGroupwareTab( QWidget* parent, const char* name )
04762   : ConfigModuleTab( parent, name )
04763 {
04764   QBoxLayout* vlay = new QVBoxLayout( this, KDialog::marginHint(),
04765                                       KDialog::spacingHint() );
04766   vlay->setAutoAdd( true );
04767 
04768   // IMAP resource setup
04769   QVGroupBox* b1 = new QVGroupBox( i18n("&IMAP Resource Folder Options"),
04770                                    this );
04771 
04772   mEnableImapResCB =
04773     new QCheckBox( i18n("&Enable IMAP resource functionality"), b1 );
04774   QToolTip::add( mEnableImapResCB,  i18n( "This enables the IMAP storage for "
04775                                           "the Kontact applications" ) );
04776   QWhatsThis::add( mEnableImapResCB,
04777         i18n( GlobalSettings::self()->theIMAPResourceEnabledItem()->whatsThis().utf8() ) );
04778   connect( mEnableImapResCB, SIGNAL( stateChanged( int ) ),
04779            this, SLOT( slotEmitChanged( void ) ) );
04780 
04781   mBox = new QWidget( b1 );
04782   QGridLayout* grid = new QGridLayout( mBox, 5, 2, 0, KDialog::spacingHint() );
04783   grid->setColStretch( 1, 1 );
04784   connect( mEnableImapResCB, SIGNAL( toggled(bool) ),
04785            mBox, SLOT( setEnabled(bool) ) );
04786 
04787   QLabel* storageFormatLA = new QLabel( i18n("&Format used for the groupware folders:"),
04788                                         mBox );
04789   QString toolTip = i18n( "Choose the format to use to store the contents of the groupware folders." );
04790   QString whatsThis = i18n( GlobalSettings::self()
04791         ->theIMAPResourceStorageFormatItem()->whatsThis().utf8() );
04792   grid->addWidget( storageFormatLA, 0, 0 );
04793   QToolTip::add( storageFormatLA, toolTip );
04794   QWhatsThis::add( storageFormatLA, whatsThis );
04795   mStorageFormatCombo = new QComboBox( false, mBox );
04796   storageFormatLA->setBuddy( mStorageFormatCombo );
04797   QStringList formatLst;
04798   formatLst << i18n("Deprecated Kolab1 (iCal/vCard)") << i18n("Kolab2 (XML)");
04799   mStorageFormatCombo->insertStringList( formatLst );
04800   grid->addWidget( mStorageFormatCombo, 0, 1 );
04801   QToolTip::add( mStorageFormatCombo, toolTip );
04802   QWhatsThis::add( mStorageFormatCombo, whatsThis );
04803   connect( mStorageFormatCombo, SIGNAL( activated( int ) ),
04804            this, SLOT( slotStorageFormatChanged( int ) ) );
04805 
04806   QLabel* languageLA = new QLabel( i18n("&Language of the groupware folders:"),
04807                                    mBox );
04808 
04809   toolTip = i18n( "Set the language of the folder names" );
04810   whatsThis = i18n( GlobalSettings::self()
04811         ->theIMAPResourceFolderLanguageItem()->whatsThis().utf8() );
04812   grid->addWidget( languageLA, 1, 0 );
04813   QToolTip::add( languageLA, toolTip );
04814   QWhatsThis::add( languageLA, whatsThis );
04815   mLanguageCombo = new QComboBox( false, mBox );
04816   languageLA->setBuddy( mLanguageCombo );
04817   QStringList lst;
04818   lst << i18n("English") << i18n("German") << i18n("French") << i18n("Dutch");
04819   mLanguageCombo->insertStringList( lst );
04820   grid->addWidget( mLanguageCombo, 1, 1 );
04821   QToolTip::add( mLanguageCombo, toolTip );
04822   QWhatsThis::add( mLanguageCombo, whatsThis );
04823   connect( mLanguageCombo, SIGNAL( activated( int ) ),
04824            this, SLOT( slotEmitChanged( void ) ) );
04825 
04826   mFolderComboLabel = new QLabel( mBox ); // text depends on storage format
04827   toolTip = i18n( "Set the parent of the resource folders" );
04828   whatsThis = i18n( GlobalSettings::self()->theIMAPResourceFolderParentItem()->whatsThis().utf8() );
04829   QToolTip::add( mFolderComboLabel, toolTip );
04830   QWhatsThis::add( mFolderComboLabel, whatsThis );
04831   grid->addWidget( mFolderComboLabel, 2, 0 );
04832 
04833   mFolderComboStack = new QWidgetStack( mBox );
04834   grid->addWidget( mFolderComboStack, 2, 1 );
04835 
04836   // First possibility in the widgetstack: a combo showing the list of all folders
04837   // This is used with the ical/vcard storage
04838   mFolderCombo = new FolderRequester( mBox,
04839       kmkernel->getKMMainWidget()->folderTree() );
04840   mFolderComboStack->addWidget( mFolderCombo, 0 );
04841   QToolTip::add( mFolderCombo, toolTip );
04842   QWhatsThis::add( mFolderCombo, whatsThis );
04843   connect( mFolderCombo, SIGNAL( folderChanged( KMFolder* ) ),
04844            this, SLOT( slotEmitChanged() ) );
04845 
04846   // Second possibility in the widgetstack: a combo showing the list of accounts
04847   // This is used with the kolab xml storage since the groupware folders
04848   // are always under the inbox.
04849   mAccountCombo = new KMail::AccountComboBox( mBox );
04850   mFolderComboStack->addWidget( mAccountCombo, 1 );
04851   QToolTip::add( mAccountCombo, toolTip );
04852   QWhatsThis::add( mAccountCombo, whatsThis );
04853   connect( mAccountCombo, SIGNAL( activated( int ) ),
04854            this, SLOT( slotEmitChanged() ) );
04855 
04856   mHideGroupwareFolders = new QCheckBox( i18n( "&Hide groupware folders" ),
04857                                          mBox, "HideGroupwareFoldersBox" );
04858   grid->addMultiCellWidget( mHideGroupwareFolders, 3, 3, 0, 0 );
04859   QToolTip::add( mHideGroupwareFolders,
04860                  i18n( "When this is checked, you will not see the IMAP "
04861                        "resource folders in the folder tree." ) );
04862   QWhatsThis::add( mHideGroupwareFolders, i18n( GlobalSettings::self()
04863            ->hideGroupwareFoldersItem()->whatsThis().utf8() ) );
04864   connect( mHideGroupwareFolders, SIGNAL( toggled( bool ) ),
04865            this, SLOT( slotEmitChanged() ) );
04866 
04867   mOnlyShowGroupwareFolders = new QCheckBox( i18n( "&Only show groupware folders for this account" ),
04868                                          mBox, "OnlyGroupwareFoldersBox" );
04869   grid->addMultiCellWidget( mOnlyShowGroupwareFolders, 3, 3, 1, 1 );
04870   QToolTip::add( mOnlyShowGroupwareFolders,
04871                  i18n( "When this is checked, you will not see normal  "
04872                        "mail folders in the folder tree for the account "
04873                        "configured for groupware." ) );
04874   QWhatsThis::add( mOnlyShowGroupwareFolders, i18n( GlobalSettings::self()
04875            ->showOnlyGroupwareFoldersForGroupwareAccountItem()->whatsThis().utf8() ) );
04876   connect( mOnlyShowGroupwareFolders, SIGNAL( toggled( bool ) ),
04877            this, SLOT( slotEmitChanged() ) );
04878 
04879   mSyncImmediately = new QCheckBox( i18n( "Synchronize groupware changes immediately" ), mBox );
04880   QToolTip::add( mSyncImmediately,
04881                  i18n( "Synchronize groupware changes in disconnected IMAP folders immediately when being online." ) );
04882   connect( mSyncImmediately, SIGNAL(toggled(bool)), SLOT(slotEmitChanged()) );
04883   grid->addMultiCellWidget( mSyncImmediately, 4, 4, 0, 1 );
04884 
04885   mDeleteInvitations = new QCheckBox(
04886              i18n( GlobalSettings::self()->deleteInvitationEmailsAfterSendingReplyItem()->label().utf8() ), mBox );
04887   QWhatsThis::add( mDeleteInvitations, i18n( GlobalSettings::self()
04888              ->deleteInvitationEmailsAfterSendingReplyItem()->whatsThis().utf8() ) );
04889     connect( mDeleteInvitations, SIGNAL(toggled(bool)), SLOT(slotEmitChanged()) );
04890     grid->addMultiCellWidget( mDeleteInvitations, 5, 5, 0, 1 );
04891 
04892   // Groupware functionality compatibility setup
04893   b1 = new QVGroupBox( i18n("Groupware Compatibility && Legacy Options"), this );
04894 
04895   gBox = new QVBox( b1 );
04896 #if 0
04897   // Currently believed to be disused.
04898   mEnableGwCB = new QCheckBox( i18n("&Enable groupware functionality"), b1 );
04899   gBox->setSpacing( KDialog::spacingHint() );
04900   connect( mEnableGwCB, SIGNAL( toggled(bool) ),
04901            gBox, SLOT( setEnabled(bool) ) );
04902   connect( mEnableGwCB, SIGNAL( stateChanged( int ) ),
04903            this, SLOT( slotEmitChanged( void ) ) );
04904 #endif
04905   mEnableGwCB = 0;
04906   mLegacyMangleFromTo = new QCheckBox( i18n( "Mangle From:/To: headers in replies to invitations" ), gBox );
04907   QToolTip::add( mLegacyMangleFromTo, i18n( "Turn this option on in order to make Outlook(tm) understand your answers to invitation replies" ) );
04908   QWhatsThis::add( mLegacyMangleFromTo, i18n( GlobalSettings::self()->
04909            legacyMangleFromToHeadersItem()->whatsThis().utf8() ) );
04910   connect( mLegacyMangleFromTo, SIGNAL( stateChanged( int ) ),
04911            this, SLOT( slotEmitChanged( void ) ) );
04912   mLegacyBodyInvites = new QCheckBox( i18n( "Send invitations in the mail body" ), gBox );
04913   QToolTip::add( mLegacyBodyInvites, i18n( "Turn this option on in order to make Outlook(tm) understand your answers to invitations" ) );
04914   QWhatsThis::add( mLegacyMangleFromTo, i18n( GlobalSettings::self()->
04915            legacyBodyInvitesItem()->whatsThis().utf8() ) );
04916   connect( mLegacyBodyInvites, SIGNAL( toggled( bool ) ),
04917            this, SLOT( slotLegacyBodyInvitesToggled( bool ) ) );
04918   connect( mLegacyBodyInvites, SIGNAL( stateChanged( int ) ),
04919            this, SLOT( slotEmitChanged( void ) ) );
04920 
04921   mExchangeCompatibleInvitations = new QCheckBox( i18n( "Exchange compatible invitation naming" ), gBox );
04922   QToolTip::add( mExchangeCompatibleInvitations, i18n( "Outlook(tm), when used in combination with a Microsoft Exchange server,\nhas a problem understanding standards-compliant groupware e-mail.\nTurn this option on to send groupware invitations and replies in an Exchange compatible way." ) );
04923   QWhatsThis::add( mExchangeCompatibleInvitations, i18n( GlobalSettings::self()->
04924            exchangeCompatibleInvitationsItem()->whatsThis().utf8() ) );
04925   connect( mExchangeCompatibleInvitations, SIGNAL( stateChanged( int ) ),
04926            this, SLOT( slotEmitChanged( void ) ) );
04927 
04928   mOutlookCompatibleInvitationComments = new QCheckBox( i18n( "Outlook compatible invitation reply comments" ), gBox );
04929   QToolTip::add( mOutlookCompatibleInvitationComments, i18n( "Send invitation reply comments in a way that Microsoft Outlook(tm) understands." ) );
04930   QWhatsThis::add( mOutlookCompatibleInvitationComments, i18n( GlobalSettings::self()->
04931            outlookCompatibleInvitationReplyCommentsItem()->whatsThis().utf8() ) );
04932   connect( mOutlookCompatibleInvitationComments, SIGNAL( stateChanged( int ) ),
04933            this, SLOT( slotEmitChanged( void ) ) );
04934 
04935   mAutomaticSending = new QCheckBox( i18n( "Automatic invitation sending" ), gBox );
04936   QToolTip::add( mAutomaticSending, i18n( "When this is on, the user will not see the mail composer window. Invitation mails are sent automatically" ) );
04937   QWhatsThis::add( mAutomaticSending, i18n( GlobalSettings::self()->
04938            automaticSendingItem()->whatsThis().utf8() ) );
04939   connect( mAutomaticSending, SIGNAL( stateChanged( int ) ),
04940            this, SLOT( slotEmitChanged( void ) ) );
04941 
04942   // Open space padding at the end
04943   new QLabel( this );
04944 }
04945 
04946 void MiscPageGroupwareTab::slotLegacyBodyInvitesToggled( bool on )
04947 {
04948   if ( on ) {
04949     QString txt = i18n( "<qt>Invitations are normally sent as attachments to "
04950                         "a mail. This switch changes the invitation mails to "
04951                         "be sent in the text of the mail instead; this is "
04952                         "necessary to send invitations and replies to "
04953                         "Microsoft Outlook.<br>But, when you do this, you no "
04954                         "longer get descriptive text that mail programs "
04955                         "can read; so, to people who have email programs "
04956                         "that do not understand the invitations, the "
04957                         "resulting messages look very odd.<br>People that have email "
04958                         "programs that do understand invitations will still "
04959                         "be able to work with this.</qt>" );
04960     KMessageBox::information( this, txt, QString::null,
04961                               "LegacyBodyInvitesWarning" );
04962   }
04963   // Invitations in the body are autosent in any case (no point in editing raw ICAL)
04964   // So the autosend option is only available if invitations are sent as attachment.
04965   mAutomaticSending->setEnabled( !mLegacyBodyInvites->isChecked() );
04966 }
04967 
04968 void MiscPage::GroupwareTab::doLoadFromGlobalSettings() {
04969   if ( mEnableGwCB ) {
04970     mEnableGwCB->setChecked( GlobalSettings::self()->groupwareEnabled() );
04971     gBox->setEnabled( mEnableGwCB->isChecked() );
04972   }
04973 
04974   mLegacyMangleFromTo->setChecked( GlobalSettings::self()->legacyMangleFromToHeaders() );
04975   mLegacyBodyInvites->blockSignals( true );
04976 
04977   mLegacyBodyInvites->setChecked( GlobalSettings::self()->legacyBodyInvites() );
04978   mLegacyBodyInvites->blockSignals( false );
04979 
04980   mExchangeCompatibleInvitations->setChecked( GlobalSettings::self()->exchangeCompatibleInvitations() );
04981 
04982   mOutlookCompatibleInvitationComments->setChecked( GlobalSettings::self()->outlookCompatibleInvitationReplyComments() );
04983 
04984   mAutomaticSending->setChecked( GlobalSettings::self()->automaticSending() );
04985   mAutomaticSending->setEnabled( !mLegacyBodyInvites->isChecked() );
04986 
04987   // Read the IMAP resource config
04988   mEnableImapResCB->setChecked( GlobalSettings::self()->theIMAPResourceEnabled() );
04989   mBox->setEnabled( mEnableImapResCB->isChecked() );
04990 
04991   mHideGroupwareFolders->setChecked( GlobalSettings::self()->hideGroupwareFolders() );
04992   int i = GlobalSettings::self()->theIMAPResourceFolderLanguage();
04993   mLanguageCombo->setCurrentItem(i);
04994   i = GlobalSettings::self()->theIMAPResourceStorageFormat();
04995   mStorageFormatCombo->setCurrentItem(i);
04996   slotStorageFormatChanged( i );
04997   mOnlyShowGroupwareFolders->setChecked( GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount() );
04998   mSyncImmediately->setChecked( GlobalSettings::self()->immediatlySyncDIMAPOnGroupwareChanges() );
04999   mDeleteInvitations->setChecked( GlobalSettings::self()->deleteInvitationEmailsAfterSendingReply() );
05000 
05001   QString folderId( GlobalSettings::self()->theIMAPResourceFolderParent() );
05002   if( !folderId.isNull() && kmkernel->findFolderById( folderId ) ) {
05003     mFolderCombo->setFolder( folderId );
05004   } else {
05005     // Folder was deleted, we have to choose a new one
05006     mFolderCombo->setFolder( i18n( "<Choose a Folder>" ) );
05007   }
05008 
05009   KMAccount* selectedAccount = 0;
05010   int accountId = GlobalSettings::self()->theIMAPResourceAccount();
05011   if ( accountId )
05012     selectedAccount = kmkernel->acctMgr()->find( accountId );
05013   else {
05014     // Fallback: iterate over accounts to select folderId if found (as an inbox folder)
05015       for( KMAccount *a = kmkernel->acctMgr()->first(); a!=0;
05016          a = kmkernel->acctMgr()->next() ) {
05017       if( a->folder() && a->folder()->child() ) {
05018         // Look inside that folder for an INBOX
05019         KMFolderNode *node;
05020         for (node = a->folder()->child()->first(); node; node = a->folder()->child()->next())
05021           if (!node->isDir() && node->name() == "INBOX") break;
05022 
05023         if ( node && static_cast<KMFolder*>(node)->idString() == folderId ) {
05024           selectedAccount = a;
05025           break;
05026         }
05027       }
05028     }
05029   }
05030   if ( selectedAccount )
05031     mAccountCombo->setCurrentAccount( selectedAccount );
05032   else if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == 1 )
05033     kdDebug(5006) << "Folder " << folderId << " not found as an account's inbox" << endl;
05034 }
05035 
05036 void MiscPage::GroupwareTab::save() {
05037   KConfigGroup groupware( KMKernel::config(), "Groupware" );
05038 
05039   // Write the groupware config
05040   if ( mEnableGwCB ) {
05041     groupware.writeEntry( "GroupwareEnabled", mEnableGwCB->isChecked() );
05042   }
05043   groupware.writeEntry( "LegacyMangleFromToHeaders", mLegacyMangleFromTo->isChecked() );
05044   groupware.writeEntry( "LegacyBodyInvites", mLegacyBodyInvites->isChecked() );
05045   groupware.writeEntry( "ExchangeCompatibleInvitations", mExchangeCompatibleInvitations->isChecked() );
05046   groupware.writeEntry( "OutlookCompatibleInvitationReplyComments", mOutlookCompatibleInvitationComments->isChecked() );
05047   groupware.writeEntry( "AutomaticSending", mAutomaticSending->isChecked() );
05048 
05049   if ( mEnableGwCB ) {
05050     GlobalSettings::self()->setGroupwareEnabled( mEnableGwCB->isChecked() );
05051   }
05052   GlobalSettings::self()->setLegacyMangleFromToHeaders( mLegacyMangleFromTo->isChecked() );
05053   GlobalSettings::self()->setLegacyBodyInvites( mLegacyBodyInvites->isChecked() );
05054   GlobalSettings::self()->setExchangeCompatibleInvitations( mExchangeCompatibleInvitations->isChecked() );
05055   GlobalSettings::self()->setOutlookCompatibleInvitationReplyComments( mOutlookCompatibleInvitationComments->isChecked() );
05056   GlobalSettings::self()->setAutomaticSending( mAutomaticSending->isChecked() );
05057 
05058   int format = mStorageFormatCombo->currentItem();
05059   GlobalSettings::self()->setTheIMAPResourceStorageFormat( format );
05060 
05061   // Write the IMAP resource config
05062   GlobalSettings::self()->setHideGroupwareFolders( mHideGroupwareFolders->isChecked() );
05063   GlobalSettings::self()->setShowOnlyGroupwareFoldersForGroupwareAccount( mOnlyShowGroupwareFolders->isChecked() );
05064   GlobalSettings::self()->setImmediatlySyncDIMAPOnGroupwareChanges( mSyncImmediately->isChecked() );
05065   GlobalSettings::self()->setDeleteInvitationEmailsAfterSendingReply( mDeleteInvitations->isChecked() );
05066 
05067   // If there is a leftover folder in the foldercombo, getFolder can
05068   // return 0. In that case we really don't have it enabled
05069   QString folderId;
05070   if (  format == 0 ) {
05071     KMFolder* folder = mFolderCombo->folder();
05072     if (  folder )
05073       folderId = folder->idString();
05074     KMAccount* account = 0;
05075     // Didn't find an easy way to find the account for a given folder...
05076     // Fallback: iterate over accounts to select folderId if found (as an inbox folder)
05077     for( KMAccount *a = kmkernel->acctMgr()->first();
05078         a && !account; // stop when found
05079         a = kmkernel->acctMgr()->next() ) {
05080       if( a->folder() && a->folder()->child() ) {
05081         KMFolderNode *node;
05082         for ( node = a->folder()->child()->first(); node; node = a->folder()->child()->next() )
05083         {
05084           if ( static_cast<KMFolder*>(node) == folder ) {
05085             account = a;
05086             break;
05087           }
05088         }
05089       }
05090     }
05091     GlobalSettings::self()->setTheIMAPResourceAccount( account ? account->id() : 0 );
05092   } else {
05093     // Inbox folder of the selected account
05094     KMAccount* acct = mAccountCombo->currentAccount();
05095     if (  acct ) {
05096       folderId = QString( ".%1.directory/INBOX" ).arg( acct->id() );
05097       GlobalSettings::self()->setTheIMAPResourceAccount( acct->id() );
05098     }
05099   }
05100 
05101   bool enabled = mEnableImapResCB->isChecked() && !folderId.isEmpty();
05102   GlobalSettings::self()->setTheIMAPResourceEnabled( enabled );
05103   GlobalSettings::self()->setTheIMAPResourceFolderLanguage( mLanguageCombo->currentItem() );
05104   GlobalSettings::self()->setTheIMAPResourceFolderParent( folderId );
05105 }
05106 
05107 void MiscPage::GroupwareTab::slotStorageFormatChanged( int format )
05108 {
05109   mLanguageCombo->setEnabled( format == 0 ); // only ical/vcard needs the language hack
05110   mFolderComboStack->raiseWidget( format );
05111   if ( format == 0 ) {
05112     mFolderComboLabel->setText( i18n("&Resource folders are subfolders of:") );
05113     mFolderComboLabel->setBuddy( mFolderCombo );
05114   } else {
05115     mFolderComboLabel->setText( i18n("&Resource folders are in account:") );
05116     mFolderComboLabel->setBuddy( mAccountCombo );
05117   }
05118   slotEmitChanged();
05119 }
05120 
05121 
05122 // *************************************************************
05123 // *                                                           *
05124 // *                     AccountUpdater                        *
05125 // *                                                           *
05126 // *************************************************************
05127 AccountUpdater::AccountUpdater(ImapAccountBase *account)
05128     : QObject()
05129 {
05130   mAccount = account;
05131 }
05132 
05133 void AccountUpdater::update()
05134 {
05135   connect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
05136           this, SLOT( namespacesFetched() ) );
05137   mAccount->makeConnection();
05138 }
05139 
05140 void AccountUpdater::namespacesFetched()
05141 {
05142   mAccount->setCheckingMail( true );
05143   mAccount->processNewMail( false );
05144   deleteLater();
05145 }
05146 
05147 #undef DIM
05148 
05149 //----------------------------
05150 #include "configuredialog.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys