/*
    kpgpui.cpp

    Copyright (C) 2001,2002 the KPGP authors
    See file AUTHORS.kpgp for details

    This file is part of KPGP, the KDE PGP/GnuPG support library.

    KPGP is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software Foundation,
    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 */

//#include <stdio.h>

#include <tqvgroupbox.h>
#include <tqvbox.h>
#include <tqlabel.h>
#include <tqwhatsthis.h>
#include <tqtooltip.h>
#include <tqapplication.h>
#include <tqtextcodec.h>
#include <tqdatetime.h>
#include <tqpixmap.h>
#include <tqlayout.h>
#include <tqtimer.h>
#include <tqpopupmenu.h>
#include <tqregexp.h>

#include <tdelocale.h>
#include <kpassdlg.h>
#include <kcharsets.h>
#include <kseparator.h>
#include <kiconloader.h>
#include <tdelistview.h>
#include <tdeconfigbase.h>
#include <tdeconfig.h>
#include <kprogress.h>
#include <tdeapplication.h>
#include <twin.h>
#if KDE_IS_VERSION( 3, 1, 90 )
#include <tdeglobalsettings.h>
#endif

#include "kpgp.h"
#include "kpgpui.h"
#include "kpgpkey.h"

#include <assert.h>
#include <string.h> // for memcpy(3)

const int Kpgp::KeySelectionDialog::sCheckSelectionDelay = 250;

namespace Kpgp {

PassphraseDialog::PassphraseDialog( TQWidget *parent,
                                    const TQString &caption, bool modal,
                                    const TQString &keyID )
  :KDialogBase( parent, 0, modal, caption, Ok|Cancel )
{
  TQHBox *hbox = makeHBoxMainWidget();
  hbox->setSpacing( spacingHint() );
  hbox->setMargin( marginHint() );

  TQLabel *label = new TQLabel(hbox);
  label->setPixmap( BarIcon("pgp-keys") );

  TQWidget *rightArea = new TQWidget( hbox );
  TQVBoxLayout *vlay = new TQVBoxLayout( rightArea, 0, spacingHint() );

  if (keyID.isNull())
    label = new TQLabel(i18n("Please enter your OpenPGP passphrase:"),rightArea);
  else
    label = new TQLabel(i18n("Please enter the OpenPGP passphrase for\n\"%1\":").arg(keyID),
                       rightArea);
  lineedit = new KPasswordEdit( rightArea );
  lineedit->setEchoMode(TQLineEdit::Password);
  lineedit->setMinimumWidth( fontMetrics().maxWidth()*20 );
  lineedit->setFocus();
  connect( lineedit, TQ_SIGNAL(returnPressed()), this, TQ_SLOT(slotOk()) );

  vlay->addWidget( label );
  vlay->addWidget( lineedit );

  disableResize();
}


PassphraseDialog::~PassphraseDialog()
{
}

TQString PassphraseDialog::passphrase()
{
  return lineedit->password();
}


// ------------------------------------------------------------------------
// Forbidden accels for KMail: AC GH OP
//                  for KNode: ACE H O
Config::Config( TQWidget *parent, const char *name, bool encrypt )
  : TQWidget( parent, name ), pgp( Module::getKpgp() )
{
  TQGroupBox * group;
  TQLabel    * label;
  TQString     msg;


  TQVBoxLayout *topLayout = new TQVBoxLayout( this, 0, KDialog::spacingHint() );

  group = new TQVGroupBox( i18n("Warning"), this );
  group->layout()->setSpacing( KDialog::spacingHint() );
  // (mmutz) work around TQt label bug in 3.0.0 (and possibly later):
  // 1. Don't use rich text: No <qt><b>...</b></qt>
  label = new TQLabel( i18n("Please check if encryption really "
  	"works before you start using it seriously. Also note that attachments "
	"are not encrypted by the PGP/GPG module."), group );
  // 2. instead, set the font to bold:
  TQFont labelFont = label->font();
  labelFont.setBold( true );
  label->setFont( labelFont );
  // 3. and activate wordwarp:
  label->setAlignment( AlignLeft|WordBreak );
  // end; to remove the workaround, add <qt><b>..</b></qt> around the
  // text and remove lines TQFont... -> label->setAlignment(...).
  topLayout->addWidget( group );

  group = new TQVGroupBox( i18n("Encryption Tool"), this );
  group->layout()->setSpacing( KDialog::spacingHint() );

  TQHBox * hbox = new TQHBox( group );
  label = new TQLabel( i18n("Select encryption tool to &use:"), hbox );
  toolCombo = new TQComboBox( false, hbox );
  toolCombo->insertStringList( TQStringList()
			       << i18n("Autodetect")
			       << i18n("GnuPG - Gnu Privacy Guard")
			       << i18n("PGP Version 2.x")
			       << i18n("PGP Version 5.x")
			       << i18n("PGP Version 6.x")
			       << i18n("Do not use any encryption tool") );
  label->setBuddy( toolCombo );
  hbox->setStretchFactor( toolCombo, 1 );
  connect( toolCombo, TQ_SIGNAL( activated( int ) ),
           this, TQ_SIGNAL( changed( void ) ) );
  // This is the place to add a KURLRequester to be used for asking
  // the user for the path to the executable...
  topLayout->addWidget( group );

  mpOptionsGroupBox = new TQVGroupBox( i18n("Options"), this );
  mpOptionsGroupBox->layout()->setSpacing( KDialog::spacingHint() );
  storePass = new TQCheckBox( i18n("&Keep passphrase in memory"),
                             mpOptionsGroupBox );
  connect( storePass, TQ_SIGNAL( toggled( bool ) ),
           this, TQ_SIGNAL( changed( void ) ) );
  msg = i18n( "<qt><p>When this option is enabled, the passphrase of your "
	      "private key will be remembered by the application as long "
	      "as the application is running. Thus you will only have to "
	      "enter the passphrase once.</p><p>Be aware that this could be a "
	      "security risk. If you leave your computer, others "
	      "can use it to send signed messages and/or read your encrypted "
	      "messages. If a core dump occurs, the contents of your RAM will "
	      "be saved onto disk, including your passphrase.</p>"
	      "<p>Note that when using KMail, this setting only applies "
	      "if you are not using gpg-agent. It is also ignored "
	      "if you are using crypto plugins.</p></qt>" );
  TQWhatsThis::add( storePass, msg );
  if( encrypt ) {
    encToSelf = new TQCheckBox( i18n("Always encr&ypt to self"),
                               mpOptionsGroupBox );
   connect( encToSelf, TQ_SIGNAL( toggled( bool ) ),
           this, TQ_SIGNAL( changed( void ) ) );

    msg = i18n( "<qt><p>When this option is enabled, the message/file "
		"will not only be encrypted with the receiver's public key, "
		"but also with your key. This will enable you to decrypt the "
		"message/file at a later time. This is generally a good idea."
		"</p></qt>" );
    TQWhatsThis::add( encToSelf, msg );
  }
  else
    encToSelf = 0;
  showCipherText = new TQCheckBox( i18n("&Show signed/encrypted text after "
                                       "composing"),
                                  mpOptionsGroupBox );
  connect( showCipherText, TQ_SIGNAL( toggled( bool ) ),
           this, TQ_SIGNAL( changed( void ) ) );

  msg = i18n( "<qt><p>When this option is enabled, the signed/encrypted text "
	      "will be shown in a separate window, enabling you to know how "
	      "it will look before it is sent. This is a good idea when "
	      "you are verifying that your encryption system works.</p></qt>" );
  TQWhatsThis::add( showCipherText, msg );
  if( encrypt ) {
    showKeyApprovalDlg = new TQCheckBox( i18n("Always show the encryption "
                                             "keys &for approval"),
                                        mpOptionsGroupBox );
    connect( showKeyApprovalDlg, TQ_SIGNAL( toggled( bool ) ),
           this, TQ_SIGNAL( changed( void ) ) );
    msg = i18n( "<qt><p>When this option is enabled, the application will "
		"always show you a list of public keys from which you can "
		"choose the one it will use for encryption. If it is off, "
		"the application will only show the dialog if it cannot find "
		"the right key or if there are several which could be used. "
		"</p></qt>" );
    TQWhatsThis::add( showKeyApprovalDlg, msg );
}
  else
    showKeyApprovalDlg = 0;

  topLayout->addWidget( mpOptionsGroupBox );

  topLayout->addStretch(1);

  setValues();  // is this needed by KNode, b/c for KMail, it's not.
}


Config::~Config()
{
}

void
Config::setValues()
{
  // set default values
  storePass->setChecked( pgp->storePassPhrase() );
  if( 0 != encToSelf )
    encToSelf->setChecked( pgp->encryptToSelf() );
  showCipherText->setChecked( pgp->showCipherText() );
  if( 0 != showKeyApprovalDlg )
    showKeyApprovalDlg->setChecked( pgp->showKeyApprovalDlg() );

  int type = 0;
  switch (pgp->pgpType) {
    // translate Kpgp::Module enum to combobox' entries:
  default:
  case Module::tAuto: type = 0; break;
  case Module::tGPG:  type = 1; break;
  case Module::tPGP2: type = 2; break;
  case Module::tPGP5: type = 3; break;
  case Module::tPGP6: type = 4; break;
  case Module::tOff:  type = 5; break;
  }
  toolCombo->setCurrentItem( type );
}

void
Config::applySettings()
{
  pgp->setStorePassPhrase(storePass->isChecked());
  if( 0 != encToSelf )
    pgp->setEncryptToSelf(encToSelf->isChecked());
  pgp->setShowCipherText(showCipherText->isChecked());
  if( 0 != showKeyApprovalDlg )
    pgp->setShowKeyApprovalDlg( showKeyApprovalDlg->isChecked() );

  Module::PGPType type;
  switch ( toolCombo->currentItem() ) {
    // convert combobox entry indices to Kpgp::Module constants:
  default:
  case 0: type = Module::tAuto; break;
  case 1: type = Module::tGPG;  break;
  case 2: type = Module::tPGP2; break;
  case 3: type = Module::tPGP5; break;
  case 4: type = Module::tPGP6; break;
  case 5: type = Module::tOff;  break;
  }
  pgp->pgpType = type;

  pgp->writeConfig(true);
}



// ------------------------------------------------------------------------
KeySelectionDialog::KeySelectionDialog( const KeyList& keyList,
                                        const TQString& title,
                                        const TQString& text,
                                        const KeyIDList& keyIds,
                                        const bool rememberChoice,
                                        const unsigned int allowedKeys,
                                        const bool extendedSelection,
                                        TQWidget *parent, const char *name,
                                        bool modal )
  : KDialogBase( parent, name, modal, title, Default|Ok|Cancel, Ok ),
    mRememberCB( 0 ),
    mAllowedKeys( allowedKeys ),
    mCurrentContextMenuItem( 0 )
{
  if ( kapp )
    KWin::setIcons( winId(), kapp->icon(), kapp->miniIcon() );
  Kpgp::Module *pgp = Kpgp::Module::getKpgp();
  TDEConfig *config = pgp->getConfig();
  TDEConfigGroup dialogConfig( config, "Key Selection Dialog" );

  TQSize defaultSize( 580, 400 );
  TQSize dialogSize = dialogConfig.readSizeEntry( "Dialog size", &defaultSize );

  resize( dialogSize );

  mCheckSelectionTimer = new TQTimer( this, "mCheckSelectionTimer" );
  mStartSearchTimer = new TQTimer( this, "mStartSearchTimer" );

  // load the key status icons
  mKeyGoodPix    = new TQPixmap( UserIcon("key_ok") );
  mKeyBadPix     = new TQPixmap( UserIcon("key_bad") );
  mKeyUnknownPix = new TQPixmap( UserIcon("key_unknown") );
  mKeyValidPix   = new TQPixmap( UserIcon("key") );

  TQFrame *page = makeMainWidget();
  TQVBoxLayout *topLayout = new TQVBoxLayout( page, 0, spacingHint() );

  if( !text.isEmpty() ) {
    TQLabel *label = new TQLabel( page );
    label->setText( text );
    topLayout->addWidget( label );
  }

  TQHBoxLayout * hlay = new TQHBoxLayout( topLayout ); // inherits spacing
  TQLineEdit * le = new TQLineEdit( page );
  hlay->addWidget( new TQLabel( le, i18n("&Search for:"), page ) );
  hlay->addWidget( le, 1 );
  le->setFocus();

  connect( le, TQ_SIGNAL(textChanged(const TQString&)),
	   this, TQ_SLOT(slotSearch(const TQString&)) );
  connect( mStartSearchTimer, TQ_SIGNAL(timeout()), TQ_SLOT(slotFilter()) );

  mListView = new TDEListView( page );
  mListView->addColumn( i18n("Key ID") );
  mListView->addColumn( i18n("User ID") );
  mListView->setAllColumnsShowFocus( true );
  mListView->setResizeMode( TQListView::LastColumn );
  mListView->setRootIsDecorated( true );
  mListView->setShowSortIndicator( true );
  mListView->setSorting( 1, true ); // sort by User ID
  mListView->setShowToolTips( true );
  if( extendedSelection ) {
    mListView->setSelectionMode( TQListView::Extended );
    //mListView->setSelectionMode( TQListView::Multi );
  }
  topLayout->addWidget( mListView, 10 );

  if (rememberChoice) {
    mRememberCB = new TQCheckBox( i18n("Remember choice"), page );
    topLayout->addWidget( mRememberCB );
    TQWhatsThis::add(mRememberCB,
                    i18n("<qt><p>If you check this box your choice will "
                         "be stored and you will not be asked again."
                         "</p></qt>"));
  }

  initKeylist( keyList, keyIds );

  TQListViewItem *lvi;
  if( extendedSelection ) {
    lvi = mListView->currentItem();
    slotCheckSelection();
  }
  else {
    lvi = mListView->selectedItem();
    slotCheckSelection( lvi );
  }
  // make sure that the selected item is visible
  // (ensureItemVisible(...) doesn't work correctly in TQt 3.0.0)
  if( lvi != 0 )
    mListView->center( mListView->contentsX(), mListView->itemPos( lvi ) );

  if( extendedSelection ) {
    connect( mCheckSelectionTimer, TQ_SIGNAL( timeout() ),
             this,                 TQ_SLOT( slotCheckSelection() ) );
    connect( mListView, TQ_SIGNAL( selectionChanged() ),
             this,      TQ_SLOT( slotSelectionChanged() ) );
  }
  else {
    connect( mListView, TQ_SIGNAL( selectionChanged( TQListViewItem* ) ),
             this,      TQ_SLOT( slotSelectionChanged( TQListViewItem* ) ) );
  }
  connect( mListView, TQ_SIGNAL( doubleClicked ( TQListViewItem *, const TQPoint &, int ) ), this, TQ_SLOT( accept() ) );

  connect( mListView, TQ_SIGNAL( contextMenuRequested( TQListViewItem*,
                                                    const TQPoint&, int ) ),
           this,      TQ_SLOT( slotRMB( TQListViewItem*, const TQPoint&, int ) ) );

  setButtonText( KDialogBase::Default, i18n("&Reread Keys") );
  connect( this, TQ_SIGNAL( defaultClicked() ),
           this, TQ_SLOT( slotRereadKeys() ) );
}


KeySelectionDialog::~KeySelectionDialog()
{
  Kpgp::Module *pgp = Kpgp::Module::getKpgp();
  TDEConfig *config = pgp->getConfig();
  TDEConfigGroup dialogConfig( config, "Key Selection Dialog" );
  dialogConfig.writeEntry( "Dialog size", size() );
  config->sync();
  delete mKeyGoodPix;
  delete mKeyBadPix;
  delete mKeyUnknownPix;
  delete mKeyValidPix;
}


KeyID KeySelectionDialog::key() const
{
  if( mListView->isMultiSelection() || mKeyIds.isEmpty() )
    return KeyID();
  else
    return mKeyIds.first();
}


void KeySelectionDialog::initKeylist( const KeyList& keyList,
                                      const KeyIDList& keyIds )
{
  TQListViewItem* firstSelectedItem = 0;
  mKeyIds.clear();
  mListView->clear();

  // build a list of all public keys
  for( KeyListIterator it( keyList ); it.current(); ++it ) {
    KeyID curKeyId = (*it)->primaryKeyID();

    TQListViewItem* primaryUserID = new TQListViewItem( mListView, curKeyId,
                                                      (*it)->primaryUserID() );

    // select and open the given key
    if( keyIds.findIndex( curKeyId ) != -1 ) {
      if( 0 == firstSelectedItem ) {
        firstSelectedItem = primaryUserID;
      }
      mListView->setSelected( primaryUserID, true );
      mKeyIds.append( curKeyId );
    }
    primaryUserID->setOpen( false );

    // set icon for this key
    switch( keyValidity( *it ) ) {
      case 0: // the key's validity can't be determined
        primaryUserID->setPixmap( 0, *mKeyUnknownPix );
        break;
      case 1: // key is valid but not trusted
        primaryUserID->setPixmap( 0, *mKeyValidPix );
        break;
      case 2: // key is valid and trusted
        primaryUserID->setPixmap( 0, *mKeyGoodPix );
        break;
      case -1: // key is invalid
        primaryUserID->setPixmap( 0, *mKeyBadPix );
        break;
    }

    TQListViewItem* childItem;

    childItem = new TQListViewItem( primaryUserID, "",
                                   i18n( "Fingerprint: %1" )
                                   .arg( beautifyFingerprint( (*it)->primaryFingerprint() ) ) );
    if( primaryUserID->isSelected() && mListView->isMultiSelection() ) {
      mListView->setSelected( childItem, true );
    }

    childItem = new TQListViewItem( primaryUserID, "", keyInfo( *it ) );
    if( primaryUserID->isSelected() && mListView->isMultiSelection() ) {
      mListView->setSelected( childItem, true );
    }

    UserIDList userIDs = (*it)->userIDs();
    UserIDListIterator uidit( userIDs );
    if( *uidit ) {
      ++uidit; // skip the primary user ID
      for( ; *uidit; ++uidit ) {
        childItem = new TQListViewItem( primaryUserID, "", (*uidit)->text() );
        if( primaryUserID->isSelected() && mListView->isMultiSelection() ) {
          mListView->setSelected( childItem, true );
        }
      }
    }
  }

  if( 0 != firstSelectedItem ) {
    mListView->setCurrentItem( firstSelectedItem );
  }
}


TQString KeySelectionDialog::keyInfo( const Kpgp::Key *key ) const
{
  TQString status, remark;
  if( key->revoked() ) {
    status = i18n("Revoked");
  }
  else if( key->expired() ) {
    status = i18n("Expired");
  }
  else if( key->disabled() ) {
    status = i18n("Disabled");
  }
  else if( key->invalid() ) {
    status = i18n("Invalid");
  }
  else {
    Validity keyTrust = key->keyTrust();
    switch( keyTrust ) {
    case KPGP_VALIDITY_UNDEFINED:
      status = i18n("Undefined trust");
      break;
    case KPGP_VALIDITY_NEVER:
      status = i18n("Untrusted");
      break;
    case KPGP_VALIDITY_MARGINAL:
      status = i18n("Marginally trusted");
      break;
    case KPGP_VALIDITY_FULL:
      status = i18n("Fully trusted");
      break;
    case KPGP_VALIDITY_ULTIMATE:
      status = i18n("Ultimately trusted");
      break;
    case KPGP_VALIDITY_UNKNOWN:
    default:
      status = i18n("Unknown");
    }
    if( key->secret() ) {
      remark = i18n("Secret key available");
    }
    else if( !key->canEncrypt() ) {
      remark = i18n("Sign only key");
    }
    else if( !key->canSign() ) {
      remark = i18n("Encryption only key");
    }
  }

  TQDateTime dt;
  dt.setTime_t( key->creationDate() );
  if( remark.isEmpty() ) {
    return " " + i18n("creation date and status of an OpenPGP key",
                      "Creation date: %1, Status: %2")
                     .arg( TDEGlobal::locale()->formatDate( dt.date(), true ) )
                     .arg( status );
  }
  else {
    return " " + i18n("creation date, status and remark of an OpenPGP key",
                      "Creation date: %1, Status: %2 (%3)")
                     .arg( TDEGlobal::locale()->formatDate( dt.date(), true ) )
                     .arg( status )
                     .arg( remark );
  }
}

TQString KeySelectionDialog::beautifyFingerprint( const TQCString& fpr ) const
{
  TQCString result;

  if( 40 == fpr.length() ) {
    // convert to this format:
    // 0000 1111 2222 3333 4444  5555 6666 7777 8888 9999
    result.fill( ' ', 50 );
    memcpy( result.data()     , fpr.data()     , 4 );
    memcpy( result.data() +  5, fpr.data() +  4, 4 );
    memcpy( result.data() + 10, fpr.data() +  8, 4 );
    memcpy( result.data() + 15, fpr.data() + 12, 4 );
    memcpy( result.data() + 20, fpr.data() + 16, 4 );
    memcpy( result.data() + 26, fpr.data() + 20, 4 );
    memcpy( result.data() + 31, fpr.data() + 24, 4 );
    memcpy( result.data() + 36, fpr.data() + 28, 4 );
    memcpy( result.data() + 41, fpr.data() + 32, 4 );
    memcpy( result.data() + 46, fpr.data() + 36, 4 );
  }
  else if( 32 == fpr.length() ) {
    // convert to this format:
    // 00 11 22 33 44 55 66 77  88 99 AA BB CC DD EE FF
    result.fill( ' ', 48 );
    memcpy( result.data()     , fpr.data()     , 2 );
    memcpy( result.data() +  3, fpr.data() +  2, 2 );
    memcpy( result.data() +  6, fpr.data() +  4, 2 );
    memcpy( result.data() +  9, fpr.data() +  6, 2 );
    memcpy( result.data() + 12, fpr.data() +  8, 2 );
    memcpy( result.data() + 15, fpr.data() + 10, 2 );
    memcpy( result.data() + 18, fpr.data() + 12, 2 );
    memcpy( result.data() + 21, fpr.data() + 14, 2 );
    memcpy( result.data() + 25, fpr.data() + 16, 2 );
    memcpy( result.data() + 28, fpr.data() + 18, 2 );
    memcpy( result.data() + 31, fpr.data() + 20, 2 );
    memcpy( result.data() + 34, fpr.data() + 22, 2 );
    memcpy( result.data() + 37, fpr.data() + 24, 2 );
    memcpy( result.data() + 40, fpr.data() + 26, 2 );
    memcpy( result.data() + 43, fpr.data() + 28, 2 );
    memcpy( result.data() + 46, fpr.data() + 30, 2 );
  }
  else { // unknown length of fingerprint
    result = fpr;
  }

  return result;
}

int KeySelectionDialog::keyValidity( const Kpgp::Key *key ) const
{
  if( 0 == key ) {
    return -1;
  }

  if( ( mAllowedKeys & EncrSignKeys ) == EncryptionKeys ) {
    // only encryption keys are allowed
    if( ( mAllowedKeys & ValidKeys ) && !key->isValidEncryptionKey() ) {
      // only valid encryption keys are allowed
      return -1;
    }
    else if( !key->canEncrypt() ) {
      return -1;
    }
  }
  else if( ( mAllowedKeys & EncrSignKeys ) == SigningKeys ) {
    // only signing keys are allowed
    if( ( mAllowedKeys & ValidKeys ) && !key->isValidSigningKey() ) {
      // only valid signing keys are allowed
      return -1;
    }
    else if( !key->canSign() ) {
      return -1;
    }
  }
  else if( ( mAllowedKeys & ValidKeys ) && !key->isValid() ) {
    // only valid keys are allowed
    return -1;
  }

  // check the key's trust
  int val = 0;
  Validity keyTrust = key->keyTrust();
  switch( keyTrust ) {
  case KPGP_VALIDITY_NEVER:
    val = -1;
    break;
  case KPGP_VALIDITY_MARGINAL:
  case KPGP_VALIDITY_FULL:
  case KPGP_VALIDITY_ULTIMATE:
    val = 2;
    break;
  case KPGP_VALIDITY_UNDEFINED:
    if( mAllowedKeys & TrustedKeys ) {
      // only trusted keys are allowed
      val = -1;
    }
    else {
      val = 1;
    }
    break;
  case KPGP_VALIDITY_UNKNOWN:
  default:
    val = 0;
  }

  return val;
}


void KeySelectionDialog::updateKeyInfo( const Kpgp::Key* key,
                                        TQListViewItem* lvi ) const
{
  if( 0 == lvi ) {
    return;
  }

  if( lvi->parent() != 0 ) {
    lvi = lvi->parent();
  }

  if( 0 == key ) {
    // the key doesn't exist anymore -> delete it from the list view
    while( lvi->firstChild() ) {
      kdDebug(5100) << "Deleting '" << lvi->firstChild()->text( 1 ) << "'\n";
      delete lvi->firstChild();
    }
    kdDebug(5100) << "Deleting key 0x" << lvi->text( 0 ) << " ("
                  << lvi->text( 1 ) << ")\n";
    delete lvi;
    lvi = 0;
    return;
  }

  // update the icon for this key
  switch( keyValidity( key ) ) {
  case 0: // the key's validity can't be determined
    lvi->setPixmap( 0, *mKeyUnknownPix );
    break;
  case 1: // key is valid but not trusted
    lvi->setPixmap( 0, *mKeyValidPix );
    break;
  case 2: // key is valid and trusted
    lvi->setPixmap( 0, *mKeyGoodPix );
    break;
  case -1: // key is invalid
    lvi->setPixmap( 0, *mKeyBadPix );
    break;
  }

  // update the key info for this key
  // the key info is identified by a leading space; this shouldn't be
  // a problem because User Ids shouldn't start with a space
  for( lvi = lvi->firstChild(); lvi; lvi = lvi->nextSibling() ) {
    if( lvi->text( 1 ).at(0) == ' ' ) {
      lvi->setText( 1, keyInfo( key ) );
      break;
    }
  }
}


int
KeySelectionDialog::keyAdmissibility( TQListViewItem* lvi,
                                      TrustCheckMode trustCheckMode ) const
{
  // Return:
  //  -1 = key must not be chosen,
  //   0 = not enough information to decide whether the give key is allowed
  //       or not,
  //   1 = key can be chosen

  if( mAllowedKeys == AllKeys ) {
    return 1;
  }

  Kpgp::Module *pgp = Kpgp::Module::getKpgp();

  if( 0 == pgp ) {
    return 0;
  }

  KeyID keyId = getKeyId( lvi );
  Kpgp::Key* key = pgp->publicKey( keyId );

  if( 0 == key ) {
    return 0;
  }

  int val = 0;
  if( trustCheckMode == ForceTrustCheck ) {
    key = pgp->rereadKey( keyId, true );
    updateKeyInfo( key, lvi );
    val = keyValidity( key );
  }
  else {
    val = keyValidity( key );
    if( ( trustCheckMode == AllowExpensiveTrustCheck ) && ( 0 == val ) ) {
      key = pgp->rereadKey( keyId, true );
      updateKeyInfo( key, lvi );
      val = keyValidity( key );
    }
  }

  switch( val ) {
  case -1: // key is not usable
    return -1;
    break;
  case 0: // key status unknown
    return 0;
    break;
  case 1: // key is valid, but untrusted
    if( mAllowedKeys & TrustedKeys ) {
      // only trusted keys are allowed
      return -1;
    }
    return 1;
    break;
  case 2: // key is trusted
    return 1;
    break;
  default:
    kdDebug( 5100 ) << "Error: Invalid key status value.\n";
  }

  return 0;
}


KeyID
KeySelectionDialog::getKeyId( const TQListViewItem* lvi ) const
{
  KeyID keyId;

  if( 0 != lvi ) {
    if( 0 != lvi->parent() ) {
      keyId = lvi->parent()->text(0).local8Bit();
    }
    else {
      keyId = lvi->text(0).local8Bit();
    }
  }

  return keyId;
}


void KeySelectionDialog::slotRereadKeys()
{
  Kpgp::Module *pgp = Kpgp::Module::getKpgp();

  if( 0 == pgp ) {
    return;
  }

  KeyList keys;

  if( PublicKeys & mAllowedKeys ) {
    pgp->readPublicKeys( true );
    keys = pgp->publicKeys();
  }
  else {
    pgp->readSecretKeys( true );
    keys = pgp->secretKeys();
  }

  // save the current position of the contents
  int offsetY = mListView->contentsY();

  if( mListView->isMultiSelection() ) {
    disconnect( mListView, TQ_SIGNAL( selectionChanged() ),
                this,      TQ_SLOT( slotSelectionChanged() ) );
  }
  else {
    disconnect( mListView, TQ_SIGNAL( selectionChanged( TQListViewItem * ) ),
                this,      TQ_SLOT( slotSelectionChanged( TQListViewItem * ) ) );
  }

  initKeylist( keys, KeyIDList( mKeyIds ) );
  slotFilter();

  if( mListView->isMultiSelection() ) {
    connect( mListView, TQ_SIGNAL( selectionChanged() ),
             this,      TQ_SLOT( slotSelectionChanged() ) );
    slotSelectionChanged();
  }
  else {
    connect( mListView, TQ_SIGNAL( selectionChanged( TQListViewItem * ) ),
             this,      TQ_SLOT( slotSelectionChanged( TQListViewItem * ) ) );
  }

  // restore the saved position of the contents
  mListView->setContentsPos( 0, offsetY );
}


void KeySelectionDialog::slotSelectionChanged( TQListViewItem * lvi )
{
  slotCheckSelection( lvi );
}


void KeySelectionDialog::slotSelectionChanged()
{
  kdDebug(5100) << "KeySelectionDialog::slotSelectionChanged()\n";

  // (re)start the check selection timer. Checking the selection is delayed
  // because else drag-selection doesn't work very good (checking key trust
  // is slow).
  mCheckSelectionTimer->start( sCheckSelectionDelay );
}


void KeySelectionDialog::slotCheckSelection( TQListViewItem* plvi /* = 0 */ )
{
  kdDebug(5100) << "KeySelectionDialog::slotCheckSelection()\n";

  if( !mListView->isMultiSelection() ) {
    mKeyIds.clear();
    KeyID keyId = getKeyId( plvi );
    if( !keyId.isEmpty() ) {
      mKeyIds.append( keyId );
      enableButtonOK( 1 == keyAdmissibility( plvi, AllowExpensiveTrustCheck ) );
    }
    else {
      enableButtonOK( false );
    }
  }
  else {
    mCheckSelectionTimer->stop();

    // As we might change the selection, we have to disconnect the slot
    // to prevent recursion
    disconnect( mListView, TQ_SIGNAL( selectionChanged() ),
                this,      TQ_SLOT( slotSelectionChanged() ) );

    KeyIDList newKeyIdList;
    TQValueList<TQListViewItem*> keysToBeChecked;

    bool keysAllowed = true;
    enum { UNKNOWN, SELECTED, DESELECTED } userAction = UNKNOWN;
    // Iterate over the tree to find selected keys.
    for( TQListViewItem *lvi = mListView->firstChild();
         0 != lvi;
         lvi = lvi->nextSibling() ) {
      // We make sure that either all items belonging to a key are selected
      // or unselected. As it's possible to select/deselect multiple keys at
      // once in extended selection mode we have to figure out whether the user
      // selected or deselected keys.

      // First count the selected items of this key
      int itemCount = 1 + lvi->childCount();
      int selectedCount = lvi->isSelected() ? 1 : 0;
      for( TQListViewItem *clvi = lvi->firstChild();
           0 != clvi;
           clvi = clvi->nextSibling() ) {
        if( clvi->isSelected() ) {
          ++selectedCount;
        }
      }

      if( userAction == UNKNOWN ) {
        // Figure out whether the user selected or deselected this key
        // Remark: A selected count of 0 doesn't mean anything since in
        //         extended selection mode a normal left click deselects
        //         the not clicked items.
        if( 0 < selectedCount ) {
          if( -1 == mKeyIds.findIndex( lvi->text(0).local8Bit() ) ) {
            // some items of this key are selected and the key wasn't selected
            // before => the user selected something
            kdDebug(5100) << "selectedCount: "<<selectedCount<<"/"<<itemCount
                          <<" --- User selected key "<<lvi->text(0)<<endl;
            userAction = SELECTED;
          }
          else if( ( itemCount > selectedCount ) &&
                   ( -1 != mKeyIds.findIndex( lvi->text(0).local8Bit() ) ) ) {
            // some items of this key are unselected and the key was selected
            // before => the user deselected something
            kdDebug(5100) << "selectedCount: "<<selectedCount<<"/"<<itemCount
                          <<" --- User deselected key "<<lvi->text(0)<<endl;
            userAction = DESELECTED;
          }
        }
      }
      if( itemCount == selectedCount ) {
        // add key to the list of selected keys
        KeyID keyId = lvi->text(0).local8Bit();
        newKeyIdList.append( keyId );
        int admissibility = keyAdmissibility( lvi, NoExpensiveTrustCheck );
        if( -1 == admissibility ) {
          keysAllowed = false;
        }
        else if ( 0 == admissibility ) {
          keysToBeChecked.append( lvi );
        }
      }
      else if( 0 < selectedCount ) {
        // not all items of this key are selected or unselected. change this
        // according to the user's action
        if( userAction == SELECTED ) {
          // select all items of this key
          mListView->setSelected( lvi, true );
          for( TQListViewItem *clvi = lvi->firstChild();
               0 != clvi;
               clvi = clvi->nextSibling() ) {
            mListView->setSelected( clvi, true );
          }
          // add key to the list of selected keys
          KeyID keyId = lvi->text(0).local8Bit();
          newKeyIdList.append( keyId );
          int admissibility = keyAdmissibility( lvi, NoExpensiveTrustCheck );
          if( -1 == admissibility ) {
            keysAllowed = false;
          }
          else if ( 0 == admissibility ) {
            keysToBeChecked.append( lvi );
          }
        }
        else { // userAction == DESELECTED
          // deselect all items of this key
          mListView->setSelected( lvi, false );
          for( TQListViewItem *clvi = lvi->firstChild();
               0 != clvi;
               clvi = clvi->nextSibling() ) {
            mListView->setSelected( clvi, false );
          }
        }
      }
    }
    kdDebug(5100) << "Selected keys: " << newKeyIdList.toStringList().join(", ") << endl;
    mKeyIds = newKeyIdList;
    if( !keysToBeChecked.isEmpty() ) {
      keysAllowed = keysAllowed && checkKeys( keysToBeChecked );
    }
    enableButtonOK( keysAllowed );

    connect( mListView, TQ_SIGNAL( selectionChanged() ),
             this,      TQ_SLOT( slotSelectionChanged() ) );
  }
}


bool KeySelectionDialog::checkKeys( const TQValueList<TQListViewItem*>& keys ) const
{
  KProgressDialog* pProgressDlg = 0;
  bool keysAllowed = true;
  kdDebug(5100) << "Checking keys...\n";

  pProgressDlg = new KProgressDialog( 0, 0, i18n("Checking Keys"),
                                      i18n("Checking key 0xMMMMMMMM..."),
                                      true );
  pProgressDlg->setAllowCancel( false );
  pProgressDlg->progressBar()->setTotalSteps( keys.count() );
  pProgressDlg->setMinimumDuration( 1000 );
  pProgressDlg->show();

  for( TQValueList<TQListViewItem*>::ConstIterator it = keys.begin();
       it != keys.end();
       ++it ) {
    kdDebug(5100) << "Checking key 0x" << getKeyId( *it ) << "...\n";
    pProgressDlg->setLabel( i18n("Checking key 0x%1...")
                            .arg( TQString( getKeyId( *it ) ) ) );
    kapp->processEvents();
    keysAllowed = keysAllowed && ( -1 != keyAdmissibility( *it, AllowExpensiveTrustCheck ) );
    pProgressDlg->progressBar()->advance( 1 );
    kapp->processEvents();
  }

  delete pProgressDlg;
  pProgressDlg = 0;

  return keysAllowed;
}


void KeySelectionDialog::slotRMB( TQListViewItem* lvi, const TQPoint& pos, int )
{
  if( !lvi ) {
    return;
  }

  mCurrentContextMenuItem = lvi;

  TQPopupMenu menu(this);
  menu.insertItem( i18n( "Recheck Key" ), this, TQ_SLOT( slotRecheckKey() ) );
  menu.exec( pos );
}


void KeySelectionDialog::slotRecheckKey()
{
  if( 0 != mCurrentContextMenuItem ) {
    // force rereading the key
    keyAdmissibility( mCurrentContextMenuItem, ForceTrustCheck );
    // recheck the selection
    slotCheckSelection( mCurrentContextMenuItem );
  }
}

void KeySelectionDialog::slotOk()
{
  if( mCheckSelectionTimer->isActive() ) {
    slotCheckSelection();
  }
  mStartSearchTimer->stop();
  accept();
}


void KeySelectionDialog::slotCancel()
{
  mCheckSelectionTimer->stop();
  mStartSearchTimer->stop();
  mKeyIds.clear();
  reject();
}

void KeySelectionDialog::slotSearch( const TQString & text )
{
  mSearchText = text.stripWhiteSpace().upper();
  mStartSearchTimer->start( sCheckSelectionDelay, true /*single-shot*/ );
}

void KeySelectionDialog::slotFilter()
{
  if ( mSearchText.isEmpty() ) {
    showAllItems();
    return;
  }

  // OK, so we need to filter:
  TQRegExp keyIdRegExp( "(?:0x)?[A-F0-9]{1,8}", false /*case-insens.*/ );
  if ( keyIdRegExp.exactMatch( mSearchText ) ) {
    if ( mSearchText.startsWith( "0X" ) )
      // search for keyID only:
      filterByKeyID( mSearchText.mid( 2 ) );
    else
      // search for UID and keyID:
      filterByKeyIDOrUID( mSearchText );
  } else {
    // search in UID:
    filterByUID( mSearchText );
  }
}

void KeySelectionDialog::filterByKeyID( const TQString & keyID )
{
  assert( keyID.length() <= 8 );
  assert( !keyID.isEmpty() ); // regexp in slotFilter should prevent these
  if ( keyID.isEmpty() )
    showAllItems();
  else
    for ( TQListViewItem * item = mListView->firstChild() ; item ; item = item->nextSibling() )
      item->setVisible( item->text( 0 ).upper().startsWith( keyID ) );
}

void KeySelectionDialog::filterByKeyIDOrUID( const TQString & str )
{
  assert( !str.isEmpty() );

  // match beginnings of words:
  TQRegExp rx( "\\b" + TQRegExp::escape( str ), false );

  for ( TQListViewItem * item = mListView->firstChild() ; item ; item = item->nextSibling() )
    item->setVisible( item->text( 0 ).upper().startsWith( str )
		      || rx.search( item->text( 1 ) ) >= 0
		      || anyChildMatches( item, rx ) );

}

void KeySelectionDialog::filterByUID( const TQString & str )
{
  assert( !str.isEmpty() );

  // match beginnings of words:
  TQRegExp rx( "\\b" + TQRegExp::escape( str ), false );

  for ( TQListViewItem * item = mListView->firstChild() ; item ; item = item->nextSibling() )
    item->setVisible( rx.search( item->text( 1 ) ) >= 0
		      || anyChildMatches( item, rx ) );
}


bool KeySelectionDialog::anyChildMatches( const TQListViewItem * item, TQRegExp & rx ) const
{
  if ( !item )
    return false;

  TQListViewItem * stop = item->nextSibling(); // It's OK if stop is NULL...

  for ( TQListViewItemIterator it( item->firstChild() ) ; it.current() && it.current() != stop ; ++it )
    if ( rx.search( it.current()->text( 1 ) ) >= 0 ) {
      //item->setOpen( true ); // do we want that?
      return true;
    }
  return false;
}

void KeySelectionDialog::showAllItems()
{
  for ( TQListViewItem * item = mListView->firstChild() ; item ; item = item->nextSibling() )
    item->setVisible( true );
}

// ------------------------------------------------------------------------
KeyRequester::KeyRequester( TQWidget * parent, bool multipleKeys,
			    unsigned int allowedKeys, const char * name )
  : TQWidget( parent, name ),
    mDialogCaption( i18n("OpenPGP Key Selection") ),
    mDialogMessage( i18n("Please select an OpenPGP key to use.") ),
    mMulti( multipleKeys ),
    mAllowedKeys( allowedKeys ),
    d( 0 )
{
  TQHBoxLayout * hlay = new TQHBoxLayout( this, 0, KDialog::spacingHint() );

  // the label where the key id is to be displayed:
  mLabel = new TQLabel( this );
  mLabel->setFrameStyle( TQFrame::Panel | TQFrame::Sunken );

  // the button to unset any key:
  mEraseButton = new TQPushButton( this );
  mEraseButton->setAutoDefault( false );
  mEraseButton->setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum,
					    TQSizePolicy::Minimum ) );
  mEraseButton->setPixmap( SmallIcon( "clear_left" ) );
  TQToolTip::add( mEraseButton, i18n("Clear") );

  // the button to call the KeySelectionDialog:
  mDialogButton = new TQPushButton( i18n("Change..."), this );
  mDialogButton->setAutoDefault( false );

  hlay->addWidget( mLabel, 1 );
  hlay->addWidget( mEraseButton );
  hlay->addWidget( mDialogButton );

  connect( mEraseButton, TQ_SIGNAL(clicked()), TQ_SLOT(slotEraseButtonClicked()) );
  connect( mDialogButton, TQ_SIGNAL(clicked()), TQ_SLOT(slotDialogButtonClicked()) );

  setSizePolicy( TQSizePolicy( TQSizePolicy::MinimumExpanding,
			      TQSizePolicy::Fixed ) );
}

KeyRequester::~KeyRequester() {

}

KeyIDList KeyRequester::keyIDs() const {
  return mKeys;
}

void KeyRequester::setKeyIDs( const KeyIDList & keyIDs ) {
  mKeys = keyIDs;
  if ( mKeys.empty() ) {
    mLabel->clear();
    return;
  }
  if ( mKeys.size() > 1 )
    setMultipleKeysEnabled( true );

  TQString s = mKeys.toStringList().join(", ");

  mLabel->setText( s );
  TQToolTip::remove( mLabel );
  TQToolTip::add( mLabel, s );
}

void KeyRequester::slotDialogButtonClicked() {
  Module * pgp = Module::getKpgp();

  if ( !pgp ) {
    kdWarning() << "Kpgp::KeyRequester::slotDialogButtonClicked(): No pgp module found!" << endl;
    return;
  }

  setKeyIDs( keyRequestHook( pgp ) );
  emit changed();
}

void KeyRequester::slotEraseButtonClicked() {
  mKeys.clear();
  mLabel->clear();
  emit changed();
}

void KeyRequester::setDialogCaption( const TQString & caption ) {
  mDialogCaption = caption;
}

void KeyRequester::setDialogMessage( const TQString & msg ) {
  mDialogMessage = msg;
}

bool KeyRequester::isMultipleKeysEnabled() const {
  return mMulti;
}

void KeyRequester::setMultipleKeysEnabled( bool multi ) {
  if ( multi == mMulti ) return;

  if ( !multi && mKeys.size() > 1 )
    mKeys.erase( ++mKeys.begin(), mKeys.end() );

  mMulti = multi;
}

int KeyRequester::allowedKeys() const {
  return mAllowedKeys;
}

void KeyRequester::setAllowedKeys( int allowedKeys ) {
  mAllowedKeys = allowedKeys;
}


PublicKeyRequester::PublicKeyRequester( TQWidget * parent, bool multi,
					unsigned int allowed, const char * name )
  : KeyRequester( parent, multi, allowed & ~SecretKeys, name )
{

}

PublicKeyRequester::~PublicKeyRequester() {

}

KeyIDList PublicKeyRequester::keyRequestHook( Module * pgp ) const {
  assert( pgp );
  return pgp->selectPublicKeys( mDialogCaption, mDialogMessage, mKeys, TQString(), mAllowedKeys );
}

SecretKeyRequester::SecretKeyRequester( TQWidget * parent, bool multi,
					unsigned int allowed, const char * name )
  : KeyRequester( parent, multi, allowed & ~PublicKeys, name )
{

}

SecretKeyRequester::~SecretKeyRequester() {

}

KeyIDList SecretKeyRequester::keyRequestHook( Module * pgp ) const {
  assert( pgp );

  KeyID keyID = mKeys.first();
  keyID = pgp->selectSecretKey( mDialogCaption, mDialogMessage, keyID );

  return KeyIDList() << keyID;
}



