/***************************************************************************
                          kpayeesview.cpp
                          ---------------
    begin                : Thu Jan 24 2002
    copyright            : (C) 2000-2002 by Michael Edwardes
    email                : mte@users.sourceforge.net
                           Javier Campos Morales <javi_c@users.sourceforge.net>
                           Felix Rodriguez <frodriguez@users.sourceforge.net>
                           John C <thetacoturtle@users.sourceforge.net>
                           Thomas Baumgart <ipwizard@users.sourceforge.net>
                           Kevin Tambascio <ktambascio@users.sourceforge.net>
                           Andreas Nicolai <Andreas.Nicolai@gmx.net>
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program 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.                                   *
 *                                                                         *
 ***************************************************************************/


// ----------------------------------------------------------------------------
// QT Includes

#include <tqpushbutton.h>
#include <tqcombobox.h>
#include <tqlineedit.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqmultilineedit.h>
#include <tqpixmap.h>
#include <tqtabwidget.h>
#include <tqcursor.h>
#include <tqcheckbox.h>
#include <tqradiobutton.h>
#include <tqpainter.h>
#include <tqheader.h>
#include <tqbuttongroup.h>
#include <tqsplitter.h>
#include <tqmap.h>

// ----------------------------------------------------------------------------
// KDE Includes

#include <tdeglobal.h>
#include <tdelocale.h>
#include <tdemessagebox.h>
#include <kpushbutton.h>
#include <kiconloader.h>
#include <kguiitem.h>
#include <kstandarddirs.h>
#include <kdebug.h>
#include <tdeapplication.h>
#include <keditlistbox.h>

// ----------------------------------------------------------------------------
// Project Includes

#include <kmymoney/mymoneyfile.h>
#include <kmymoney/kmymoneyaccounttree.h>
#include <kmymoney/kmymoneyglobalsettings.h>
#include "../widgets/tdelistviewsearchline.h"

#include "kpayeesview.h"

/* -------------------------------------------------------------------------------*/
/*                               KTransactionPtrVector                            */
/* -------------------------------------------------------------------------------*/

int KTransactionPtrVector::compareItems(const TQString& s1, const TQString& s2) const
{
  if(s1 == s2)
    return 0;
  if(s1 < s2)
    return -1;
  return 1;
}

int KTransactionPtrVector::compareItems(KTransactionPtrVector::Item d1, KTransactionPtrVector::Item d2)
{
  int   rc = 0;
  MyMoneyTransaction* t1 = static_cast<MyMoneyTransaction*>(d1);
  MyMoneyTransaction* t2 = static_cast<MyMoneyTransaction*>(d2);
  MyMoneyMoney tmp;

  try {
    MyMoneySplit s1;
    MyMoneySplit s2;
    switch(m_idMode) {
      case AccountMode:
        s1 = t1->splitByAccount(m_id);
        s2 = t2->splitByAccount(m_id);
        break;
      case PayeeMode:
        s1 = t1->splitByPayee(m_id);
        s2 = t2->splitByPayee(m_id);
        break;
    }
    TQString p1, p2;

    switch(m_sortType) {
      case SortValue:
        rc = 1;
        tmp = s2.value() - s1.value();
        if(tmp.isZero()) {
          // same value? Sort by date
          rc = t2->postDate().daysTo(t1->postDate());
          if(rc == 0) {
            // same date? Sort by id
            rc = compareItems(t1->id(), t2->id());
          }
        } else if(tmp.isNegative()) {
          rc = -1;
        }
        break;

      case SortEntryDate:
        rc = t2->entryDate().daysTo(t1->entryDate());
        if(rc == 0) {
          // on same day, lower check numbers show up first
          rc = compareItems(s1.number(), s2.number());
          if(rc == 0) {
            // same number (e.g. empty)? larger amounts show up first
            rc = 1;
            tmp = s2.value() - s1.value();
            if(tmp.isZero()) {
              // same value? Sort by id
              rc = compareItems(t1->id(), t2->id());
            } else if(tmp.isNegative()) {
              rc = -1;
            }
          }
        }
        break;

      case SortEntryOrder:
        // sort by id
        rc = compareItems(t1->id(), t2->id());
        break;

      case SortTypeNr:
        rc = compareItems(s1.action(), s2.action());

        if(rc == 0) {
          // same action? Sort by nr
          rc = compareItems(s1.number(), s2.number());
          if(rc == 0) {
            // same number? Sort by date
            rc = t2->postDate().daysTo(t1->postDate());
            if(rc == 0) {
              // same date? Sort by value
              rc = 1;
              tmp = s2.value() - s1.value();
              if(tmp.isZero()) {
                // same value? sort by id
                rc = compareItems(t1->id(), t2->id());
              } else if(tmp.isNegative()) {
                rc = -1;
              }
            }
          }
        }
        break;

      case SortReceiver:
        if(!s2.payeeId().isEmpty()) {
          p2 = MyMoneyFile::instance()->payee(s2.payeeId()).name();
        }
        if(!s1.payeeId().isEmpty()) {
          p1 = MyMoneyFile::instance()->payee(s1.payeeId()).name();
        }

        rc = compareItems(p1, p2);

        if(rc == 0) {
          // same payee? Sort by date
          rc = t2->postDate().daysTo(t1->postDate());
          if(rc == 0) {
            // same date? Sort by value
            rc = 1;
            tmp = s2.value() - s1.value();
            if(tmp.isZero()) {
              // same value? sort by id
              rc = compareItems(t1->id(), t2->id());
            } else if(tmp.isNegative()) {
              rc = -1;
            }
          }
        }
        break;

      case SortNr:
        rc = compareItems(s1.number(), s2.number());
        if(rc == 0) {
          // same number? Sort by date
          rc = t2->postDate().daysTo(t1->postDate());
          if(rc == 0) {
            // same date? Sort by value
            rc = 1;
            tmp = s2.value() - s1.value();
            if(tmp.isZero()) {
              // same value? sort by id
              rc = compareItems(t1->id(), t2->id());
            } else if(tmp.isNegative()) {
              rc = -1;
            }
          }
        }
        break;

      case SortPostDate:
      // tricky fall through here!
      default:
        // sort by post date
        rc = t2->postDate().daysTo(t1->postDate());
        if(rc == 0) {
          // on same day, lower check numbers show up first
          rc = compareItems(s1.number(), s2.number());
          if(rc == 0) {
            // same number (e.g. empty)? larger amounts show up first
            rc = 1;
            tmp = s2.value() - s1.value();
            if(tmp.isZero()) {
              // same value? Sort by id
              rc = compareItems(t1->id(), t2->id());
            } else if(tmp.isNegative()) {
              rc = -1;
            }
          }
        }
        break;
    }
  } catch (MyMoneyException *e) {
    delete e;
  }
  return rc;
}

