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