// ------------------------------------------------------------------------
KeyApprovalDialog::KeyApprovalDialog( const TQStringList& addresses,
                                      const TQValueVector<KeyIDList>& keyIDs,
                                      const int allowedKeys,
                                      TQWidget *parent, const char *name,
                                      bool modal )
  : KDialogBase( parent, name, modal, i18n("Encryption Key Approval"),
                 Ok|Cancel, Ok ),
    mKeys( keyIDs ),
    mAllowedKeys( allowedKeys ),
    mPrefsChanged( false )
{
  Kpgp::Module *pgp = Kpgp::Module::getKpgp();

  if( pgp == 0 )
    return;

  // ##### error handling
  // if( addresses.isEmpty() || keyList.isEmpty() ||
  //     addresses.count()+1 != keyList.count() )
  //   do something;

  TQFrame *page = makeMainWidget();
  TQVBoxLayout *topLayout = new TQVBoxLayout( page, 0, KDialog::spacingHint() );

  TQLabel *label = new TQLabel( i18n("The following keys will be used for "
                                   "encryption:"),
                              page );
  topLayout->addWidget( label );

  TQScrollView* sv = new TQScrollView( page );
  sv->setResizePolicy( TQScrollView::AutoOneFit );
  topLayout->addWidget( sv );
  TQVBox* bigvbox = new TQVBox( sv->viewport() );
  bigvbox->setMargin( KDialog::marginHint() );
  bigvbox->setSpacing( KDialog::spacingHint() );
  sv->addChild( bigvbox );

  TQButtonGroup *mChangeButtonGroup = new TQButtonGroup( bigvbox );
  mChangeButtonGroup->hide();
  mAddressLabels.resize( addresses.count() );
  mKeyIdsLabels.resize( keyIDs.size() );
  //mKeyIdListBoxes.resize( keyIDs.size() );
  mEncrPrefCombos.resize( addresses.count() );

  // the sender's key
  if( pgp->encryptToSelf() ) {
    mEncryptToSelf = 1;
    TQHBox* hbox = new TQHBox( bigvbox );
    new TQLabel( i18n("Your keys:"), hbox );
    TQLabel* keyidsL = new TQLabel( hbox );
    if( keyIDs[0].isEmpty() ) {
      keyidsL->setText( i18n("<none> means 'no key'", "<none>") );
    }
    else {
      keyidsL->setText( "0x" + keyIDs[0].toStringList().join( "\n0x" ) );
    }
    keyidsL->setFrameStyle( TQFrame::Panel | TQFrame::Sunken );
    /*
    TQListBox* keyidLB = new TQListBox( hbox );
    if( keyIDs[0].isEmpty() ) {
      keyidLB->insertItem( i18n("<none>") );
    }
    else {
      keyidLB->insertStringList( keyIDs[0].toStringList() );
    }
    keyidLB->setSelectionMode( TQListBox::NoSelection );
    keyidLB->setFrameStyle( TQFrame::Panel | TQFrame::Sunken );
    */
    TQPushButton *button = new TQPushButton( i18n("Change..."), hbox );
    mChangeButtonGroup->insert( button );
    button->setAutoDefault( false );
    hbox->setStretchFactor( keyidsL, 10 );
    mKeyIdsLabels.insert( 0, keyidsL );
    //hbox->setStretchFactor( keyidLB, 10 );
    //mKeyIdListBoxes.insert( 0, keyidLB );

    new KSeparator( TQt::Horizontal, bigvbox );
  }
  else {
    mEncryptToSelf = 0;
    // insert dummy KeyIdListBox
    mKeyIdsLabels.insert( 0, 0 );
    //mKeyIdListBoxes.insert( 0, 0 );
  }

  TQStringList::ConstIterator ait;
  TQValueVector<KeyIDList>::const_iterator kit;
  int i;
  for( ait = addresses.begin(), kit = keyIDs.begin(), i = 0;
       ( ait != addresses.end() ) && ( kit != keyIDs.end() );
       ++ait, ++kit, ++i ) {
    if( i == 0 ) {
      ++kit; // skip the sender's key id
    }
    else {
      new KSeparator( TQt::Horizontal, bigvbox );
    }

    TQHBox *hbox = new TQHBox( bigvbox );
    new TQLabel( i18n("Recipient:"), hbox );
    TQLabel *addressL = new TQLabel( *ait, hbox );
    hbox->setStretchFactor( addressL, 10 );
    mAddressLabels.insert( i, addressL  );

    hbox = new TQHBox( bigvbox );
    new TQLabel( i18n("Encryption keys:"), hbox );
    TQLabel* keyidsL = new TQLabel( hbox );
    if( (*kit).isEmpty() ) {
      keyidsL->setText( i18n("<none> means 'no key'", "<none>") );
    }
    else {
      keyidsL->setText( "0x" + (*kit).toStringList().join( "\n0x" ) );
    }
    keyidsL->setFrameStyle( TQFrame::Panel | TQFrame::Sunken );
    /*
    TQListBox* keyidLB = new TQListBox( hbox );
    if( (*kit).isEmpty() ) {
      keyidLB->insertItem( i18n("<none>") );
    }
    else {
      keyidLB->insertStringList( (*kit).toStringList() );
    }
    keyidLB->setSelectionMode( TQListBox::NoSelection );
    keyidLB->setFrameStyle( TQFrame::Panel | TQFrame::Sunken );
    */
    TQPushButton *button = new TQPushButton( i18n("Change..."), hbox );
    mChangeButtonGroup->insert( button );
    button->setAutoDefault( false );
    hbox->setStretchFactor( keyidsL, 10 );
    mKeyIdsLabels.insert( i + 1, keyidsL );
    //hbox->setStretchFactor( keyidLB, 10 );
    //mKeyIdListBoxes.insert( i + 1, keyidLB );

    hbox = new TQHBox( bigvbox );
    new TQLabel( i18n("Encryption preference:"), hbox );
    TQComboBox *encrPrefCombo = new TQComboBox( hbox );
    encrPrefCombo->insertItem( i18n("<none>") );
    encrPrefCombo->insertItem( i18n("Never Encrypt with This Key") );
    encrPrefCombo->insertItem( i18n("Always Encrypt with This Key") );
    encrPrefCombo->insertItem( i18n("Encrypt Whenever Encryption is Possible") );
    encrPrefCombo->insertItem( i18n("Always Ask") );
    encrPrefCombo->insertItem( i18n("Ask Whenever Encryption is Possible") );

    EncryptPref encrPref = pgp->encryptionPreference( *ait );
    switch( encrPref ) {
      case NeverEncrypt:
        encrPrefCombo->setCurrentItem( 1 );
        break;
      case AlwaysEncrypt:
        encrPrefCombo->setCurrentItem( 2 );
        break;
      case AlwaysEncryptIfPossible:
        encrPrefCombo->setCurrentItem( 3 );
        break;
      case AlwaysAskForEncryption:
        encrPrefCombo->setCurrentItem( 4 );
        break;
      case AskWheneverPossible:
        encrPrefCombo->setCurrentItem( 5 );
        break;
      default:
        encrPrefCombo->setCurrentItem( 0 );
    }
    connect( encrPrefCombo, TQ_SIGNAL(activated(int)),
             this, TQ_SLOT(slotPrefsChanged(int)) );
    mEncrPrefCombos.insert( i, encrPrefCombo );
  }
  connect( mChangeButtonGroup, TQ_SIGNAL(clicked(int)),
           this, TQ_SLOT(slotChangeEncryptionKey(int)) );

  // calculate the optimal width for the dialog
  int dialogWidth = marginHint()
                  + sv->frameWidth()
                  + bigvbox->sizeHint().width()
                  + sv->verticalScrollBar()->sizeHint().width()
                  + sv->frameWidth()
                  + marginHint()
                  + 2;
  // calculate the optimal height for the dialog
  int dialogHeight = marginHint()
                   + label->sizeHint().height()
                   + topLayout->spacing()
                   + sv->frameWidth()
                   + bigvbox->sizeHint().height()
                   + sv->horizontalScrollBar()->sizeHint().height()
                   + sv->frameWidth()
                   + topLayout->spacing()
                   + actionButton( KDialogBase::Cancel )->sizeHint().height()
                   + marginHint()
                   + 2;
  // don't make the dialog too large
  TQRect desk = TDEGlobalSettings::desktopGeometry(this);
  int screenWidth = desk.width();
  if( dialogWidth > 3*screenWidth/4 )
    dialogWidth = 3*screenWidth/4;
  int screenHeight = desk.height();
  if( dialogHeight > 7*screenHeight/8 )
    dialogHeight = 7*screenHeight/8;

  setInitialSize( TQSize( dialogWidth, dialogHeight ) );
}