void KTransactionPtrVector::setSortType(const TransactionSortE type)
{
  m_sortType = type;
  sort();
}

void KTransactionPtrVector::setAccountId(const TQString& id)
{
  m_id = id;
  m_idMode = AccountMode;
}

void KTransactionPtrVector::setPayeeId(const TQString& id)
{
  m_id = id;
  m_idMode = PayeeMode;
}


// *** KPayeeListItem Implementation ***

KPayeeListItem::KPayeeListItem(TDEListView *parent, const MyMoneyPayee& payee) :
  TDEListViewItem(parent),
  m_payee(payee)
{
  setText(0, payee.name());
  // allow in column rename
  setRenameEnabled(0, true);
}

KPayeeListItem::~KPayeeListItem()
{
}

void KPayeeListItem::paintCell(TQPainter *p, const TQColorGroup & cg, int column, int width, int align)
{
  TQColorGroup cg2(cg);

  if(isAlternate())
    cg2.setColor(TQColorGroup::Base, KMyMoneyGlobalSettings::listColor());
  else
    cg2.setColor(TQColorGroup::Base, KMyMoneyGlobalSettings::listBGColor());

  p->setFont(KMyMoneyGlobalSettings::listCellFont());

  TQListViewItem::paintCell(p, cg2, column, width, align);
}

KTransactionListItem::KTransactionListItem(TDEListView* view, KTransactionListItem* parent, const TQString& accountId, const TQString& transactionId) :
  TDEListViewItem(view, parent)
{
  m_accountId = accountId;
  m_transactionId = transactionId;
}

KTransactionListItem::~KTransactionListItem()
{
}

void KTransactionListItem::paintCell(TQPainter *p, const TQColorGroup &cg, int column, int width, int alignment)
{
  TQColorGroup _cg = cg;
  _cg.setColor(TQColorGroup::Base, backgroundColor());
  TQListViewItem::paintCell(p, _cg, column, width, alignment);
}

const TQColor KTransactionListItem::backgroundColor(void)
{
  return isAlternate() ? KMyMoneyGlobalSettings::listBGColor() : KMyMoneyGlobalSettings::listColor();
}




// *** KPayeesView Implementation ***

KPayeesView::KPayeesView(TQWidget *parent, const char *name ) :
  KPayeesViewDecl(parent,name),
  m_needReload(false),
  m_needConnection(true),
  m_updatesQueued(0),
  m_inSelection(false)
{
  // create the searchline widget
  // and insert it into the existing layout
  m_searchWidget = new TDEListViewSearchLineWidget(m_payeesList, this);
  m_searchWidget->setSizePolicy(TQSizePolicy(TQSizePolicy::Preferred, TQSizePolicy::Fixed));
  KPayeesViewDeclLayout->insertWidget(0, m_searchWidget);

  m_splitter = new TQSplitter(this);
  m_payeesList->reparent(m_splitter, TQPoint(0,0), true);
  m_tabWidget->reparent(m_splitter, TQPoint(0, 0), true);
  m_splitter->setResizeMode(m_tabWidget, TQSplitter::Stretch);
  m_splitter->setOpaqueResize();
  layout10->addWidget(m_splitter);

  // use the size settings of the last run (if any)
  TDEConfig *config = TDEGlobal::config();
  config->setGroup("Last Use Settings");
  TQValueList<int> sizes = config->readIntListEntry("KPayeesViewSplitterSize");
  if(sizes.size() == 2) {
    if(!sizes[0] || !sizes[1]) {
      sizes[0] = 1;
      sizes[1] = 2;
    }
    m_splitter->setSizes(sizes);
  }

  m_transactionView->setSorting(-1);
  m_transactionView->setColumnWidthMode(2, TQListView::Manual);
  m_transactionView->setColumnAlignment(3, TQt::AlignRight);
  // never show horizontal scroll bars
  m_transactionView->setHScrollBarMode(TQScrollView::AlwaysOff);

  m_payeesList->addColumn(i18n("Name"));

  m_updateButton->setEnabled(false);
  radioNoMatch->setChecked(true);

  checkMatchIgnoreCase->setEnabled(false);

  checkEnableDefaultAccount->setChecked(false);
  labelDefaultAccount->setEnabled(false);
  comboDefaultAccount->setEnabled(false);

  TDEIconLoader* il = TDEGlobal::iconLoader();
  KGuiItem updateButtenItem( i18n("Update"),
                    TQIconSet(il->loadIcon("button_ok", TDEIcon::Small, TDEIcon::SizeSmall)),
                    i18n("Accepts the entered data and stores it"),
                    i18n("Use this to accept the modified data."));
  m_updateButton->setGuiItem(updateButtenItem);

  connect(m_payeesList, TQT_SIGNAL(selectionChanged()), this, TQT_SLOT(slotSelectPayee()));
  connect(m_payeesList, TQT_SIGNAL(itemRenamed(TQListViewItem*,int,const TQString&)), this, TQT_SLOT(slotRenamePayee(TQListViewItem*,int,const TQString&)));

  connect(addressEdit, TQT_SIGNAL(textChanged()), this, TQT_SLOT(slotPayeeDataChanged()));
  connect(postcodeEdit, TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(slotPayeeDataChanged()));
  connect(telephoneEdit, TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(slotPayeeDataChanged()));
  connect(emailEdit, TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(slotPayeeDataChanged()));
  connect(notesEdit, TQT_SIGNAL(textChanged()), this, TQT_SLOT(slotPayeeDataChanged()));
  connect(matchKeyEditList, TQT_SIGNAL(changed()), this, TQT_SLOT(slotKeyListChanged()));

  connect(radioNoMatch, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(slotPayeeDataChanged()));
  connect(radioNameMatch, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(slotPayeeDataChanged()));
  connect(radioKeyMatch, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(slotPayeeDataChanged()));
  connect(checkMatchIgnoreCase, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(slotPayeeDataChanged()));

  connect(checkEnableDefaultAccount, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(slotPayeeDataChanged()));
  connect(comboDefaultAccount, TQT_SIGNAL(accountSelected(const TQString&)), this, TQT_SLOT(slotPayeeDataChanged()));
  connect(buttonSelectMyAccount, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotChooseDefaultAccount()));

  connect(m_updateButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotUpdatePayee()));
  connect(m_helpButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotHelp()));

  connect(m_payeesList, TQT_SIGNAL(contextMenu(TDEListView*, TQListViewItem*, const TQPoint&)), this, TQT_SLOT(slotOpenContextMenu(TDEListView*, TQListViewItem*, const TQPoint&)));

//  connect(m_payeesList, TQT_SIGNAL(rightButtonClicked(TQListViewItem* , const TQPoint&, int)),
//    this, TQT_SLOT(slotOpenContextMenu(TQListViewItem*)));

  connect(m_transactionView, TQT_SIGNAL(doubleClicked(TQListViewItem*)),
          this, TQT_SLOT(slotTransactionDoubleClicked(TQListViewItem*)));

  connect(m_tabWidget, TQT_SIGNAL(currentChanged(TQWidget*)), this, TQT_SLOT(rearrange(void)));

  connect(MyMoneyFile::instance(), TQT_SIGNAL(dataChanged()), this, TQT_SLOT(slotLoadPayees()));
}

KPayeesView::~KPayeesView()
{
  // remember the splitter settings for startup
  TDEConfig *config = TDEGlobal::config();
  config->setGroup("Last Use Settings");
  config->writeEntry("KPayeesViewSplitterSize", m_splitter->sizes());
}

void KPayeesView::slotQueueUpdate(void)
{
  m_updatesQueued++;
  // The TDEListViewSearchLineWidget has an internal timer for update purposes
  // of 200 ms, so we should be safe with 250 ms here
  TQTimer::singleShot(250, this, TQT_SLOT(slotActivateUpdate()));
}

void KPayeesView::slotActivateUpdate(void)
{
  --m_updatesQueued;
  if(m_updatesQueued == 0)
    slotSelectPayee();
}

