kmail

configuredialog.cpp

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