void
KeyApprovalDialog::slotChangeEncryptionKey( int nr )
{
  Kpgp::Module *pgp = Kpgp::Module::getKpgp();

  kdDebug(5100)<<"Key approval dialog size is "
               <<width()<<"x"<<height()<<endl;

  if( pgp == 0 )
    return;

  if( !mEncryptToSelf )
    nr++;
  KeyIDList keyIds = mKeys[nr];
  if( nr == 0 ) {
    keyIds = pgp->selectPublicKeys( i18n("Encryption Key Selection"),
                                    i18n("if in your language something like "
                                         "'key(s)' isn't possible please "
                                         "use the plural in the translation",
                                         "Select the key(s) which should "
                                         "be used to encrypt the message "
                                         "to yourself."),
                                    keyIds,
                                    "",
                                    mAllowedKeys );
  }
  else {
    keyIds = pgp->selectPublicKeys( i18n("Encryption Key Selection"),
                                    i18n("if in your language something like "
                                         "'key(s)' isn't possible please "
                                         "use the plural in the translation",
                                         "Select the key(s) which should "
                                         "be used to encrypt the message "
                                         "for\n%1")
                                    .arg( mAddressLabels[nr-1]->text() ),
                                    keyIds,
                                    mAddressLabels[nr-1]->text(),
                                    mAllowedKeys );
  }
  if( !keyIds.isEmpty() ) {
    mKeys[nr] = keyIds;
    TQLabel* keyidsL = mKeyIdsLabels[nr];
    keyidsL->setText( "0x" + keyIds.toStringList().join( "\n0x" ) );
    /*
    TQListBox* qlb = mKeyIdListBoxes[nr];
    qlb->clear();
    qlb->insertStringList( keyIds.toStringList() );
    */
  }
}