void KPayeesView::slotChooseDefaultAccount(void)
{
    MyMoneyFile* file = MyMoneyFile::instance();
    TQMap<TQString,int> account_count;

  for (int i = 0; i < m_transactionPtrVector.size(); ++i) {
    KMyMoneyTransaction* t = m_transactionPtrVector[i];
    MyMoneySplit s = t->splitById(t->splitId());
    const MyMoneyAccount& acc = file->account(s.accountId());

    TQString txt;
    if (s.action() != MyMoneySplit::ActionAmortization
        && acc.accountType() != MyMoneyAccount::AssetLoan
        && !file->isTransfer(*t)
        && t->splitCount() == 2) {
      MyMoneySplit s0 = t->splitByAccount(s.accountId(), false);
      if (account_count.contains(s0.accountId())) {
        account_count[s0.accountId()]++;
      }
      else {
        account_count[s0.accountId()] = 1;
      }
    }
  }

  TQMapIterator<TQString,int> most_frequent, iter;
  most_frequent = account_count.end();
  for (iter = account_count.begin(); iter != account_count.end(); iter++) {
    if (iter.data() > most_frequent.data()) {
      most_frequent = iter;
    }
  }

  if (most_frequent != account_count.end()) {
    checkEnableDefaultAccount->setChecked(true);
    comboDefaultAccount->setSelected(most_frequent.key());
  }
}

void KPayeesView::slotStartRename(void)
{
  TQListViewItemIterator it_l(m_payeesList, TQListViewItemIterator::Selected);
  TQListViewItem* it_v;
  if((it_v = it_l.current()) != 0) {
    it_v->startRename(0);
  }
}

// This variant is only called when a single payee is selected and renamed.
void KPayeesView::slotRenamePayee(TQListViewItem* p , int /* col */, const TQString& txt)
{
  //kdDebug() << "[KPayeesView::slotRenamePayee]" << endl;
  // create a copy of the new name without appended whitespaces
  TQString new_name = txt.stripWhiteSpace();
  if (m_payee.name() != new_name) {
    MyMoneyFileTransaction ft;
    try {
      // check if we already have a payee with the new name
      try {
        // this function call will throw an exception, if the payee
        // hasn't been found.
        MyMoneyFile::instance()->payeeByName(new_name);
        // the name already exists, ask the user whether he's sure to keep the name
        if (KMessageBox::questionYesNo(this,
          i18n("A payee with the name '%1' already exists. It is not advisable to have "
            "multiple payees with the same identification name. Are you sure you would like "
            "to rename the payee?").arg(new_name)) != KMessageBox::Yes)
        {
          p->setText(0,m_payee.name());
          return;
        }
      } catch(MyMoneyException *e) {
        // all ok, the name is unique
        delete e;
      }

      m_payee.setName(new_name);
      m_newName = new_name;
      MyMoneyFile::instance()->modifyPayee(m_payee);

      // the above call to modifyPayee will reload the view so
      // all references and pointers to the view have to be
      // re-established.

      // make sure, that the record is visible even if it moved
      // out of sight due to the rename operation
      ensurePayeeVisible(m_payee.id());

      ft.commit();

    } catch(MyMoneyException *e) {
      KMessageBox::detailedSorry(0, i18n("Unable to modify payee"),
        (e->what() + " " + i18n("thrown in") + " " + e->file()+ ":%1").arg(e->line()));
      delete e;
    }
  }
  else {
    p->setText(0, new_name);
  }
}

void KPayeesView::ensurePayeeVisible(const TQString& id)
{
  for (TQListViewItem * item = m_payeesList->firstChild(); item; item = item->itemBelow()) {
    KPayeeListItem* p = dynamic_cast<KPayeeListItem*>(item);
    if(p && p->payee().id() == id) {
      if(p->itemAbove())
        m_payeesList->ensureItemVisible(p->itemAbove());
      if(p->itemBelow())
        m_payeesList->ensureItemVisible(p->itemBelow());

      m_payeesList->setCurrentItem(p);      // active item and deselect all others
      m_payeesList->setSelected(p, true);   // and select it
      m_payeesList->ensureItemVisible(p);
      break;
    }
  }
}

void KPayeesView::selectedPayees(TQValueList<MyMoneyPayee>& payeesList) const
{
  TQListViewItemIterator it_l(m_payeesList, TQListViewItemIterator::Selected | TQListViewItemIterator::Visible);
  TQListViewItem* it_v;
  while((it_v = it_l.current()) != 0) {
    KPayeeListItem* item = dynamic_cast<KPayeeListItem*>(it_v);
    if(item)
      payeesList << item->payee();
    ++it_l;
  }
}

