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