void
KeyApprovalDialog::slotOk()
{
  Kpgp::Module *pgp = Kpgp::Module::getKpgp();

  if( pgp == 0 ) {
    accept();
    return;
  }

  if( mPrefsChanged ) {
    // store the changed preferences
    for( unsigned int i = 0; i < mAddressLabels.size(); i++ ) {
      // traverse all Address and Encryption Preference widgets
      EncryptPref encrPref;
      switch( mEncrPrefCombos[i]->currentItem() ) {
        case 1:
          encrPref = NeverEncrypt;
          break;
        case 2:
          encrPref = AlwaysEncrypt;
          break;
        case 3:
          encrPref = AlwaysEncryptIfPossible;
          break;
        case 4:
          encrPref = AlwaysAskForEncryption;
          break;
        case 5:
          encrPref = AskWheneverPossible;
          break;
        default:
        case 0:
          encrPref = UnknownEncryptPref;
      }
      pgp->setEncryptionPreference( mAddressLabels[i]->text(), encrPref );
    }
  }

  accept();
}


void
KeyApprovalDialog::slotCancel()
{
  reject();
}



// ------------------------------------------------------------------------
CipherTextDialog::CipherTextDialog( const TQCString & text,
                                    const TQCString & charset, TQWidget *parent,
                                    const char *name, bool modal )
  :KDialogBase( parent, name, modal, i18n("OpenPGP Information"), Ok|Cancel, Ok)
{
  // FIXME (post KDE2.2): show some more info, e.g. the output of GnuPG/PGP
  TQFrame *page = makeMainWidget();
  TQVBoxLayout *topLayout = new TQVBoxLayout( page, 0, spacingHint() );

  TQLabel *label = new TQLabel( page );
  label->setText(i18n("Result of the last encryption/sign operation:"));
  topLayout->addWidget( label );

  mEditBox = new TQMultiLineEdit( page );
  mEditBox->setReadOnly(true);
  topLayout->addWidget( mEditBox, 10 );

  TQString unicodeText;
  if (charset.isEmpty())
    unicodeText = TQString::fromLocal8Bit(text.data());
  else {
    bool ok=true;
    TQTextCodec *codec = TDEGlobal::charsets()->codecForName(charset, ok);
    if(!ok)
      unicodeText = TQString::fromLocal8Bit(text.data());
    else
      unicodeText = codec->toUnicode(text.data(), text.length());
  }

  mEditBox->setText(unicodeText);

  setMinimumSize();
}