void KPayeesView::slotSelectPayee(void)
{
  // check if the content of a currently selected payee was modified
  // and ask to store the data
  if (m_updateButton->isEnabled()) {
    if (KMessageBox::questionYesNo(this, TQString("<qt>%1</qt>").arg(
          i18n("Do you want to save the changes for <b>%1</b>?").arg(m_newName)),
          i18n("Save changes")) == KMessageBox::Yes) {
      m_inSelection = true;
      slotUpdatePayee();
      m_inSelection = false;
    }
  }

  // loop over all payees and count the number of payees, also
  // optain last selected payee
  TQValueList<MyMoneyPayee> payeesList;
  selectedPayees(payeesList);

  emit selectObjects(payeesList);

  if (payeesList.count() == 0) {
    m_tabWidget->setEnabled(false); // disable tab widget
    clearItemData();
    m_payee = MyMoneyPayee();
    return; // make sure we don't access an undefined payee
  }

  // if we have multiple payees selected, clear and disable the payee informations
  if (payeesList.count() > 1) {
    m_tabWidget->setEnabled(false); // disable tab widget
    clearItemData();
    // disable renaming in all listviewitem
    for (TQListViewItem * i = m_payeesList->firstChild(); i; i = i->itemBelow())
      i->setRenameEnabled(0, false);
    return;
  }
  // otherwise we have just one selected, enable payee information widget
  m_tabWidget->setEnabled(true);
  // enable renaming in all listviewitem
  for (TQListViewItem * i = m_payeesList->firstChild(); i; i = i->itemBelow())
    i->setRenameEnabled(0, true);

  // as of now we are updating only the last selected payee, and until
  // selection mode of the TQListView has been changed to Extended, this
  // will also be the only selection and behave exactly as before - Andreas
  try {
    m_payee = payeesList[0];
    m_newName = m_payee.name();

    addressEdit->setEnabled(true);
    addressEdit->setText(m_payee.address());
    postcodeEdit->setEnabled(true);
    postcodeEdit->setText(m_payee.postcode());
    telephoneEdit->setEnabled(true);
    telephoneEdit->setText(m_payee.telephone());
    emailEdit->setEnabled(true);
    emailEdit->setText(m_payee.email());
    notesEdit->setText(m_payee.notes());

    TQStringList keys;
    bool ignorecase = false;
    MyMoneyPayee::payeeMatchType type = m_payee.matchData(ignorecase, keys);

    m_matchType->setButton(static_cast<int>(type));
    matchKeyEditList->clear();
    matchKeyEditList->insertStringList(keys);
    checkMatchIgnoreCase->setChecked(ignorecase);

    checkEnableDefaultAccount->setChecked(m_payee.defaultAccountEnabled());
    comboDefaultAccount->setSelected(m_payee.defaultAccountId());

    slotPayeeDataChanged();

    showTransactions();

  } catch(MyMoneyException *e) {
    tqDebug("exception during display of payee: %s at %s:%ld", e->what().latin1(), e->file().latin1(), e->line());
    m_transactionView->clear();
    m_payee = MyMoneyPayee();
    delete e;
  }
}

void KPayeesView::clearItemData(void)
{
  addressEdit->setText(TQString());
  postcodeEdit->setText(TQString());
  telephoneEdit->setText(TQString());
  emailEdit->setText(TQString());
  notesEdit->setText(TQString());
  showTransactions();
}

void KPayeesView::showTransactions(void)
{
  MyMoneyFile* file = MyMoneyFile::instance();
  MyMoneyMoney balance(0);
  unsigned int   i;

  // clear the current transaction listview
  m_transactionView->clear();

  if(m_payee.id().isEmpty() || !m_tabWidget->isEnabled()) {
    m_balanceLabel->setText(i18n("Balance: %1").arg(balance.formatMoney(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction())));
    return;
  }

  // setup the list and the pointer vector
  MyMoneyTransactionFilter filter;
  filter.addPayee(m_payee.id());
  filter.setDateFilter(KMyMoneyGlobalSettings::startDate().date(), TQDate());

  TQValueList<MyMoneyTransaction> list = file->transactionList(filter);
  m_transactionList.clear();

  m_transactionPtrVector.clear();
  m_transactionPtrVector.resize(list.size());
  m_transactionPtrVector.setPayeeId(m_payee.id());
  m_transactionPtrVector.setSortType(KTransactionPtrVector::SortPostDate);

  TQValueList<MyMoneyTransaction>::ConstIterator it_t;
  TQString lastId;
  int ofs = 0;

  for(i = 0, it_t = list.begin(); it_t != list.end(); ++it_t) {
    KMyMoneyTransaction k(*it_t);

    filter.match(*it_t);
    if(lastId != (*it_t).id()) {
      ofs = 0;
      lastId = (*it_t).id();
    } else
      ofs++;

    k.setSplitId(filter.matchingSplits()[ofs].id());
    MyMoneyAccount acc = MyMoneyFile::instance()->account(filter.matchingSplits()[ofs].accountId());
    if(acc.accountGroup() == MyMoneyAccount::Asset
    || acc.accountGroup() == MyMoneyAccount::Liability) {
      TQValueList<KMyMoneyTransaction>::ConstIterator it_k;
      it_k = m_transactionList.append(k);
      balance += k.splitById(k.splitId()).value();
      m_transactionPtrVector.insert(i, &(*it_k));
      ++i;
    }
  }
  m_transactionPtrVector.resize(i);

  // sort the transactions
  m_transactionPtrVector.sort();

  // and fill the m_transactionView
  KTransactionListItem *item = 0;

  for(i = 0; i < m_transactionPtrVector.size(); ++i) {
    KMyMoneyTransaction* t = m_transactionPtrVector[i];
    MyMoneySplit s = t->splitById(t->splitId());
    const MyMoneyAccount& acc = file->account(s.accountId());

    item = new KTransactionListItem(m_transactionView, item, s.accountId(), t->id());
    item->setText(0, s.number());
    item->setText(1, TDEGlobal::locale()->formatDate(t->postDate(), true));

    TQString txt;
    if(s.action() == MyMoneySplit::ActionAmortization) {
      if(acc.accountType() == MyMoneyAccount::Loan) {
        if(s.value().isPositive()) {
          txt = i18n("Amortization of %1").arg(acc.name());
        } else {
          txt = i18n("Payment to %1").arg(acc.name());
        }
      } else if(acc.accountType() == MyMoneyAccount::AssetLoan) {
        if(s.value().isNegative()) {
          txt = i18n("Amortization of %1").arg(acc.name());
        } else {
          txt = i18n("Payment to %1").arg(acc.name());
        }
      } else {
        txt = i18n("Loan payment from %1").arg(acc.name());
      }
    } else if (file->isTransfer(*t)) {
      if(!s.value().isNegative()) {
        txt = i18n("Transfer to %1").arg(acc.name());
      } else {
        txt = i18n("Transfer from %1").arg(acc.name());
      }
    } else if(t->splitCount() > 2) {
      txt = i18n("Split transaction (category replacement)", "Split transaction");
    } else if(t->splitCount() == 2) {
      MyMoneySplit s0 = t->splitByAccount(s.accountId(), false);
      txt = MyMoneyFile::instance()->accountToCategory(s0.accountId());
    }
    item->setText(2, txt);
    item->setText(3, s.value().formatMoney(acc.fraction()));
  }
  m_balanceLabel->setText(i18n("Balance: %1").arg(balance.formatMoney(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction())));

  // Trick: it seems, that the initial sizing of the view does
  // not work correctly. At least, the columns do not get displayed
  // correct. Reason: the return value of m_transactionView->visibleWidth()
  // is incorrect. If the widget is visible, resizing works correctly.
  // So, we let the dialog show up and resize it then. It's not really
  // clean, but the only way I got the damned thing working.
  TQTimer::singleShot(50, this, TQT_SLOT(rearrange()));
}

