kmail Library API Documentation

configuredialog.cpp

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