void CipherTextDialog::setMinimumSize()
{
  // this seems to force a layout of the entire document, so we get a
  // a proper contentsWidth(). Is there a better way?
  for ( int i = 0; i < mEditBox->paragraphs(); i++ )
      (void) mEditBox->paragraphRect( i );

  mEditBox->setMinimumHeight( mEditBox->fontMetrics().lineSpacing() * 25 );

  int textWidth = mEditBox->contentsWidth() + 30;


#if KDE_IS_VERSION( 3, 1, 90 )
  int maxWidth = TDEGlobalSettings::desktopGeometry(parentWidget()).width()-100;
#else
  TDEConfig gc("kdeglobals", false, false);
  gc.setGroup("Windows");
  int maxWidth;
  if (TQApplication::desktop()->isVirtualDesktop() &&
      gc.readBoolEntry("XineramaEnabled", true) &&
      gc.readBoolEntry("XineramaPlacementEnabled", true)) {
    maxWidth = TQApplication::desktop()->screenGeometry(TQApplication::desktop()->screenNumber(parentWidget())).width()-100;
  } else {
    maxWidth = TQApplication::desktop()->geometry().width()-100;
  }
#endif

  mEditBox->setMinimumWidth( TQMIN( textWidth, maxWidth ) );
}

void KeyRequester::virtual_hook( int, void* ) {}

void PublicKeyRequester::virtual_hook( int id, void* data ) {
  base::virtual_hook( id, data );
}

void SecretKeyRequester::virtual_hook( int id, void* data ) {
  base::virtual_hook( id, data );
}

} // namespace Kpgp



#include "kpgpui.moc"