void KPayeesView::slotKeyListChanged(void)
{
  bool rc = false;
  bool ignorecase = false;
  TQStringList keys;
  // J.Rodehueser: delete unused variable 'type'
  // orig:  MyMoneyPayee::payeeMatchType type = m_payee.matchData(ignorecase, keys);
  m_payee.matchData(ignorecase, keys);
  if(m_matchType->selectedId() == MyMoneyPayee::matchKey) {
    rc |= (keys != matchKeyEditList->items());
  }
  m_updateButton->setEnabled(rc);
}

void KPayeesView::slotPayeeDataChanged(void)
{
  kdDebug(2) << "KPayeesView::slotPayeeDataChanged(void)" << endl;

  bool rc = false;

  if(m_tabWidget->isEnabled()) {
    rc |= ((m_payee.email().isEmpty() != emailEdit->text().isEmpty())
        || (!emailEdit->text().isEmpty() && m_payee.email() != emailEdit->text()));
    rc |= ((m_payee.address().isEmpty() != addressEdit->text().isEmpty())
        || (!addressEdit->text().isEmpty() && m_payee.address() != addressEdit->text()));
    rc |= ((m_payee.postcode().isEmpty() != postcodeEdit->text().isEmpty())
        || (!postcodeEdit->text().isEmpty() && m_payee.postcode() != postcodeEdit->text()));
    rc |= ((m_payee.telephone().isEmpty() != telephoneEdit->text().isEmpty())
        || (!telephoneEdit->text().isEmpty() && m_payee.telephone() != telephoneEdit->text()));
    rc |= ((m_payee.name().isEmpty() != m_newName.isEmpty())
        || (!m_newName.isEmpty() && m_payee.name() != m_newName));
    rc |= ((m_payee.notes().isEmpty() != notesEdit->text().isEmpty())
        || (!notesEdit->text().isEmpty() && m_payee.notes() != notesEdit->text()));

    bool ignorecase = false;
    TQStringList keys;

    MyMoneyPayee::payeeMatchType type = m_payee.matchData(ignorecase, keys);
    rc |= (static_cast<int>(type) != m_matchType->selectedId());

    checkMatchIgnoreCase->setEnabled(false);
    matchKeyEditList->setEnabled(false);

    if(m_matchType->selectedId() != MyMoneyPayee::matchDisabled) {
      checkMatchIgnoreCase->setEnabled(true);
      // if we turn matching on, we default to 'ignore case'
      // TODO maybe make the default a user option
      if(type == MyMoneyPayee::matchDisabled && m_matchType->selectedId() != MyMoneyPayee::matchDisabled)
        checkMatchIgnoreCase->setChecked(true);
      rc |= (ignorecase != checkMatchIgnoreCase->isChecked());
      if(m_matchType->selectedId() == MyMoneyPayee::matchKey) {
        matchKeyEditList->setEnabled(true);
        rc |= (keys != matchKeyEditList->items());
      }
    }

    rc |= (checkEnableDefaultAccount->isChecked() != m_payee.defaultAccountEnabled());
    if (checkEnableDefaultAccount->isChecked()) {
      comboDefaultAccount->setEnabled(true);
      labelDefaultAccount->setEnabled(true);
      // this is only going to understand the first in the list of selected accounts
      if (comboDefaultAccount->selectedAccounts().empty()) {
        rc |= !m_payee.defaultAccountId().isEmpty();
      }
      else {
        TQString temp = comboDefaultAccount->selectedAccounts().front();
        rc |= ( temp.isEmpty() != m_payee.defaultAccountId().isEmpty())
              || (!m_payee.defaultAccountId().isEmpty() && temp != m_payee.defaultAccountId());
      }
    }
    else {
      comboDefaultAccount->setEnabled(false);
      labelDefaultAccount->setEnabled(false);
    }
  }
  m_updateButton->setEnabled(rc);
}

