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   KMAccount *account
01181     = kmkernel->acctMgr()->create( QString::fromLatin1( accountType ) );
01182   if ( !account ) {
01183     // ### FIXME: Give the user more information. Is this error
01184     // recoverable?
01185     KMessageBox::sorry( this, i18n("Unable to create account") );
01186     return;
01187   }
01188 
01189   account->init(); // fill the account fields with good default values
01190 
01191   AccountDialog dialog( i18n("Add Account"), account, this );
01192 
01193   QStringList accountNames = occupiedNames();
01194 
01195   if( dialog.exec() != QDialog::Accepted ) {
01196     delete account;
01197     return;
01198   }
01199 
01200   account->deinstallTimer();
01201   account->setName( uniqueName( accountNames, account->name() ) );
01202 
01203   QListViewItem *after = mAccountList->firstChild();
01204   while ( after && after->nextSibling() )
01205     after = after->nextSibling();
01206 
01207   QListViewItem *listItem =
01208     new QListViewItem( mAccountList, after, account->name(), account->type() );
01209   if( account->folder() )
01210     listItem->setText( 2, account->folder()->label() );
01211 
01212   mNewAccounts.append( account );
01213   emit changed( true );
01214 }
01215 
01216 
01217 
01218 void AccountsPage::ReceivingTab::slotModifySelectedAccount()
01219 {
01220   QListViewItem *listItem = mAccountList->selectedItem();
01221   if( !listItem ) return;
01222 
01223   KMAccount *account = 0;
01224   QValueList<ModifiedAccountsType*>::Iterator j;
01225   for (j = mModifiedAccounts.begin(); j != mModifiedAccounts.end(); ++j )
01226     if ( (*j)->newAccount->name() == listItem->text(0) ) {
01227       account = (*j)->newAccount;
01228       break;
01229     }
01230 
01231   if ( !account ) {
01232     QValueList< QGuardedPtr<KMAccount> >::Iterator it;
01233     for ( it = mNewAccounts.begin() ; it != mNewAccounts.end() ; ++it )
01234       if ( (*it)->name() == listItem->text(0) ) {
01235         account = *it;
01236         break;
01237       }
01238 
01239     if ( !account ) {
01240       account = kmkernel->acctMgr()->findByName( listItem->text(0) );
01241       if( !account ) {
01242         // ### FIXME: How should this happen? See above.
01243         KMessageBox::sorry( this, i18n("Unable to locate account") );
01244         return;
01245       }
01246       if ( account->type() == "imap" || account->type() == "cachedimap" )
01247       {
01248         ImapAccountBase* ai = static_cast<ImapAccountBase*>( account );
01249         if ( ai->namespaces().isEmpty() || ai->namespaceToDelimiter().isEmpty() )
01250         {
01251           // connect to server - the namespaces are fetched automatically
01252           kdDebug(5006) << "slotModifySelectedAccount - connect" << endl;
01253           ai->makeConnection();
01254         }
01255       }
01256 
01257       ModifiedAccountsType *mod = new ModifiedAccountsType;
01258       mod->oldAccount = account;
01259       mod->newAccount = kmkernel->acctMgr()->create( account->type(),
01260                                                    account->name() );
01261       mod->newAccount->pseudoAssign( account );
01262       mModifiedAccounts.append( mod );
01263       account = mod->newAccount;
01264     }
01265   }
01266 
01267   QStringList accountNames = occupiedNames();
01268   accountNames.remove( account->name() );
01269 
01270   AccountDialog dialog( i18n("Modify Account"), account, this );
01271 
01272   if( dialog.exec() != QDialog::Accepted ) return;
01273 
01274   account->setName( uniqueName( accountNames, account->name() ) );
01275 
01276   listItem->setText( 0, account->name() );
01277   listItem->setText( 1, account->type() );
01278   if( account->folder() )
01279     listItem->setText( 2, account->folder()->label() );
01280 
01281   emit changed( true );
01282 }
01283 
01284 
01285 
01286 void AccountsPage::ReceivingTab::slotRemoveSelectedAccount() {
01287   QListViewItem *listItem = mAccountList->selectedItem();
01288   if( !listItem ) return;
01289 
01290   KMAccount *acct = 0;
01291   QValueList<ModifiedAccountsType*>::Iterator j;
01292   for ( j = mModifiedAccounts.begin() ; j != mModifiedAccounts.end() ; ++j )
01293     if ( (*j)->newAccount->name() == listItem->text(0) ) {
01294       acct = (*j)->oldAccount;
01295       mAccountsToDelete.append( acct );
01296       mModifiedAccounts.remove( j );
01297       break;
01298     }
01299   if ( !acct ) {
01300     QValueList< QGuardedPtr<KMAccount> >::Iterator it;
01301     for ( it = mNewAccounts.begin() ; it != mNewAccounts.end() ; ++it )
01302       if ( (*it)->name() == listItem->text(0) ) {
01303         acct = *it;
01304         mNewAccounts.remove( it );
01305         break;
01306       }
01307   }
01308   if ( !acct ) {
01309     acct = kmkernel->acctMgr()->findByName( listItem->text(0) );
01310     if ( acct )
01311       mAccountsToDelete.append( acct );
01312   }
01313   if ( !acct ) {
01314     // ### FIXME: see above
01315     KMessageBox::sorry( this, i18n("<qt>Unable to locate account <b>%1</b>.</qt>")
01316                         .arg(listItem->text(0)) );
01317     return;
01318   }
01319 
01320   QListViewItem * item = listItem->itemBelow();
01321   if ( !item ) item = listItem->itemAbove();
01322   delete listItem;
01323 
01324   if ( item )
01325     mAccountList->setSelected( item, true );
01326 
01327   emit changed( true );
01328 }
01329 
01330 void AccountsPage::ReceivingTab::slotEditNotifications()
01331 {
01332   if(kmkernel->xmlGuiInstance())
01333     KNotifyDialog::configure(this, 0, kmkernel->xmlGuiInstance()->aboutData());
01334   else
01335     KNotifyDialog::configure(this);
01336 }
01337 
01338 void AccountsPage::ReceivingTab::doLoadFromGlobalSettings() {
01339   mVerboseNotificationCheck->setChecked( GlobalSettings::self()->verboseNewMailNotification() );
01340 }
01341 
01342 void AccountsPage::ReceivingTab::doLoadOther() {
01343   KConfigGroup general( KMKernel::config(), "General" );
01344 
01345   mAccountList->clear();
01346   QListViewItem *top = 0;
01347 
01348   for( KMAccount *a = kmkernel->acctMgr()->first(); a!=0;
01349        a = kmkernel->acctMgr()->next() ) {
01350     QListViewItem *listItem =
01351       new QListViewItem( mAccountList, top, a->name(), a->type() );
01352     if( a->folder() )
01353       listItem->setText( 2, a->folder()->label() );
01354     top = listItem;
01355   }
01356   QListViewItem *listItem = mAccountList->firstChild();
01357   if ( listItem ) {
01358     mAccountList->setCurrentItem( listItem );
01359     mAccountList->setSelected( listItem, true );
01360   }
01361 
01362   mBeepNewMailCheck->setChecked( general.readBoolEntry("beep-on-mail", false ) );
01363   mCheckmailStartupCheck->setChecked( general.readBoolEntry("checkmail-startup", false) );
01364   QTimer::singleShot( 0, this, SLOT( slotTweakAccountList() ) );
01365 }
01366 
01367 void AccountsPage::ReceivingTab::slotTweakAccountList()
01368 {
01369   // Force the contentsWidth of mAccountList to be recalculated so that items can be
01370   // selected in the normal way. It would be best if this were not necessary.
01371   mAccountList->resizeContents( mAccountList->visibleWidth(), mAccountList->contentsHeight() );
01372 }
01373 
01374 void AccountsPage::ReceivingTab::save() {
01375   // Add accounts marked as new
01376   QValueList< QGuardedPtr<KMAccount> >::Iterator it;
01377   for (it = mNewAccounts.begin(); it != mNewAccounts.end(); ++it ) {
01378     kmkernel->acctMgr()->add( *it );
01379     (*it)->installTimer();
01380   }
01381 
01382   // Update accounts that have been modified
01383   QValueList<ModifiedAccountsType*>::Iterator j;
01384   for ( j = mModifiedAccounts.begin() ; j != mModifiedAccounts.end() ; ++j ) {
01385     (*j)->oldAccount->pseudoAssign( (*j)->newAccount );
01386     delete (*j)->newAccount;
01387     delete (*j);
01388   }
01389   mModifiedAccounts.clear();
01390 
01391   // Delete accounts marked for deletion
01392   for ( it = mAccountsToDelete.begin() ;
01393         it != mAccountsToDelete.end() ; ++it ) {
01394     kmkernel->acctMgr()->writeConfig( true );
01395     if ( (*it) && !kmkernel->acctMgr()->remove(*it) )
01396       KMessageBox::sorry( this, i18n("<qt>Unable to locate account <b>%1</b>.</qt>")
01397                           .arg( (*it)->name() ) );
01398   }
01399   mAccountsToDelete.clear();
01400 
01401   // Incoming mail
01402   kmkernel->acctMgr()->writeConfig( false );
01403   kmkernel->cleanupImapFolders();
01404 
01405   // Save Mail notification settings
01406   KConfigGroup general( KMKernel::config(), "General" );
01407   general.writeEntry( "beep-on-mail", mBeepNewMailCheck->isChecked() );
01408   GlobalSettings::self()->setVerboseNewMailNotification( mVerboseNotificationCheck->isChecked() );
01409 
01410   general.writeEntry( "checkmail-startup", mCheckmailStartupCheck->isChecked() );
01411 
01412   // Sync new IMAP accounts ASAP:
01413   for (it = mNewAccounts.begin(); it != mNewAccounts.end(); ++it ) {
01414     KMAccount *macc = (*it);
01415     ImapAccountBase *acc = dynamic_cast<ImapAccountBase*> (macc);
01416     if ( acc ) {
01417       AccountUpdater *au = new AccountUpdater( acc );
01418       au->update();
01419     }
01420   }
01421   mNewAccounts.clear();
01422 
01423 }
01424 
01425 // *************************************************************
01426 // *                                                           *
01427 // *                     AppearancePage                        *
01428 // *                                                           *
01429 // *************************************************************
01430 QString AppearancePage::helpAnchor() const {
01431   return QString::fromLatin1("configure-appearance");
01432 }
01433 
01434 AppearancePage::AppearancePage( QWidget * parent, const char * name )
01435   : ConfigModuleWithTabs( parent, name )
01436 {
01437   //
01438   // "Fonts" tab:
01439   //
01440   mFontsTab = new FontsTab();
01441   addTab( mFontsTab, i18n("&Fonts") );
01442 
01443   //
01444   // "Colors" tab:
01445   //
01446   mColorsTab = new ColorsTab();
01447   addTab( mColorsTab, i18n("Color&s") );
01448 
01449   //
01450   // "Layout" tab:
01451   //
01452   mLayoutTab = new LayoutTab();
01453   addTab( mLayoutTab, i18n("La&yout") );
01454 
01455   //
01456   // "Headers" tab:
01457   //
01458   mHeadersTab = new HeadersTab();
01459   addTab( mHeadersTab, i18n("M&essage List") );
01460 
01461   //
01462   // "Reader window" tab:
01463   //
01464   mReaderTab = new ReaderTab();
01465   addTab( mReaderTab, i18n("Message W&indow") );
01466 
01467   //
01468   // "System Tray" tab:
01469   //
01470   mSystemTrayTab = new SystemTrayTab();
01471   addTab( mSystemTrayTab, i18n("System &Tray") );
01472 
01473   load();
01474 }
01475 
01476 
01477 QString AppearancePage::FontsTab::helpAnchor() const {
01478   return QString::fromLatin1("configure-appearance-fonts");
01479 }
01480 
01481 static const struct {
01482   const char * configName;
01483   const char * displayName;
01484   bool   enableFamilyAndSize;
01485   bool   onlyFixed;
01486 } fontNames[] = {
01487   { "body-font", I18N_NOOP("Message Body"), true, false },
01488   { "list-font", I18N_NOOP("Message List"), true, false },
01489   { "list-new-font", I18N_NOOP("Message List - New Messages"), true, false },
01490   { "list-unread-font", I18N_NOOP("Message List - Unread Messages"), true, false },
01491   { "list-important-font", I18N_NOOP("Message List - Important Messages"), true, false },
01492   { "list-todo-font", I18N_NOOP("Message List - Todo Messages"), true, false },
01493   { "list-date-font", I18N_NOOP("Message List - Date Field"), true, false },
01494   { "folder-font", I18N_NOOP("Folder List"), true, false },
01495   { "quote1-font", I18N_NOOP("Quoted Text - First Level"), false, false },
01496   { "quote2-font", I18N_NOOP("Quoted Text - Second Level"), false, false },
01497   { "quote3-font", I18N_NOOP("Quoted Text - Third Level"), false, false },
01498   { "fixed-font", I18N_NOOP("Fixed Width Font"), true, true },
01499   { "composer-font", I18N_NOOP("Composer"), true, false },
01500   { "print-font",  I18N_NOOP("Printing Output"), true, false },
01501 };
01502 static const int numFontNames = sizeof fontNames / sizeof *fontNames;
01503 
01504 AppearancePageFontsTab::AppearancePageFontsTab( QWidget * parent, const char * name )
01505   : ConfigModuleTab( parent, name ), mActiveFontIndex( -1 )
01506 {
01507   assert( numFontNames == sizeof mFont / sizeof *mFont );
01508   // tmp. vars:
01509   QVBoxLayout *vlay;
01510   QHBoxLayout *hlay;
01511   QLabel      *label;
01512 
01513   // "Use custom fonts" checkbox, followed by <hr>
01514   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
01515   mCustomFontCheck = new QCheckBox( i18n("&Use custom fonts"), this );
01516   vlay->addWidget( mCustomFontCheck );
01517   vlay->addWidget( new KSeparator( KSeparator::HLine, this ) );
01518   connect ( mCustomFontCheck, SIGNAL( stateChanged( int ) ),
01519             this, SLOT( slotEmitChanged( void ) ) );
01520 
01521   // "font location" combo box and label:
01522   hlay = new QHBoxLayout( vlay ); // inherites spacing
01523   mFontLocationCombo = new QComboBox( false, this );
01524   mFontLocationCombo->setEnabled( false ); // !mCustomFontCheck->isChecked()
01525 
01526   QStringList fontDescriptions;
01527   for ( int i = 0 ; i < numFontNames ; i++ )
01528     fontDescriptions << i18n( fontNames[i].displayName );
01529   mFontLocationCombo->insertStringList( fontDescriptions );
01530 
01531   label = new QLabel( mFontLocationCombo, i18n("Apply &to:"), this );
01532   label->setEnabled( false ); // since !mCustomFontCheck->isChecked()
01533   hlay->addWidget( label );
01534 
01535   hlay->addWidget( mFontLocationCombo );
01536   hlay->addStretch( 10 );
01537   vlay->addSpacing( KDialog::spacingHint() );
01538   mFontChooser = new KFontChooser( this, "font", false, QStringList(),
01539                                    false, 4 );
01540   mFontChooser->setEnabled( false ); // since !mCustomFontCheck->isChecked()
01541   vlay->addWidget( mFontChooser );
01542   connect ( mFontChooser, SIGNAL( fontSelected( const QFont& ) ),
01543             this, SLOT( slotEmitChanged( void ) ) );
01544 
01545 
01546   // {en,dis}able widgets depending on the state of mCustomFontCheck:
01547   connect( mCustomFontCheck, SIGNAL(toggled(bool)),
01548            label, SLOT(setEnabled(bool)) );
01549   connect( mCustomFontCheck, SIGNAL(toggled(bool)),
01550            mFontLocationCombo, SLOT(setEnabled(bool)) );
01551   connect( mCustomFontCheck, SIGNAL(toggled(bool)),
01552            mFontChooser, SLOT(setEnabled(bool)) );
01553   // load the right font settings into mFontChooser:
01554   connect( mFontLocationCombo, SIGNAL(activated(int) ),
01555            this, SLOT(slotFontSelectorChanged(int)) );
01556 }
01557 
01558 
01559 void AppearancePage::FontsTab::slotFontSelectorChanged( int index )
01560 {
01561   kdDebug(5006) << "slotFontSelectorChanged() called" << endl;
01562   if( index < 0 || index >= mFontLocationCombo->count() )
01563     return; // Should never happen, but it is better to check.
01564 
01565   // Save current fontselector setting before we install the new:
01566   if( mActiveFontIndex == 0 ) {
01567     mFont[0] = mFontChooser->font();
01568     // hardcode the family and size of "message body" dependant fonts:
01569     for ( int i = 0 ; i < numFontNames ; i++ )
01570       if ( !fontNames[i].enableFamilyAndSize ) {
01571         // ### shall we copy the font and set the save and re-set
01572         // {regular,italic,bold,bold italic} property or should we
01573         // copy only family and pointSize?
01574         mFont[i].setFamily( mFont[0].family() );
01575         mFont[i].setPointSize/*Float?*/( mFont[0].pointSize/*Float?*/() );
01576       }
01577   } else if ( mActiveFontIndex > 0 )
01578     mFont[ mActiveFontIndex ] = mFontChooser->font();
01579   mActiveFontIndex = index;
01580 
01581   // Disonnect so the "Apply" button is not activated by the change
01582   disconnect ( mFontChooser, SIGNAL( fontSelected( const QFont& ) ),
01583             this, SLOT( slotEmitChanged( void ) ) );
01584 
01585   // Display the new setting:
01586   mFontChooser->setFont( mFont[index], fontNames[index].onlyFixed );
01587 
01588   connect ( mFontChooser, SIGNAL( fontSelected( const QFont& ) ),
01589             this, SLOT( slotEmitChanged( void ) ) );
01590 
01591   // Disable Family and Size list if we have selected a quote font:
01592   mFontChooser->enableColumn( KFontChooser::FamilyList|KFontChooser::SizeList,
01593                               fontNames[ index ].enableFamilyAndSize );
01594 }
01595 
01596 void AppearancePage::FontsTab::doLoadOther() {
01597   KConfigGroup fonts( KMKernel::config(), "Fonts" );
01598 
01599   mFont[0] = KGlobalSettings::generalFont();
01600   QFont fixedFont = KGlobalSettings::fixedFont();
01601   for ( int i = 0 ; i < numFontNames ; i++ )
01602     mFont[i] = fonts.readFontEntry( fontNames[i].configName,
01603       (fontNames[i].onlyFixed) ? &fixedFont : &mFont[0] );
01604 
01605   mCustomFontCheck->setChecked( !fonts.readBoolEntry( "defaultFonts", true ) );
01606   mFontLocationCombo->setCurrentItem( 0 );
01607   slotFontSelectorChanged( 0 );
01608 }
01609 
01610 void AppearancePage::FontsTab::installProfile( KConfig * profile ) {
01611   KConfigGroup fonts( profile, "Fonts" );
01612 
01613   // read fonts that are defined in the profile:
01614   bool needChange = false;
01615   for ( int i = 0 ; i < numFontNames ; i++ )
01616     if ( fonts.hasKey( fontNames[i].configName ) ) {
01617       needChange = true;
01618       mFont[i] = fonts.readFontEntry( fontNames[i].configName );
01619       kdDebug(5006) << "got font \"" << fontNames[i].configName
01620                 << "\" thusly: \"" << mFont[i].toString() << "\"" << endl;
01621     }
01622   if ( needChange && mFontLocationCombo->currentItem() > 0 )
01623     mFontChooser->setFont( mFont[ mFontLocationCombo->currentItem() ],
01624       fontNames[ mFontLocationCombo->currentItem() ].onlyFixed );
01625 
01626   if ( fonts.hasKey( "defaultFonts" ) )
01627     mCustomFontCheck->setChecked( !fonts.readBoolEntry( "defaultFonts" ) );
01628 }
01629 
01630 void AppearancePage::FontsTab::save() {
01631   KConfigGroup fonts( KMKernel::config(), "Fonts" );
01632 
01633   // read the current font (might have been modified)
01634   if ( mActiveFontIndex >= 0 )
01635     mFont[ mActiveFontIndex ] = mFontChooser->font();
01636 
01637   bool customFonts = mCustomFontCheck->isChecked();
01638   fonts.writeEntry( "defaultFonts", !customFonts );
01639   for ( int i = 0 ; i < numFontNames ; i++ )
01640     if ( customFonts || fonts.hasKey( fontNames[i].configName ) )
01641       // Don't write font info when we use default fonts, but write
01642       // if it's already there:
01643       fonts.writeEntry( fontNames[i].configName, mFont[i] );
01644 }
01645 
01646 QString AppearancePage::ColorsTab::helpAnchor() const {
01647   return QString::fromLatin1("configure-appearance-colors");
01648 }
01649 
01650 
01651 static const struct {
01652   const char * configName;
01653   const char * displayName;
01654 } colorNames[] = { // adjust setup() if you change this:
01655   { "BackgroundColor", I18N_NOOP("Composer Background") },
01656   { "AltBackgroundColor", I18N_NOOP("Alternative Background Color") },
01657   { "ForegroundColor", I18N_NOOP("Normal Text") },
01658   { "QuotedText1", I18N_NOOP("Quoted Text - First Level") },
01659   { "QuotedText2", I18N_NOOP("Quoted Text - Second Level") },
01660   { "QuotedText3", I18N_NOOP("Quoted Text - Third Level") },
01661   { "LinkColor", I18N_NOOP("Link") },
01662   { "FollowedColor", I18N_NOOP("Followed Link") },
01663   { "MisspelledColor", I18N_NOOP("Misspelled Words") },
01664   { "NewMessage", I18N_NOOP("New Message") },
01665   { "UnreadMessage", I18N_NOOP("Unread Message") },
01666   { "FlagMessage", I18N_NOOP("Important Message") },
01667   { "TodoMessage", I18N_NOOP("Todo Message") },
01668   { "PGPMessageEncr", I18N_NOOP("OpenPGP Message - Encrypted") },
01669   { "PGPMessageOkKeyOk", I18N_NOOP("OpenPGP Message - Valid Signature with Trusted Key") },
01670   { "PGPMessageOkKeyBad", I18N_NOOP("OpenPGP Message - Valid Signature with Untrusted Key") },
01671   { "PGPMessageWarn", I18N_NOOP("OpenPGP Message - Unchecked Signature") },
01672   { "PGPMessageErr", I18N_NOOP("OpenPGP Message - Bad Signature") },
01673   { "HTMLWarningColor", I18N_NOOP("Border Around Warning Prepending HTML Messages") },
01674   { "ColorbarBackgroundPlain", I18N_NOOP("HTML Status Bar Background - No HTML Message") },
01675   { "ColorbarForegroundPlain", I18N_NOOP("HTML Status Bar Foreground - No HTML Message") },
01676   { "ColorbarBackgroundHTML",  I18N_NOOP("HTML Status Bar Background - HTML Message") },
01677   { "ColorbarForegroundHTML",  I18N_NOOP("HTML Status Bar Foreground - HTML Message") },
01678 };
01679 static const int numColorNames = sizeof colorNames / sizeof *colorNames;
01680 
01681 AppearancePageColorsTab::AppearancePageColorsTab( QWidget * parent, const char * name )
01682   : ConfigModuleTab( parent, name )
01683 {
01684   // tmp. vars:
01685   QVBoxLayout *vlay;
01686 
01687   // "use custom colors" check box
01688   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
01689   mCustomColorCheck = new QCheckBox( i18n("&Use custom colors"), this );
01690   vlay->addWidget( mCustomColorCheck );
01691   connect( mCustomColorCheck, SIGNAL( stateChanged( int ) ),
01692            this, SLOT( slotEmitChanged( void ) ) );
01693 
01694   // color list box:
01695   mColorList = new ColorListBox( this );
01696   mColorList->setEnabled( false ); // since !mCustomColorCheck->isChecked()
01697   QStringList modeList;
01698   for ( int i = 0 ; i < numColorNames ; i++ )
01699     mColorList->insertItem( new ColorListItem( i18n( colorNames[i].displayName ) ) );
01700   vlay->addWidget( mColorList, 1 );
01701 
01702   // "recycle colors" check box:
01703   mRecycleColorCheck =
01704     new QCheckBox( i18n("Recycle colors on deep &quoting"), this );
01705   mRecycleColorCheck->setEnabled( false );
01706   vlay->addWidget( mRecycleColorCheck );
01707   connect( mRecycleColorCheck, SIGNAL( stateChanged( int ) ),
01708            this, SLOT( slotEmitChanged( void ) ) );
01709 
01710   // {en,dir}able widgets depending on the state of mCustomColorCheck:
01711   connect( mCustomColorCheck, SIGNAL(toggled(bool)),
01712            mColorList, SLOT(setEnabled(bool)) );
01713   connect( mCustomColorCheck, SIGNAL(toggled(bool)),
01714            mRecycleColorCheck, SLOT(setEnabled(bool)) );
01715   connect( mCustomColorCheck, SIGNAL( stateChanged( int ) ),
01716            this, SLOT( slotEmitChanged( void ) ) );
01717 }
01718 
01719 void AppearancePage::ColorsTab::doLoadOther() {
01720   KConfigGroup reader( KMKernel::config(), "Reader" );
01721 
01722   mCustomColorCheck->setChecked( !reader.readBoolEntry( "defaultColors", true ) );
01723   mRecycleColorCheck->setChecked( reader.readBoolEntry( "RecycleQuoteColors", false ) );
01724 
01725   static const QColor defaultColor[ numColorNames ] = {
01726     kapp->palette().active().base(), // bg
01727     KGlobalSettings::alternateBackgroundColor(), // alt bg
01728     kapp->palette().active().text(), // fg
01729     QColor( 0x00, 0x80, 0x00 ), // quoted l1
01730     QColor( 0x00, 0x70, 0x00 ), // quoted l2
01731     QColor( 0x00, 0x60, 0x00 ), // quoted l3
01732     KGlobalSettings::linkColor(), // link
01733     KGlobalSettings::visitedLinkColor(), // visited link
01734     Qt::red, // misspelled words
01735     Qt::red, // new msg
01736     Qt::blue, // unread mgs
01737     QColor( 0x00, 0x7F, 0x00 ), // important msg
01738     QColor( 0x00, 0x80, 0xFF ), // light blue // pgp encrypted
01739     QColor( 0x40, 0xFF, 0x40 ), // light green // pgp ok, trusted key
01740     QColor( 0xFF, 0xFF, 0x40 ), // light yellow // pgp ok, untrusted key
01741     QColor( 0xFF, 0xFF, 0x40 ), // light yellow // pgp unchk
01742     Qt::red, // pgp bad
01743     QColor( 0xFF, 0x40, 0x40 ), // warning text color: light red
01744     Qt::lightGray, // colorbar plain bg
01745     Qt::black,     // colorbar plain fg
01746     Qt::black,     // colorbar html  bg
01747     Qt::white,     // colorbar html  fg
01748   };
01749 
01750   for ( int i = 0 ; i < numColorNames ; i++ )
01751     mColorList->setColor( i,
01752       reader.readColorEntry( colorNames[i].configName, &defaultColor[i] ) );
01753   connect( mColorList, SIGNAL( changed( ) ),
01754            this, SLOT( slotEmitChanged( void ) ) );
01755 }
01756 
01757 void AppearancePage::ColorsTab::installProfile( KConfig * profile ) {
01758   KConfigGroup reader( profile, "Reader" );
01759 
01760   if ( reader.hasKey( "defaultColors" ) )
01761     mCustomColorCheck->setChecked( !reader.readBoolEntry( "defaultColors" ) );
01762   if ( reader.hasKey( "RecycleQuoteColors" ) )
01763     mRecycleColorCheck->setChecked( reader.readBoolEntry( "RecycleQuoteColors" ) );
01764 
01765   for ( int i = 0 ; i < numColorNames ; i++ )
01766     if ( reader.hasKey( colorNames[i].configName ) )
01767       mColorList->setColor( i, reader.readColorEntry( colorNames[i].configName ) );
01768 }
01769 
01770 void AppearancePage::ColorsTab::save() {
01771   KConfigGroup reader( KMKernel::config(), "Reader" );
01772 
01773   bool customColors = mCustomColorCheck->isChecked();
01774   reader.writeEntry( "defaultColors", !customColors );
01775 
01776   for ( int i = 0 ; i < numColorNames ; i++ )
01777     // Don't write color info when we use default colors, but write
01778     // if it's already there:
01779     if ( customColors || reader.hasKey( colorNames[i].configName ) )
01780       reader.writeEntry( colorNames[i].configName, mColorList->color(i) );
01781 
01782   reader.writeEntry( "RecycleQuoteColors", mRecycleColorCheck->isChecked() );
01783 }
01784 
01785 QString AppearancePage::LayoutTab::helpAnchor() const {
01786   return QString::fromLatin1("configure-appearance-layout");
01787 }
01788 
01789 static const EnumConfigEntryItem folderListModes[] = {
01790   { "long", I18N_NOOP("Lon&g folder list") },
01791   { "short", I18N_NOOP("Shor&t folder list" ) }
01792 };
01793 static const EnumConfigEntry folderListMode = {
01794   "Geometry", "FolderList", I18N_NOOP("Folder List"),
01795   folderListModes, DIM(folderListModes), 0
01796 };
01797 
01798 
01799 static const EnumConfigEntryItem mimeTreeLocations[] = {
01800   { "top", I18N_NOOP("Abo&ve the message pane") },
01801   { "bottom", I18N_NOOP("&Below the message pane") }
01802 };
01803 static const EnumConfigEntry mimeTreeLocation = {
01804   "Reader", "MimeTreeLocation", I18N_NOOP("Message Structure Viewer Placement"),
01805   mimeTreeLocations, DIM(mimeTreeLocations), 1
01806 };
01807 
01808 static const EnumConfigEntryItem mimeTreeModes[] = {
01809   { "never", I18N_NOOP("Show &never") },
01810   { "smart", I18N_NOOP("Show only for non-plaintext &messages") },
01811   { "always", I18N_NOOP("Show alway&s") }
01812 };
01813 static const EnumConfigEntry mimeTreeMode = {
01814   "Reader", "MimeTreeMode", I18N_NOOP("Message Structure Viewer"),
01815   mimeTreeModes, DIM(mimeTreeModes), 1
01816 };
01817 
01818 
01819 static const EnumConfigEntryItem readerWindowModes[] = {
01820   { "hide", I18N_NOOP("&Do not show a message preview pane") },
01821   { "below", I18N_NOOP("Show the message preview pane belo&w the message list") },
01822   { "right", I18N_NOOP("Show the message preview pane ne&xt to the message list") }
01823 };
01824 static const EnumConfigEntry readerWindowMode = {
01825   "Geometry", "readerWindowMode", I18N_NOOP("Message Preview Pane"),
01826   readerWindowModes, DIM(readerWindowModes), 1
01827 };
01828 
01829 AppearancePageLayoutTab::AppearancePageLayoutTab( QWidget * parent, const char * name )
01830   : ConfigModuleTab( parent, name )
01831 {
01832   // tmp. vars:
01833   QVBoxLayout * vlay;
01834 
01835   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
01836 
01837   // "folder list" radio buttons:
01838   populateButtonGroup( mFolderListGroup = new QHButtonGroup( this ), folderListMode );
01839   vlay->addWidget( mFolderListGroup );
01840   connect( mFolderListGroup, SIGNAL ( clicked( int ) ),
01841            this, SLOT( slotEmitChanged() ) );
01842 
01843   // "show reader window" radio buttons:
01844   populateButtonGroup( mReaderWindowModeGroup = new QVButtonGroup( this ), readerWindowMode );
01845   vlay->addWidget( mReaderWindowModeGroup );
01846   connect( mReaderWindowModeGroup, SIGNAL ( clicked( int ) ),
01847            this, SLOT( slotEmitChanged() ) );
01848 
01849   // "Show MIME Tree" radio buttons:
01850   populateButtonGroup( mMIMETreeModeGroup = new QVButtonGroup( this ), mimeTreeMode );
01851   vlay->addWidget( mMIMETreeModeGroup );
01852   connect( mMIMETreeModeGroup, SIGNAL ( clicked( int ) ),
01853            this, SLOT( slotEmitChanged() ) );
01854 
01855   // "MIME Tree Location" radio buttons:
01856   populateButtonGroup( mMIMETreeLocationGroup = new QHButtonGroup( this ), mimeTreeLocation );
01857   vlay->addWidget( mMIMETreeLocationGroup );
01858   connect( mMIMETreeLocationGroup, SIGNAL ( clicked( int ) ),
01859            this, SLOT( slotEmitChanged() ) );
01860 
01861   vlay->addStretch( 10 ); // spacer
01862 }
01863 
01864 void AppearancePage::LayoutTab::doLoadOther() {
01865   const KConfigGroup reader( KMKernel::config(), "Reader" );
01866   const KConfigGroup geometry( KMKernel::config(), "Geometry" );
01867 
01868   loadWidget( mFolderListGroup, geometry, folderListMode );
01869   loadWidget( mMIMETreeLocationGroup, reader, mimeTreeLocation );
01870   loadWidget( mMIMETreeModeGroup, reader, mimeTreeMode );
01871   loadWidget( mReaderWindowModeGroup, geometry, readerWindowMode );
01872 }
01873 
01874 void AppearancePage::LayoutTab::installProfile( KConfig * profile ) {
01875   const KConfigGroup reader( profile, "Reader" );
01876   const KConfigGroup geometry( profile, "Geometry" );
01877 
01878   loadProfile( mFolderListGroup, geometry, folderListMode );
01879   loadProfile( mMIMETreeLocationGroup, reader, mimeTreeLocation );
01880   loadProfile( mMIMETreeModeGroup, reader, mimeTreeMode );
01881   loadProfile( mReaderWindowModeGroup, geometry, readerWindowMode );
01882 }
01883 
01884 void AppearancePage::LayoutTab::save() {
01885   KConfigGroup reader( KMKernel::config(), "Reader" );
01886   KConfigGroup geometry( KMKernel::config(), "Geometry" );
01887 
01888   saveButtonGroup( mFolderListGroup, geometry, folderListMode );
01889   saveButtonGroup( mMIMETreeLocationGroup, reader, mimeTreeLocation );
01890   saveButtonGroup( mMIMETreeModeGroup, reader, mimeTreeMode );
01891   saveButtonGroup( mReaderWindowModeGroup, geometry, readerWindowMode );
01892 }
01893 
01894 //
01895 // Appearance Message List
01896 //
01897 
01898 QString AppearancePage::HeadersTab::helpAnchor() const {
01899   return QString::fromLatin1("configure-appearance-headers");
01900 }
01901 
01902 static const struct {
01903   const char * displayName;
01904   DateFormatter::FormatType dateDisplay;
01905 } dateDisplayConfig[] = {
01906   { I18N_NOOP("Sta&ndard format (%1)"), KMime::DateFormatter::CTime },
01907   { I18N_NOOP("Locali&zed format (%1)"), KMime::DateFormatter::Localized },
01908   { I18N_NOOP("Fancy for&mat (%1)"), KMime::DateFormatter::Fancy },
01909   { I18N_NOOP("C&ustom format (Shift+F1 for help):"),
01910     KMime::DateFormatter::Custom }
01911 };
01912 static const int numDateDisplayConfig =
01913   sizeof dateDisplayConfig / sizeof *dateDisplayConfig;
01914 
01915 AppearancePageHeadersTab::AppearancePageHeadersTab( QWidget * parent, const char * name )
01916   : ConfigModuleTab( parent, name ),
01917     mCustomDateFormatEdit( 0 )
01918 {
01919   // tmp. vars:
01920   QButtonGroup * group;
01921   QRadioButton * radio;
01922 
01923   QVBoxLayout * vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
01924 
01925   // "General Options" group:
01926   group = new QVButtonGroup( i18n( "General Options" ), this );
01927   group->layout()->setSpacing( KDialog::spacingHint() );
01928 
01929   mMessageSizeCheck = new QCheckBox( i18n("Display messa&ge sizes"), group );
01930 
01931   mCryptoIconsCheck = new QCheckBox( i18n( "Show crypto &icons" ), group );
01932 
01933   mAttachmentCheck = new QCheckBox( i18n("Show attachment icon"), group );
01934 
01935   mNestedMessagesCheck =
01936     new QCheckBox( i18n("&Threaded message list"), group );
01937 
01938   connect( mMessageSizeCheck, SIGNAL( stateChanged( int ) ),
01939            this, SLOT( slotEmitChanged( void ) ) );
01940   connect( mAttachmentCheck, SIGNAL( stateChanged( int ) ),
01941            this, SLOT( slotEmitChanged( void ) ) );
01942   connect( mCryptoIconsCheck, SIGNAL( stateChanged( int ) ),
01943            this, SLOT( slotEmitChanged( void ) ) );
01944   connect( mNestedMessagesCheck, SIGNAL( stateChanged( int ) ),
01945            this, SLOT( slotEmitChanged( void ) ) );
01946 
01947 
01948   vlay->addWidget( group );
01949 
01950   // "Message Header Threading Options" group:
01951   mNestingPolicy =
01952     new QVButtonGroup( i18n("Threaded Message List Options"), this );
01953   mNestingPolicy->layout()->setSpacing( KDialog::spacingHint() );
01954 
01955   mNestingPolicy->insert(
01956     new QRadioButton( i18n("Always &keep threads open"),
01957                       mNestingPolicy ), 0 );
01958   mNestingPolicy->insert(
01959     new QRadioButton( i18n("Threads default to o&pen"),
01960                       mNestingPolicy ), 1 );
01961   mNestingPolicy->insert(
01962     new QRadioButton( i18n("Threads default to closed"),
01963                       mNestingPolicy ), 2 );
01964   mNestingPolicy->insert(
01965     new QRadioButton( i18n("Open threads that contain ne&w, unread "
01966                            "or important messages and open watched threads."),
01967                       mNestingPolicy ), 3 );
01968 
01969   vlay->addWidget( mNestingPolicy );
01970 
01971   connect( mNestingPolicy, SIGNAL( clicked( int ) ),
01972            this, SLOT( slotEmitChanged( void ) ) );
01973 
01974   // "Date Display" group:
01975   mDateDisplay = new QVButtonGroup( i18n("Date Display"), this );
01976   mDateDisplay->layout()->setSpacing( KDialog::spacingHint() );
01977 
01978   for ( int i = 0 ; i < numDateDisplayConfig ; i++ ) {
01979     QString buttonLabel = i18n(dateDisplayConfig[i].displayName);
01980     if ( buttonLabel.contains("%1") )
01981       buttonLabel = buttonLabel.arg( DateFormatter::formatCurrentDate( dateDisplayConfig[i].dateDisplay ) );
01982     radio = new QRadioButton( buttonLabel, mDateDisplay );
01983     mDateDisplay->insert( radio, i );
01984     if ( dateDisplayConfig[i].dateDisplay == DateFormatter::Custom ) {
01985       mCustomDateFormatEdit = new KLineEdit( mDateDisplay );
01986       mCustomDateFormatEdit->setEnabled( false );
01987       connect( radio, SIGNAL(toggled(bool)),
01988                mCustomDateFormatEdit, SLOT(setEnabled(bool)) );
01989       connect( mCustomDateFormatEdit, SIGNAL(textChanged(const QString&)),
01990                this, SLOT(slotEmitChanged(void)) );
01991       QString customDateWhatsThis =
01992         i18n("<qt><p><strong>These expressions may be used for the date:"
01993              "</strong></p>"
01994              "<ul>"
01995              "<li>d - the day as a number without a leading zero (1-31)</li>"
01996              "<li>dd - the day as a number with a leading zero (01-31)</li>"
01997              "<li>ddd - the abbreviated day name (Mon - Sun)</li>"
01998              "<li>dddd - the long day name (Monday - Sunday)</li>"
01999              "<li>M - the month as a number without a leading zero (1-12)</li>"
02000              "<li>MM - the month as a number with a leading zero (01-12)</li>"
02001              "<li>MMM - the abbreviated month name (Jan - Dec)</li>"
02002              "<li>MMMM - the long month name (January - December)</li>"
02003              "<li>yy - the year as a two digit number (00-99)</li>"
02004              "<li>yyyy - the year as a four digit number (0000-9999)</li>"
02005              "</ul>"
02006              "<p><strong>These expressions may be used for the time:"
02007              "</string></p> "
02008              "<ul>"
02009              "<li>h - the hour without a leading zero (0-23 or 1-12 if AM/PM display)</li>"
02010              "<li>hh - the hour with a leading zero (00-23 or 01-12 if AM/PM display)</li>"
02011              "<li>m - the minutes without a leading zero (0-59)</li>"
02012              "<li>mm - the minutes with a leading zero (00-59)</li>"
02013              "<li>s - the seconds without a leading zero (0-59)</li>"
02014              "<li>ss - the seconds with a leading zero (00-59)</li>"
02015              "<li>z - the milliseconds without leading zeroes (0-999)</li>"
02016              "<li>zzz - the milliseconds with leading zeroes (000-999)</li>"
02017              "<li>AP - switch to AM/PM display. AP will be replaced by either \"AM\" or \"PM\".</li>"
02018              "<li>ap - switch to AM/PM display. ap will be replaced by either \"am\" or \"pm\".</li>"
02019              "<li>Z - time zone in numeric form (-0500)</li>"
02020              "</ul>"
02021              "<p><strong>All other input characters will be ignored."
02022              "</strong></p></qt>");
02023       QWhatsThis::add( mCustomDateFormatEdit, customDateWhatsThis );
02024       QWhatsThis::add( radio, customDateWhatsThis );
02025     }
02026   } // end for loop populating mDateDisplay
02027 
02028   vlay->addWidget( mDateDisplay );
02029   connect( mDateDisplay, SIGNAL( clicked( int ) ),
02030            this, SLOT( slotEmitChanged( void ) ) );
02031 
02032 
02033   vlay->addStretch( 10 ); // spacer
02034 }
02035 
02036 void AppearancePage::HeadersTab::doLoadOther() {
02037   KConfigGroup general( KMKernel::config(), "General" );
02038   KConfigGroup geometry( KMKernel::config(), "Geometry" );
02039 
02040   // "General Options":
02041   mNestedMessagesCheck->setChecked( geometry.readBoolEntry( "nestedMessages", false ) );
02042   mMessageSizeCheck->setChecked( general.readBoolEntry( "showMessageSize", false ) );
02043   mCryptoIconsCheck->setChecked( general.readBoolEntry( "showCryptoIcons", false ) );
02044   mAttachmentCheck->setChecked( general.readBoolEntry( "showAttachmentIcon", true ) );
02045 
02046   // "Message Header Threading Options":
02047   int num = geometry.readNumEntry( "nestingPolicy", 3 );
02048   if ( num < 0 || num > 3 ) num = 3;
02049   mNestingPolicy->setButton( num );
02050 
02051   // "Date Display":
02052   setDateDisplay( general.readNumEntry( "dateFormat", DateFormatter::Fancy ),
02053                   general.readEntry( "customDateFormat" ) );
02054 }
02055 
02056 void AppearancePage::HeadersTab::setDateDisplay( int num, const QString & format ) {
02057   DateFormatter::FormatType dateDisplay =
02058     static_cast<DateFormatter::FormatType>( num );
02059 
02060   // special case: needs text for the line edit:
02061   if ( dateDisplay == DateFormatter::Custom )
02062     mCustomDateFormatEdit->setText( format );
02063 
02064   for ( int i = 0 ; i < numDateDisplayConfig ; i++ )
02065     if ( dateDisplay == dateDisplayConfig[i].dateDisplay ) {
02066       mDateDisplay->setButton( i );
02067       return;
02068     }
02069   // fell through since none found:
02070   mDateDisplay->setButton( numDateDisplayConfig - 2 ); // default
02071 }
02072 
02073 void AppearancePage::HeadersTab::installProfile( KConfig * profile ) {
02074   KConfigGroup general( profile, "General" );
02075   KConfigGroup geometry( profile, "Geometry" );
02076 
02077   if ( geometry.hasKey( "nestedMessages" ) )
02078     mNestedMessagesCheck->setChecked( geometry.readBoolEntry( "nestedMessages" ) );
02079   if ( general.hasKey( "showMessageSize" ) )
02080     mMessageSizeCheck->setChecked( general.readBoolEntry( "showMessageSize" ) );
02081 
02082   if( general.hasKey( "showCryptoIcons" ) )
02083     mCryptoIconsCheck->setChecked( general.readBoolEntry( "showCryptoIcons" ) );
02084   if ( general.hasKey( "showAttachmentIcon" ) )
02085     mAttachmentCheck->setChecked( general.readBoolEntry( "showAttachmentIcon" ) );
02086 
02087   if ( geometry.hasKey( "nestingPolicy" ) ) {
02088     int num = geometry.readNumEntry( "nestingPolicy" );
02089     if ( num < 0 || num > 3 ) num = 3;
02090     mNestingPolicy->setButton( num );
02091   }
02092 
02093   if ( general.hasKey( "dateFormat" ) )
02094     setDateDisplay( general.readNumEntry( "dateFormat" ),
02095                    general.readEntry( "customDateFormat" ) );
02096 }
02097 
02098 void AppearancePage::HeadersTab::save() {
02099   KConfigGroup general( KMKernel::config(), "General" );
02100   KConfigGroup geometry( KMKernel::config(), "Geometry" );
02101 
02102   if ( geometry.readBoolEntry( "nestedMessages", false )
02103        != mNestedMessagesCheck->isChecked() ) {
02104     int result = KMessageBox::warningContinueCancel( this,
02105                    i18n("Changing the global threading setting will override "
02106                         "all folder specific values."),
02107                    QString::null, KStdGuiItem::cont(), "threadOverride" );
02108     if ( result == KMessageBox::Continue ) {
02109       geometry.writeEntry( "nestedMessages", mNestedMessagesCheck->isChecked() );
02110       // remove all threadMessagesOverride keys from all [Folder-*] groups:
02111       QStringList groups = KMKernel::config()->groupList().grep( QRegExp("^Folder-") );
02112       kdDebug(5006) << "groups.count() == " << groups.count() << endl;
02113       for ( QStringList::const_iterator it = groups.begin() ; it != groups.end() ; ++it ) {
02114         KConfigGroup group( KMKernel::config(), *it );
02115         group.deleteEntry( "threadMessagesOverride" );
02116       }
02117     }
02118   }
02119 
02120   geometry.writeEntry( "nestingPolicy",
02121                        mNestingPolicy->id( mNestingPolicy->selected() ) );
02122   general.writeEntry( "showMessageSize", mMessageSizeCheck->isChecked() );
02123   general.writeEntry( "showCryptoIcons", mCryptoIconsCheck->isChecked() );
02124   general.writeEntry( "showAttachmentIcon", mAttachmentCheck->isChecked() );
02125 
02126   int dateDisplayID = mDateDisplay->id( mDateDisplay->selected() );
02127   // check bounds:
02128   assert( dateDisplayID >= 0 ); assert( dateDisplayID < numDateDisplayConfig );
02129   general.writeEntry( "dateFormat",
02130                       dateDisplayConfig[ dateDisplayID ].dateDisplay );
02131   general.writeEntry( "customDateFormat", mCustomDateFormatEdit->text() );
02132 }
02133 
02134 
02135 //
02136 // Message Window
02137 //
02138 
02139 
02140 static const BoolConfigEntry showColorbarMode = {
02141   "Reader", "showColorbar", I18N_NOOP("Show HTML stat&us bar"), false
02142 };
02143 
02144 static const BoolConfigEntry showSpamStatusMode = {
02145   "Reader", "showSpamStatus", I18N_NOOP("Show s&pam status in fancy headers"), true
02146 };
02147 
02148 static const BoolConfigEntry showEmoticons = {
02149   "Reader", "ShowEmoticons", I18N_NOOP("Replace smileys by emoticons"), true
02150 };
02151 
02152 static const BoolConfigEntry shrinkQuotes = {
02153   "Reader", "ShrinkQuotes", I18N_NOOP("Use smaller font for quoted text"), false
02154 };
02155 
02156 static const BoolConfigEntry showExpandQuotesMark= {
02157   "Reader", "ShowExpandQuotesMark", I18N_NOOP("Show expand/collapse quote marks"), false
02158 };
02159 
02160 
02161 QString AppearancePage::ReaderTab::helpAnchor() const {
02162   return QString::fromLatin1("configure-appearance-reader");
02163 }
02164 
02165 AppearancePageReaderTab::AppearancePageReaderTab( QWidget * parent,
02166                                                   const char * name )
02167   : ConfigModuleTab( parent, name )
02168 {
02169   QVBoxLayout *vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
02170 
02171   // "show colorbar" check box:
02172   populateCheckBox( mShowColorbarCheck = new QCheckBox( this ), showColorbarMode );
02173   vlay->addWidget( mShowColorbarCheck );
02174   connect( mShowColorbarCheck, SIGNAL ( stateChanged( int ) ),
02175            this, SLOT( slotEmitChanged() ) );
02176 
02177   // "show spam status" check box;
02178   populateCheckBox( mShowSpamStatusCheck = new QCheckBox( this ), showSpamStatusMode );
02179   vlay->addWidget( mShowSpamStatusCheck );
02180   connect( mShowSpamStatusCheck, SIGNAL ( stateChanged( int ) ),
02181            this, SLOT( slotEmitChanged() ) );
02182 
02183   // "replace smileys by emoticons" check box;
02184   populateCheckBox( mShowEmoticonsCheck = new QCheckBox( this ), showEmoticons );
02185   vlay->addWidget( mShowEmoticonsCheck );
02186   connect( mShowEmoticonsCheck, SIGNAL ( stateChanged( int ) ),
02187            this, SLOT( slotEmitChanged() ) );
02188 
02189   // "Use smaller font for quoted text" check box
02190   mShrinkQuotesCheck = new QCheckBox( i18n( shrinkQuotes.desc ), this,
02191                                       "kcfg_ShrinkQuotes" );
02192   vlay->addWidget( mShrinkQuotesCheck );
02193   connect( mShrinkQuotesCheck, SIGNAL( stateChanged( int ) ),
02194            this, SLOT( slotEmitChanged() ) );
02195 
02196   // "Show expand/collaps quote marks" check box;
02197   QHBoxLayout *hlay= new QHBoxLayout( vlay ); // inherits spacing
02198   populateCheckBox( mShowExpandQuotesMark= new QCheckBox( this ), showExpandQuotesMark);
02199   hlay->addWidget( mShowExpandQuotesMark);
02200   connect( mShowExpandQuotesMark, SIGNAL ( stateChanged( int ) ),
02201            this, SLOT( slotEmitChanged() ) );
02202 
02203   hlay->addStretch( 1 );
02204   mCollapseQuoteLevelSpin = new KIntSpinBox( 0/*min*/,10/*max*/,1/*step*/,
02205       3/*init*/,10/*base*/,this );
02206 
02207   QLabel *label = new QLabel( mCollapseQuoteLevelSpin,
02208            GlobalSettings::self()->collapseQuoteLevelSpinItem()->label(), this );
02209 
02210   hlay->addWidget( label );
02211 
02212   mCollapseQuoteLevelSpin->setEnabled( false ); //since !mShowExpandQuotesMark->isCheckec()
02213   connect(  mCollapseQuoteLevelSpin, SIGNAL( valueChanged( int ) ),
02214       this, SLOT( slotEmitChanged( void ) ) );
02215   hlay->addWidget( mCollapseQuoteLevelSpin);
02216 
02217   connect( mShowExpandQuotesMark, SIGNAL( toggled( bool ) ),
02218       mCollapseQuoteLevelSpin, SLOT( setEnabled( bool ) ) );
02219 
02220   // Fallback Character Encoding
02221   hlay = new QHBoxLayout( vlay ); // inherits spacing
02222   mCharsetCombo = new QComboBox( this );
02223   mCharsetCombo->insertStringList( KMMsgBase::supportedEncodings( false ) );
02224 
02225   connect( mCharsetCombo, SIGNAL( activated( int ) ),
02226            this, SLOT( slotEmitChanged( void ) ) );
02227 
02228   QString fallbackCharsetWhatsThis =
02229     i18n( GlobalSettings::self()->fallbackCharacterEncodingItem()->whatsThis().utf8() );
02230   QWhatsThis::add( mCharsetCombo, fallbackCharsetWhatsThis );
02231 
02232   label = new QLabel( i18n("Fallback ch&aracter encoding:"), this );
02233   label->setBuddy( mCharsetCombo );
02234 
02235   hlay->addWidget( label );
02236   hlay->addWidget( mCharsetCombo );
02237 
02238   // Override Character Encoding
02239   QHBoxLayout *hlay2 = new QHBoxLayout( vlay ); // inherits spacing
02240   mOverrideCharsetCombo = new QComboBox( this );
02241   QStringList encodings = KMMsgBase::supportedEncodings( false );
02242   encodings.prepend( i18n( "Auto" ) );
02243   mOverrideCharsetCombo->insertStringList( encodings );
02244   mOverrideCharsetCombo->setCurrentItem(0);
02245 
02246   connect( mOverrideCharsetCombo, SIGNAL( activated( int ) ),
02247            this, SLOT( slotEmitChanged( void ) ) );
02248 
02249   QString overrideCharsetWhatsThis =
02250     i18n( GlobalSettings::self()->overrideCharacterEncodingItem()->whatsThis().utf8() );
02251   QWhatsThis::add( mOverrideCharsetCombo, overrideCharsetWhatsThis );
02252 
02253   label = new QLabel( i18n("&Override character encoding:"), this );
02254   label->setBuddy( mOverrideCharsetCombo );
02255 
02256   hlay2->addWidget( label );
02257   hlay2->addWidget( mOverrideCharsetCombo );
02258 
02259   vlay->addStretch( 100 ); // spacer
02260 }
02261 
02262 
02263 void AppearancePage::ReaderTab::readCurrentFallbackCodec()
02264 {
02265   QStringList encodings = KMMsgBase::supportedEncodings( false );
02266   QStringList::ConstIterator it( encodings.begin() );
02267   QStringList::ConstIterator end( encodings.end() );
02268   QString currentEncoding = GlobalSettings::self()->fallbackCharacterEncoding();
02269   currentEncoding = currentEncoding.replace( "iso ", "iso-", false );
02271   int i = 0;
02272   int indexOfLatin9 = 0;
02273   bool found = false;
02274   for( ; it != end; ++it)
02275   {
02276     const QString encoding = KGlobal::charsets()->encodingForName(*it);
02277     if ( encoding == "iso-8859-15" )
02278         indexOfLatin9 = i;
02279     if( encoding == currentEncoding )
02280     {
02281       mCharsetCombo->setCurrentItem( i );
02282       found = true;
02283       break;
02284     }
02285     i++;
02286   }
02287   if ( !found ) // nothing matched, use latin9
02288     mCharsetCombo->setCurrentItem( indexOfLatin9 );
02289 }
02290 
02291 void AppearancePage::ReaderTab::readCurrentOverrideCodec()
02292 {
02293   const QString &currentOverrideEncoding = GlobalSettings::self()->overrideCharacterEncoding();
02294   if ( currentOverrideEncoding.isEmpty() ) {
02295     mOverrideCharsetCombo->setCurrentItem( 0 );
02296     return;
02297   }
02298   QStringList encodings = KMMsgBase::supportedEncodings( false );
02299   encodings.prepend( i18n( "Auto" ) );
02300   QStringList::Iterator it( encodings.begin() );
02301   QStringList::Iterator end( encodings.end() );
02302   int i = 0;
02303   for( ; it != end; ++it)
02304   {
02305     if( KGlobal::charsets()->encodingForName(*it) == currentOverrideEncoding )
02306     {
02307       mOverrideCharsetCombo->setCurrentItem( i );
02308       break;
02309     }
02310     i++;
02311   }
02312   if ( i == encodings.size() ) {
02313     // the current value of overrideCharacterEncoding is an unknown encoding => reset to Auto
02314     kdWarning(5006) << "Unknown override character encoding \"" << currentOverrideEncoding
02315                     << "\". Resetting to Auto." << endl;
02316     mOverrideCharsetCombo->setCurrentItem( 0 );
02317     GlobalSettings::self()->setOverrideCharacterEncoding( QString::null );
02318   }
02319 }
02320 
02321 void AppearancePage::ReaderTab::doLoadFromGlobalSettings()
02322 {
02323   mShowEmoticonsCheck->setChecked( GlobalSettings::self()->showEmoticons() );
02324   mShrinkQuotesCheck->setChecked( GlobalSettings::self()->shrinkQuotes() );
02325   mShowExpandQuotesMark->setChecked( GlobalSettings::self()->showExpandQuotesMark() );
02326   mCollapseQuoteLevelSpin->setValue( GlobalSettings::self()->collapseQuoteLevelSpin() );
02327   readCurrentFallbackCodec();
02328   readCurrentOverrideCodec();
02329 }
02330 
02331 void AppearancePage::ReaderTab::doLoadOther()
02332 {
02333   const KConfigGroup reader( KMKernel::config(), "Reader" );
02334   loadWidget( mShowColorbarCheck, reader, showColorbarMode );
02335   loadWidget( mShowSpamStatusCheck, reader, showSpamStatusMode );
02336 }
02337 
02338 
02339 void AppearancePage::ReaderTab::save() {
02340   KConfigGroup reader( KMKernel::config(), "Reader" );
02341   saveCheckBox( mShowColorbarCheck, reader, showColorbarMode );
02342   saveCheckBox( mShowSpamStatusCheck, reader, showSpamStatusMode );
02343   GlobalSettings::self()->setShowEmoticons( mShowEmoticonsCheck->isChecked() );
02344   GlobalSettings::self()->setShrinkQuotes( mShrinkQuotesCheck->isChecked() );
02345   GlobalSettings::self()->setShowExpandQuotesMark( mShowExpandQuotesMark->isChecked() );
02346 
02347   GlobalSettings::self()->setCollapseQuoteLevelSpin( mCollapseQuoteLevelSpin->value() );
02348   GlobalSettings::self()->setFallbackCharacterEncoding(
02349       KGlobal::charsets()->encodingForName( mCharsetCombo->currentText() ) );
02350   GlobalSettings::self()->setOverrideCharacterEncoding(
02351       mOverrideCharsetCombo->currentItem() == 0 ?
02352         QString() :
02353         KGlobal::charsets()->encodingForName( mOverrideCharsetCombo->currentText() ) );
02354 }
02355 
02356 
02357 void AppearancePage::ReaderTab::installProfile( KConfig * /* profile */ ) {
02358   const KConfigGroup reader( KMKernel::config(), "Reader" );
02359   loadProfile( mShowColorbarCheck, reader, showColorbarMode );
02360   loadProfile( mShowSpamStatusCheck, reader, showSpamStatusMode );
02361   loadProfile( mShowEmoticonsCheck, reader, showEmoticons );
02362   loadProfile( mShrinkQuotesCheck, reader, shrinkQuotes );
02363   loadProfile( mShowExpandQuotesMark, reader, showExpandQuotesMark);
02364 }
02365 
02366 
02367 QString AppearancePage::SystemTrayTab::helpAnchor() const {
02368   return QString::fromLatin1("configure-appearance-systemtray");
02369 }
02370 
02371 AppearancePageSystemTrayTab::AppearancePageSystemTrayTab( QWidget * parent,
02372                                                           const char * name )
02373   : ConfigModuleTab( parent, name )
02374 {
02375   QVBoxLayout * vlay = new QVBoxLayout( this, KDialog::marginHint(),
02376                                         KDialog::spacingHint() );
02377 
02378   // "Enable system tray applet" check box
02379   mSystemTrayCheck = new QCheckBox( i18n("Enable system tray icon"), this );
02380   vlay->addWidget( mSystemTrayCheck );
02381   connect( mSystemTrayCheck, SIGNAL( stateChanged( int ) ),
02382            this, SLOT( slotEmitChanged( void ) ) );
02383 
02384   // System tray modes
02385   mSystemTrayGroup = new QVButtonGroup( i18n("System Tray Mode"), this );
02386   mSystemTrayGroup->layout()->setSpacing( KDialog::spacingHint() );
02387   vlay->addWidget( mSystemTrayGroup );
02388   connect( mSystemTrayGroup, SIGNAL( clicked( int ) ),
02389            this, SLOT( slotEmitChanged( void ) ) );
02390   connect( mSystemTrayCheck, SIGNAL( toggled( bool ) ),
02391            mSystemTrayGroup, SLOT( setEnabled( bool ) ) );
02392 
02393   mSystemTrayGroup->insert( new QRadioButton( i18n("Always show KMail in system tray"), mSystemTrayGroup ),
02394                             GlobalSettings::EnumSystemTrayPolicy::ShowAlways );
02395 
02396   mSystemTrayGroup->insert( new QRadioButton( i18n("Only show KMail in system tray if there are unread messages"), mSystemTrayGroup ),
02397                             GlobalSettings::EnumSystemTrayPolicy::ShowOnUnread );
02398 
02399   vlay->addStretch( 10 ); // spacer
02400 }
02401 
02402 void AppearancePage::SystemTrayTab::doLoadFromGlobalSettings() {
02403   mSystemTrayCheck->setChecked( GlobalSettings::self()->systemTrayEnabled() );
02404   mSystemTrayGroup->setButton( GlobalSettings::self()->systemTrayPolicy() );
02405   mSystemTrayGroup->setEnabled( mSystemTrayCheck->isChecked() );
02406 }
02407 
02408 void AppearancePage::SystemTrayTab::installProfile( KConfig * profile ) {
02409   KConfigGroup general( profile, "General" );
02410 
02411   if ( general.hasKey( "SystemTrayEnabled" ) ) {
02412     mSystemTrayCheck->setChecked( general.readBoolEntry( "SystemTrayEnabled" ) );
02413   }
02414   if ( general.hasKey( "SystemTrayPolicy" ) ) {
02415     mSystemTrayGroup->setButton( general.readNumEntry( "SystemTrayPolicy" ) );
02416   }
02417   mSystemTrayGroup->setEnabled( mSystemTrayCheck->isChecked() );
02418 }
02419 
02420 void AppearancePage::SystemTrayTab::save() {
02421   GlobalSettings::self()->setSystemTrayEnabled( mSystemTrayCheck->isChecked() );
02422   GlobalSettings::self()->setSystemTrayPolicy( mSystemTrayGroup->id( mSystemTrayGroup->selected() ) );
02423 }
02424 
02425 
02426 // *************************************************************
02427 // *                                                           *
02428 // *                      ComposerPage                         *
02429 // *                                                           *
02430 // *************************************************************
02431 
02432 QString ComposerPage::helpAnchor() const {
02433   return QString::fromLatin1("configure-composer");
02434 }
02435 
02436 ComposerPage::ComposerPage( QWidget * parent, const char * name )
02437   : ConfigModuleWithTabs( parent, name )
02438 {
02439   //
02440   // "General" tab:
02441   //
02442   mGeneralTab = new GeneralTab();
02443   addTab( mGeneralTab, i18n("&General") );
02444   addConfig( GlobalSettings::self(), mGeneralTab );
02445 
02446   //
02447   // "Phrases" tab:
02448   //
02449   mPhrasesTab = new PhrasesTab();
02450   addTab( mPhrasesTab, i18n("&Phrases") );
02451 
02452   //
02453   // "Subject" tab:
02454   //
02455   mSubjectTab = new SubjectTab();
02456   addTab( mSubjectTab, i18n("&Subject") );
02457   addConfig( GlobalSettings::self(), mSubjectTab );
02458 
02459   //
02460   // "Charset" tab:
02461   //
02462   mCharsetTab = new CharsetTab();
02463   addTab( mCharsetTab, i18n("Cha&rset") );
02464 
02465   //
02466   // "Headers" tab:
02467   //
02468   mHeadersTab = new HeadersTab();
02469   addTab( mHeadersTab, i18n("H&eaders") );
02470 
02471   //
02472   // "Attachments" tab:
02473   //
02474   mAttachmentsTab = new AttachmentsTab();
02475   addTab( mAttachmentsTab, i18n("Config->Composer->Attachments", "A&ttachments") );
02476   load();
02477 }
02478 
02479 QString ComposerPage::GeneralTab::helpAnchor() const {
02480   return QString::fromLatin1("configure-composer-general");
02481 }
02482 
02483 ComposerPageGeneralTab::ComposerPageGeneralTab( QWidget * parent, const char * name )
02484   : ConfigModuleTab( parent, name )
02485 {
02486   // tmp. vars:
02487   QVBoxLayout *vlay;
02488   QHBoxLayout *hlay;
02489   QGroupBox   *group;
02490   QLabel      *label;
02491   QHBox       *hbox;
02492   QString      msg;
02493 
02494   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
02495 
02496   // some check buttons...
02497   mAutoAppSignFileCheck = new QCheckBox(
02498            GlobalSettings::self()->autoTextSignatureItem()->label(),
02499            this );
02500   vlay->addWidget( mAutoAppSignFileCheck );
02501   connect( mAutoAppSignFileCheck, SIGNAL( stateChanged(int) ),
02502            this, SLOT( slotEmitChanged( void ) ) );
02503 
02504   mSmartQuoteCheck = new QCheckBox(
02505            GlobalSettings::self()->smartQuoteItem()->label(),
02506            this, "kcfg_SmartQuote" );
02507   vlay->addWidget( mSmartQuoteCheck );
02508   connect( mSmartQuoteCheck, SIGNAL( stateChanged(int) ),
02509            this, SLOT( slotEmitChanged( void ) ) );
02510 
02511   mAutoRequestMDNCheck = new QCheckBox(
02512            GlobalSettings::self()->requestMDNItem()->label(),
02513            this, "kcfg_RequestMDN" );
02514   vlay->addWidget( mAutoRequestMDNCheck );
02515   connect( mAutoRequestMDNCheck, SIGNAL( stateChanged(int) ),
02516            this, SLOT( slotEmitChanged( void ) ) );
02517 
02518   mShowRecentAddressesInComposer = new QCheckBox(
02519            GlobalSettings::self()->showRecentAddressesInComposerItem()->label(),
02520            this, "kcfg_ShowRecentAddressesInComposer" );
02521   vlay->addWidget( mShowRecentAddressesInComposer );
02522   connect( mShowRecentAddressesInComposer, SIGNAL( stateChanged(int) ),
02523            this, SLOT( slotEmitChanged( void ) ) );
02524 
02525   // a checkbox for "word wrap" and a spinbox for the column in
02526   // which to wrap:
02527   hlay = new QHBoxLayout( vlay ); // inherits spacing
02528   mWordWrapCheck = new QCheckBox(
02529            GlobalSettings::self()->wordWrapItem()->label(),
02530            this, "kcfg_WordWrap" );
02531   hlay->addWidget( mWordWrapCheck );
02532   connect( mWordWrapCheck, SIGNAL( stateChanged(int) ),
02533            this, SLOT( slotEmitChanged( void ) ) );
02534 
02535   mWrapColumnSpin = new KIntSpinBox( 30/*min*/, 78/*max*/, 1/*step*/,
02536            78/*init*/, 10 /*base*/, this, "kcfg_LineWrapWidth" );
02537   mWrapColumnSpin->setEnabled( false ); // since !mWordWrapCheck->isChecked()
02538   connect( mWrapColumnSpin, SIGNAL( valueChanged(int) ),
02539            this, SLOT( slotEmitChanged( void ) ) );
02540 
02541   hlay->addWidget( mWrapColumnSpin );
02542   hlay->addStretch( 1 );
02543   // only enable the spinbox if the checkbox is checked:
02544   connect( mWordWrapCheck, SIGNAL(toggled(bool)),
02545            mWrapColumnSpin, SLOT(setEnabled(bool)) );
02546 
02547   hlay = new QHBoxLayout( vlay ); // inherits spacing
02548   mAutoSave = new KIntSpinBox( 0, 60, 1, 1, 10, this, "kcfg_AutosaveInterval" );
02549   label = new QLabel( mAutoSave,
02550            GlobalSettings::self()->autosaveIntervalItem()->label(), this );
02551   hlay->addWidget( label );
02552   hlay->addWidget( mAutoSave );
02553   mAutoSave->setSpecialValueText( i18n("No autosave") );
02554   mAutoSave->setSuffix( i18n(" min") );
02555   hlay->addStretch( 1 );
02556   connect( mAutoSave, SIGNAL( valueChanged(int) ),
02557            this, SLOT( slotEmitChanged( void ) ) );
02558 
02559   hlay = new QHBoxLayout( vlay ); // inherits spacing
02560   QPushButton *completionOrderBtn = new QPushButton( i18n( "Configure Completion Order" ), this );
02561   connect( completionOrderBtn, SIGNAL( clicked() ),
02562            this, SLOT( slotConfigureCompletionOrder() ) );
02563   hlay->addWidget( completionOrderBtn );
02564   hlay->addItem( new QSpacerItem(0, 0) );
02565 
02566   // recent addresses
02567   hlay = new QHBoxLayout( vlay ); // inherits spacing
02568   QPushButton *recentAddressesBtn = new QPushButton( i18n( "Edit Recent Addresses" ), this );
02569   connect( recentAddressesBtn, SIGNAL( clicked() ),
02570            this, SLOT( slotConfigureRecentAddresses() ) );
02571   hlay->addWidget( recentAddressesBtn );
02572   hlay->addItem( new QSpacerItem(0, 0) );
02573 
02574   // The "external editor" group:
02575   group = new QVGroupBox( i18n("External Editor"), this );
02576   group->layout()->setSpacing( KDialog::spacingHint() );
02577 
02578   mExternalEditorCheck = new QCheckBox(
02579            GlobalSettings::self()->useExternalEditorItem()->label(),
02580            group, "kcfg_UseExternalEditor" );
02581   connect( mExternalEditorCheck, SIGNAL( toggled( bool ) ),
02582            this, SLOT( slotEmitChanged( void ) ) );
02583 
02584   hbox = new QHBox( group );
02585   label = new QLabel( GlobalSettings::self()->externalEditorItem()->label(),
02586                    hbox );
02587   mEditorRequester = new KURLRequester( hbox, "kcfg_ExternalEditor" );
02588   connect( mEditorRequester, SIGNAL( urlSelected(const QString&) ),
02589            this, SLOT( slotEmitChanged( void ) ) );
02590   connect( mEditorRequester, SIGNAL( textChanged(const QString&) ),
02591            this, SLOT( slotEmitChanged( void ) ) );
02592 
02593   hbox->setStretchFactor( mEditorRequester, 1 );
02594   label->setBuddy( mEditorRequester );
02595   label->setEnabled( false ); // since !mExternalEditorCheck->isChecked()
02596   // ### FIXME: allow only executables (x-bit when available..)
02597   mEditorRequester->setFilter( "application/x-executable "
02598                                "application/x-shellscript "
02599                                "application/x-desktop" );
02600   mEditorRequester->setEnabled( false ); // !mExternalEditorCheck->isChecked()
02601   connect( mExternalEditorCheck, SIGNAL(toggled(bool)),
02602            label, SLOT(setEnabled(bool)) );
02603   connect( mExternalEditorCheck, SIGNAL(toggled(bool)),
02604            mEditorRequester, SLOT(setEnabled(bool)) );
02605 
02606   label = new QLabel( i18n("<b>%f</b> will be replaced with the "
02607                            "filename to edit."), group );
02608   label->setEnabled( false ); // see above
02609   connect( mExternalEditorCheck, SIGNAL(toggled(bool)),
02610            label, SLOT(setEnabled(bool)) );
02611 
02612   vlay->addWidget( group );
02613   vlay->addStretch( 100 );
02614 }
02615 
02616 void ComposerPage::GeneralTab::doLoadFromGlobalSettings() {
02617   // various check boxes:
02618 
02619   mAutoAppSignFileCheck->setChecked(
02620            GlobalSettings::self()->autoTextSignature()=="auto" );
02621   mSmartQuoteCheck->setChecked( GlobalSettings::self()->smartQuote() );
02622   mAutoRequestMDNCheck->setChecked( GlobalSettings::self()->requestMDN() );
02623   mWordWrapCheck->setChecked( GlobalSettings::self()->wordWrap() );
02624 
02625   mWrapColumnSpin->setValue( GlobalSettings::self()->lineWrapWidth() );
02626   mAutoSave->setValue( GlobalSettings::self()->autosaveInterval() );
02627 
02628   // editor group:
02629   mExternalEditorCheck->setChecked( GlobalSettings::self()->useExternalEditor() );
02630   mEditorRequester->setURL( GlobalSettings::self()->externalEditor() );
02631 }
02632 
02633 void ComposerPage::GeneralTab::installProfile( KConfig * profile ) {
02634   KConfigGroup composer( profile, "Composer" );
02635   KConfigGroup general( profile, "General" );
02636 
02637   if ( composer.hasKey( "signature" ) ) {
02638     bool state = composer.readBoolEntry("signature");
02639     mAutoAppSignFileCheck->setChecked( state );
02640   }
02641   if ( composer.hasKey( "smart-quote" ) )
02642     mSmartQuoteCheck->setChecked( composer.readBoolEntry( "smart-quote" ) );
02643   if ( composer.hasKey( "request-mdn" ) )
02644     mAutoRequestMDNCheck->setChecked( composer.readBoolEntry( "request-mdn" ) );
02645   if ( composer.hasKey( "word-wrap" ) )
02646     mWordWrapCheck->setChecked( composer.readBoolEntry( "word-wrap" ) );
02647   if ( composer.hasKey( "break-at" ) )
02648     mWrapColumnSpin->setValue( composer.readNumEntry( "break-at" ) );
02649   if ( composer.hasKey( "autosave" ) )
02650     mAutoSave->setValue( composer.readNumEntry( "autosave" ) );
02651 
02652   if ( general.hasKey( "use-external-editor" )
02653        && general.hasKey( "external-editor" ) ) {
02654     mExternalEditorCheck->setChecked( general.readBoolEntry( "use-external-editor" ) );
02655     mEditorRequester->setURL( general.readPathEntry( "external-editor" ) );
02656   }
02657 }
02658 
02659 void ComposerPage::GeneralTab::save() {
02660   GlobalSettings::self()->setAutoTextSignature(
02661          mAutoAppSignFileCheck->isChecked() ? "auto" : "manual" );
02662   GlobalSettings::self()->setSmartQuote( mSmartQuoteCheck->isChecked() );
02663   GlobalSettings::self()->setRequestMDN( mAutoRequestMDNCheck->isChecked() );
02664   GlobalSettings::self()->setWordWrap( mWordWrapCheck->isChecked() );
02665 
02666   GlobalSettings::self()->setLineWrapWidth( mWrapColumnSpin->value() );
02667   GlobalSettings::self()->setAutosaveInterval( mAutoSave->value() );
02668 
02669   // editor group:
02670   GlobalSettings::self()->setUseExternalEditor( mExternalEditorCheck->isChecked() );
02671   GlobalSettings::self()->setExternalEditor( mEditorRequester->url() );
02672 }
02673 
02674 void ComposerPage::GeneralTab::slotConfigureRecentAddresses( )
02675 {
02676   KRecentAddress::RecentAddressDialog dlg( this );
02677   dlg.setAddresses( RecentAddresses::self( KMKernel::config() )->addresses() );
02678   if ( dlg.exec() ) {
02679     RecentAddresses::self( KMKernel::config() )->clear();
02680     const QStringList &addrList = dlg.addresses();
02681     QStringList::ConstIterator it;
02682     for ( it = addrList.constBegin(); it != addrList.constEnd(); ++it )
02683       RecentAddresses::self( KMKernel::config() )->add( *it );
02684   }
02685 }
02686 
02687 void ComposerPage::GeneralTab::slotConfigureCompletionOrder( )
02688 {
02689   KPIM::LdapSearch search;
02690   KPIM::CompletionOrderEditor editor( &search, this );
02691   editor.exec();
02692 }
02693 
02694 QString ComposerPage::PhrasesTab::helpAnchor() const {
02695   return QString::fromLatin1("configure-composer-phrases");
02696 }
02697 
02698 ComposerPagePhrasesTab::ComposerPagePhrasesTab( QWidget * parent, const char * name )
02699   : ConfigModuleTab( parent, name )
02700 {
02701   // tmp. vars:
02702   QGridLayout *glay;
02703   QPushButton *button;
02704 
02705   glay = new QGridLayout( this, 7, 3, KDialog::spacingHint() );
02706   glay->setMargin( KDialog::marginHint() );
02707   glay->setColStretch( 1, 1 );
02708   glay->setColStretch( 2, 1 );
02709   glay->setRowStretch( 7, 1 );
02710 
02711   // row 0: help text
02712   glay->addMultiCellWidget( new QLabel( i18n("<qt>The following placeholders are "
02713                                              "supported in the reply phrases:<br>"
02714                                              "<b>%D</b>: date, <b>%S</b>: subject,<br>"
02715                                              "<b>%e</b>: sender's address, <b>%F</b>: sender's name, <b>%f</b>: sender's initials,<br>"
02716                                              "<b>%T</b>: recipient's name, <b>%t</b>: recipient's name and address,<br>"
02717                                              "<b>%C</b>: carbon copy names, <b>%c</b>: carbon copy names and addresses,<br>"
02718                                              "<b>%%</b>: percent sign, <b>%_</b>: space, "
02719                                              "<b>%L</b>: linebreak</qt>"), this ),
02720                             0, 0, 0, 2 ); // row 0; cols 0..2
02721 
02722   // row 1: label and language combo box:
02723   mPhraseLanguageCombo = new LanguageComboBox( false, this );
02724   glay->addWidget( new QLabel( mPhraseLanguageCombo,
02725                                i18n("Lang&uage:"), this ), 1, 0 );
02726   glay->addMultiCellWidget( mPhraseLanguageCombo, 1, 1, 1, 2 );
02727   connect( mPhraseLanguageCombo, SIGNAL(activated(const QString&)),
02728            this, SLOT(slotLanguageChanged(const QString&)) );
02729 
02730   // row 2: "add..." and "remove" push buttons:
02731   button = new QPushButton( i18n("A&dd..."), this );
02732   button->setAutoDefault( false );
02733   glay->addWidget( button, 2, 1 );
02734   mRemoveButton = new QPushButton( i18n("Re&move"), this );
02735   mRemoveButton->setAutoDefault( false );
02736   mRemoveButton->setEnabled( false ); // combo doesn't contain anything...
02737   glay->addWidget( mRemoveButton, 2, 2 );
02738   connect( button, SIGNAL(clicked()),
02739            this, SLOT(slotNewLanguage()) );
02740   connect( mRemoveButton, SIGNAL(clicked()),
02741            this, SLOT(slotRemoveLanguage()) );
02742 
02743   // row 3: "reply to sender" line edit and label:
02744   mPhraseReplyEdit = new KLineEdit( this );
02745   connect( mPhraseReplyEdit, SIGNAL( textChanged( const QString& ) ),
02746            this, SLOT( slotEmitChanged( void ) ) );
02747   glay->addWidget( new QLabel( mPhraseReplyEdit,
02748                                i18n("Reply to se&nder:"), this ), 3, 0 );
02749   glay->addMultiCellWidget( mPhraseReplyEdit, 3, 3, 1, 2 ); // cols 1..2
02750 
02751   // row 4: "reply to all" line edit and label:
02752   mPhraseReplyAllEdit = new KLineEdit( this );
02753   connect( mPhraseReplyAllEdit, SIGNAL( textChanged( const QString& ) ),
02754            this, SLOT( slotEmitChanged( void ) ) );
02755   glay->addWidget( new QLabel( mPhraseReplyAllEdit,
02756                                i18n("Repl&y to all:"), this ), 4, 0 );
02757   glay->addMultiCellWidget( mPhraseReplyAllEdit, 4, 4, 1, 2 ); // cols 1..2
02758 
02759   // row 5: "forward" line edit and label:
02760   mPhraseForwardEdit = new KLineEdit( this );
02761   connect( mPhraseForwardEdit, SIGNAL( textChanged( const QString& ) ),
02762            this, SLOT( slotEmitChanged( void ) ) );
02763   glay->addWidget( new QLabel( mPhraseForwardEdit,
02764                                i18n("&Forward:"), this ), 5, 0 );
02765   glay->addMultiCellWidget( mPhraseForwardEdit, 5, 5, 1, 2 ); // cols 1..2
02766 
02767   // row 6: "quote indicator" line edit and label:
02768   mPhraseIndentPrefixEdit = new KLineEdit( this );
02769   connect( mPhraseIndentPrefixEdit, SIGNAL( textChanged( const QString& ) ),
02770            this, SLOT( slotEmitChanged( void ) ) );
02771   glay->addWidget( new QLabel( mPhraseIndentPrefixEdit,
02772                                i18n("&Quote indicator:"), this ), 6, 0 );
02773   glay->addMultiCellWidget( mPhraseIndentPrefixEdit, 6, 6, 1, 2 );
02774 
02775   // row 7: spacer
02776 }
02777 
02778 
02779 void ComposerPage::PhrasesTab::setLanguageItemInformation( int index ) {
02780   assert( 0 <= index && index < (int)mLanguageList.count() );
02781 
02782   LanguageItem &l = *mLanguageList.at( index );
02783 
02784   mPhraseReplyEdit->setText( l.mReply );
02785   mPhraseReplyAllEdit->setText( l.mReplyAll );
02786   mPhraseForwardEdit->setText( l.mForward );
02787   mPhraseIndentPrefixEdit->setText( l.mIndentPrefix );
02788 }
02789 
02790 void ComposerPage::PhrasesTab::saveActiveLanguageItem() {
02791   int index = mActiveLanguageItem;
02792   if (index == -1) return;
02793   assert( 0 <= index && index < (int)mLanguageList.count() );
02794 
02795   LanguageItem &l = *mLanguageList.at( index );
02796 
02797   l.mReply = mPhraseReplyEdit->text();
02798   l.mReplyAll = mPhraseReplyAllEdit->text();
02799   l.mForward = mPhraseForwardEdit->text();
02800   l.mIndentPrefix = mPhraseIndentPrefixEdit->text();
02801 }
02802 
02803 void ComposerPage::PhrasesTab::slotNewLanguage()
02804 {
02805   NewLanguageDialog dialog( mLanguageList, parentWidget(), "New", true );
02806   if ( dialog.exec() == QDialog::Accepted ) slotAddNewLanguage( dialog.language() );
02807 }
02808 
02809 void ComposerPage::PhrasesTab::slotAddNewLanguage( const QString& lang )
02810 {
02811   mPhraseLanguageCombo->setCurrentItem(
02812     mPhraseLanguageCombo->insertLanguage( lang ) );
02813   KLocale locale("kmail");
02814   locale.setLanguage( lang );
02815   mLanguageList.append(
02816      LanguageItem( lang,
02817                    locale.translate("On %D, you wrote:"),
02818                    locale.translate("On %D, %F wrote:"),
02819                    locale.translate("Forwarded Message"),
02820                    locale.translate(">%_") ) );
02821   mRemoveButton->setEnabled( true );
02822   slotLanguageChanged( QString::null );
02823 }
02824 
02825 void ComposerPage::PhrasesTab::slotRemoveLanguage()
02826 {
02827   assert( mPhraseLanguageCombo->count() > 1 );
02828   int index = mPhraseLanguageCombo->currentItem();
02829   assert( 0 <= index && index < (int)mLanguageList.count() );
02830 
02831   // remove current item from internal list and combobox:
02832   mLanguageList.remove( mLanguageList.at( index ) );
02833   mPhraseLanguageCombo->removeItem( index );
02834 
02835   if ( index >= (int)mLanguageList.count() ) index--;
02836 
02837   mActiveLanguageItem = index;
02838   setLanguageItemInformation( index );
02839   mRemoveButton->setEnabled( mLanguageList.count() > 1 );
02840   emit changed( true );
02841 }
02842 
02843 void ComposerPage::PhrasesTab::slotLanguageChanged( const QString& )
02844 {
02845   int index = mPhraseLanguageCombo->currentItem();
02846   assert( index < (int)mLanguageList.count() );
02847   saveActiveLanguageItem();
02848   mActiveLanguageItem = index;
02849   setLanguageItemInformation( index );
02850   emit changed( true );
02851 }
02852 
02853 
02854 void ComposerPage::PhrasesTab::doLoadFromGlobalSettings() {
02855   mLanguageList.clear();
02856   mPhraseLanguageCombo->clear();
02857   mActiveLanguageItem = -1;
02858 
02859   int numLang = GlobalSettings::self()->replyLanguagesCount();
02860   int currentNr = GlobalSettings::self()->replyCurrentLanguage();
02861 
02862   // build mLanguageList and mPhraseLanguageCombo:
02863   for ( int i = 0 ; i < numLang ; i++ ) {
02864     ReplyPhrases replyPhrases( QString::number(i) );
02865     replyPhrases.readConfig();
02866     QString lang = replyPhrases.language();
02867     mLanguageList.append(
02868          LanguageItem( lang,
02869                        replyPhrases.phraseReplySender(),
02870                        replyPhrases.phraseReplyAll(),
02871                        replyPhrases.phraseForward(),
02872                        replyPhrases.indentPrefix() ) );
02873     mPhraseLanguageCombo->insertLanguage( lang );
02874   }
02875 
02876   if ( currentNr >= numLang || currentNr < 0 )
02877     currentNr = 0;
02878 
02879   if ( numLang == 0 ) {
02880     slotAddNewLanguage( KGlobal::locale()->language() );
02881   }
02882 
02883   mPhraseLanguageCombo->setCurrentItem( currentNr );
02884   mActiveLanguageItem = currentNr;
02885   setLanguageItemInformation( currentNr );
02886   mRemoveButton->setEnabled( mLanguageList.count() > 1 );
02887 }
02888 
02889 void ComposerPage::PhrasesTab::save() {
02890   GlobalSettings::self()->setReplyLanguagesCount( mLanguageList.count() );
02891   GlobalSettings::self()->setReplyCurrentLanguage( mPhraseLanguageCombo->currentItem() );
02892 
02893   saveActiveLanguageItem();
02894   LanguageItemList::Iterator it = mLanguageList.begin();
02895   for ( int i = 0 ; it != mLanguageList.end() ; ++it, ++i ) {
02896     ReplyPhrases replyPhrases( QString::number(i) );
02897     replyPhrases.setLanguage( (*it).mLanguage );
02898     replyPhrases.setPhraseReplySender( (*it).mReply );
02899     replyPhrases.setPhraseReplyAll( (*it).mReplyAll );
02900     replyPhrases.setPhraseForward( (*it).mForward );
02901     replyPhrases.setIndentPrefix( (*it).mIndentPrefix );
02902     replyPhrases.writeConfig();
02903   }
02904 }
02905 
02906 QString ComposerPage::SubjectTab::helpAnchor() const {
02907   return QString::fromLatin1("configure-composer-subject");
02908 }
02909 
02910 ComposerPageSubjectTab::ComposerPageSubjectTab( QWidget * parent, const char * name )
02911   : ConfigModuleTab( parent, name )
02912 {
02913   // tmp. vars:
02914   QVBoxLayout *vlay;
02915   QGroupBox   *group;
02916   QLabel      *label;
02917 
02918 
02919   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
02920 
02921   group = new QVGroupBox( i18n("Repl&y Subject Prefixes"), this );
02922   group->layout()->setSpacing( KDialog::spacingHint() );
02923 
02924   // row 0: help text:
02925   label = new QLabel( i18n("Recognize any sequence of the following prefixes\n"
02926                            "(entries are case-insensitive regular expressions):"), group );
02927   label->setAlignment( AlignLeft|WordBreak );
02928 
02929   // row 1, string list editor:
02930   SimpleStringListEditor::ButtonCode buttonCode =
02931     static_cast<SimpleStringListEditor::ButtonCode>( SimpleStringListEditor::Add | SimpleStringListEditor::Remove | SimpleStringListEditor::Modify );
02932   mReplyListEditor =
02933     new SimpleStringListEditor( group, 0, buttonCode,
02934                                 i18n("A&dd..."), i18n("Re&move"),
02935                                 i18n("Mod&ify..."),
02936                                 i18n("Enter new reply prefix:") );
02937   connect( mReplyListEditor, SIGNAL( changed( void ) ),
02938            this, SLOT( slotEmitChanged( void ) ) );
02939 
02940   // row 2: "replace [...]" check box:
02941   mReplaceReplyPrefixCheck = new QCheckBox(
02942      GlobalSettings::self()->replaceReplyPrefixItem()->label(),
02943      group, "kcfg_ReplaceReplyPrefix" );
02944   connect( mReplaceReplyPrefixCheck, SIGNAL( stateChanged( int ) ),
02945            this, SLOT( slotEmitChanged( void ) ) );
02946 
02947   vlay->addWidget( group );
02948 
02949 
02950   group = new QVGroupBox( i18n("For&ward Subject Prefixes"), this );
02951   group->layout()->setSpacing( KDialog::marginHint() );
02952 
02953   // row 0: help text:
02954   label= new QLabel( i18n("Recognize any sequence of the following prefixes\n"
02955                           "(entries are case-insensitive regular expressions):"), group );
02956   label->setAlignment( AlignLeft|WordBreak );
02957 
02958   // row 1: string list editor
02959   mForwardListEditor =
02960     new SimpleStringListEditor( group, 0, buttonCode,
02961                                 i18n("Add..."),
02962                                 i18n("Remo&ve"),
02963                                 i18n("Modify..."),
02964                                 i18n("Enter new forward prefix:") );
02965   connect( mForwardListEditor, SIGNAL( changed( void ) ),
02966            this, SLOT( slotEmitChanged( void ) ) );
02967 
02968   // row 3: "replace [...]" check box:
02969   mReplaceForwardPrefixCheck = new QCheckBox(
02970        GlobalSettings::self()->replaceForwardPrefixItem()->label(),
02971        group, "kcfg_ReplaceForwardPrefix" );
02972   connect( mReplaceForwardPrefixCheck, SIGNAL( stateChanged( int ) ),
02973            this, SLOT( slotEmitChanged( void ) ) );
02974 
02975   vlay->addWidget( group );
02976 }
02977 
02978 void ComposerPage::SubjectTab::doLoadFromGlobalSettings() {
02979   mReplyListEditor->setStringList( GlobalSettings::self()->replyPrefixes() );
02980   mReplaceReplyPrefixCheck->setChecked( GlobalSettings::self()->replaceReplyPrefix() );
02981   mForwardListEditor->setStringList( GlobalSettings::self()->forwardPrefixes() );
02982   mReplaceForwardPrefixCheck->setChecked( GlobalSettings::self()->replaceForwardPrefix() );
02983 }
02984 
02985 void ComposerPage::SubjectTab::save() {
02986   GlobalSettings::self()->setReplyPrefixes( mReplyListEditor->stringList() );
02987   GlobalSettings::self()->setForwardPrefixes( mForwardListEditor->stringList() );
02988 }
02989 
02990 QString ComposerPage::CharsetTab::helpAnchor() const {
02991   return QString::fromLatin1("configure-composer-charset");
02992 }
02993 
02994 ComposerPageCharsetTab::ComposerPageCharsetTab( QWidget * parent, const char * name )
02995   : ConfigModuleTab( parent, name )
02996 {
02997   // tmp. vars:
02998   QVBoxLayout *vlay;
02999   QLabel      *label;
03000 
03001   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
03002 
03003   label = new QLabel( i18n("This list is checked for every outgoing message "
03004                            "from the top to the bottom for a charset that "
03005                            "contains all required characters."), this );
03006   label->setAlignment( WordBreak);
03007   vlay->addWidget( label );
03008 
03009   mCharsetListEditor =
03010     new SimpleStringListEditor( this, 0, SimpleStringListEditor::All,
03011                                 i18n("A&dd..."), i18n("Remo&ve"),
03012                                 i18n("&Modify..."), i18n("Enter charset:") );
03013   connect( mCharsetListEditor, SIGNAL( changed( void ) ),
03014            this, SLOT( slotEmitChanged( void ) ) );
03015 
03016   vlay->addWidget( mCharsetListEditor, 1 );
03017 
03018   mKeepReplyCharsetCheck = new QCheckBox( i18n("&Keep original charset when "
03019                                                 "replying or forwarding (if "
03020                                                 "possible)"), this );
03021   connect( mKeepReplyCharsetCheck, SIGNAL ( stateChanged( int ) ),
03022            this, SLOT( slotEmitChanged( void ) ) );
03023   vlay->addWidget( mKeepReplyCharsetCheck );
03024 
03025   connect( mCharsetListEditor, SIGNAL(aboutToAdd(QString&)),
03026            this, SLOT(slotVerifyCharset(QString&)) );
03027 }
03028 
03029 void ComposerPage::CharsetTab::slotVerifyCharset( QString & charset ) {
03030   if ( charset.isEmpty() ) return;
03031 
03032   // KCharsets::codecForName("us-ascii") returns "iso-8859-1" (cf. Bug #49812)
03033   // therefore we have to treat this case specially
03034   if ( charset.lower() == QString::fromLatin1("us-ascii") ) {
03035     charset = QString::fromLatin1("us-ascii");
03036     return;
03037   }
03038 
03039   if ( charset.lower() == QString::fromLatin1("locale") ) {
03040     charset =  QString::fromLatin1("%1 (locale)")
03041       .arg( QCString( kmkernel->networkCodec()->mimeName() ).lower() );
03042     return;
03043   }
03044 
03045   bool ok = false;
03046   QTextCodec *codec = KGlobal::charsets()->codecForName( charset, ok );
03047   if ( ok && codec ) {
03048     charset = QString::fromLatin1( codec->mimeName() ).lower();
03049     return;
03050   }
03051 
03052   KMessageBox::sorry( this, i18n("This charset is not supported.") );
03053   charset = QString::null;
03054 }
03055 
03056 void ComposerPage::CharsetTab::doLoadOther() {
03057   KConfigGroup composer( KMKernel::config(), "Composer" );
03058 
03059   QStringList charsets = composer.readListEntry( "pref-charsets" );
03060   for ( QStringList::Iterator it = charsets.begin() ;
03061         it != charsets.end() ; ++it )
03062     if ( (*it) == QString::fromLatin1("locale") ) {
03063       QCString cset = kmkernel->networkCodec()->mimeName();
03064       KPIM::kAsciiToLower( cset.data() );
03065       (*it) = QString("%1 (locale)").arg( cset );
03066     }
03067 
03068   mCharsetListEditor->setStringList( charsets );
03069   mKeepReplyCharsetCheck->setChecked( !composer.readBoolEntry( "force-reply-charset", false ) );
03070 }
03071 
03072 void ComposerPage::CharsetTab::save() {
03073   KConfigGroup composer( KMKernel::config(), "Composer" );
03074 
03075   QStringList charsetList = mCharsetListEditor->stringList();
03076   QStringList::Iterator it = charsetList.begin();
03077   for ( ; it != charsetList.end() ; ++it )
03078     if ( (*it).endsWith("(locale)") )
03079       (*it) = "locale";
03080   composer.writeEntry( "pref-charsets", charsetList );
03081   composer.writeEntry( "force-reply-charset",
03082                        !mKeepReplyCharsetCheck->isChecked() );
03083 }
03084 
03085 QString ComposerPage::HeadersTab::helpAnchor() const {
03086   return QString::fromLatin1("configure-composer-headers");
03087 }
03088 
03089 ComposerPageHeadersTab::ComposerPageHeadersTab( QWidget * parent, const char * name )
03090   : ConfigModuleTab( parent, name )
03091 {
03092   // tmp. vars:
03093   QVBoxLayout *vlay;
03094   QHBoxLayout *hlay;
03095   QGridLayout *glay;
03096   QLabel      *label;
03097   QPushButton *button;
03098 
03099   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
03100 
03101   // "Use custom Message-Id suffix" checkbox:
03102   mCreateOwnMessageIdCheck =
03103     new QCheckBox( i18n("&Use custom message-id suffix"), this );
03104   connect( mCreateOwnMessageIdCheck, SIGNAL ( stateChanged( int ) ),
03105            this, SLOT( slotEmitChanged( void ) ) );
03106   vlay->addWidget( mCreateOwnMessageIdCheck );
03107 
03108   // "Message-Id suffix" line edit and label:
03109   hlay = new QHBoxLayout( vlay ); // inherits spacing
03110   mMessageIdSuffixEdit = new KLineEdit( this );
03111   // only ASCII letters, digits, plus, minus and dots are allowed
03112   mMessageIdSuffixValidator =
03113     new QRegExpValidator( QRegExp( "[a-zA-Z0-9+-]+(?:\\.[a-zA-Z0-9+-]+)*" ), this );
03114   mMessageIdSuffixEdit->setValidator( mMessageIdSuffixValidator );
03115   label = new QLabel( mMessageIdSuffixEdit,
03116                       i18n("Custom message-&id suffix:"), this );
03117   label->setEnabled( false ); // since !mCreateOwnMessageIdCheck->isChecked()
03118   mMessageIdSuffixEdit->setEnabled( false );
03119   hlay->addWidget( label );
03120   hlay->addWidget( mMessageIdSuffixEdit, 1 );
03121   connect( mCreateOwnMessageIdCheck, SIGNAL(toggled(bool) ),
03122            label, SLOT(setEnabled(bool)) );
03123   connect( mCreateOwnMessageIdCheck, SIGNAL(toggled(bool) ),
03124            mMessageIdSuffixEdit, SLOT(setEnabled(bool)) );
03125   connect( mMessageIdSuffixEdit, SIGNAL( textChanged( const QString& ) ),
03126            this, SLOT( slotEmitChanged( void ) ) );
03127 
03128   // horizontal rule and "custom header fields" label:
03129   vlay->addWidget( new KSeparator( KSeparator::HLine, this ) );
03130   vlay->addWidget( new QLabel( i18n("Define custom mime header fields:"), this) );
03131 
03132   // "custom header fields" listbox:
03133   glay = new QGridLayout( vlay, 5, 3 ); // inherits spacing
03134   glay->setRowStretch( 2, 1 );
03135   glay->setColStretch( 1, 1 );
03136   mTagList = new ListView( this, "tagList" );
03137   mTagList->addColumn( i18n("Name") );
03138   mTagList->addColumn( i18n("Value") );
03139   mTagList->setAllColumnsShowFocus( true );
03140   mTagList->setSorting( -1 );
03141   connect( mTagList, SIGNAL(selectionChanged()),
03142            this, SLOT(slotMimeHeaderSelectionChanged()) );
03143   glay->addMultiCellWidget( mTagList, 0, 2, 0, 1 );
03144 
03145   // "new" and "remove" buttons:
03146   button = new QPushButton( i18n("Ne&w"), this );
03147   connect( button, SIGNAL(clicked()), this, SLOT(slotNewMimeHeader()) );
03148   button->setAutoDefault( false );
03149   glay->addWidget( button, 0, 2 );
03150   mRemoveHeaderButton = new QPushButton( i18n("Re&move"), this );
03151   connect( mRemoveHeaderButton, SIGNAL(clicked()),
03152            this, SLOT(slotRemoveMimeHeader()) );
03153   button->setAutoDefault( false );
03154   glay->addWidget( mRemoveHeaderButton, 1, 2 );
03155 
03156   // "name" and "value" line edits and labels:
03157   mTagNameEdit = new KLineEdit( this );
03158   mTagNameEdit->setEnabled( false );
03159   mTagNameLabel = new QLabel( mTagNameEdit, i18n("&Name:"), this );
03160   mTagNameLabel->setEnabled( false );
03161   glay->addWidget( mTagNameLabel, 3, 0 );
03162   glay->addWidget( mTagNameEdit, 3, 1 );
03163   connect( mTagNameEdit, SIGNAL(textChanged(const QString&)),
03164            this, SLOT(slotMimeHeaderNameChanged(const QString&)) );
03165 
03166   mTagValueEdit = new KLineEdit( this );
03167   mTagValueEdit->setEnabled( false );
03168   mTagValueLabel = new QLabel( mTagValueEdit, i18n("&Value:"), this );
03169   mTagValueLabel->setEnabled( false );
03170   glay->addWidget( mTagValueLabel, 4, 0 );
03171   glay->addWidget( mTagValueEdit, 4, 1 );
03172   connect( mTagValueEdit, SIGNAL(textChanged(const QString&)),
03173            this, SLOT(slotMimeHeaderValueChanged(const QString&)) );
03174 }
03175 
03176 void ComposerPage::HeadersTab::slotMimeHeaderSelectionChanged()
03177 {
03178   QListViewItem * item = mTagList->selectedItem();
03179 
03180   if ( item ) {
03181     mTagNameEdit->setText( item->text( 0 ) );
03182     mTagValueEdit->setText( item->text( 1 ) );
03183   } else {
03184     mTagNameEdit->clear();
03185     mTagValueEdit->clear();
03186   }
03187   mRemoveHeaderButton->setEnabled( item );
03188   mTagNameEdit->setEnabled( item );
03189   mTagValueEdit->setEnabled( item );
03190   mTagNameLabel->setEnabled( item );
03191   mTagValueLabel->setEnabled( item );
03192 }
03193 
03194 
03195 void ComposerPage::HeadersTab::slotMimeHeaderNameChanged( const QString & text ) {
03196   // is called on ::setup(), when clearing the line edits. So be
03197   // prepared to not find a selection:
03198   QListViewItem * item = mTagList->selectedItem();
03199   if ( item )
03200     item->setText( 0, text );
03201   emit changed( true );
03202 }
03203 
03204 
03205 void ComposerPage::HeadersTab::slotMimeHeaderValueChanged( 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( 1, text );
03211   emit changed( true );
03212 }
03213 
03214 
03215 void ComposerPage::HeadersTab::slotNewMimeHeader()
03216 {
03217   QListViewItem *listItem = new QListViewItem( mTagList );
03218   mTagList->setCurrentItem( listItem );
03219   mTagList->setSelected( listItem, true );
03220   emit changed( true );
03221 }
03222 
03223 
03224 void ComposerPage::HeadersTab::slotRemoveMimeHeader()
03225 {
03226   // calling this w/o selection is a programming error:
03227   QListViewItem * item = mTagList->selectedItem();
03228   if ( !item ) {
03229     kdDebug(5006) << "==================================================\n"
03230                   << "Error: Remove button was pressed although no custom header was selected\n"
03231                   << "==================================================\n";
03232     return;
03233   }
03234 
03235   QListViewItem * below = item->nextSibling();
03236   delete item;
03237 
03238   if ( below )
03239     mTagList->setSelected( below, true );
03240   else if ( mTagList->lastItem() )
03241     mTagList->setSelected( mTagList->lastItem(), true );
03242   emit changed( true );
03243 }
03244 
03245 void ComposerPage::HeadersTab::doLoadOther() {
03246   KConfigGroup general( KMKernel::config(), "General" );
03247 
03248   QString suffix = general.readEntry( "myMessageIdSuffix" );
03249   mMessageIdSuffixEdit->setText( suffix );
03250   bool state = ( !suffix.isEmpty() &&
03251             general.readBoolEntry( "useCustomMessageIdSuffix", false ) );
03252   mCreateOwnMessageIdCheck->setChecked( state );
03253 
03254   mTagList->clear();
03255   mTagNameEdit->clear();
03256   mTagValueEdit->clear();
03257 
03258   QListViewItem * item = 0;
03259 
03260   int count = general.readNumEntry( "mime-header-count", 0 );
03261   for( int i = 0 ; i < count ; i++ ) {
03262     KConfigGroup config( KMKernel::config(),
03263                          QCString("Mime #") + QCString().setNum(i) );
03264     QString name  = config.readEntry( "name" );
03265     QString value = config.readEntry( "value" );
03266     if( !name.isEmpty() )
03267       item = new QListViewItem( mTagList, item, name, value );
03268   }
03269   if ( mTagList->childCount() ) {
03270     mTagList->setCurrentItem( mTagList->firstChild() );
03271     mTagList->setSelected( mTagList->firstChild(), true );
03272   }
03273   else {
03274     // disable the "Remove" button
03275     mRemoveHeaderButton->setEnabled( false );
03276   }
03277 }
03278 
03279 void ComposerPage::HeadersTab::save() {
03280   KConfigGroup general( KMKernel::config(), "General" );
03281 
03282   general.writeEntry( "useCustomMessageIdSuffix",
03283                       mCreateOwnMessageIdCheck->isChecked() );
03284   general.writeEntry( "myMessageIdSuffix",
03285                       mMessageIdSuffixEdit->text() );
03286 
03287   int numValidEntries = 0;
03288   QListViewItem * item = mTagList->firstChild();
03289   for ( ; item ; item = item->itemBelow() )
03290     if( !item->text(0).isEmpty() ) {
03291       KConfigGroup config( KMKernel::config(), QCString("Mime #")
03292                              + QCString().setNum( numValidEntries ) );
03293       config.writeEntry( "name",  item->text( 0 ) );
03294       config.writeEntry( "value", item->text( 1 ) );
03295       numValidEntries++;
03296     }
03297   general.writeEntry( "mime-header-count", numValidEntries );
03298 }
03299 
03300 QString ComposerPage::AttachmentsTab::helpAnchor() const {
03301   return QString::fromLatin1("configure-composer-attachments");
03302 }
03303 
03304 ComposerPageAttachmentsTab::ComposerPageAttachmentsTab( QWidget * parent,
03305                                                         const char * name )
03306   : ConfigModuleTab( parent, name ) {
03307   // tmp. vars:
03308   QVBoxLayout *vlay;
03309   QLabel      *label;
03310 
03311   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
03312 
03313   // "Outlook compatible attachment naming" check box
03314   mOutlookCompatibleCheck =
03315     new QCheckBox( i18n( "Outlook-compatible attachment naming" ), this );
03316   mOutlookCompatibleCheck->setChecked( false );
03317   QToolTip::add( mOutlookCompatibleCheck, i18n(
03318     "Turn this option on to make Outlook(tm) understand attachment names "
03319     "containing non-English characters" ) );
03320   connect( mOutlookCompatibleCheck, SIGNAL( stateChanged( int ) ),
03321            this, SLOT( slotEmitChanged( void ) ) );
03322   connect( mOutlookCompatibleCheck, SIGNAL( clicked() ),
03323            this, SLOT( slotOutlookCompatibleClicked() ) );
03324   vlay->addWidget( mOutlookCompatibleCheck );
03325   vlay->addSpacing( 5 );
03326 
03327   // "Enable detection of missing attachments" check box
03328   mMissingAttachmentDetectionCheck =
03329     new QCheckBox( i18n("E&nable detection of missing attachments"), this );
03330   mMissingAttachmentDetectionCheck->setChecked( true );
03331   connect( mMissingAttachmentDetectionCheck, SIGNAL( stateChanged( int ) ),
03332            this, SLOT( slotEmitChanged( void ) ) );
03333   vlay->addWidget( mMissingAttachmentDetectionCheck );
03334 
03335   // "Attachment key words" label and string list editor
03336   label = new QLabel( i18n("Recognize any of the following key words as "
03337                            "intention to attach a file:"), this );
03338   label->setAlignment( AlignLeft|WordBreak );
03339   vlay->addWidget( label );
03340 
03341   SimpleStringListEditor::ButtonCode buttonCode =
03342     static_cast<SimpleStringListEditor::ButtonCode>( SimpleStringListEditor::Add | SimpleStringListEditor::Remove | SimpleStringListEditor::Modify );
03343   mAttachWordsListEditor =
03344     new SimpleStringListEditor( this, 0, buttonCode,
03345                                 i18n("A&dd..."), i18n("Re&move"),
03346                                 i18n("Mod&ify..."),
03347                                 i18n("Enter new key word:") );
03348   connect( mAttachWordsListEditor, SIGNAL( changed( void ) ),
03349            this, SLOT( slotEmitChanged( void ) ) );
03350   vlay->addWidget( mAttachWordsListEditor );
03351 
03352   connect( mMissingAttachmentDetectionCheck, SIGNAL(toggled(bool) ),
03353            label, SLOT(setEnabled(bool)) );
03354   connect( mMissingAttachmentDetectionCheck, SIGNAL(toggled(bool) ),
03355            mAttachWordsListEditor, SLOT(setEnabled(bool)) );
03356 }
03357 
03358 void ComposerPage::AttachmentsTab::doLoadFromGlobalSettings() {
03359   mOutlookCompatibleCheck->setChecked(
03360     GlobalSettings::self()->outlookCompatibleAttachments() );
03361   mMissingAttachmentDetectionCheck->setChecked(
03362     GlobalSettings::self()->showForgottenAttachmentWarning() );
03363   QStringList attachWordsList = GlobalSettings::self()->attachmentKeywords();
03364   if ( attachWordsList.isEmpty() ) {
03365     // default value
03366     attachWordsList << QString::fromLatin1("attachment")
03367                     << QString::fromLatin1("attached");
03368     if ( QString::fromLatin1("attachment") != i18n("attachment") )
03369       attachWordsList << i18n("attachment");
03370     if ( QString::fromLatin1("attached") != i18n("attached") )
03371       attachWordsList << i18n("attached");
03372   }
03373 
03374   mAttachWordsListEditor->setStringList( attachWordsList );
03375 }
03376 
03377 void ComposerPage::AttachmentsTab::save() {
03378   GlobalSettings::self()->setOutlookCompatibleAttachments(
03379     mOutlookCompatibleCheck->isChecked() );
03380   GlobalSettings::self()->setShowForgottenAttachmentWarning(
03381     mMissingAttachmentDetectionCheck->isChecked() );
03382   GlobalSettings::self()->setAttachmentKeywords(
03383     mAttachWordsListEditor->stringList() );
03384 }
03385 
03386 void ComposerPageAttachmentsTab::slotOutlookCompatibleClicked()
03387 {
03388   if (mOutlookCompatibleCheck->isChecked()) {
03389     KMessageBox::information(0,i18n("You have chosen to "
03390     "encode attachment names containing non-English characters in a way that "
03391     "is understood by Outlook(tm) and other mail clients that do not "
03392     "support standard-compliant encoded attachment names.\n"
03393     "Note that KMail may create non-standard compliant messages, "
03394     "and consequently it is possible that your messages will not be "
03395     "understood by standard-compliant mail clients; so, unless you have no "
03396     "other choice, you should not enable this option." ) );
03397   }
03398 }
03399 
03400 // *************************************************************
03401 // *                                                           *
03402 // *                      SecurityPage                         *
03403 // *                                                           *
03404 // *************************************************************
03405 QString SecurityPage::helpAnchor() const {
03406   return QString::fromLatin1("configure-security");
03407 }
03408 
03409 SecurityPage::SecurityPage( QWidget * parent, const char * name )
03410   : ConfigModuleWithTabs( parent, name )
03411 {
03412   //
03413   // "Reading" tab:
03414   //
03415   mGeneralTab = new GeneralTab(); //  @TODO: rename
03416   addTab( mGeneralTab, i18n("&Reading") );
03417 
03418   //
03419   // "Composing" tab:
03420   //
03421   mComposerCryptoTab = new ComposerCryptoTab();
03422   addTab( mComposerCryptoTab, i18n("Composing") );
03423 
03424   //
03425   // "Warnings" tab:
03426   //
03427   mWarningTab = new WarningTab();
03428   addTab( mWarningTab, i18n("Warnings") );
03429 
03430   //
03431   // "S/MIME Validation" tab:
03432   //
03433   mSMimeTab = new SMimeTab();
03434   addTab( mSMimeTab, i18n("S/MIME &Validation") );
03435 
03436   //
03437   // "Crypto Backends" tab:
03438   //
03439   mCryptPlugTab = new CryptPlugTab();
03440   addTab( mCryptPlugTab, i18n("Crypto Backe&nds") );
03441   load();
03442 }
03443 
03444 
03445 void SecurityPage::installProfile( KConfig * profile ) {
03446   mGeneralTab->installProfile( profile );
03447   mComposerCryptoTab->installProfile( profile );
03448   mWarningTab->installProfile( profile );
03449   mSMimeTab->installProfile( profile );
03450 }
03451 
03452 QString SecurityPage::GeneralTab::helpAnchor() const {
03453   return QString::fromLatin1("configure-security-reading");
03454 }
03455 
03456 SecurityPageGeneralTab::SecurityPageGeneralTab( QWidget * parent, const char * name )
03457   : ConfigModuleTab ( parent, name )
03458 {
03459   // tmp. vars:
03460   QVBoxLayout  *vlay;
03461   QHBox        *hbox;
03462   QGroupBox    *group;
03463   QRadioButton *radio;
03464   KActiveLabel *label;
03465   QWidget      *w;
03466   QString       msg;
03467 
03468   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
03469 
03470   // QWhat'sThis texts
03471   QString htmlWhatsThis = i18n( "<qt><p>Messages sometimes come in both formats. "
03472               "This option controls whether you want the HTML part or the plain "
03473               "text part to be displayed.</p>"
03474               "<p>Displaying the HTML part makes the message look better, "
03475               "but at the same time increases the risk of security holes "
03476               "being exploited.</p>"
03477               "<p>Displaying the plain text part loses much of the message's "
03478               "formatting, but makes it almost <em>impossible</em> "
03479               "to exploit security holes in the HTML renderer (Konqueror).</p>"
03480               "<p>The option below guards against one common misuse of HTML "
03481               "messages, but it cannot guard against security issues that were "
03482               "not known at the time this version of KMail was written.</p>"
03483               "<p>It is therefore advisable to <em>not</em> prefer HTML to "
03484               "plain text.</p>"
03485               "<p><b>Note:</b> You can set this option on a per-folder basis "
03486               "from the <i>Folder</i> menu of KMail's main window.</p></qt>" );
03487 
03488   QString externalWhatsThis = i18n( "<qt><p>Some mail advertisements are in HTML "
03489               "and contain references to, for example, images that the advertisers"
03490               " employ to find out that you have read their message "
03491               "(&quot;web bugs&quot;).</p>"
03492               "<p>There is no valid reason to load images off the Internet like "
03493               "this, since the sender can always attach the required images "
03494               "directly to the message.</p>"
03495               "<p>To guard from such a misuse of the HTML displaying feature "
03496               "of KMail, this option is <em>disabled</em> by default.</p>"
03497               "<p>However, if you wish to, for example, view images in HTML "
03498               "messages that were not attached to it, you can enable this "
03499               "option, but you should be aware of the possible problem.</p></qt>" );
03500 
03501   QString receiptWhatsThis = i18n( "<qt><h3>Message Disposition "
03502               "Notification Policy</h3>"
03503               "<p>MDNs are a generalization of what is commonly called <b>read "
03504               "receipt</b>. The message author requests a disposition "
03505               "notification to be sent and the receiver's mail program "
03506               "generates a reply from which the author can learn what "
03507               "happened to his message. Common disposition types include "
03508               "<b>displayed</b> (i.e. read), <b>deleted</b> and <b>dispatched</b> "
03509               "(e.g. forwarded).</p>"
03510               "<p>The following options are available to control KMail's "
03511               "sending of MDNs:</p>"
03512               "<ul>"
03513               "<li><em>Ignore</em>: Ignores any request for disposition "
03514               "notifications. No MDN will ever be sent automatically "
03515               "(recommended).</li>"
03516               "<li><em>Ask</em>: Answers requests only after asking the user "
03517               "for permission. This way, you can send MDNs for selected "
03518               "messages while denying or ignoring them for others.</li>"
03519               "<li><em>Deny</em>: Always sends a <b>denied</b> notification. This "
03520               "is only <em>slightly</em> better than always sending MDNs. "
03521               "The author will still know that the messages has been acted "
03522               "upon, he just cannot tell whether it was deleted or read etc.</li>"
03523               "<li><em>Always send</em>: Always sends the requested "
03524               "disposition notification. That means that the author of the "
03525               "message gets to know when the message was acted upon and, "
03526               "in addition, what happened to it (displayed, deleted, "
03527               "etc.). This option is strongly discouraged, but since it "
03528               "makes much sense e.g. for customer relationship management, "
03529               "it has been made available.</li>"
03530               "</ul></qt>" );
03531 
03532 
03533   // "HTML Messages" group box:
03534   group = new QVGroupBox( i18n( "HTML Messages" ), this );
03535   group->layout()->setSpacing( KDialog::spacingHint() );
03536 
03537   mHtmlMailCheck = new QCheckBox( i18n("Prefer H&TML to plain text"), group );
03538   QWhatsThis::add( mHtmlMailCheck, htmlWhatsThis );
03539   connect( mHtmlMailCheck, SIGNAL( stateChanged( int ) ),
03540            this, SLOT( slotEmitChanged( void ) ) );
03541   mExternalReferences = new QCheckBox( i18n("Allow messages to load e&xternal "
03542                                             "references from the Internet" ), group );
03543   QWhatsThis::add( mExternalReferences, externalWhatsThis );
03544   connect( mExternalReferences, SIGNAL( stateChanged( int ) ),
03545            this, SLOT( slotEmitChanged( void ) ) );
03546   label = new KActiveLabel( i18n("<b>WARNING:</b> Allowing HTML in email may "
03547                            "increase the risk that your system will be "
03548                            "compromised by present and anticipated security "
03549                            "exploits. <a href=\"whatsthis:%1\">More about "
03550                            "HTML mails...</a> <a href=\"whatsthis:%2\">More "
03551                            "about external references...</a>")
03552                            .arg(htmlWhatsThis).arg(externalWhatsThis),
03553                            group );
03554 
03555   vlay->addWidget( group );
03556 
03557   // "Message Disposition Notification" groupbox:
03558   group = new QVGroupBox( i18n("Message Disposition Notifications"), this );
03559   group->layout()->setSpacing( KDialog::spacingHint() );
03560 
03561 
03562   // "ignore", "ask", "deny", "always send" radiobutton line:
03563   mMDNGroup = new QButtonGroup( group );
03564   mMDNGroup->hide();
03565   connect( mMDNGroup, SIGNAL( clicked( int ) ),
03566            this, SLOT( slotEmitChanged( void ) ) );
03567   hbox = new QHBox( group );
03568   hbox->setSpacing( KDialog::spacingHint() );
03569 
03570   (void)new QLabel( i18n("Send policy:"), hbox );
03571 
03572   radio = new QRadioButton( i18n("&Ignore"), hbox );
03573   mMDNGroup->insert( radio );
03574 
03575   radio = new QRadioButton( i18n("As&k"), hbox );
03576   mMDNGroup->insert( radio );
03577 
03578   radio = new QRadioButton( i18n("&Deny"), hbox );
03579   mMDNGroup->insert( radio );
03580 
03581   radio = new QRadioButton( i18n("Al&ways send"), hbox );
03582   mMDNGroup->insert( radio );
03583 
03584   for ( int i = 0 ; i < mMDNGroup->count() ; ++i )
03585       QWhatsThis::add( mMDNGroup->find( i ), receiptWhatsThis );
03586 
03587   w = new QWidget( hbox ); // spacer
03588   hbox->setStretchFactor( w, 1 );
03589 
03590   // "Original Message quote" radiobutton line:
03591   mOrigQuoteGroup = new QButtonGroup( group );
03592   mOrigQuoteGroup->hide();
03593   connect( mOrigQuoteGroup, SIGNAL( clicked( int ) ),
03594            this, SLOT( slotEmitChanged( void ) ) );
03595 
03596   hbox = new QHBox( group );
03597   hbox->setSpacing( KDialog::spacingHint() );
03598 
03599   (void)new QLabel( i18n("Quote original message:"), hbox );
03600 
03601   radio = new QRadioButton( i18n("Nothin&g"), hbox );
03602   mOrigQuoteGroup->insert( radio );
03603 
03604   radio = new QRadioButton( i18n("&Full message"), hbox );
03605   mOrigQuoteGroup->insert( radio );
03606 
03607   radio = new QRadioButton( i18n("Onl&y headers"), hbox );
03608   mOrigQuoteGroup->insert( radio );
03609 
03610   w = new QWidget( hbox );
03611   hbox->setStretchFactor( w, 1 );
03612 
03613   mNoMDNsWhenEncryptedCheck = new QCheckBox( i18n("Do not send MDNs in response to encrypted messages"), group );
03614   connect( mNoMDNsWhenEncryptedCheck, SIGNAL(toggled(bool)), SLOT(slotEmitChanged()) );
03615 
03616   // Warning label:
03617   label = new KActiveLabel( i18n("<b>WARNING:</b> Unconditionally returning "
03618                            "confirmations undermines your privacy. "
03619                            "<a href=\"whatsthis:%1\">More...</a>")
03620                              .arg(receiptWhatsThis),
03621                            group );
03622 
03623   vlay->addWidget( group );
03624 
03625   // "Attached keys" group box:
03626   group = new QVGroupBox( i18n( "Certificate && Key Bundle Attachments" ), this );
03627   group->layout()->setSpacing( KDialog::spacingHint() );
03628 
03629   mAutomaticallyImportAttachedKeysCheck = new QCheckBox( i18n("Automatically import keys and certificates"), group );
03630   connect( mAutomaticallyImportAttachedKeysCheck, SIGNAL(toggled(bool)), SLOT(slotEmitChanged()) );
03631 
03632   vlay->addWidget( group );
03633 
03634 
03635 
03636   vlay->addStretch( 10 ); // spacer
03637 }
03638 
03639 void SecurityPage::GeneralTab::doLoadOther() {
03640   const KConfigGroup reader( KMKernel::config(), "Reader" );
03641 
03642   mHtmlMailCheck->setChecked( reader.readBoolEntry( "htmlMail", false ) );
03643   mExternalReferences->setChecked( reader.readBoolEntry( "htmlLoadExternal", false ) );
03644   mAutomaticallyImportAttachedKeysCheck->setChecked( reader.readBoolEntry( "AutoImportKeys", false ) );
03645 
03646   const KConfigGroup mdn( KMKernel::config(), "MDN" );
03647 
03648   int num = mdn.readNumEntry( "default-policy", 0 );
03649   if ( num < 0 || num >= mMDNGroup->count() ) num = 0;
03650   mMDNGroup->setButton( num );
03651   num = mdn.readNumEntry( "quote-message", 0 );
03652   if ( num < 0 || num >= mOrigQuoteGroup->count() ) num = 0;
03653   mOrigQuoteGroup->setButton( num );
03654   mNoMDNsWhenEncryptedCheck->setChecked(mdn.readBoolEntry( "not-send-when-encrypted", true ));
03655 }
03656 
03657 void SecurityPage::GeneralTab::installProfile( KConfig * profile ) {
03658   const KConfigGroup reader( profile, "Reader" );
03659   const KConfigGroup mdn( profile, "MDN" );
03660 
03661   if ( reader.hasKey( "htmlMail" ) )
03662     mHtmlMailCheck->setChecked( reader.readBoolEntry( "htmlMail" ) );
03663   if ( reader.hasKey( "htmlLoadExternal" ) )
03664     mExternalReferences->setChecked( reader.readBoolEntry( "htmlLoadExternal" ) );
03665   if ( reader.hasKey( "AutoImportKeys" ) )
03666     mAutomaticallyImportAttachedKeysCheck->setChecked( reader.readBoolEntry( "AutoImportKeys" ) );
03667 
03668   if ( mdn.hasKey( "default-policy" ) ) {
03669       int num = mdn.readNumEntry( "default-policy" );
03670       if ( num < 0 || num >= mMDNGroup->count() ) num = 0;
03671       mMDNGroup->setButton( num );
03672   }
03673   if ( mdn.hasKey( "quote-message" ) ) {
03674       int num = mdn.readNumEntry( "quote-message" );
03675       if ( num < 0 || num >= mOrigQuoteGroup->count() ) num = 0;
03676       mOrigQuoteGroup->setButton( num );
03677   }
03678   if ( mdn.hasKey( "not-send-when-encrypted" ) )
03679       mNoMDNsWhenEncryptedCheck->setChecked(mdn.readBoolEntry( "not-send-when-encrypted" ));
03680 }
03681 
03682 void SecurityPage::GeneralTab::save() {
03683   KConfigGroup reader( KMKernel::config(), "Reader" );
03684   KConfigGroup mdn( KMKernel::config(), "MDN" );
03685 
03686   if (reader.readBoolEntry( "htmlMail", false ) != mHtmlMailCheck->isChecked())
03687   {
03688     if (KMessageBox::warningContinueCancel(this, i18n("Changing the global "
03689       "HTML setting will override all folder specific values."), QString::null,
03690       KStdGuiItem::cont(), "htmlMailOverride") == KMessageBox::Continue)
03691     {
03692       reader.writeEntry( "htmlMail", mHtmlMailCheck->isChecked() );
03693       QStringList names;
03694       QValueList<QGuardedPtr<KMFolder> > folders;
03695       kmkernel->folderMgr()->createFolderList(&names, &folders);
03696       kmkernel->imapFolderMgr()->createFolderList(&names, &folders);
03697       kmkernel->dimapFolderMgr()->createFolderList(&names, &folders);
03698       kmkernel->searchFolderMgr()->createFolderList(&names, &folders);
03699       for (QValueList<QGuardedPtr<KMFolder> >::iterator it = folders.begin();
03700         it != folders.end(); ++it)
03701       {
03702         if (*it)
03703         {
03704           KConfigGroupSaver saver(KMKernel::config(),
03705             "Folder-" + (*it)->idString());
03706           KMKernel::config()->writeEntry("htmlMailOverride", false);
03707         }
03708       }
03709     }
03710   }
03711   reader.writeEntry( "htmlLoadExternal", mExternalReferences->isChecked() );
03712   reader.writeEntry( "AutoImportKeys", mAutomaticallyImportAttachedKeysCheck->isChecked() );
03713   mdn.writeEntry( "default-policy", mMDNGroup->id( mMDNGroup->selected() ) );
03714   mdn.writeEntry( "quote-message", mOrigQuoteGroup->id( mOrigQuoteGroup->selected() ) );
03715   mdn.writeEntry( "not-send-when-encrypted", mNoMDNsWhenEncryptedCheck->isChecked() );
03716 }
03717 
03718 
03719 QString SecurityPage::ComposerCryptoTab::helpAnchor() const {
03720   return QString::fromLatin1("configure-security-composing");
03721 }
03722 
03723 SecurityPageComposerCryptoTab::SecurityPageComposerCryptoTab( QWidget * parent, const char * name )
03724   : ConfigModuleTab ( parent, name )
03725 {
03726   // the margins are inside mWidget itself
03727   QVBoxLayout* vlay = new QVBoxLayout( this, 0, 0 );
03728 
03729   mWidget = new ComposerCryptoConfiguration( this );
03730   connect( mWidget->mAutoSignature, SIGNAL( toggled(bool) ), this, SLOT( slotEmitChanged() ) );
03731   connect( mWidget->mEncToSelf, SIGNAL( toggled(bool) ), this, SLOT( slotEmitChanged() ) );
03732   connect( mWidget->mShowEncryptionResult, SIGNAL( toggled(bool) ), this, SLOT( slotEmitChanged() ) );
03733   connect( mWidget->mShowKeyApprovalDlg, SIGNAL( toggled(bool) ), this, SLOT( slotEmitChanged() ) );
03734   connect( mWidget->mAutoEncrypt, SIGNAL( toggled(bool) ), this, SLOT( slotEmitChanged() ) );
03735   connect( mWidget->mNeverEncryptWhenSavingInDrafts, SIGNAL( toggled(bool) ), this, SLOT( slotEmitChanged() ) );
03736   connect( mWidget->mStoreEncrypted, SIGNAL( toggled(bool) ), this, SLOT( slotEmitChanged() ) );
03737   vlay->addWidget( mWidget );
03738 }
03739 
03740 void SecurityPage::ComposerCryptoTab::doLoadOther() {
03741   const KConfigGroup composer( KMKernel::config(), "Composer" );
03742 
03743   // If you change default values, sync messagecomposer.cpp too
03744 
03745   mWidget->mAutoSignature->setChecked( composer.readBoolEntry( "pgp-auto-sign", false ) );
03746 
03747   mWidget->mEncToSelf->setChecked( composer.readBoolEntry( "crypto-encrypt-to-self", true ) );
03748   mWidget->mShowEncryptionResult->setChecked( false ); //composer.readBoolEntry( "crypto-show-encryption-result", true ) );
03749   mWidget->mShowEncryptionResult->hide();
03750   mWidget->mShowKeyApprovalDlg->setChecked( composer.readBoolEntry( "crypto-show-keys-for-approval", true ) );
03751 
03752   mWidget->mAutoEncrypt->setChecked( composer.readBoolEntry( "pgp-auto-encrypt", false ) );
03753   mWidget->mNeverEncryptWhenSavingInDrafts->setChecked( composer.readBoolEntry( "never-encrypt-drafts", true ) );
03754 
03755   mWidget->mStoreEncrypted->setChecked( composer.readBoolEntry( "crypto-store-encrypted", true ) );
03756 }
03757 
03758 void SecurityPage::ComposerCryptoTab::installProfile( KConfig * profile ) {
03759   const KConfigGroup composer( profile, "Composer" );
03760 
03761   if ( composer.hasKey( "pgp-auto-sign" ) )
03762     mWidget->mAutoSignature->setChecked( composer.readBoolEntry( "pgp-auto-sign" ) );
03763 
03764   if ( composer.hasKey( "crypto-encrypt-to-self" ) )
03765     mWidget->mEncToSelf->setChecked( composer.readBoolEntry( "crypto-encrypt-to-self" ) );
03766   if ( composer.hasKey( "crypto-show-encryption-result" ) )
03767     mWidget->mShowEncryptionResult->setChecked( composer.readBoolEntry( "crypto-show-encryption-result" ) );
03768   if ( composer.hasKey( "crypto-show-keys-for-approval" ) )
03769     mWidget->mShowKeyApprovalDlg->setChecked( composer.readBoolEntry( "crypto-show-keys-for-approval" ) );
03770   if ( composer.hasKey( "pgp-auto-encrypt" ) )
03771     mWidget->mAutoEncrypt->setChecked( composer.readBoolEntry( "pgp-auto-encrypt" ) );
03772   if ( composer.hasKey( "never-encrypt-drafts" ) )
03773     mWidget->mNeverEncryptWhenSavingInDrafts->setChecked( composer.readBoolEntry( "never-encrypt-drafts" ) );
03774 
03775   if ( composer.hasKey( "crypto-store-encrypted" ) )
03776     mWidget->mStoreEncrypted->setChecked( composer.readBoolEntry( "crypto-store-encrypted" ) );
03777 }
03778 
03779 void SecurityPage::ComposerCryptoTab::save() {
03780   KConfigGroup composer( KMKernel::config(), "Composer" );
03781 
03782   composer.writeEntry( "pgp-auto-sign", mWidget->mAutoSignature->isChecked() );
03783 
03784   composer.writeEntry( "crypto-encrypt-to-self", mWidget->mEncToSelf->isChecked() );
03785   composer.writeEntry( "crypto-show-encryption-result", mWidget->mShowEncryptionResult->isChecked() );
03786   composer.writeEntry( "crypto-show-keys-for-approval", mWidget->mShowKeyApprovalDlg->isChecked() );
03787 
03788   composer.writeEntry( "pgp-auto-encrypt", mWidget->mAutoEncrypt->isChecked() );
03789   composer.writeEntry( "never-encrypt-drafts", mWidget->mNeverEncryptWhenSavingInDrafts->isChecked() );
03790 
03791   composer.writeEntry( "crypto-store-encrypted", mWidget->mStoreEncrypted->isChecked() );
03792 }
03793 
03794 QString SecurityPage::WarningTab::helpAnchor() const {
03795   return QString::fromLatin1("configure-security-warnings");
03796 }
03797 
03798 SecurityPageWarningTab::SecurityPageWarningTab( QWidget * parent, const char * name )
03799   : ConfigModuleTab( parent, name )
03800 {
03801   // the margins are inside mWidget itself
03802   QVBoxLayout* vlay = new QVBoxLayout( this, 0, 0 );
03803 
03804   mWidget = new WarningConfiguration( this );
03805   vlay->addWidget( mWidget );
03806 
03807   connect( mWidget->warnGroupBox, SIGNAL(toggled(bool)), SLOT(slotEmitChanged()) );
03808   connect( mWidget->mWarnUnsigned, SIGNAL(toggled(bool)), SLOT(slotEmitChanged()) );
03809   connect( mWidget->warnUnencryptedCB, SIGNAL(toggled(bool)), SLOT(slotEmitChanged()) );
03810   connect( mWidget->warnReceiverNotInCertificateCB, SIGNAL(toggled(bool)), SLOT(slotEmitChanged()) );
03811   connect( mWidget->mWarnSignKeyExpiresSB, SIGNAL( valueChanged( int ) ), SLOT( slotEmitChanged() ) );
03812   connect( mWidget->mWarnSignChainCertExpiresSB, SIGNAL( valueChanged( int ) ), SLOT( slotEmitChanged() ) );
03813   connect( mWidget->mWarnSignRootCertExpiresSB, SIGNAL( valueChanged( int ) ), SLOT( slotEmitChanged() ) );
03814 
03815   connect( mWidget->mWarnEncrKeyExpiresSB, SIGNAL( valueChanged( int ) ), SLOT( slotEmitChanged() ) );
03816   connect( mWidget->mWarnEncrChainCertExpiresSB, SIGNAL( valueChanged( int ) ), SLOT( slotEmitChanged() ) );
03817   connect( mWidget->mWarnEncrRootCertExpiresSB, SIGNAL( valueChanged( int ) ), SLOT( slotEmitChanged() ) );
03818 
03819   connect( mWidget->enableAllWarningsPB, SIGNAL(clicked()),
03820            SLOT(slotReenableAllWarningsClicked()) );
03821 }
03822 
03823 void SecurityPage::WarningTab::doLoadOther() {
03824   const KConfigGroup composer( KMKernel::config(), "Composer" );
03825 
03826   mWidget->warnUnencryptedCB->setChecked( composer.readBoolEntry( "crypto-warning-unencrypted", false ) );
03827   mWidget->mWarnUnsigned->setChecked( composer.readBoolEntry( "crypto-warning-unsigned", false ) );
03828   mWidget->warnReceiverNotInCertificateCB->setChecked( composer.readBoolEntry( "crypto-warn-recv-not-in-cert", true ) );
03829 
03830   // The "-int" part of the key name is because there used to be a separate boolean
03831   // config entry for enabling/disabling. This is done with the single bool value now.
03832   mWidget->warnGroupBox->setChecked( composer.readBoolEntry( "crypto-warn-when-near-expire", true ) );
03833 
03834   mWidget->mWarnSignKeyExpiresSB->setValue( composer.readNumEntry( "crypto-warn-sign-key-near-expire-int", 14 ) );
03835   mWidget->mWarnSignChainCertExpiresSB->setValue( composer.readNumEntry( "crypto-warn-sign-chaincert-near-expire-int", 14 ) );
03836   mWidget->mWarnSignRootCertExpiresSB->setValue( composer.readNumEntry( "crypto-warn-sign-root-near-expire-int", 14 ) );
03837 
03838   mWidget->mWarnEncrKeyExpiresSB->setValue( composer.readNumEntry( "crypto-warn-encr-key-near-expire-int", 14 ) );
03839   mWidget->mWarnEncrChainCertExpiresSB->setValue( composer.readNumEntry( "crypto-warn-encr-chaincert-near-expire-int", 14 ) );
03840   mWidget->mWarnEncrRootCertExpiresSB->setValue( composer.readNumEntry( "crypto-warn-encr-root-near-expire-int", 14 ) );
03841 
03842   mWidget->enableAllWarningsPB->setEnabled( true );
03843 }
03844 
03845 void SecurityPage::WarningTab::installProfile( KConfig * profile ) {
03846   const KConfigGroup composer( profile, "Composer" );
03847 
03848   if ( composer.hasKey( "crypto-warning-unencrypted" ) )
03849     mWidget->warnUnencryptedCB->setChecked( composer.readBoolEntry( "crypto-warning-unencrypted" ) );
03850   if ( composer.hasKey( "crypto-warning-unsigned" ) )
03851     mWidget->mWarnUnsigned->setChecked( composer.readBoolEntry( "crypto-warning-unsigned" ) );
03852   if ( composer.hasKey( "crypto-warn-recv-not-in-cert" ) )
03853     mWidget->warnReceiverNotInCertificateCB->setChecked( composer.readBoolEntry( "crypto-warn-recv-not-in-cert" ) );
03854 
03855   if ( composer.hasKey( "crypto-warn-when-near-expire" ) )
03856     mWidget->warnGroupBox->setChecked( composer.readBoolEntry( "crypto-warn-when-near-expire" ) );
03857 
03858   if ( composer.hasKey( "crypto-warn-sign-key-near-expire-int" ) )
03859     mWidget->mWarnSignKeyExpiresSB->setValue( composer.readNumEntry( "crypto-warn-sign-key-near-expire-int" ) );
03860   if ( composer.hasKey( "crypto-warn-sign-chaincert-near-expire-int" ) )
03861     mWidget->mWarnSignChainCertExpiresSB->setValue( composer.readNumEntry( "crypto-warn-sign-chaincert-near-expire-int" ) );
03862   if ( composer.hasKey( "crypto-warn-sign-root-near-expire-int" ) )
03863     mWidget->mWarnSignRootCertExpiresSB->setValue( composer.readNumEntry( "crypto-warn-sign-root-near-expire-int" ) );
03864 
03865   if ( composer.hasKey( "crypto-warn-encr-key-near-expire-int" ) )
03866     mWidget->mWarnEncrKeyExpiresSB->setValue( composer.readNumEntry( "crypto-warn-encr-key-near-expire-int" ) );
03867   if ( composer.hasKey( "crypto-warn-encr-chaincert-near-expire-int" ) )
03868     mWidget->mWarnEncrChainCertExpiresSB->setValue( composer.readNumEntry( "crypto-warn-encr-chaincert-near-expire-int" ) );
03869   if ( composer.hasKey( "crypto-warn-encr-root-near-expire-int" ) )
03870     mWidget->mWarnEncrRootCertExpiresSB->setValue( composer.readNumEntry( "crypto-warn-encr-root-near-expire-int" ) );
03871 }
03872 
03873 void SecurityPage::WarningTab::save() {
03874   KConfigGroup composer( KMKernel::config(), "Composer" );
03875 
03876   composer.writeEntry( "crypto-warn-recv-not-in-cert", mWidget->warnReceiverNotInCertificateCB->isChecked() );
03877   composer.writeEntry( "crypto-warning-unencrypted", mWidget->warnUnencryptedCB->isChecked() );
03878   composer.writeEntry( "crypto-warning-unsigned", mWidget->mWarnUnsigned->isChecked() );
03879 
03880   composer.writeEntry( "crypto-warn-when-near-expire", mWidget->warnGroupBox->isChecked() );
03881   composer.writeEntry( "crypto-warn-sign-key-near-expire-int",
03882                        mWidget->mWarnSignKeyExpiresSB->value() );
03883   composer.writeEntry( "crypto-warn-sign-chaincert-near-expire-int",
03884                        mWidget->mWarnSignChainCertExpiresSB->value() );
03885   composer.writeEntry( "crypto-warn-sign-root-near-expire-int",
03886                        mWidget->mWarnSignRootCertExpiresSB->value() );
03887 
03888   composer.writeEntry( "crypto-warn-encr-key-near-expire-int",
03889                        mWidget->mWarnEncrKeyExpiresSB->value() );
03890   composer.writeEntry( "crypto-warn-encr-chaincert-near-expire-int",
03891                        mWidget->mWarnEncrChainCertExpiresSB->value() );
03892   composer.writeEntry( "crypto-warn-encr-root-near-expire-int",
03893                        mWidget->mWarnEncrRootCertExpiresSB->value() );
03894 }
03895 
03896 void SecurityPage::WarningTab::slotReenableAllWarningsClicked() {
03897   KMessageBox::enableAllMessages();
03898   mWidget->enableAllWarningsPB->setEnabled( false );
03899 }
03900 
03902 
03903 QString SecurityPage::SMimeTab::helpAnchor() const {
03904   return QString::fromLatin1("configure-security-smime-validation");
03905 }
03906 
03907 SecurityPageSMimeTab::SecurityPageSMimeTab( QWidget * parent, const char * name )
03908   : ConfigModuleTab( parent, name )
03909 {
03910   // the margins are inside mWidget itself
03911   QVBoxLayout* vlay = new QVBoxLayout( this, 0, 0 );
03912 
03913   mWidget = new SMimeConfiguration( this );
03914   vlay->addWidget( mWidget );
03915 
03916   // Button-group for exclusive radiobuttons
03917   QButtonGroup* bg = new QButtonGroup( mWidget );
03918   bg->hide();
03919   bg->insert( mWidget->CRLRB );
03920   bg->insert( mWidget->OCSPRB );
03921 
03922   // Settings for the keyrequester custom widget
03923   mWidget->OCSPResponderSignature->setAllowedKeys(
03924      Kleo::KeySelectionDialog::SMIMEKeys
03925      | Kleo::KeySelectionDialog::TrustedKeys
03926      | Kleo::KeySelectionDialog::ValidKeys
03927      | Kleo::KeySelectionDialog::SigningKeys
03928      | Kleo::KeySelectionDialog::PublicKeys );
03929   mWidget->OCSPResponderSignature->setMultipleKeysEnabled( false );
03930 
03931   mConfig = Kleo::CryptoBackendFactory::instance()->config();
03932 
03933   connect( mWidget->CRLRB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
03934   connect( mWidget->OCSPRB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
03935   connect( mWidget->OCSPResponderURL, SIGNAL( textChanged( const QString& ) ), this, SLOT( slotEmitChanged() ) );
03936   connect( mWidget->OCSPResponderSignature, SIGNAL( changed() ), this, SLOT( slotEmitChanged() ) );
03937   connect( mWidget->doNotCheckCertPolicyCB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
03938   connect( mWidget->neverConsultCB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
03939   connect( mWidget->fetchMissingCB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
03940 
03941   connect( mWidget->ignoreServiceURLCB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
03942   connect( mWidget->ignoreHTTPDPCB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
03943   connect( mWidget->disableHTTPCB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
03944   connect( mWidget->honorHTTPProxyRB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
03945   connect( mWidget->useCustomHTTPProxyRB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
03946   connect( mWidget->customHTTPProxy, SIGNAL( textChanged( const QString& ) ), this, SLOT( slotEmitChanged() ) );
03947   connect( mWidget->ignoreLDAPDPCB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
03948   connect( mWidget->disableLDAPCB, SIGNAL( toggled( bool ) ), this, SLOT( slotEmitChanged() ) );
03949   connect( mWidget->customLDAPProxy, SIGNAL( textChanged( const QString& ) ), this, SLOT( slotEmitChanged() ) );
03950 
03951   connect( mWidget->disableHTTPCB, SIGNAL( toggled( bool ) ),
03952            this, SLOT( slotUpdateHTTPActions() ) );
03953   connect( mWidget->ignoreHTTPDPCB, SIGNAL( toggled( bool ) ),
03954            this, SLOT( slotUpdateHTTPActions() ) );
03955 
03956   // Button-group for exclusive radiobuttons
03957   QButtonGroup* bgHTTPProxy = new QButtonGroup( mWidget );
03958   bgHTTPProxy->hide();
03959   bgHTTPProxy->insert( mWidget->honorHTTPProxyRB );
03960   bgHTTPProxy->insert( mWidget->useCustomHTTPProxyRB );
03961 
03962   if ( !connectDCOPSignal( 0, "KPIM::CryptoConfig", "changed()",
03963                            "load()", false ) )
03964     kdError(5650) << "SecurityPageSMimeTab: connection to CryptoConfig's changed() failed" << endl;
03965 
03966 }
03967 
03968 SecurityPageSMimeTab::~SecurityPageSMimeTab()
03969 {
03970 }
03971 
03972 static void disableDirmngrWidget( QWidget* w ) {
03973   w->setEnabled( false );
03974   QWhatsThis::remove( w );
03975   QWhatsThis::add( w, i18n( "This option requires dirmngr >= 0.9.0" ) );
03976 }
03977 
03978 static void initializeDirmngrCheckbox( QCheckBox* cb, Kleo::CryptoConfigEntry* entry ) {
03979   if ( entry )
03980     cb->setChecked( entry->boolValue() );
03981   else
03982     disableDirmngrWidget( cb );
03983 }
03984 
03985 struct SMIMECryptoConfigEntries {
03986   SMIMECryptoConfigEntries( Kleo::CryptoConfig* config )
03987     : mConfig( config ) {
03988 
03989     // Checkboxes
03990     mCheckUsingOCSPConfigEntry = configEntry( "gpgsm", "Security", "enable-ocsp", Kleo::CryptoConfigEntry::ArgType_None, false );
03991     mEnableOCSPsendingConfigEntry = configEntry( "dirmngr", "OCSP", "allow-ocsp", Kleo::CryptoConfigEntry::ArgType_None, false );
03992     mDoNotCheckCertPolicyConfigEntry = configEntry( "gpgsm", "Security", "disable-policy-checks", Kleo::CryptoConfigEntry::ArgType_None, false );
03993     mNeverConsultConfigEntry = configEntry( "gpgsm", "Security", "disable-crl-checks", Kleo::CryptoConfigEntry::ArgType_None, false );
03994     mFetchMissingConfigEntry = configEntry( "gpgsm", "Security", "auto-issuer-key-retrieve", Kleo::CryptoConfigEntry::ArgType_None, false );
03995     // dirmngr-0.9.0 options
03996     mIgnoreServiceURLEntry = configEntry( "dirmngr", "OCSP", "ignore-ocsp-service-url", Kleo::CryptoConfigEntry::ArgType_None, false );
03997     mIgnoreHTTPDPEntry = configEntry( "dirmngr", "HTTP", "ignore-http-dp", Kleo::CryptoConfigEntry::ArgType_None, false );
03998     mDisableHTTPEntry = configEntry( "dirmngr", "HTTP", "disable-http", Kleo::CryptoConfigEntry::ArgType_None, false );
03999     mHonorHTTPProxy = configEntry( "dirmngr", "HTTP", "honor-http-proxy", Kleo::CryptoConfigEntry::ArgType_None, false );
04000 
04001     mIgnoreLDAPDPEntry = configEntry( "dirmngr", "LDAP", "ignore-ldap-dp", Kleo::CryptoConfigEntry::ArgType_None, false );
04002     mDisableLDAPEntry = configEntry( "dirmngr", "LDAP", "disable-ldap", Kleo::CryptoConfigEntry::ArgType_None, false );
04003     // Other widgets
04004     mOCSPResponderURLConfigEntry = configEntry( "dirmngr", "OCSP", "ocsp-responder", Kleo::CryptoConfigEntry::ArgType_String, false );
04005     mOCSPResponderSignature = configEntry( "dirmngr", "OCSP", "ocsp-signer", Kleo::CryptoConfigEntry::ArgType_String, false );
04006     mCustomHTTPProxy = configEntry( "dirmngr", "HTTP", "http-proxy", Kleo::CryptoConfigEntry::ArgType_String, false );
04007     mCustomLDAPProxy = configEntry( "dirmngr", "LDAP", "ldap-proxy", Kleo::CryptoConfigEntry::ArgType_String, false );
04008   }
04009 
04010   Kleo::CryptoConfigEntry* configEntry( const char* componentName,
04011                                         const char* groupName,
04012                                         const char* entryName,
04013                                         int argType,
04014                                         bool isList );
04015 
04016   // Checkboxes
04017   Kleo::CryptoConfigEntry* mCheckUsingOCSPConfigEntry;
04018   Kleo::CryptoConfigEntry* mEnableOCSPsendingConfigEntry;
04019   Kleo::CryptoConfigEntry* mDoNotCheckCertPolicyConfigEntry;
04020   Kleo::CryptoConfigEntry* mNeverConsultConfigEntry;
04021   Kleo::CryptoConfigEntry* mFetchMissingConfigEntry;
04022   Kleo::CryptoConfigEntry* mIgnoreServiceURLEntry;
04023   Kleo::CryptoConfigEntry* mIgnoreHTTPDPEntry;
04024   Kleo::CryptoConfigEntry* mDisableHTTPEntry;
04025   Kleo::CryptoConfigEntry* mHonorHTTPProxy;
04026   Kleo::CryptoConfigEntry* mIgnoreLDAPDPEntry;
04027   Kleo::CryptoConfigEntry* mDisableLDAPEntry;
04028   // Other widgets
04029   Kleo::CryptoConfigEntry* mOCSPResponderURLConfigEntry;
04030   Kleo::CryptoConfigEntry* mOCSPResponderSignature;
04031   Kleo::CryptoConfigEntry* mCustomHTTPProxy;
04032   Kleo::CryptoConfigEntry* mCustomLDAPProxy;
04033 
04034   Kleo::CryptoConfig* mConfig;
04035 };
04036 
04037 void SecurityPage::SMimeTab::doLoadOther() {
04038   if ( !mConfig ) {
04039     setEnabled( false );
04040     return;
04041   }
04042 
04043   // Force re-parsing gpgconf data, in case e.g. kleopatra or "configure backend" was used
04044   // (which ends up calling us via dcop)
04045   mConfig->clear();
04046 
04047   // Create config entries
04048   // Don't keep them around, they'll get deleted by clear(), which could be
04049   // done by the "configure backend" button even before we save().
04050   SMIMECryptoConfigEntries e( mConfig );
04051 
04052   // Initialize GUI items from the config entries
04053 
04054   if ( e.mCheckUsingOCSPConfigEntry ) {
04055     bool b = e.mCheckUsingOCSPConfigEntry->boolValue();
04056     mWidget->OCSPRB->setChecked( b );
04057     mWidget->CRLRB->setChecked( !b );
04058     mWidget->OCSPGroupBox->setEnabled( b );
04059   } else {
04060     mWidget->OCSPGroupBox->setEnabled( false );
04061   }
04062   if ( e.mDoNotCheckCertPolicyConfigEntry )
04063     mWidget->doNotCheckCertPolicyCB->setChecked( e.mDoNotCheckCertPolicyConfigEntry->boolValue() );
04064   if ( e.mNeverConsultConfigEntry )
04065     mWidget->neverConsultCB->setChecked( e.mNeverConsultConfigEntry->boolValue() );
04066   if ( e.mFetchMissingConfigEntry )
04067     mWidget->fetchMissingCB->setChecked( e.mFetchMissingConfigEntry->boolValue() );
04068 
04069   if ( e.mOCSPResponderURLConfigEntry )
04070     mWidget->OCSPResponderURL->setText( e.mOCSPResponderURLConfigEntry->stringValue() );
04071   if ( e.mOCSPResponderSignature ) {
04072     mWidget->OCSPResponderSignature->setFingerprint( e.mOCSPResponderSignature->stringValue() );
04073   }
04074 
04075   // dirmngr-0.9.0 options
04076   initializeDirmngrCheckbox( mWidget->ignoreServiceURLCB, e.mIgnoreServiceURLEntry );
04077   initializeDirmngrCheckbox( mWidget->ignoreHTTPDPCB, e.mIgnoreHTTPDPEntry );
04078   initializeDirmngrCheckbox( mWidget->disableHTTPCB, e.mDisableHTTPEntry );
04079   initializeDirmngrCheckbox( mWidget->ignoreLDAPDPCB, e.mIgnoreLDAPDPEntry );
04080   initializeDirmngrCheckbox( mWidget->disableLDAPCB, e.mDisableLDAPEntry );
04081   if ( e.mCustomHTTPProxy ) {
04082     QString systemProxy = QString::fromLocal8Bit( getenv( "http_proxy" ) );
04083     if ( systemProxy.isEmpty() )
04084       systemProxy = i18n( "no proxy" );
04085     mWidget->systemHTTPProxy->setText( i18n( "(Current system setting: %1)" ).arg( systemProxy ) );
04086     bool honor = e.mHonorHTTPProxy && e.mHonorHTTPProxy->boolValue();
04087     mWidget->honorHTTPProxyRB->setChecked( honor );
04088     mWidget->useCustomHTTPProxyRB->setChecked( !honor );
04089     mWidget->customHTTPProxy->setText( e.mCustomHTTPProxy->stringValue() );
04090   } else {
04091     disableDirmngrWidget( mWidget->honorHTTPProxyRB );
04092     disableDirmngrWidget( mWidget->useCustomHTTPProxyRB );
04093     disableDirmngrWidget( mWidget->systemHTTPProxy );
04094     disableDirmngrWidget( mWidget->customHTTPProxy );
04095   }
04096   if ( e.mCustomLDAPProxy )
04097     mWidget->customLDAPProxy->setText( e.mCustomLDAPProxy->stringValue() );
04098   else {
04099     disableDirmngrWidget( mWidget->customLDAPProxy );
04100     disableDirmngrWidget( mWidget->customLDAPLabel );
04101   }
04102   slotUpdateHTTPActions();
04103 }
04104 
04105 void SecurityPage::SMimeTab::slotUpdateHTTPActions() {
04106   mWidget->ignoreHTTPDPCB->setEnabled( !mWidget->disableHTTPCB->isChecked() );
04107 
04108   // The proxy settings only make sense when "Ignore HTTP CRL DPs of certificate" is checked.
04109   bool enableProxySettings = !mWidget->disableHTTPCB->isChecked()
04110                           && mWidget->ignoreHTTPDPCB->isChecked();
04111   mWidget->systemHTTPProxy->setEnabled( enableProxySettings );
04112   mWidget->useCustomHTTPProxyRB->setEnabled( enableProxySettings );
04113   mWidget->honorHTTPProxyRB->setEnabled( enableProxySettings );
04114   mWidget->customHTTPProxy->setEnabled( enableProxySettings );
04115 }
04116 
04117 void SecurityPage::SMimeTab::installProfile( KConfig * ) {
04118 }
04119 
04120 static void saveCheckBoxToKleoEntry( QCheckBox* cb, Kleo::CryptoConfigEntry* entry ) {
04121   const bool b = cb->isChecked();
04122   if ( entry && entry->boolValue() != b )
04123     entry->setBoolValue( b );
04124 }
04125 
04126 void SecurityPage::SMimeTab::save() {
04127   if ( !mConfig ) {
04128     return;
04129   }
04130   // Create config entries
04131   // Don't keep them around, they'll get deleted by clear(), which could be done by the
04132   // "configure backend" button.
04133   SMIMECryptoConfigEntries e( mConfig );
04134 
04135   bool b = mWidget->OCSPRB->isChecked();
04136   if ( e.mCheckUsingOCSPConfigEntry && e.mCheckUsingOCSPConfigEntry->boolValue() != b )
04137     e.mCheckUsingOCSPConfigEntry->setBoolValue( b );
04138   // Set allow-ocsp together with enable-ocsp
04139   if ( e.mEnableOCSPsendingConfigEntry && e.mEnableOCSPsendingConfigEntry->boolValue() != b )
04140     e.mEnableOCSPsendingConfigEntry->setBoolValue( b );
04141 
04142   saveCheckBoxToKleoEntry( mWidget->doNotCheckCertPolicyCB, e.mDoNotCheckCertPolicyConfigEntry );
04143   saveCheckBoxToKleoEntry( mWidget->neverConsultCB, e.mNeverConsultConfigEntry );
04144   saveCheckBoxToKleoEntry( mWidget->fetchMissingCB, e.mFetchMissingConfigEntry );
04145 
04146   QString txt = mWidget->OCSPResponderURL->text();
04147   if ( e.mOCSPResponderURLConfigEntry && e.mOCSPResponderURLConfigEntry->stringValue() != txt )
04148     e.mOCSPResponderURLConfigEntry->setStringValue( txt );
04149 
04150   txt = mWidget->OCSPResponderSignature->fingerprint();
04151   if ( e.mOCSPResponderSignature && e.mOCSPResponderSignature->stringValue() != txt ) {
04152     e.mOCSPResponderSignature->setStringValue( txt );
04153   }
04154 
04155   //dirmngr-0.9.0 options
04156   saveCheckBoxToKleoEntry( mWidget->ignoreServiceURLCB, e.mIgnoreServiceURLEntry );
04157   saveCheckBoxToKleoEntry( mWidget->ignoreHTTPDPCB, e.mIgnoreHTTPDPEntry );
04158   saveCheckBoxToKleoEntry( mWidget->disableHTTPCB, e.mDisableHTTPEntry );
04159   saveCheckBoxToKleoEntry( mWidget->ignoreLDAPDPCB, e.mIgnoreLDAPDPEntry );
04160   saveCheckBoxToKleoEntry( mWidget->disableLDAPCB, e.mDisableLDAPEntry );
04161   if ( e.mCustomHTTPProxy ) {
04162     const bool honor = mWidget->honorHTTPProxyRB->isChecked();
04163     if ( e.mHonorHTTPProxy && e.mHonorHTTPProxy->boolValue() != honor )
04164         e.mHonorHTTPProxy->setBoolValue( honor );
04165 
04166     QString chosenProxy = mWidget->customHTTPProxy->text();
04167     if ( chosenProxy != e.mCustomHTTPProxy->stringValue() )
04168       e.mCustomHTTPProxy->setStringValue( chosenProxy );
04169   }
04170   txt = mWidget->customLDAPProxy->text();
04171   if ( e.mCustomLDAPProxy && e.mCustomLDAPProxy->stringValue() != txt )
04172     e.mCustomLDAPProxy->setStringValue( mWidget->customLDAPProxy->text() );
04173 
04174   mConfig->sync( true );
04175 }
04176 
04177 bool SecurityPageSMimeTab::process(const QCString &fun, const QByteArray &data, QCString& replyType, QByteArray &replyData)
04178 {
04179     if ( fun == "load()" ) {
04180         replyType = "void";
04181         load();
04182     } else {
04183         return DCOPObject::process( fun, data, replyType, replyData );
04184     }
04185     return true;
04186 }
04187 
04188 QCStringList SecurityPageSMimeTab::interfaces()
04189 {
04190   QCStringList ifaces = DCOPObject::interfaces();
04191   ifaces += "SecurityPageSMimeTab";
04192   return ifaces;
04193 }
04194 
04195 QCStringList SecurityPageSMimeTab::functions()
04196 {
04197   // Hide our slot, just because it's simpler to do so.
04198   return DCOPObject::functions();
04199 }
04200 
04201 Kleo::CryptoConfigEntry* SMIMECryptoConfigEntries::configEntry( const char* componentName,
04202                                                                 const char* groupName,
04203                                                                 const char* entryName,
04204                                                                 int /*Kleo::CryptoConfigEntry::ArgType*/ argType,
04205                                                                 bool isList )
04206 {
04207     Kleo::CryptoConfigEntry* entry = mConfig->entry( componentName, groupName, entryName );
04208     if ( !entry ) {
04209         kdWarning(5006) << QString( "Backend error: gpgconf doesn't seem to know the entry for %1/%2/%3" ).arg( componentName, groupName, entryName ) << endl;
04210         return 0;
04211     }
04212     if( entry->argType() != argType || entry->isList() != isList ) {
04213         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;
04214         return 0;
04215     }
04216     return entry;
04217 }
04218 
04220 
04221 QString SecurityPage::CryptPlugTab::helpAnchor() const {
04222   return QString::fromLatin1("configure-security-crypto-backends");
04223 }
04224 
04225 SecurityPageCryptPlugTab::SecurityPageCryptPlugTab( QWidget * parent, const char * name )
04226   : ConfigModuleTab( parent, name )
04227 {
04228   QVBoxLayout * vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
04229 
04230   mBackendConfig = Kleo::CryptoBackendFactory::instance()->configWidget( this, "mBackendConfig" );
04231   connect( mBackendConfig, SIGNAL( changed( bool ) ), this, SIGNAL( changed( bool ) ) );
04232 
04233   vlay->addWidget( mBackendConfig );
04234 }
04235 
04236 SecurityPageCryptPlugTab::~SecurityPageCryptPlugTab()
04237 {
04238 
04239 }
04240 
04241 void SecurityPage::CryptPlugTab::doLoadOther() {
04242   mBackendConfig->load();
04243 }
04244 
04245 void SecurityPage::CryptPlugTab::save() {
04246   mBackendConfig->save();
04247 }
04248 
04249 // *************************************************************
04250 // *                                                           *
04251 // *                        MiscPage                           *
04252 // *                                                           *
04253 // *************************************************************
04254 QString MiscPage::helpAnchor() const {
04255   return QString::fromLatin1("configure-misc");
04256 }
04257 
04258 MiscPage::MiscPage( QWidget * parent, const char * name )
04259   : ConfigModuleWithTabs( parent, name )
04260 {
04261   mFolderTab = new FolderTab();
04262   addTab( mFolderTab, i18n("&Folders") );
04263 
04264   mGroupwareTab = new GroupwareTab();
04265   addTab( mGroupwareTab, i18n("&Groupware") );
04266   load();
04267 }
04268 
04269 QString MiscPage::FolderTab::helpAnchor() const {
04270   return QString::fromLatin1("configure-misc-folders");
04271 }
04272 
04273 MiscPageFolderTab::MiscPageFolderTab( QWidget * parent, const char * name )
04274   : ConfigModuleTab( parent, name )
04275 {
04276   // temp. vars:
04277   QVBoxLayout *vlay;
04278   QHBoxLayout *hlay;
04279   QLabel      *label;
04280 
04281   vlay = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
04282 
04283   // "confirm before emptying folder" check box: stretch 0
04284   mEmptyFolderConfirmCheck =
04285     new QCheckBox( i18n("Corresponds to Folder->Move All Messages to Trash",
04286                         "Ask for co&nfirmation before moving all messages to "
04287                         "trash"),
04288                    this );
04289   vlay->addWidget( mEmptyFolderConfirmCheck );
04290   connect( mEmptyFolderConfirmCheck, SIGNAL( stateChanged( int ) ),
04291            this, SLOT( slotEmitChanged( void ) ) );
04292   mExcludeImportantFromExpiry =
04293     new QCheckBox( i18n("E&xclude important messages from expiry"), this );
04294   vlay->addWidget( mExcludeImportantFromExpiry );
04295   connect( mExcludeImportantFromExpiry, SIGNAL( stateChanged( int ) ),
04296            this, SLOT( slotEmitChanged( void ) ) );
04297 
04298   // "when trying to find unread messages" combo + label: stretch 0
04299   hlay = new QHBoxLayout( vlay ); // inherits spacing
04300   mLoopOnGotoUnread = new QComboBox( false, this );
04301   label = new QLabel( mLoopOnGotoUnread,
04302            i18n("to be continued with \"do not loop\", \"loop in current folder\", "
04303                 "and \"loop in all folders\".",
04304                 "When trying to find unread messages:"), this );
04305   mLoopOnGotoUnread->insertStringList( QStringList()
04306       << i18n("continuation of \"When trying to find unread messages:\"",
04307               "Do not Loop")
04308       << i18n("continuation of \"When trying to find unread messages:\"",
04309               "Loop in Current Folder")
04310       << i18n("continuation of \"When trying to find unread messages:\"",
04311               "Loop in All Folders"));
04312   hlay->addWidget( label );
04313   hlay->addWidget( mLoopOnGotoUnread, 1 );
04314   connect( mLoopOnGotoUnread, SIGNAL( activated( int ) ),
04315            this, SLOT( slotEmitChanged( void ) ) );
04316 
04317   // when entering a folder
04318   hlay = new QHBoxLayout( vlay ); // inherits spacing
04319   mActionEnterFolder = new QComboBox( false, this );
04320   label = new QLabel( mActionEnterFolder,
04321            i18n("to be continued with \"jump to first new message\", "
04322                 "\"jump to first unread or new message\","
04323                 "and \"jump to last selected message\".",
04324                 "When entering a folder:"), this );
04325   mActionEnterFolder->insertStringList( QStringList()
04326       << i18n("continuation of \"When entering a folder:\"",
04327               "Jump to First New Message")
04328       << i18n("continuation of \"When entering a folder:\"",
04329               "Jump to First Unread or New Message")
04330       << i18n("continuation of \"When entering a folder:\"",
04331               "Jump to Last Selected Message"));
04332   hlay->addWidget( label );
04333   hlay->addWidget( mActionEnterFolder, 1 );
04334   connect( mActionEnterFolder, SIGNAL( activated( int ) ),
04335            this, SLOT( slotEmitChanged( void ) ) );
04336 
04337   hlay = new QHBoxLayout( vlay ); // inherits spacing
04338   mDelayedMarkAsRead = new QCheckBox( i18n("Mar&k selected message as read after"), this );
04339   hlay->addWidget( mDelayedMarkAsRead );
04340   mDelayedMarkTime = new KIntSpinBox( 0 /*min*/, 60 /*max*/, 1/*step*/,
04341                                       0 /*init*/, 10 /*base*/, this);
04342   mDelayedMarkTime->setSuffix( i18n(" sec") );
04343   mDelayedMarkTime->setEnabled( false ); // since mDelayedMarkAsREad is off
04344   hlay->addWidget( mDelayedMarkTime );
04345   hlay->addStretch( 1 );
04346   connect( mDelayedMarkTime, SIGNAL( valueChanged( int ) ),
04347            this, SLOT( slotEmitChanged( void ) ) );
04348   connect( mDelayedMarkAsRead, SIGNAL(toggled(bool)),
04349            mDelayedMarkTime, SLOT(setEnabled(bool)));
04350   connect( mDelayedMarkAsRead, SIGNAL(toggled(bool)),
04351            this , SLOT(slotEmitChanged( void )));
04352 
04353   // "show popup after Drag'n'Drop" checkbox: stretch 0
04354   mShowPopupAfterDnD =
04355     new QCheckBox( i18n("Ask for action after &dragging messages to another folder"), this );
04356   vlay->addWidget( mShowPopupAfterDnD );
04357   connect( mShowPopupAfterDnD, SIGNAL( stateChanged( int ) ),
04358            this, SLOT( slotEmitChanged( void ) ) );
04359 
04360   // "default mailbox format" combo + label: stretch 0
04361   hlay = new QHBoxLayout( vlay ); // inherits spacing
04362   mMailboxPrefCombo = new QComboBox( false, this );
04363   label = new QLabel( mMailboxPrefCombo,
04364                       i18n("to be continued with \"flat files\" and "
04365                            "\"directories\", resp.",
04366                            "By default, &message folders on disk are:"), this );
04367   mMailboxPrefCombo->insertStringList( QStringList()
04368           << i18n("continuation of \"By default, &message folders on disk are\"",
04369                   "Flat Files (\"mbox\" format)")
04370           << i18n("continuation of \"By default, &message folders on disk are\"",
04371                   "Directories (\"maildir\" format)") );
04372   hlay->addWidget( label );
04373   hlay->addWidget( mMailboxPrefCombo, 1 );
04374   connect( mMailboxPrefCombo, SIGNAL( activated( int ) ),
04375            this, SLOT( slotEmitChanged( void ) ) );
04376 
04377   // "On startup..." option:
04378   hlay = new QHBoxLayout( vlay ); // inherits spacing
04379   mOnStartupOpenFolder = new FolderRequester( this,
04380       kmkernel->getKMMainWidget()->folderTree() );
04381   label = new QLabel( mOnStartupOpenFolder,
04382                       i18n("Open this folder on startup:"), this );
04383   hlay->addWidget( label );
04384   hlay->addWidget( mOnStartupOpenFolder, 1 );
04385   connect( mOnStartupOpenFolder, SIGNAL( folderChanged( KMFolder* ) ),
04386            this, SLOT( slotEmitChanged( void ) ) );
04387 
04388   // "Empty &trash on program exit" option:
04389   mEmptyTrashCheck = new QCheckBox( i18n("Empty local &trash folder on program exit"),
04390                                     this );
04391   vlay->addWidget( mEmptyTrashCheck );
04392   connect( mEmptyTrashCheck, SIGNAL( stateChanged( int ) ),
04393            this, SLOT( slotEmitChanged( void ) ) );
04394 
04395 #ifdef HAVE_INDEXLIB
04396   // indexing enabled option:
04397   mIndexingEnabled = new QCheckBox( i18n("Enable full text &indexing"), this );
04398   vlay->addWidget( mIndexingEnabled );
04399   connect( mIndexingEnabled, SIGNAL( stateChanged( int ) ),
04400            this, SLOT( slotEmitChanged( void ) ) );
04401 #endif
04402 
04403 
04404 
04405   vlay->addStretch( 1 );
04406 
04407   // and now: add QWhatsThis:
04408   QString msg = i18n( "what's this help",
04409                       "<qt><p>This selects which mailbox format will be "
04410                       "the default for local folders:</p>"
04411                       "<p><b>mbox:</b> KMail's mail "
04412                       "folders are represented by a single file each. "
04413                       "Individual messages are separated from each other by a "
04414                       "line starting with \"From \". This saves space on "
04415                       "disk, but may be less robust, e.g. when moving messages "
04416                       "between folders.</p>"
04417                       "<p><b>maildir:</b> KMail's mail folders are "
04418                       "represented by real folders on disk. Individual messages "
04419                       "are separate files. This may waste a bit of space on "
04420                       "disk, but should be more robust, e.g. when moving "
04421                       "messages between folders.</p></qt>");
04422   QWhatsThis::add( mMailboxPrefCombo, msg );
04423   QWhatsThis::add( label, msg );
04424   // @TODO: Till, move into .kcgc file
04425   msg = i18n( "what's this help",
04426             "<qt><p>When jumping to the next unread message, it may occur "
04427             "that no more unread messages are below the current message.</p>"
04428             "<p><b>Do not loop:</b> The search will stop at the last message in "
04429             "the current folder.</p>"
04430             "<p><b>Loop in current folder:</b> The search will continue at the "
04431             "top of the message list, but not go to another folder.</p>"
04432             "<p><b>Loop in all folders:</b> The search will continue at the top of "
04433             "the message list. If no unread messages are found it will then continue "
04434             "to the next folder.</p>"
04435             "<p>Similarly, when searching for the previous unread message, "
04436             "the search will start from the bottom of the message list and continue to "
04437             "the previous folder depending on which option is selected.</p></qt>" );
04438   QWhatsThis::add( mLoopOnGotoUnread, msg );
04439 
04440 #ifdef HAVE_INDEXLIB
04441  // this is probably overly pessimistic
04442   msg = i18n( "what's this help",
04443           "<qt><p>Full text indexing allows very fast searches on the content "
04444           "of your messages. When enabled, the search dialog will work very fast. "
04445           "Also, the search tool bar will select messages based on content.</p>"
04446           "<p>It takes up a certain amount of disk space "
04447           "(about half the disk space for the messages).</p>"
04448           "<p>After enabling, the index will need to be built, but you can continue to use KMail "
04449           "while this operation is running.</p>"
04450           "</qt>"
04451         );
04452 
04453   QWhatsThis::add( mIndexingEnabled, msg );
04454 #endif
04455 }
04456 
04457 void MiscPage::FolderTab::doLoadFromGlobalSettings() {
04458   mExcludeImportantFromExpiry->setChecked( GlobalSettings::self()->excludeImportantMailFromExpiry() );
04459   // default = "Loop in current folder"
04460   mLoopOnGotoUnread->setCurrentItem( GlobalSettings::self()->loopOnGotoUnread() );
04461   mActionEnterFolder->setCurrentItem( GlobalSettings::self()->actionEnterFolder() );
04462   mDelayedMarkAsRead->setChecked( GlobalSettings::self()->delayedMarkAsRead() );
04463   mDelayedMarkTime->setValue( GlobalSettings::self()->delayedMarkTime() );
04464   mShowPopupAfterDnD->setChecked( GlobalSettings::self()->showPopupAfterDnD() );
04465 }
04466 
04467 void MiscPage::FolderTab::doLoadOther() {
04468   KConfigGroup general( KMKernel::config(), "General" );
04469 
04470   mEmptyTrashCheck->setChecked( general.readBoolEntry( "empty-trash-on-exit", true ) );
04471   mOnStartupOpenFolder->setFolder( general.readEntry( "startupFolder",
04472                                                   kmkernel->inboxFolder()->idString() ) );
04473   mEmptyFolderConfirmCheck->setChecked( general.readBoolEntry( "confirm-before-empty", true ) );
04474 
04475   int num = general.readNumEntry("default-mailbox-format", 1 );
04476   if ( num < 0 || num > 1 ) num = 1;
04477   mMailboxPrefCombo->setCurrentItem( num );
04478 
04479 #ifdef HAVE_INDEXLIB
04480   mIndexingEnabled->setChecked( kmkernel->msgIndex() && kmkernel->msgIndex()->isEnabled() );
04481 #endif
04482 }
04483 
04484 void MiscPage::FolderTab::save() {
04485   KConfigGroup general( KMKernel::config(), "General" );
04486 
04487   general.writeEntry( "empty-trash-on-exit", mEmptyTrashCheck->isChecked() );
04488   general.writeEntry( "confirm-before-empty", mEmptyFolderConfirmCheck->isChecked() );
04489   general.writeEntry( "default-mailbox-format", mMailboxPrefCombo->currentItem() );
04490   general.writeEntry( "startupFolder", mOnStartupOpenFolder->folder() ?
04491                                   mOnStartupOpenFolder->folder()->idString() : QString::null );
04492 
04493   GlobalSettings::self()->setDelayedMarkAsRead( mDelayedMarkAsRead->isChecked() );
04494   GlobalSettings::self()->setDelayedMarkTime( mDelayedMarkTime->value() );
04495   GlobalSettings::self()->setActionEnterFolder( mActionEnterFolder->currentItem() );
04496   GlobalSettings::self()->setLoopOnGotoUnread( mLoopOnGotoUnread->currentItem() );
04497   GlobalSettings::self()->setShowPopupAfterDnD( mShowPopupAfterDnD->isChecked() );
04498   GlobalSettings::self()->setExcludeImportantMailFromExpiry(
04499         mExcludeImportantFromExpiry->isChecked() );
04500 #ifdef HAVE_INDEXLIB
04501   if ( kmkernel->msgIndex() ) kmkernel->msgIndex()->setEnabled( mIndexingEnabled->isChecked() );
04502 #endif
04503 }
04504 
04505 QString MiscPage::GroupwareTab::helpAnchor() const {
04506   return QString::fromLatin1("configure-misc-groupware");
04507 }
04508 
04509 MiscPageGroupwareTab::MiscPageGroupwareTab( QWidget* parent, const char* name )
04510   : ConfigModuleTab( parent, name )
04511 {
04512   QBoxLayout* vlay = new QVBoxLayout( this, KDialog::marginHint(),
04513                                       KDialog::spacingHint() );
04514   vlay->setAutoAdd( true );
04515 
04516   // IMAP resource setup
04517   QVGroupBox* b1 = new QVGroupBox( i18n("&IMAP Resource Folder Options"),
04518                                    this );
04519 
04520   mEnableImapResCB =
04521     new QCheckBox( i18n("&Enable IMAP resource functionality"), b1 );
04522   QToolTip::add( mEnableImapResCB,  i18n( "This enables the IMAP storage for "
04523                                           "the Kontact applications" ) );
04524   QWhatsThis::add( mEnableImapResCB,
04525         i18n( GlobalSettings::self()->theIMAPResourceEnabledItem()->whatsThis().utf8() ) );
04526   connect( mEnableImapResCB, SIGNAL( stateChanged( int ) ),
04527            this, SLOT( slotEmitChanged( void ) ) );
04528 
04529   mBox = new QWidget( b1 );
04530   QGridLayout* grid = new QGridLayout( mBox, 4, 2, 0, KDialog::spacingHint() );
04531   grid->setColStretch( 1, 1 );
04532   connect( mEnableImapResCB, SIGNAL( toggled(bool) ),
04533            mBox, SLOT( setEnabled(bool) ) );
04534 
04535   QLabel* storageFormatLA = new QLabel( i18n("&Format used for the groupware folders:"),
04536                                         mBox );
04537   QString toolTip = i18n( "Choose the format to use to store the contents of the groupware folders." );
04538   QString whatsThis = i18n( GlobalSettings::self()
04539         ->theIMAPResourceStorageFormatItem()->whatsThis().utf8() );
04540   grid->addWidget( storageFormatLA, 0, 0 );
04541   QToolTip::add( storageFormatLA, toolTip );
04542   QWhatsThis::add( storageFormatLA, whatsThis );
04543   mStorageFormatCombo = new QComboBox( false, mBox );
04544   storageFormatLA->setBuddy( mStorageFormatCombo );
04545   QStringList formatLst;
04546   formatLst << i18n("Standard (Ical / Vcard)") << i18n("Kolab (XML)");
04547   mStorageFormatCombo->insertStringList( formatLst );
04548   grid->addWidget( mStorageFormatCombo, 0, 1 );
04549   QToolTip::add( mStorageFormatCombo, toolTip );
04550   QWhatsThis::add( mStorageFormatCombo, whatsThis );
04551   connect( mStorageFormatCombo, SIGNAL( activated( int ) ),
04552            this, SLOT( slotStorageFormatChanged( int ) ) );
04553 
04554   QLabel* languageLA = new QLabel( i18n("&Language of the groupware folders:"),
04555                                    mBox );
04556 
04557   toolTip = i18n( "Set the language of the folder names" );
04558   whatsThis = i18n( GlobalSettings::self()
04559         ->theIMAPResourceFolderLanguageItem()->whatsThis().utf8() );
04560   grid->addWidget( languageLA, 1, 0 );
04561   QToolTip::add( languageLA, toolTip );
04562   QWhatsThis::add( languageLA, whatsThis );
04563   mLanguageCombo = new QComboBox( false, mBox );
04564   languageLA->setBuddy( mLanguageCombo );
04565   QStringList lst;
04566   lst << i18n("English") << i18n("German") << i18n("French") << i18n("Dutch");
04567   mLanguageCombo->insertStringList( lst );
04568   grid->addWidget( mLanguageCombo, 1, 1 );
04569   QToolTip::add( mLanguageCombo, toolTip );
04570   QWhatsThis::add( mLanguageCombo, whatsThis );
04571   connect( mLanguageCombo, SIGNAL( activated( int ) ),
04572            this, SLOT( slotEmitChanged( void ) ) );
04573 
04574   mFolderComboLabel = new QLabel( mBox ); // text depends on storage format
04575   toolTip = i18n( "Set the parent of the resource folders" );
04576   whatsThis = i18n( GlobalSettings::self()->theIMAPResourceFolderParentItem()->whatsThis().utf8() );
04577   QToolTip::add( mFolderComboLabel, toolTip );
04578   QWhatsThis::add( mFolderComboLabel, whatsThis );
04579   grid->addWidget( mFolderComboLabel, 2, 0 );
04580 
04581   mFolderComboStack = new QWidgetStack( mBox );
04582   grid->addWidget( mFolderComboStack, 2, 1 );
04583 
04584   // First possibility in the widgetstack: a combo showing the list of all folders
04585   // This is used with the ical/vcard storage
04586   mFolderCombo = new FolderRequester( mBox,
04587       kmkernel->getKMMainWidget()->folderTree() );
04588   mFolderComboStack->addWidget( mFolderCombo, 0 );
04589   QToolTip::add( mFolderCombo, toolTip );
04590   QWhatsThis::add( mFolderCombo, whatsThis );
04591   connect( mFolderCombo, SIGNAL( folderChanged( KMFolder* ) ),
04592            this, SLOT( slotEmitChanged() ) );
04593 
04594   // Second possibility in the widgetstack: a combo showing the list of accounts
04595   // This is used with the kolab xml storage since the groupware folders
04596   // are always under the inbox.
04597   mAccountCombo = new KMail::AccountComboBox( mBox );
04598   mFolderComboStack->addWidget( mAccountCombo, 1 );
04599   QToolTip::add( mAccountCombo, toolTip );
04600   QWhatsThis::add( mAccountCombo, whatsThis );
04601   connect( mAccountCombo, SIGNAL( activated( int ) ),
04602            this, SLOT( slotEmitChanged() ) );
04603 
04604   mHideGroupwareFolders = new QCheckBox( i18n( "&Hide groupware folders" ),
04605                                          mBox, "HideGroupwareFoldersBox" );
04606   grid->addMultiCellWidget( mHideGroupwareFolders, 3, 3, 0, 1 );
04607   QToolTip::add( mHideGroupwareFolders,
04608                  i18n( "When this is checked, you will not see the IMAP "
04609                        "resource folders in the folder tree." ) );
04610   QWhatsThis::add( mHideGroupwareFolders, i18n( GlobalSettings::self()
04611            ->hideGroupwareFoldersItem()->whatsThis().utf8() ) );
04612   connect( mHideGroupwareFolders, SIGNAL( toggled( bool ) ),
04613            this, SLOT( slotEmitChanged() ) );
04614 
04615   // Groupware functionality compatibility setup
04616   b1 = new QVGroupBox( i18n("Groupware Compatibility && Legacy Options"), this );
04617 
04618   gBox = new QVBox( b1 );
04619 #if 0
04620   // Currently believed to be disused.
04621   mEnableGwCB = new QCheckBox( i18n("&Enable groupware functionality"), b1 );
04622   gBox->setSpacing( KDialog::spacingHint() );
04623   connect( mEnableGwCB, SIGNAL( toggled(bool) ),
04624            gBox, SLOT( setEnabled(bool) ) );
04625   connect( mEnableGwCB, SIGNAL( stateChanged( int ) ),
04626            this, SLOT( slotEmitChanged( void ) ) );
04627 #endif
04628   mEnableGwCB = 0;
04629   mLegacyMangleFromTo = new QCheckBox( i18n( "Mangle From:/To: headers in replies to invitations" ), gBox );
04630   QToolTip::add( mLegacyMangleFromTo, i18n( "Turn this option on in order to make Outlook(tm) understand your answers to invitation replies" ) );
04631   QWhatsThis::add( mLegacyMangleFromTo, i18n( GlobalSettings::self()->
04632            legacyMangleFromToHeadersItem()->whatsThis().utf8() ) );
04633   connect( mLegacyMangleFromTo, SIGNAL( stateChanged( int ) ),
04634            this, SLOT( slotEmitChanged( void ) ) );
04635   mLegacyBodyInvites = new QCheckBox( i18n( "Send invitations in the mail body" ), gBox );
04636   QToolTip::add( mLegacyBodyInvites, i18n( "Turn this option on in order to make Outlook(tm) understand your answers to invitations" ) );
04637   QWhatsThis::add( mLegacyMangleFromTo, i18n( GlobalSettings::self()->
04638            legacyBodyInvitesItem()->whatsThis().utf8() ) );
04639   connect( mLegacyBodyInvites, SIGNAL( toggled( bool ) ),
04640            this, SLOT( slotLegacyBodyInvitesToggled( bool ) ) );
04641   connect( mLegacyBodyInvites, SIGNAL( stateChanged( int ) ),
04642            this, SLOT( slotEmitChanged( void ) ) );
04643   mAutomaticSending = new QCheckBox( i18n( "Automatic invitation sending" ), gBox );
04644   QToolTip::add( mAutomaticSending, i18n( "When this is on, the user will not see the mail composer window. Invitation mails are sent automatically" ) );
04645   QWhatsThis::add( mAutomaticSending, i18n( GlobalSettings::self()->
04646            automaticSendingItem()->whatsThis().utf8() ) );
04647   connect( mAutomaticSending, SIGNAL( stateChanged( int ) ),
04648            this, SLOT( slotEmitChanged( void ) ) );
04649 
04650   // Open space padding at the end
04651   new QLabel( this );
04652 }
04653 
04654 void MiscPageGroupwareTab::slotLegacyBodyInvitesToggled( bool on )
04655 {
04656   if ( on ) {
04657     QString txt = i18n( "<qt>Invitations are normally sent as attachments to "
04658                         "a mail. This switch changes the invitation mails to "
04659                         "be sent in the text of the mail instead; this is "
04660                         "necessary to send invitations and replies to "
04661                         "Microsoft Outlook.<br>But, when you do this, you no "
04662                         "longer get descriptive text that mail programs "
04663                         "can read; so, to people who have email programs "
04664                         "that do not understand the invitations, the "
04665                         "resulting messages look very odd.<br>People that have email "
04666                         "programs that do understand invitations will still "
04667                         "be able to work with this.</qt>" );
04668     KMessageBox::information( this, txt, QString::null,
04669                               "LegacyBodyInvitesWarning" );
04670   }
04671   // Invitations in the body are autosent in any case (no point in editing raw ICAL)
04672   // So the autosend option is only available if invitations are sent as attachment.
04673   mAutomaticSending->setEnabled( !mLegacyBodyInvites->isChecked() );
04674 }
04675 
04676 void MiscPage::GroupwareTab::doLoadFromGlobalSettings() {
04677   // Read the groupware config
04678   if ( mEnableGwCB ) {
04679     mEnableGwCB->setChecked( GlobalSettings::self()->groupwareEnabled() );
04680     gBox->setEnabled( mEnableGwCB->isChecked() );
04681   }
04682   mLegacyMangleFromTo->setChecked( GlobalSettings::self()->legacyMangleFromToHeaders() );
04683   mLegacyBodyInvites->blockSignals( true );
04684   mLegacyBodyInvites->setChecked( GlobalSettings::self()->legacyBodyInvites() );
04685   mLegacyBodyInvites->blockSignals( false );
04686   mAutomaticSending->setChecked( GlobalSettings::self()->automaticSending() );
04687   mAutomaticSending->setEnabled( !mLegacyBodyInvites->isChecked() );
04688 
04689   // Read the IMAP resource config
04690   mEnableImapResCB->setChecked( GlobalSettings::self()->theIMAPResourceEnabled() );
04691   mBox->setEnabled( mEnableImapResCB->isChecked() );
04692 
04693   mHideGroupwareFolders->setChecked( GlobalSettings::self()->hideGroupwareFolders() );
04694   int i = GlobalSettings::self()->theIMAPResourceFolderLanguage();
04695   mLanguageCombo->setCurrentItem(i);
04696   i = GlobalSettings::self()->theIMAPResourceStorageFormat();
04697   mStorageFormatCombo->setCurrentItem(i);
04698   slotStorageFormatChanged( i );
04699 
04700   QString folderId( GlobalSettings::self()->theIMAPResourceFolderParent() );
04701   if( !folderId.isNull() && kmkernel->findFolderById( folderId ) ) {
04702     mFolderCombo->setFolder( folderId );
04703   } else {
04704     // Folder was deleted, we have to choose a new one
04705     mFolderCombo->setFolder( i18n( "<Choose a Folder>" ) );
04706   }
04707 
04708   KMAccount* selectedAccount = 0;
04709   int accountId = GlobalSettings::self()->theIMAPResourceAccount();
04710   if ( accountId )
04711     selectedAccount = kmkernel->acctMgr()->find( accountId );
04712   else {
04713     // Fallback: iterate over accounts to select folderId if found (as an inbox folder)
04714       for( KMAccount *a = kmkernel->acctMgr()->first(); a!=0;
04715          a = kmkernel->acctMgr()->next() ) {
04716       if( a->folder() && a->folder()->child() ) {
04717         // Look inside that folder for an INBOX
04718         KMFolderNode *node;
04719         for (node = a->folder()->child()->first(); node; node = a->folder()->child()->next())
04720           if (!node->isDir() && node->name() == "INBOX") break;
04721 
04722         if ( node && static_cast<KMFolder*>(node)->idString() == folderId ) {
04723           selectedAccount = a;
04724           break;
04725         }
04726       }
04727     }
04728   }
04729   if ( selectedAccount )
04730     mAccountCombo->setCurrentAccount( selectedAccount );
04731   else if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == 1 )
04732     kdDebug(5006) << "Folder " << folderId << " not found as an account's inbox" << endl;
04733 }
04734 
04735 void MiscPage::GroupwareTab::save() {
04736   // Write the groupware config
04737   if ( mEnableGwCB )
04738     GlobalSettings::self()->setGroupwareEnabled( mEnableGwCB->isChecked() );
04739   GlobalSettings::self()->setLegacyMangleFromToHeaders( mLegacyMangleFromTo->isChecked() );
04740   GlobalSettings::self()->setLegacyBodyInvites( mLegacyBodyInvites->isChecked() );
04741   GlobalSettings::self()->setAutomaticSending( mAutomaticSending->isChecked() );
04742 
04743   int format = mStorageFormatCombo->currentItem();
04744   GlobalSettings::self()->setTheIMAPResourceStorageFormat( format );
04745 
04746   // Write the IMAP resource config
04747   GlobalSettings::self()->setHideGroupwareFolders( mHideGroupwareFolders->isChecked() );
04748 
04749   // If there is a leftover folder in the foldercombo, getFolder can
04750   // return 0. In that case we really don't have it enabled
04751   QString folderId;
04752   if (  format == 0 ) {
04753     KMFolder* folder = mFolderCombo->folder();
04754     if (  folder )
04755       folderId = folder->idString();
04756   } else {
04757     // Inbox folder of the selected account
04758     KMAccount* acct = mAccountCombo->currentAccount();
04759     if (  acct ) {
04760       folderId = QString( ".%1.directory/INBOX" ).arg( acct->id() );
04761       GlobalSettings::self()->setTheIMAPResourceAccount( acct->id() );
04762     }
04763   }
04764 
04765   bool enabled = mEnableImapResCB->isChecked() && !folderId.isEmpty();
04766   GlobalSettings::self()->setTheIMAPResourceEnabled( enabled );
04767   GlobalSettings::self()->setTheIMAPResourceFolderLanguage( mLanguageCombo->currentItem() );
04768   GlobalSettings::self()->setTheIMAPResourceFolderParent( folderId );
04769 }
04770 
04771 void MiscPage::GroupwareTab::slotStorageFormatChanged( int format )
04772 {
04773   mLanguageCombo->setEnabled( format == 0 ); // only ical/vcard needs the language hack
04774   mFolderComboStack->raiseWidget( format );
04775   if ( format == 0 ) {
04776     mFolderComboLabel->setText( i18n("&Resource folders are subfolders of:") );
04777     mFolderComboLabel->setBuddy( mFolderCombo );
04778   } else {
04779     mFolderComboLabel->setText( i18n("&Resource folders are in account:") );
04780     mFolderComboLabel->setBuddy( mAccountCombo );
04781   }
04782   slotEmitChanged();
04783 }
04784 
04785 
04786 // *************************************************************
04787 // *                                                           *
04788 // *                     AccountUpdater                        *
04789 // *                                                           *
04790 // *************************************************************
04791 AccountUpdater::AccountUpdater(ImapAccountBase *account)
04792     : QObject()
04793 {
04794   mAccount = account;
04795 }
04796 
04797 void AccountUpdater::update()
04798 {
04799   connect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
04800           this, SLOT( namespacesFetched() ) );
04801   mAccount->makeConnection();
04802 }
04803 
04804 void AccountUpdater::namespacesFetched()
04805 {
04806   mAccount->setCheckingMail( true );
04807   mAccount->processNewMail( false );
04808   deleteLater();
04809 }
04810 
04811 #undef DIM
04812 
04813 //----------------------------
04814 #include "configuredialog.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys