kmail

configuredialog.cpp

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