void KPayeesView::slotUpdatePayee(void)
{
  if(m_updateButton->isEnabled()) {
    MyMoneyFileTransaction ft;
    m_updateButton->setEnabled(false);
    try {
      m_payee.setName(m_newName);
      m_payee.setAddress(addressEdit->text());
      m_payee.setPostcode(postcodeEdit->text());
      m_payee.setTelephone(telephoneEdit->text());
      m_payee.setEmail(emailEdit->text());
      m_payee.setNotes(notesEdit->text());
      m_payee.setMatchData(static_cast<MyMoneyPayee::payeeMatchType>(m_matchType->selectedId()), checkMatchIgnoreCase->isChecked(), matchKeyEditList->items());
      m_payee.setDefaultAccountId();

      if (checkEnableDefaultAccount->isChecked()) {
        TQString temp;
        if (!comboDefaultAccount->selectedAccounts().empty()) {
          temp = comboDefaultAccount->selectedAccounts().front();
          m_payee.setDefaultAccountId(temp);
        }
      }

      MyMoneyFile::instance()->modifyPayee(m_payee);
      ft.commit();

    } catch(MyMoneyException *e) {
      KMessageBox::detailedSorry(0, i18n("Unable to modify payee"),
        (e->what() + " " + i18n("thrown in") + " " + e->file()+ ":%1").arg(e->line()));
      delete e;
    }
  }
}

void KPayeesView::readConfig(void)
{
  m_transactionView->setFont(KMyMoneyGlobalSettings::listCellFont());

  TQFontMetrics fm( KMyMoneyGlobalSettings::listHeaderFont() );
  int height = fm.lineSpacing()+6;

  m_transactionView->header()->setMinimumHeight(height);
  m_transactionView->header()->setMaximumHeight(height);
  m_transactionView->header()->setFont(KMyMoneyGlobalSettings::listHeaderFont());

  m_payeesList->setDefaultRenameAction(
           KMyMoneyGlobalSettings::focusChangeIsEnter() ? TQListView::Accept : TQListView::Reject);

  //initialize the account list?
  comboDefaultAccount->loadList((KMyMoneyUtils::categoryTypeE)(KMyMoneyUtils::asset | KMyMoneyUtils::liability | MyMoneyAccount::Income | MyMoneyAccount::Expense));

}

void KPayeesView::show(void)
{
  // since we could not construct the connection in our own ctor,
  // we set it up now. The widgets of the TDEListViewSearchLineWidget must exist by now.
  // If you want to learn about the details, see the source of TDEListViewSearchLineWidget's
  // constructor
  if(m_needConnection) {
    connect(m_searchWidget->searchLine(), TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(slotQueueUpdate(void)));
    m_needConnection = false;
  }

  if(m_needReload) {
    loadPayees();
    m_needReload = false;
  }

  // fixup the layout
  TQTimer::singleShot(0, this, TQT_SLOT(rearrange()));

  // don't forget base class implementation
  KPayeesViewDecl::show();

  TQValueList<MyMoneyPayee> list;
  selectedPayees(list);
  emit selectObjects(list);
}

void KPayeesView::slotLoadPayees(void)
{
  if(isVisible()) {
    if(m_inSelection)
      TQTimer::singleShot(0, this, TQT_SLOT(slotLoadPayees()));
    else
      loadPayees();
  } else {
    m_needReload = true;
  }
}

void KPayeesView::loadPayees(void)
{
  if(m_inSelection)
    return;

  TQMap<TQString, bool> isSelected;
  TQString id;

  ::timetrace("Start KPayeesView::loadPayees");
  readConfig();

  // remember which items are selected in the list
  TQListViewItemIterator it_l(m_payeesList, TQListViewItemIterator::Selected);
  TQListViewItem* it_v;
  while((it_v = it_l.current()) != 0) {
    KPayeeListItem* item = dynamic_cast<KPayeeListItem*>(it_v);
    if(item)
      isSelected[item->payee().id()] = true;
    ++it_l;
  }

  // keep current selected item
  KPayeeListItem *currentItem = static_cast<KPayeeListItem *>(m_payeesList->currentItem());
  if(currentItem)
    id = currentItem->payee().id();

  // remember the upper left corner of the viewport
  TQPoint startPoint = m_payeesList->viewportToContents(TQPoint(0, 0));

  // turn off updates to avoid flickering during reload
  m_payeesList->setUpdatesEnabled(false);

  // clear the list
  m_payeesList->clear();
  m_transactionView->clear();
  currentItem = 0;

  TQValueList<MyMoneyPayee>list = MyMoneyFile::instance()->payeeList();
  TQValueList<MyMoneyPayee>::ConstIterator it;

  for (it = list.begin(); it != list.end(); ++it) {
    KPayeeListItem* item = new KPayeeListItem(m_payeesList, *it);
    if(item->payee().id() == id)
      currentItem = item;
    if(isSelected[item->payee().id()])
      item->setSelected(true);
  }

  if (currentItem) {
    m_payeesList->setCurrentItem(currentItem);
  }

  // reposition viewport
  m_payeesList->setContentsPos(startPoint.x(), startPoint.y());

  m_searchWidget->searchLine()->updateSearch(TQString());

  // turn updates back on
  m_payeesList->setUpdatesEnabled(true);
  m_payeesList->repaintContents();

  slotSelectPayee();

  ::timetrace("End KPayeesView::loadPayees");
}

void KPayeesView::rearrange(void)
{
  resizeEvent(0);
}

void KPayeesView::resizeEvent(TQResizeEvent* ev)
{
  // resize the register
  int w = m_transactionView->visibleWidth();
  w -= m_transactionView->columnWidth(0);
  w -= m_transactionView->columnWidth(1);
  w -= m_transactionView->columnWidth(3);
  m_transactionView->setColumnWidth(2, w);
  m_transactionView->resizeContents(
    m_transactionView->visibleWidth(),
    m_transactionView->contentsHeight());

  m_payeesList->setColumnWidth(0, m_payeesList->visibleWidth());
  KPayeesViewDecl::resizeEvent(ev);
}

void KPayeesView::slotTransactionDoubleClicked(TQListViewItem* i)
{
  KTransactionListItem* item = static_cast<KTransactionListItem *>(i);
  if (item)
    emit transactionSelected(item->accountId(), item->transactionId());
}

void KPayeesView::slotSelectPayeeAndTransaction(const TQString& payeeId, const TQString& accountId, const TQString& transactionId)
{
  if(!isVisible())
    return;

  try {
    // clear filter
    m_searchWidget->searchLine()->clear();
    m_searchWidget->searchLine()->updateSearch();

    // deselect all other selected items
    TQListViewItemIterator it_l(m_payeesList, TQListViewItemIterator::Selected);
    TQListViewItem* it_v;
    while((it_v = it_l.current()) != 0) {
      KPayeeListItem* item = dynamic_cast<KPayeeListItem*>(it_v);
      if(item)
        item->setSelected(false);
      ++it_l;
    }

    // find the payee in the list
    TQListViewItem* it;
    for(it = m_payeesList->firstChild(); it; it = it->itemBelow()) {
      KPayeeListItem* item = dynamic_cast<KPayeeListItem *>(it);
      if(item && item->payee().id() == payeeId) {
        if(it->itemAbove())
          m_payeesList->ensureItemVisible(it->itemAbove());
        if(it->itemBelow())
          m_payeesList->ensureItemVisible(it->itemBelow());

        m_payeesList->setCurrentItem(it);     // active item and deselect all others
        m_payeesList->setSelected(it,true);   // and select it
        m_payeesList->ensureItemVisible(it);

        KTransactionListItem* item = dynamic_cast<KTransactionListItem*> (m_transactionView->firstChild());
        while(item != 0) {
          if(item->accountId() == accountId && item->transactionId() == transactionId)
            break;
          item = dynamic_cast<KTransactionListItem*> (item->nextSibling());
        }
        if(!item) {
          item = dynamic_cast<KTransactionListItem*> (m_transactionView->firstChild());
        }
        if(item) {
          m_transactionView->setSelected(item, true);
          m_transactionView->ensureItemVisible(item);
        }
        // quit out of for() loop
        break;
      }
    }

  } catch(MyMoneyException *e) {
    tqWarning("Unexpected exception in KPayeesView::slotSelectPayeeAndTransaction");
    delete e;
  }
}

void KPayeesView::slotOpenContextMenu(TDEListView* lv, TQListViewItem* i, const TQPoint& p)
{
  Q_UNUSED(p);
  if(lv == m_payeesList) {
    KPayeeListItem* item = dynamic_cast<KPayeeListItem*>(i);
    if(item) {
      emit openContextMenu(item->payee());
    }
  }
}

void KPayeesView::slotHelp(void)
{
  kapp->invokeHelp("details.payees.personalinformation");
}

#include "kpayeesview.moc"
// vim:cin:si:ai:et:ts=2:sw=2:
