/***************************************************************************
                             transactioneditor.cpp
                             ----------
    begin                : Wed Jun 07 2006
    copyright            : (C) 2006 by Thomas Baumgart
    email                : Thomas Baumgart <ipwizard@users.sourceforge.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 <tqlabel.h>
#include <tqapplication.h>
#include <tqeventloop.h>
#include <tqradiobutton.h>
#include <tqbuttongroup.h>
#include <tqtooltip.h>

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

#include <tdeglobal.h>
#include <ktextedit.h>
#include <tdelocale.h>
#include <kcombobox.h>
#include <kpushbutton.h>
#include <tdemessagebox.h>
#include <kstdguiitem.h>
#include <kiconloader.h>
#include <kguiitem.h>

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

#include <kmymoney/transactioneditor.h>
#include <kmymoney/kmymoneycategory.h>
#include <kmymoney/kmymoneydateinput.h>
#include <kmymoney/kmymoneyedit.h>
#include <kmymoney/kmymoneylineedit.h>
#include <kmymoney/kmymoneyaccountcompletion.h>
#include <kmymoney/kmymoneyaccountselector.h>
#include <kmymoney/mymoneyfile.h>
#include <kmymoney/kmymoneyutils.h>
#include <kmymoney/transactionform.h>
#include <kmymoney/kmymoneyglobalsettings.h>

#include "../dialogs/ksplittransactiondlg.h"
#include "../dialogs/kcurrencycalculator.h"
#include "../dialogs/tdeselecttransactionsdlg.h"

using namespace KMyMoneyRegister;
using namespace KMyMoneyTransactionForm;

TransactionEditor::TransactionEditor(TransactionEditorContainer* regForm, KMyMoneyRegister::Transaction* item, const KMyMoneyRegister::SelectedTransactions& list, const TQDate& lastPostDate) :
  m_transactions(list),
  m_regForm(regForm),
  m_item(item),
  m_transaction(item->transaction()),
  m_split(item->split()),
  m_lastPostDate(lastPostDate),
  m_openEditSplits(false)
{
  m_item->startEditMode();
  connect(MyMoneyFile::instance(), TQT_SIGNAL(dataChanged()), this, TQT_SLOT(slotUpdateAccount()));
}

TransactionEditor::~TransactionEditor()
{
  // Make sure the widgets do not send out signals to the editor anymore
  // After all, the editor is about to die
  TQMap<TQString, TQWidget*>::iterator it_w;
  for(it_w = m_editWidgets.begin(); it_w != m_editWidgets.end(); ++it_w) {
    (*it_w)->disconnect(this);
  }

  m_regForm->removeEditWidgets(m_editWidgets);
  m_item->leaveEditMode();
  emit finishEdit(m_transactions);
}

void TransactionEditor::slotUpdateAccount(const TQString& id)
{
  m_account = MyMoneyFile::instance()->account(id);
  setupPrecision();
}

void TransactionEditor::slotUpdateAccount(void)
{
  // reload m_account as it might have been changed
  m_account = MyMoneyFile::instance()->account(m_account.id());
  setupPrecision();
}

void TransactionEditor::setupPrecision(void)
{
  const int prec = (m_account.id().isEmpty()) ? 2 : MyMoneyMoney::denomToPrec(m_account.fraction());
  TQStringList widgets = TQStringList::split(",", "amount,deposit,payment");
  TQStringList::const_iterator it_w;
  for(it_w = widgets.begin(); it_w != widgets.end(); ++it_w) {
    TQWidget * w;
    if((w = haveWidget(*it_w)) != 0) {
      dynamic_cast<kMyMoneyEdit*>(w)->setPrecision(prec);
    }
  }
}

void TransactionEditor::setup(TQWidgetList& tabOrderWidgets, const MyMoneyAccount& account, KMyMoneyRegister::Action action)
{
  m_account = account;
  m_initialAction = action;
  createEditWidgets();
  m_regForm->arrangeEditWidgets(m_editWidgets, m_item);
  m_regForm->tabOrder(tabOrderWidgets, m_item);
  TQWidget* w = haveWidget("tabbar");
  if(w)
    tabOrderWidgets.append(w);
  loadEditWidgets(action);
  m_editWidgets.removeOrphans();
  clearFinalWidgets();
  setupFinalWidgets();
  slotUpdateButtonState();
}

void TransactionEditor::clearFinalWidgets(void)
{
  m_finalEditWidgets.clear();
}

void TransactionEditor::addFinalWidget(const TQWidget* w)
{
  if(w) {
    m_finalEditWidgets << w;
  }
}

void TransactionEditor::slotReloadEditWidgets(void)
{
}

bool TransactionEditor::eventFilter(TQObject* o, TQEvent* e)
{
  bool rc = false;
  if(TQT_BASE_OBJECT(o) == TQT_BASE_OBJECT(haveWidget("number"))) {
    if(e->type() == TQEvent::MouseButtonDblClick) {
      emit assignNumber();
      rc = true;
    }
  }

  // if the object is a widget, the event is a key press event and
  // the object is one of our edit widgets, then ....
  if(o->isWidgetType()
  && (e->type() == TQEvent::KeyPress)
  && m_editWidgets.values().contains(dynamic_cast<TQWidget*>(o))) {
    TQKeyEvent* k = dynamic_cast<TQKeyEvent*>(e);
    if((k->state() & TQt::KeyButtonMask) == 0) {
      bool isFinal = false;
      TQValueList<const TQWidget*>::const_iterator it_w;
      switch(k->key()) {
        case TQt::Key_Return:
        case TQt::Key_Enter:
          // we check, if the object is one of the m_finalEditWidgets and if it's
          // a kMyMoneyEdit object that the value is not 0. If any of that is the
          // case, it's the final object. In other cases, we convert the enter
          // key into a TAB key to move between the fields. Of course, we only need
          // to do this as long as the appropriate option is set. In all other cases,
          // we treat the return/enter key as such.
          if(KMyMoneyGlobalSettings::enterMovesBetweenFields()) {
            for(it_w = m_finalEditWidgets.begin(); !isFinal && it_w != m_finalEditWidgets.end(); ++it_w) {
              if(TQT_BASE_OBJECT_CONST(*it_w) == TQT_BASE_OBJECT(o)) {
                if(dynamic_cast<const kMyMoneyEdit*>(*it_w)) {
                  isFinal = !(dynamic_cast<const kMyMoneyEdit*>(*it_w)->value().isZero());
                } else
                  isFinal = true;
              }
            }
          } else
            isFinal = true;

          // for the non-final objects, we treat the return key as a TAB
          if(!isFinal) {
            TQKeyEvent evt(e->type(),
                          Key_Tab, 0, k->state(), TQString(),
                          k->isAutoRepeat(), k->count());

            TQApplication::sendEvent( o, &evt );
              // in case of a category item and the split button is visible
              // send a second event so that we get passed the button.
            if(dynamic_cast<KMyMoneyCategory*>(o) && dynamic_cast<KMyMoneyCategory*>(o)->splitButton())
              TQApplication::sendEvent( o, &evt );

          } else {
            TQTimer::singleShot(0, this, TQT_SIGNAL(returnPressed()));
          }
          // don't process any further
          rc = true;
          break;

        case TQt::Key_Escape:
          TQTimer::singleShot(0, this, TQT_SIGNAL(escapePressed()));
          break;
      }
    }
  }
  return rc;
}

void TransactionEditor::slotNumberChanged(const TQString& txt)
{
  kMyMoneyLineEdit* number = dynamic_cast<kMyMoneyLineEdit*>(haveWidget("number"));

  if(number) {
    if(MyMoneyFile::instance()->checkNoUsed(m_account.id(), txt)) {
      if(KMessageBox::questionYesNo(m_regForm, TQString("<qt>")+i18n("The number <b>%1</b> has already been used in account <b>%2</b>. Do you want to replace it with the next available number?").arg(txt).arg(m_account.name())+TQString("</qt>"), i18n("Duplicate number")) == KMessageBox::Yes) {
        number->loadText(KMyMoneyUtils::nextCheckNumber(m_account));
      }
    }
  }
}

void TransactionEditor::slotUpdateButtonState(void)
{
  TQString reason;
  emit transactionDataSufficient(isComplete(reason));
}

TQWidget* TransactionEditor::haveWidget(const TQString& name) const
{
  return m_editWidgets.haveWidget(name);
}

int TransactionEditor::slotEditSplits(void)
{
  return TQDialog::Rejected;
}

#if 0
  // If we deal with multiple currencies we make sure, that for
  // transactions with two splits, the transaction's commodity is the
  // currency of the currently selected account. This saves us from a
  // lot of grieve later on.
  // Editing a transaction which has more than two splits and a commodity
  // that differs from the currency of the current selected account is
  // not a good idea. We will warn the user and give him a hint if there
  // is an account where he can perfom the edit operation much better.
  if(m_transaction.commodity() != m_account.currencyId()) {
    if(m_transaction.splitCount() == 2) {
      // in case of two splits, it's easy. We just have to switch the
      // transactions commodity. Let's assume the following scenario:
      // - transactions commodity is CA
      // - account's currencyId is CB
      // - second split is of course in CA (otherwise we have a real problem)
      // - Value is V in both splits
      // - Shares in this account's split is SB
      // - Shares in the other account's split is SA (and equal to V)
      //
      // We do the following:
      // - change transactions commodity to CB
      // - set V in both splits to SB
      // - modify the splits in the transaction
      try {
        MyMoneySplit split = m_transaction.splitByAccount(m_account.id(), false);
        m_transaction.setCommodity(m_account.currencyId());
        m_split.setValue(m_split.shares());
        split.setValue(-m_split.shares());
        m_transaction.modifySplit(m_split);
        m_transaction.modifySplit(split);

        if(m_transactionPtr) {
          KMyMoneyTransaction k(m_transaction);
          k.setSplitId(m_split.id());
          *m_transactionPtr = k;
        }
      } catch(MyMoneyException *e) {
        tqDebug("Unable to update commodity to second splits currency in %s: '%s'", m_transaction.id().data(), e->what().data());
        delete e;
      }

    } else {
      // Find a suitable account
      MyMoneySecurity sec = MyMoneyFile::instance()->currency(m_transaction.commodity());
      MyMoneyAccount acc;
      for(it = m_transaction.splits().begin(); it != m_transaction.splits().end(); ++it) {
        if((*it).id() == m_split.id())
          continue;
        acc = MyMoneyFile::instance()->account((*it).accountId());
        if((acc.accountGroup() == MyMoneyAccount::Asset
        || acc.accountGroup() == MyMoneyAccount::Liability)
        && acc.accountType() != MyMoneyAccount::Stock) {
          if(m_transaction.commodity() == acc.currencyId())
            break;
        }
        acc = MyMoneyAccount();
      }
      TQString msg;
      msg = TQString("<p>")+i18n("This transaction has more than two splits and is based on a different currency (%1). Using this account to modify the transaction is currently not very well supported by KMyMoney and may result in false results.").arg(sec.name())+TQString(" ");
      if(acc.id().isEmpty()) {
        msg += i18n("KMyMoney was not able to find a more appropriate account to edit this transaction. Nevertheless, you are allowed to modify the transaction. If you don't want to edit this transaction, please cancel from editing next.");
      } else {
         msg += i18n("Using e.g. <b>%1</b> to edit this transaction is a better choice. Nevertheless, you are allowed to modify the transaction. If you want to use the suggested account instead, please cancel from editing next and change the view to the suggested account.").arg(acc.name());
      }
      KMessageBox::information(0, msg);
    }
  }
#endif

void TransactionEditor::setTransaction(const MyMoneyTransaction& t, const MyMoneySplit& s)
{
  m_transaction = t;
  m_split = s;
  loadEditWidgets();
}

bool TransactionEditor::fixTransactionCommodity(const MyMoneyAccount& account)
{
  bool rc = true;
  bool firstTimeMultiCurrency = true;
  m_account = account;

  MyMoneyFile* file = MyMoneyFile::instance();

  // determine the max fraction for this account
  MyMoneySecurity sec = file->security(m_account.currencyId());
  int fract = m_account.fraction();

  // scan the list of selected transactions
  KMyMoneyRegister::SelectedTransactions::iterator it_t;
  for(it_t = m_transactions.begin(); (rc == true) && (it_t != m_transactions.end()); ++it_t) {
    // there was a time when the schedule editor did not setup the transaction commodity
    // let's give a helping hand here for those old schedules
    if((*it_t).transaction().commodity().isEmpty())
      (*it_t).transaction().setCommodity(m_account.currencyId());
    // we need to check things only if a different commodity is used
    if(m_account.currencyId() != (*it_t).transaction().commodity()) {
      MyMoneySecurity osec = file->security((*it_t).transaction().commodity());
      switch((*it_t).transaction().splitCount()) {
        case 0:
          // new transaction, guess nothing's here yet ;)
          break;

        case 1:
          try {
            // make sure, that the value is equal to the shares, don't forget our own copy
            MyMoneySplit& splitB = (*it_t).split();  // reference usage wanted here
            if(m_split == splitB)
              m_split.setValue(splitB.shares());
            splitB.setValue(splitB.shares());
            (*it_t).transaction().modifySplit(splitB);

          } catch(MyMoneyException *e) {
            tqDebug("Unable to update commodity to second splits currency in %s: '%s'", (*it_t).transaction().id().data(), e->what().data());
            delete e;
          }
          break;

        case 2:
          // If we deal with multiple currencies we make sure, that for
          // transactions with two splits, the transaction's commodity is the
          // currency of the currently selected account. This saves us from a
          // lot of grieve later on.  We just have to switch the
          // transactions commodity. Let's assume the following scenario:
          // - transactions commodity is CA
          // - splitB and account's currencyId is CB
          // - splitA is of course in CA (otherwise we have a real problem)
          // - Value is V in both splits
          // - Shares in splitB is SB
          // - Shares in splitA is SA (and equal to V)
          //
          // We do the following:
          // - change transactions commodity to CB
          // - set V in both splits to SB
          // - modify the splits in the transaction
          try {
            // retrieve the splits
            MyMoneySplit& splitB = (*it_t).split();  // reference usage wanted here
            MyMoneySplit splitA = (*it_t).transaction().splitByAccount(m_account.id(), false);

            // - set V in both splits to SB. Don't forget our own copy
            if(m_split == splitB) {
              m_split.setValue(splitB.shares());
            }
            splitB.setValue(splitB.shares());
            splitA.setValue(-splitB.shares());
            (*it_t).transaction().modifySplit(splitA);
            (*it_t).transaction().modifySplit(splitB);

          } catch(MyMoneyException *e) {
            tqDebug("Unable to update commodity to second splits currency in %s: '%s'", (*it_t).transaction().id().data(), e->what().data());
            delete e;
          }
          break;

        default:
          // TODO: use new logic by adjusting all splits by the price
          // extracted from the selected split. Inform the user that
          // this will happen and allow him to stop the processing (rc = false)

          try {
            TQString msg;
            if(firstTimeMultiCurrency) {
              firstTimeMultiCurrency = false;
              if(!isMultiSelection()) {
                msg = i18n("This transaction has more than two splits and is originally based on a different currency (%1). Using this account to modify the transaction may result in rounding errors. Do you want to continue?").arg(osec.name());
              } else {
                msg = i18n("At least one of the selected transactions has more than two splits and is originally based on a different currency (%1). Using this account to modify the transactions may result in rounding errors. Do you want to continue?").arg(osec.name());
              }

              if(KMessageBox::warningContinueCancel(0, TQString("<qt>%1</qt>").arg(msg)) == KMessageBox::Cancel) {
                rc = false;
              }
            }

            if(rc == true) {
              MyMoneyMoney price;
              if(!(*it_t).split().shares().isZero() && !(*it_t).split().value().isZero())
                price = (*it_t).split().shares() / (*it_t).split().value();
              TQValueList<MyMoneySplit>::iterator it_s;
              MyMoneySplit& mySplit = (*it_t).split();
              for(it_s = (*it_t).transaction().splits().begin(); it_s != (*it_t).transaction().splits().end(); ++it_s) {
                MyMoneySplit s = (*it_s);
                if(s == mySplit) {
                  s.setValue(s.shares());
                  if(mySplit == m_split) {
                    m_split = s;
                  }
                  mySplit = s;
                } else {
                  s.setValue((s.value() * price).convert(fract));
                }
                (*it_t).transaction().modifySplit(s);
              }
            }
          } catch(MyMoneyException *e) {
            tqDebug("Unable to update commodity of split currency in %s: '%s'", (*it_t).transaction().id().data(), e->what().data());
            delete e;
          }
          break;
      }

      // set the transaction's ommodity to this account's currency
      (*it_t).transaction().setCommodity(m_account.currencyId());

      // update our copy of the transaction that has the focus
      if((*it_t).transaction().id() == m_transaction.id()) {
        m_transaction = (*it_t).transaction();
      }
    }
  }
  return rc;
}

void TransactionEditor::assignNextNumber(void)
{
  if(canAssignNumber()) {
    kMyMoneyLineEdit* number = dynamic_cast<kMyMoneyLineEdit*>(haveWidget("number"));
    number->loadText(KMyMoneyUtils::nextCheckNumber(m_account));
  }
}

bool TransactionEditor::canAssignNumber(void) const
{
  kMyMoneyLineEdit* number = dynamic_cast<kMyMoneyLineEdit*>(haveWidget("number"));
  return (number != 0) && (number->text().isEmpty());
}

void TransactionEditor::setupCategoryWidget(KMyMoneyCategory* category, const TQValueList<MyMoneySplit>& splits, TQString& categoryId, const char* splitEditSlot, bool /* allowObjectCreation */)
{
  disconnect(category, TQT_SIGNAL(focusIn()), this, splitEditSlot);
#if 0
  // FIXME must deal with the logic that suppressObjectCreation is
  // automatically turned off when the createItem() signal is connected
  if(allowObjectCreation)
    category->setSuppressObjectCreation(false);
#endif

  switch(splits.count()) {
    case 0:
      categoryId = TQString();
      if(!category->currentText().isEmpty()) {
        category->setCurrentText();
        // make sure, we don't see the selector
        category->completion()->hide();
      }
      category->completion()->setSelected(TQString());
      break;

    case 1:
      categoryId = splits[0].accountId();
      category->completion()->setSelected(categoryId);
      category->slotItemSelected(categoryId);
      break;

    default:
      categoryId = TQString();
      category->setSplitTransaction();
      connect(category, TQT_SIGNAL(focusIn()), this, splitEditSlot);
#if 0
  // FIXME must deal with the logic that suppressObjectCreation is
  // automatically turned off when the createItem() signal is connected
      if(allowObjectCreation)
        category->setSuppressObjectCreation(true);
#endif
      break;
  }
}

bool TransactionEditor::enterTransactions(TQString& newId, bool askForSchedule, bool suppressBalanceWarnings)
{
  newId = TQString();
  MyMoneyFile* file = MyMoneyFile::instance();

  // make sure to run through all stuff that is tied to 'focusout events'.
  m_regForm->parentWidget()->setFocus();
  TQApplication::eventLoop()->processEvents(TQEventLoop::ExcludeUserInput, 10);

  // we don't need to update our widgets anymore, so we just disconnect the signal
  disconnect(file, TQT_SIGNAL(dataChanged()), this, TQT_SLOT(slotReloadEditWidgets()));

  KMyMoneyRegister::SelectedTransactions::iterator it_t;
  MyMoneyTransaction t;
  bool newTransactionCreated = false;

  // make sure, that only a single new transaction can be created.
  // we need to update m_transactions to contain the new transaction
  // which is then stored in the variable t when we leave the loop.
  // m_transactions will be sent out in finishEdit() and forces
  // the new transaction to be selected in the ledger view

  // collect the transactions to be stored in the engine in a local
  // list first, so that the user has a chance to interrupt the storage
  // process
  TQValueList<MyMoneyTransaction> list;
  bool storeTransactions = true;

  // collect transactions
  for(it_t = m_transactions.begin(); storeTransactions && !newTransactionCreated && it_t != m_transactions.end(); ++it_t) {
    storeTransactions = createTransaction(t, (*it_t).transaction(), (*it_t).split());
    // if the transaction was created successfully, append it to the list
    if(storeTransactions)
      list.append(t);

    // if we created a new transaction keep that in mind
    if(t.id().isEmpty())
      newTransactionCreated = true;
  }

  // if not interrupted by user, continue to store them in the engine
  if(storeTransactions) {
    int i = 0;
    emit statusMsg(i18n("Storing transactions"));
    emit statusProgress(0, list.count());

    MyMoneyFileTransaction ft;

    try {
      TQValueList<MyMoneyTransaction>::iterator it_ts;
      TQMap<TQString, bool> minBalanceEarly;
      TQMap<TQString, bool> minBalanceAbsolute;
      TQMap<TQString, bool> maxCreditEarly;
      TQMap<TQString, bool> maxCreditAbsolute;
      TQMap<TQString, bool> accountIds;

      for(it_ts = list.begin(); it_ts != list.end(); ++it_ts) {
        // if we have a categorization, make sure we remove
        // the 'imported' flag automagically
        if((*it_ts).splitCount() > 1)
          (*it_ts).setImported(false);

        // create information about min and max balances
        TQValueList<MyMoneySplit>::const_iterator it_s;
        for(it_s = (*it_ts).splits().begin(); it_s != (*it_ts).splits().end(); ++it_s) {
          MyMoneyAccount acc = file->account((*it_s).accountId());
          accountIds[acc.id()] = true;
          MyMoneyMoney balance = file->balance(acc.id());
          if(!acc.value("minBalanceEarly").isEmpty()) {
            minBalanceEarly[acc.id()] = balance < MyMoneyMoney(acc.value("minBalanceEarly"));
          }
          if(!acc.value("minBalanceAbsolute").isEmpty()) {
            minBalanceAbsolute[acc.id()] = balance < MyMoneyMoney(acc.value("minBalanceAbsolute"));
            minBalanceEarly[acc.id()] = false;
          }
          if(!acc.value("maxCreditEarly").isEmpty()) {
            maxCreditEarly[acc.id()] = balance < MyMoneyMoney(acc.value("maxCreditEarly"));
          }
          if(!acc.value("maxCreditAbsolute").isEmpty()) {
            maxCreditAbsolute[acc.id()] = balance < MyMoneyMoney(acc.value("maxCreditAbsolute"));
            maxCreditEarly[acc.id()] = false;
          }
        }

        if((*it_ts).id().isEmpty()) {
          bool enter = true;
          if(askForSchedule && (*it_ts).postDate() > TQDate::currentDate()) {
            KGuiItem enterItem;
            TDEIconLoader* il = TDEGlobal::iconLoader();
            KGuiItem enterButton( i18n("&Enter" ),
                    TQIconSet(il->loadIcon("kontact_journal", TDEIcon::Small, TDEIcon::SizeSmall)),
                    i18n("Accepts the entered data and stores it"),
                    i18n("Use this to enter the transaction into the ledger."));
            KGuiItem scheduleButton( i18n("&Schedule" ),
                    TQIconSet(il->loadIcon("kontact_date", TDEIcon::Small, TDEIcon::SizeSmall)),
                    i18n("Accepts the entered data and stores it as schedule"),
                    i18n("Use this to schedule the transaction for later entry into the ledger."));

            enter = KMessageBox::questionYesNo(m_regForm, TQString("<qt>%1</qt>").arg(i18n("The transaction you are about to enter has a post date in the future.<br/><br/>Do you want to enter it in the ledger or add it to the schedules?")), i18n("Dialog caption for 'Enter or schedule' dialog", "Enter or schedule?"), enterButton, scheduleButton, "EnterOrScheduleTransactionInFuture") == KMessageBox::Yes;
          }
          if(enter) {
            // add new transaction
            file->addTransaction(*it_ts);
            // pass the newly assigned id on to the caller
            newId = (*it_ts).id();
            // refresh account and transaction object because they might have changed
            m_account = file->account(m_account.id());
            t = (*it_ts);

            // if a new transaction has a valid number, keep it with the account
            TQString number = (*it_ts).splits()[0].number();
            if(!number.isEmpty()) {
              m_account.setValue("lastNumberUsed", number);
              file->modifyAccount(m_account);
            }

          } else {
            // turn object creation on, so that moving the focus does
            // not screw up the dialog that might be popping up
            emit objectCreation(true);
            emit scheduleTransaction(*it_ts, MyMoneySchedule::OCCUR_ONCE);
            emit objectCreation(false);

            newTransactionCreated = false;
          }

          // send out the post date of this transaction
          emit lastPostDateUsed((*it_ts).postDate());
        } else {
          // modify existing transaction
          file->modifyTransaction(*it_ts);
        }
      }
      emit statusProgress(i++, 0);

      // update m_transactions to contain the newly created transaction so that
      // it is selected as the current one
      // we need to do that before we commit the transaction to the engine
      // as we need it during the update of the views that is caused by committing already.
      if(newTransactionCreated) {
        m_transactions.clear();
        MyMoneySplit s;
        // a transaction w/o a single split should not exist and adding it
        // should throw an exception in MyMoneyFile::addTransaction, but we
        // remain on the save side of things to check for it
        if(t.splitCount() > 0)
          s = t.splits()[0];
        KMyMoneyRegister::SelectedTransaction st(t, s);
        m_transactions.append(st);
      }

      ft.commit();

      // now analyse the balances and spit out warnings to the user
      TQMap<TQString, bool>::const_iterator it_a;

      if(!suppressBalanceWarnings) {
        for(it_a = accountIds.begin(); it_a != accountIds.end(); ++it_a) {
          TQString msg;
          MyMoneyAccount acc = file->account(it_a.key());
          MyMoneyMoney balance = file->balance(acc.id());
          const MyMoneySecurity& sec = file->security(acc.currencyId());
          TQString key;
          key = "minBalanceEarly";
          if(!acc.value(key).isEmpty()) {
            if(minBalanceEarly[acc.id()] == false && balance < MyMoneyMoney(acc.value(key))) {
              msg = TQString("<qt>%1</qt>").arg(i18n("The balance of account <b>%1</b> dropped below the warning balance of %2.").arg(acc.name(), MyMoneyMoney(acc.value(key)).formatMoney(acc, sec)));
            }
          }
          key = "minBalanceAbsolute";
          if(!acc.value(key).isEmpty()) {
            if(minBalanceAbsolute[acc.id()] == false && balance < MyMoneyMoney(acc.value(key))) {
              msg = TQString("<qt>%1</qt>").arg(i18n("The balance of account <b>%1</b> dropped below the minimum balance of %2.").arg(acc.name(), MyMoneyMoney(acc.value(key)).formatMoney(acc, sec)));
            }
          }
          key = "maxCreditEarly";
          if(!acc.value(key).isEmpty()) {
            if(maxCreditEarly[acc.id()] == false && balance < MyMoneyMoney(acc.value(key))) {
              msg = TQString("<qt>%1</qt>").arg(i18n("The balance of account <b>%1</b> dropped below the maximum credit warning limit of %2.").arg(acc.name(), MyMoneyMoney(acc.value(key)).formatMoney(acc, sec)));
            }
          }
          key = "maxCreditAbsolute";
          if(!acc.value(key).isEmpty()) {
            if(maxCreditAbsolute[acc.id()] == false && balance < MyMoneyMoney(acc.value(key))) {
              msg = TQString("<qt>%1</qt>").arg(i18n("The balance of account <b>%1</b> dropped below the maximum credit limit of %2.").arg(acc.name(), MyMoneyMoney(acc.value(key)).formatMoney(acc, sec)));
            }
          }

          if(!msg.isEmpty()) {
            emit balanceWarning(m_regForm, acc, msg);
          }
        }
      }
    } catch(MyMoneyException * e) {
      tqDebug("Unable to store transaction within engine: %s", e->what().latin1());
      delete e;
      newTransactionCreated = false;
    }

    emit statusProgress(-1, -1);
    emit statusMsg(TQString());

  }
  return storeTransactions;
}


StdTransactionEditor::StdTransactionEditor()
{
}

StdTransactionEditor::StdTransactionEditor(TransactionEditorContainer* regForm, KMyMoneyRegister::Transaction* item, const KMyMoneyRegister::SelectedTransactions& list, const TQDate& lastPostDate) :
  TransactionEditor(regForm, item, list, lastPostDate),
  m_inUpdateVat(false)
{
}

StdTransactionEditor::~StdTransactionEditor()
{
  KMyMoneyTransactionForm::TransactionForm* form = dynamic_cast<KMyMoneyTransactionForm::TransactionForm*>(m_regForm);
  if(form) {
    form->enableTabBar(true);
  }
}

bool StdTransactionEditor::eventFilter(TQObject* o, TQEvent* e)
{
  bool rc = TransactionEditor::eventFilter(o, e);

#if 0
  // this is sofar dead code here, as the focus out event for Comboboxes
  // never comes along here. I don't know why (ipwizard - 2009-10-03)
  if((e->type() == TQEvent::FocusOut)
  && (haveWidget("payee") == dynamic_cast<TQWidget*>(o))) {
    // loosing the focus on the payee widget?
    tqDebug("Loosing focus on payee");
    KMyMoneyPayeeCombo* p = dynamic_cast<KMyMoneyPayeeCombo*>(haveWidget("payee"));
    if(!p->selectedItem().isEmpty())
      slotUpdatePayee(p->selectedItem());
  }
#endif
  return rc;
}

void StdTransactionEditor::createEditWidgets(void)
{
  KMyMoneyCategory* account = new KMyMoneyCategory;
  account->setHint(i18n("Account"));
  m_editWidgets["account"] = account;
  connect(account, TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(slotUpdateButtonState()));
  connect(account, TQT_SIGNAL(itemSelected(const TQString&)), this, TQT_SLOT(slotUpdateAccount(const TQString&)));

  KMyMoneyPayeeCombo* payee = new KMyMoneyPayeeCombo;
  payee->setHint(i18n("Payer/Receiver"));
  m_editWidgets["payee"] = payee;
  connect(payee, TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(slotUpdateButtonState()));
  connect(payee, TQT_SIGNAL(createItem(const TQString&, TQString&)), this, TQT_SIGNAL(createPayee(const TQString&, TQString&)));
  connect(payee, TQT_SIGNAL(objectCreation(bool)), this, TQT_SIGNAL(objectCreation(bool)));
  connect(payee, TQT_SIGNAL(itemSelected(const TQString&)), this, TQT_SLOT(slotUpdatePayee(const TQString&)));

  KMyMoneyCategory* category = new KMyMoneyCategory(0, 0, true);
  category->setHint(i18n("Category/Account"));
  m_editWidgets["category"] = category;
  connect(category, TQT_SIGNAL(itemSelected(const TQString&)), this, TQT_SLOT(slotUpdateCategory(const TQString&)));
  connect(category, TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(slotUpdateButtonState()));
  connect(category, TQT_SIGNAL(createItem(const TQString&, TQString&)), this, TQT_SLOT(slotCreateCategory(const TQString&, TQString&)));
  connect(category, TQT_SIGNAL(objectCreation(bool)), this, TQT_SIGNAL(objectCreation(bool)));
  connect(category->splitButton(), TQT_SIGNAL(clicked()), this, TQT_SLOT(slotEditSplits()));
  category->splitButton()->setDisabled(true);

  KTextEdit* memo = new KTextEdit;
  memo->setTabChangesFocus(true);
  m_editWidgets["memo"] = memo;

  bool showNumberField = true;
  switch(m_account.accountType()) {
    case MyMoneyAccount::Savings:
    case MyMoneyAccount::Cash:
    case MyMoneyAccount::Loan:
    case MyMoneyAccount::AssetLoan:
    case MyMoneyAccount::Asset:
    case MyMoneyAccount::Liability:
    case MyMoneyAccount::Equity:
      showNumberField = KMyMoneyGlobalSettings::alwaysShowNrField();
      break;

    case MyMoneyAccount::Income:
    case MyMoneyAccount::Expense:
      showNumberField = false;
      break;

    default:
      break;
  }

  if(showNumberField) {
    kMyMoneyLineEdit* number = new kMyMoneyLineEdit;
    number->setHint(i18n("Number"));
    m_editWidgets["number"] = number;
    connect(number, TQT_SIGNAL(lineChanged(const TQString&)), this, TQT_SLOT(slotNumberChanged(const TQString&)));
    // number->installEventFilter(this);
  }

  m_editWidgets["postdate"] = new kMyMoneyDateInput;
  connect(m_editWidgets["postdate"], TQT_SIGNAL(dateChanged(const TQDate&)), this, TQT_SLOT(slotUpdateButtonState()));

  kMyMoneyEdit* value = new kMyMoneyEdit;
  m_editWidgets["amount"] = value;
  value->setResetButtonVisible(false);
  connect(value, TQT_SIGNAL(valueChanged(const TQString&)), this, TQT_SLOT(slotUpdateAmount(const TQString&)));
  connect(value, TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(slotUpdateButtonState()));

  value = new kMyMoneyEdit;
  m_editWidgets["payment"] = value;
  value->setResetButtonVisible(false);
  connect(value, TQT_SIGNAL(valueChanged(const TQString&)), this, TQT_SLOT(slotUpdatePayment(const TQString&)));
  connect(value, TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(slotUpdateButtonState()));

  value = new kMyMoneyEdit;
  m_editWidgets["deposit"] = value;
  value->setResetButtonVisible(false);
  connect(value, TQT_SIGNAL(valueChanged(const TQString&)), this, TQT_SLOT(slotUpdateDeposit(const TQString&)));
  connect(value, TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(slotUpdateButtonState()));

  KMyMoneyCashFlowCombo* cashflow = new KMyMoneyCashFlowCombo(0, 0, m_account.accountGroup());
  m_editWidgets["cashflow"] = cashflow;
  connect(cashflow, TQT_SIGNAL(directionSelected(KMyMoneyRegister::CashFlowDirection)), this, TQT_SLOT(slotUpdateCashFlow(KMyMoneyRegister::CashFlowDirection)));
  connect(cashflow, TQT_SIGNAL(directionSelected(KMyMoneyRegister::CashFlowDirection)), this, TQT_SLOT(slotUpdateButtonState()));

  KMyMoneyReconcileCombo* reconcile = new KMyMoneyReconcileCombo;
  m_editWidgets["status"] = reconcile;
  connect(reconcile, TQT_SIGNAL(itemSelected(const TQString&)), this, TQT_SLOT(slotUpdateButtonState()));

  KMyMoneyRegister::TQWidgetContainer::iterator it_w;
  for(it_w = m_editWidgets.begin(); it_w != m_editWidgets.end(); ++it_w) {
    (*it_w)->installEventFilter(this);
  }
  // if we don't have more than 1 selected transaction, we don't need
  // the "don't change" item in some of the combo widgets
  if(!isMultiSelection()) {
    reconcile->removeDontCare();
    cashflow->removeDontCare();
  }

  TQLabel* label;
  m_editWidgets["category-label"] = label = new TQLabel(i18n("Category"), 0);
  label->setAlignment(TQt::AlignVCenter | TQt::DontClip);

  // create a copy of tabbar above the form (if we are created for a form)
  KMyMoneyTransactionForm::TransactionForm* form = dynamic_cast<KMyMoneyTransactionForm::TransactionForm*>(m_regForm);
  if(form) {
    form->enableTabBar(false);
    KMyMoneyTransactionForm::TabBar* tabbar = new KMyMoneyTransactionForm::TabBar;
    m_editWidgets["tabbar"] = tabbar;
    tabbar->copyTabs(form->tabBar());
    connect(tabbar, TQT_SIGNAL(tabSelected(int)), this, TQT_SLOT(slotUpdateAction(int)));
  }

  label = new TQLabel(i18n("Date"), 0);
  label->setAlignment(TQt::AlignVCenter | TQt::DontClip);
  m_editWidgets["date-label"] = label;

  label = new TQLabel(i18n("Number"), 0);
  label->setAlignment(TQt::AlignVCenter | TQt::DontClip);
  m_editWidgets["number-label"] = label;

  setupPrecision();
}

void StdTransactionEditor::setupCategoryWidget(TQString& categoryId)
{
  TransactionEditor::setupCategoryWidget(dynamic_cast<KMyMoneyCategory*>(m_editWidgets["category"]), m_splits, categoryId, TQT_SLOT(slotEditSplits()));

  if(m_splits.count() == 1)
    m_shares = m_splits[0].shares();
}

bool StdTransactionEditor::isTransfer(const TQString& accId1, const TQString& accId2) const
{
  if(accId1.isEmpty() || accId2.isEmpty())
    return false;

  return MyMoneyFile::instance()->account(accId1).isIncomeExpense() == MyMoneyFile::instance()->account(accId2).isIncomeExpense();
}

void StdTransactionEditor::loadEditWidgets(KMyMoneyRegister::Action action)
{
  // don't kick off VAT processing from here
  m_inUpdateVat = true;

  TQMap<TQString, TQWidget*>::const_iterator it_w;
  TQWidget* w;
  AccountSet aSet;

  // load the account widget
  KMyMoneyCategory* account = dynamic_cast<KMyMoneyCategory*>(haveWidget("account"));
  if(account) {
    aSet.addAccountGroup(MyMoneyAccount::Asset);
    aSet.addAccountGroup(MyMoneyAccount::Liability);
    aSet.removeAccountType(MyMoneyAccount::AssetLoan);
    aSet.removeAccountType(MyMoneyAccount::CertificateDep);
    aSet.removeAccountType(MyMoneyAccount::Investment);
    aSet.removeAccountType(MyMoneyAccount::Stock);
    aSet.removeAccountType(MyMoneyAccount::MoneyMarket);
    aSet.removeAccountType(MyMoneyAccount::Loan);
    aSet.load(account->selector());
    account->completion()->setSelected(m_account.id());
    account->slotItemSelected(m_account.id());
  }

  // load the payee widget
  KMyMoneyPayeeCombo* payee = dynamic_cast<KMyMoneyPayeeCombo*>(m_editWidgets["payee"]);
  payee->loadPayees(MyMoneyFile::instance()->payeeList());

  // load the category widget
  KMyMoneyCategory* category = dynamic_cast<KMyMoneyCategory*>(m_editWidgets["category"]);
  disconnect(category, TQT_SIGNAL(focusIn()), this, TQT_SLOT(slotEditSplits()));

  // check if the current transaction has a reference to an equity account
  bool haveEquityAccount = false;
  TQValueList<MyMoneySplit>::const_iterator it_s;
  for(it_s = m_transaction.splits().begin(); !haveEquityAccount && it_s != m_transaction.splits().end(); ++it_s) {
    MyMoneyAccount acc = MyMoneyFile::instance()->account((*it_s).accountId());
    if(acc.accountType() == MyMoneyAccount::Equity)
      haveEquityAccount = true;
  }

  aSet.clear();
  aSet.addAccountGroup(MyMoneyAccount::Asset);
  aSet.addAccountGroup(MyMoneyAccount::Liability);
  aSet.addAccountGroup(MyMoneyAccount::Income);
  aSet.addAccountGroup(MyMoneyAccount::Expense);
  if(KMyMoneyGlobalSettings::expertMode() || haveEquityAccount)
    aSet.addAccountGroup(MyMoneyAccount::Equity);

  aSet.removeAccountType(MyMoneyAccount::CertificateDep);
  aSet.removeAccountType(MyMoneyAccount::Investment);
  aSet.removeAccountType(MyMoneyAccount::Stock);
  aSet.removeAccountType(MyMoneyAccount::MoneyMarket);
  aSet.load(category->selector());

  // if an account is specified then remove it from the widget so that the user
  // cannot create a transfer with from and to account being the same account
  if(!m_account.id().isEmpty())
    category->selector()->removeItem(m_account.id());

  if(!isMultiSelection()) {
    dynamic_cast<KTextEdit*>(m_editWidgets["memo"])->setText(m_split.memo());
    if(m_transaction.postDate().isValid())
      dynamic_cast<kMyMoneyDateInput*>(m_editWidgets["postdate"])->setDate(m_transaction.postDate());
    else if(m_lastPostDate.isValid())
      dynamic_cast<kMyMoneyDateInput*>(m_editWidgets["postdate"])->setDate(m_lastPostDate);
    else
      dynamic_cast<kMyMoneyDateInput*>(m_editWidgets["postdate"])->setDate(TQDate::currentDate());

    if((w = haveWidget("number")) != 0) {
      dynamic_cast<kMyMoneyLineEdit*>(w)->loadText(m_split.number());
      if(m_transaction.id().isEmpty()                            // new transaction
      && dynamic_cast<kMyMoneyLineEdit*>(w)->text().isEmpty()    // no number filled in
      && m_account.accountType() == MyMoneyAccount::Checkings    // checkings account
      && KMyMoneyGlobalSettings::autoIncCheckNumber()) {         // and auto inc number turned on?
        assignNextNumber();
      }
    }
    dynamic_cast<KMyMoneyReconcileCombo*>(m_editWidgets["status"])->setState(m_split.reconcileFlag());

    TQString payeeId = m_split.payeeId();
    if(!payeeId.isEmpty()) {
      payee->setSelectedItem(payeeId);
    }

    m_splits.clear();
    if(m_transaction.splitCount() < 2) {
      category->completion()->setSelected(TQString());
    } else {
      TQValueList<MyMoneySplit>::const_iterator it_s;
      for(it_s = m_transaction.splits().begin(); it_s != m_transaction.splits().end(); ++it_s) {
        if((*it_s) == m_split)
          continue;
        m_splits.append(*it_s);
      }
    }
    TQString categoryId;
    setupCategoryWidget(categoryId);

    if((w = haveWidget("cashflow")) != 0) {
      KMyMoneyCashFlowCombo* cashflow = dynamic_cast<KMyMoneyCashFlowCombo*>(w);
      cashflow->setDirection(m_split.value().isNegative() ? KMyMoneyRegister::Payment : KMyMoneyRegister::Deposit);
    }

    if((w = haveWidget("category-label")) != 0) {
      TQLabel *categoryLabel = dynamic_cast<TQLabel*>(w);
      if(isTransfer(m_split.accountId(), categoryId)) {
        if(m_split.value().isPositive())
          categoryLabel->setText(i18n("Transfer from"));
        else
          categoryLabel->setText(i18n("Transfer to"));
      }
    }

    MyMoneyMoney value = m_split.shares();

    if(haveWidget("deposit")) {
      if(m_split.shares().isNegative()) {
        dynamic_cast<kMyMoneyEdit*>(m_editWidgets["deposit"])->loadText("");
        dynamic_cast<kMyMoneyEdit*>(m_editWidgets["payment"])->setValue(value.abs());
      } else {
        dynamic_cast<kMyMoneyEdit*>(m_editWidgets["deposit"])->setValue(value.abs());
        dynamic_cast<kMyMoneyEdit*>(m_editWidgets["payment"])->loadText("");
      }
    }
    if((w = haveWidget("amount")) != 0) {
      dynamic_cast<kMyMoneyEdit*>(w)->setValue(value.abs());
    }

    slotUpdateCategory(categoryId);

    // try to preset for specific action if a new transaction is being started
    if(m_transaction.id().isEmpty()) {
      if((w = haveWidget("category-label")) != 0) {
        TabBar* tabbar = dynamic_cast<TabBar*>(haveWidget("tabbar"));
        if(action == KMyMoneyRegister::ActionNone) {
          if(tabbar) {
            action = static_cast<KMyMoneyRegister::Action>(tabbar->currentTab());
          }
        }
        if(action != KMyMoneyRegister::ActionNone) {
          TQLabel *categoryLabel = dynamic_cast<TQLabel*>(w);
          if(action == KMyMoneyRegister::ActionTransfer) {
            if(m_split.value().isPositive())
              categoryLabel->setText(i18n("Transfer from"));
            else
              categoryLabel->setText(i18n("Transfer to"));
          }
          if((w = haveWidget("cashflow")) != 0) {
            KMyMoneyCashFlowCombo* cashflow = dynamic_cast<KMyMoneyCashFlowCombo*>(w);
            if(action == KMyMoneyRegister::ActionDeposit || (action == KMyMoneyRegister::ActionTransfer && m_split.value().isPositive()))
              cashflow->setDirection(KMyMoneyRegister::Deposit);
            else
              cashflow->setDirection(KMyMoneyRegister::Payment);
          }
          if(tabbar) {
            tabbar->setCurrentTab(action);
          }
        }
      }
    } else {
      TabBar* tabbar = dynamic_cast<TabBar*>(haveWidget("tabbar"));
      if(tabbar) {
        if(!isTransfer(m_split.accountId(), categoryId)) {
          tabbar->setCurrentTab(m_split.value().isNegative() ? KMyMoneyRegister::ActionWithdrawal : KMyMoneyRegister::ActionDeposit);
        } else {
          tabbar->setCurrentTab(KMyMoneyRegister::ActionTransfer);
        }
      }
    }

  } else {
    dynamic_cast<kMyMoneyDateInput*>(m_editWidgets["postdate"])->loadDate(TQDate());
    dynamic_cast<KMyMoneyReconcileCombo*>(m_editWidgets["status"])->setState(MyMoneySplit::Unknown);
    if(haveWidget("deposit")) {
      dynamic_cast<kMyMoneyEdit*>(m_editWidgets["deposit"])->loadText("");
      dynamic_cast<kMyMoneyEdit*>(m_editWidgets["deposit"])->setAllowEmpty();
      dynamic_cast<kMyMoneyEdit*>(m_editWidgets["payment"])->loadText("");
      dynamic_cast<kMyMoneyEdit*>(m_editWidgets["payment"])->setAllowEmpty();
    }
    if((w = haveWidget("amount")) != 0) {
      dynamic_cast<kMyMoneyEdit*>(w)->loadText("");
      dynamic_cast<kMyMoneyEdit*>(w)->setAllowEmpty();
    }

    if((w = haveWidget("cashflow")) != 0) {
      KMyMoneyCashFlowCombo* cashflow = dynamic_cast<KMyMoneyCashFlowCombo*>(w);
      cashflow->setDirection(KMyMoneyRegister::Unknown);
    }
    if((w = haveWidget("tabbar")) != 0) {
      w->setEnabled(false);
    }

    category->completion()->setSelected(TQString());
  }

  // allow kick off VAT processing again
  m_inUpdateVat = false;
}

TQWidget* StdTransactionEditor::firstWidget(void) const
{
  TQWidget* w = 0;
  if(m_initialAction != KMyMoneyRegister::ActionNone)
    w = haveWidget("payee");
  return w;
}

void StdTransactionEditor::slotReloadEditWidgets(void)
{
  // reload category widget
  KMyMoneyCategory* category = dynamic_cast<KMyMoneyCategory*>(m_editWidgets["category"]);
  TQString categoryId = category->selectedItem();

  AccountSet aSet;
  aSet.addAccountGroup(MyMoneyAccount::Asset);
  aSet.addAccountGroup(MyMoneyAccount::Liability);
  aSet.addAccountGroup(MyMoneyAccount::Income);
  aSet.addAccountGroup(MyMoneyAccount::Expense);
  if(KMyMoneyGlobalSettings::expertMode())
    aSet.addAccountGroup(MyMoneyAccount::Equity);
  aSet.load(category->selector());

  // if an account is specified then remove it from the widget so that the user
  // cannot create a transfer with from and to account being the same account
  if(!m_account.id().isEmpty())
    category->selector()->removeItem(m_account.id());

  if(!categoryId.isEmpty())
    category->setSelectedItem(categoryId);


  // reload payee widget
  KMyMoneyPayeeCombo* payee = dynamic_cast<KMyMoneyPayeeCombo*>(m_editWidgets["payee"]);
  TQString payeeId = payee->selectedItem();

  payee->loadPayees(MyMoneyFile::instance()->payeeList());

  if(!payeeId.isEmpty()) {
    payee->setSelectedItem(payeeId);
  }
}

void StdTransactionEditor::slotUpdatePayee(const TQString& payeeId)
{
  // we have a new payee assigned to this transaction.
  // retrieve some information about the state of the category widget
  KMyMoneyCategory* category = dynamic_cast<KMyMoneyCategory*>(m_editWidgets["category"]);
  TQStringList list;
  category->selectedItems(list);

  // If payee has associated default account (category), set that now if
  // category is not filled
  const MyMoneyPayee& payeeObj = MyMoneyFile::instance()->payee(payeeId);
  if (payeeObj.defaultAccountEnabled() && list.isEmpty()) {
    KMyMoneyCategory* category = dynamic_cast<KMyMoneyCategory*>(m_editWidgets["category"]);
    category->slotItemSelected(payeeObj.defaultAccountId());
    return;
  }

  // in case there is no category assigned, no value entered and no
  // memo available, we search for the last transaction of this payee
  // in the account.
  if(m_transaction.id().isEmpty()
  && m_splits.count() == 0
  && KMyMoneyGlobalSettings::autoFillTransaction() != 0
  && list.isEmpty()) {
    // check if memo is empty
    KTextEdit* memo = dynamic_cast<KTextEdit*>(m_editWidgets["memo"]);
    if(memo && !memo->text().isEmpty())
      return;

    // check if all value fields are empty
    kMyMoneyEdit* amount;
    TQStringList fields;
    fields << "amount" << "payment" << "deposit";
    TQStringList::const_iterator it_f;
    for(it_f = fields.begin(); it_f != fields.end(); ++it_f) {
      amount = dynamic_cast<kMyMoneyEdit*>(haveWidget(*it_f));
      if(amount && !amount->value().isZero())
        return;
    }

#if 0
    // Tony mentioned, that autofill does not work when he changed the date. Well,
    // that certainly makes sense when you enter transactions in register mode as
    // opposed to form mode, because the date field is located prior to the date
    // field in the tab order of the widgets and the user might have already
    // changed it.
    //
    // So I commented out the code that checks the date but left it in for reference.
    // (ipwizard, 2008-04-07)

    // check if date has been altered by user
    kMyMoneyDateInput* postDate = dynamic_cast<kMyMoneyDateInput*>(m_editWidgets["postdate"]);
    if((m_lastPostDate.isValid() && (postDate->date() != m_lastPostDate))
    || (!m_lastPostDate.isValid() && (postDate->date() != TQDate::currentDate())))
      return;
#endif

    // if we got here, we have to autofill
    autoFill(payeeId);
  }
}

MyMoneyMoney StdTransactionEditor::shares(const MyMoneyTransaction& t) const
{
  MyMoneyMoney result;
  TQValueList<MyMoneySplit>::const_iterator it_s;
  for(it_s = t.splits().begin(); it_s != t.splits().end(); ++it_s) {
    if((*it_s).accountId() == m_account.id()) {
      result += (*it_s).shares();
    }
  }
  return result;
}

struct uniqTransaction {
  const MyMoneyTransaction* t;
  int   cnt;
};

void StdTransactionEditor::autoFill(const TQString& payeeId)
{
  TQValueList<TQPair<MyMoneyTransaction, MyMoneySplit> >  list;
  MyMoneyTransactionFilter filter(m_account.id());
  filter.addPayee(payeeId);
  MyMoneyFile::instance()->transactionList(list, filter);
  if(!list.empty()) {
    // ok, we found at least one previous transaction. now we clear out
    // what we have collected so far and add those splits from
    // the previous transaction.
    TQValueList<TQPair<MyMoneyTransaction, MyMoneySplit> >::const_iterator  it_t;
    TQMap<TQString, struct uniqTransaction> uniqList;

    // collect the transactions and see if we have any duplicates
    for(it_t = list.begin(); it_t != list.end(); ++it_t) {
      TQString key = (*it_t).first.accountSignature();
      int cnt = 0;
      TQMap<TQString, struct uniqTransaction>::iterator it_u;
      do {
        TQString ukey = TQString("%1-%2").arg(key).arg(cnt);
        it_u = uniqList.find(ukey);
        if(it_u == uniqList.end()) {
          uniqList[ukey].t = &((*it_t).first);
          uniqList[ukey].cnt = 1;
        } else if (KMyMoneyGlobalSettings::autoFillTransaction() == 1) {
          // we already have a transaction with this signature. we must
          // now check, if we should really treat it as a duplicate according
          // to the value comparison delta.
          MyMoneyMoney s1 = shares(*((*it_u).t));
          MyMoneyMoney s2 = shares((*it_t).first);
          if(s2.abs() > s1.abs()) {
            MyMoneyMoney t(s1);
            s1 = s2;
            s2 = t;
          }
          MyMoneyMoney diff;
          if(s2.isZero())
            diff = s1.abs();
          else
            diff = ((s1 - s2) / s2).convert(10000);
          if(diff.isPositive() && diff <= MyMoneyMoney(KMyMoneyGlobalSettings::autoFillDifference(),100)) {
            uniqList[ukey].t = &((*it_t).first);
            break;    // end while loop
          }
        } else if (KMyMoneyGlobalSettings::autoFillTransaction() == 2) {
          (*it_u).cnt++;
          break;      // end while loop
        }
        ++cnt;
      } while(it_u != uniqList.end());

    }

    MyMoneyTransaction t;
    if (KMyMoneyGlobalSettings::autoFillTransaction() != 2) {
#if 0
      // I removed this code to allow cancellation of an autofill if
      // it does not match even if there is only a single matching
      // transaction for the payee in question. In case, we want to revert
      // to the old behavior, don't forget to uncomment the closing
      // brace further down in the code as well. (ipwizard 2009-01-16)
      if(uniqList.count() == 1) {
        t = list.last().first;
      } else {
#endif
        TDESelectTransactionsDlg dlg(m_account, m_regForm);
        dlg.setCaption(i18n("Select autofill transaction"));

        TQMap<TQString, struct uniqTransaction>::const_iterator it_u;
        for(it_u = uniqList.begin(); it_u != uniqList.end(); ++it_u) {
          dlg.addTransaction(*(*it_u).t);
        }

        // setup sort order
        dlg.m_register->setSortOrder("1,-9,-4");
        // sort the transactions according to the sort setting
        dlg.m_register->sortItems();

        // and select the last item
        if(dlg.m_register->lastItem())
          dlg.m_register->selectItem(dlg.m_register->lastItem());

        if(dlg.exec() == TQDialog::Accepted) {
          t = dlg.transaction();
        }
#if 0
      }
#endif
    } else {
      int maxCnt = 0;
      TQMap<TQString, struct uniqTransaction>::const_iterator it_u;
      for(it_u = uniqList.begin(); it_u != uniqList.end(); ++it_u) {
        if((*it_u).cnt > maxCnt) {
          t = *(*it_u).t;
          maxCnt = (*it_u).cnt;
        }
      }
    }

    if(t != MyMoneyTransaction()) {
      m_transaction.removeSplits();
      m_split = MyMoneySplit();
      MyMoneySplit otherSplit;
      TQValueList<MyMoneySplit>::ConstIterator it;
      for(it = t.splits().begin(); it != t.splits().end(); ++it) {
        MyMoneySplit s(*it);
        s.setReconcileFlag(MyMoneySplit::NotReconciled);
        s.setReconcileDate(TQDate());
        s.clearId();
        s.setBankID(TQString());
        // older versions of KMyMoney used to set the action
        // we don't need this anymore
        if(s.action() != MyMoneySplit::ActionAmortization
        && s.action() != MyMoneySplit::ActionInterest)  {
          s.setAction(TQString());
        }

        // FIXME update check number. The old comment contained
        //
        // <quote>
        // If a check number is already specified by the user it is
        // used. If the input field is empty and the previous transaction
        // contains a checknumber, the next usuable check no will be assigned
        // to the transaction.
        // </quote>

        kMyMoneyLineEdit* editNr = dynamic_cast<kMyMoneyLineEdit*>(haveWidget("number"));
        if(editNr && !editNr->text().isEmpty()) {
          s.setNumber(editNr->text());
        } else if(!s.number().isEmpty()) {
          s.setNumber(KMyMoneyUtils::nextCheckNumber(m_account));
        }

        // if the transaction has exactly two splits, remove
        // the memo text of the split that does not reference
        // the current account. This allows the user to change
        // the autofilled memo text which will then also be used
        // in this split. See createTransaction() for this logic.
        if(s.accountId() != m_account.id() && t.splitCount() == 2)
          s.setMemo(TQString());

        m_transaction.addSplit(s);
        if(s.accountId() == m_account.id() && m_split == MyMoneySplit()) {
          m_split = s;
        } else {
          otherSplit = s;
        }
      }

      // make sure to extract the right action
      KMyMoneyRegister::Action action;
      action = m_split.shares().isNegative() ? KMyMoneyRegister::ActionWithdrawal : KMyMoneyRegister::ActionDeposit;

      if(m_transaction.splitCount() == 2) {
        MyMoneyAccount acc = MyMoneyFile::instance()->account(otherSplit.accountId());
        if(acc.isAssetLiability())
          action = KMyMoneyRegister::ActionTransfer;
      }

      // now setup the widgets with the new data but keep the date
      TQDate date = dynamic_cast<kMyMoneyDateInput*>(m_editWidgets["postdate"])->date();
      loadEditWidgets(action);
      dynamic_cast<kMyMoneyDateInput*>(m_editWidgets["postdate"])->setDate(date);
    }
  }

  // focus jumps into the category field
  TQWidget* w;
  if((w = haveWidget("payee")) != 0) {
    w->setFocus();
  }
}

void StdTransactionEditor::slotUpdateAction(int action)
{
  TabBar* tabbar = dynamic_cast<TabBar*>(haveWidget("tabbar"));
  if(tabbar) {
    TQLabel* categoryLabel = dynamic_cast<TQLabel*>(haveWidget("category-label"));
    KMyMoneyCashFlowCombo* cashflow = dynamic_cast<KMyMoneyCashFlowCombo*>(m_editWidgets["cashflow"]);
    switch(action) {
      case KMyMoneyRegister::ActionDeposit:
        categoryLabel->setText(i18n("Category"));
        cashflow->setDirection(KMyMoneyRegister::Deposit);
        break;
      case KMyMoneyRegister::ActionTransfer:
        categoryLabel->setText(i18n("Transfer from"));
        slotUpdateCashFlow(cashflow->direction());
        break;
      case KMyMoneyRegister::ActionWithdrawal:
        categoryLabel->setText(i18n("Category"));
        cashflow->setDirection(KMyMoneyRegister::Payment);
        break;
    }
  }
}

void StdTransactionEditor::slotUpdateCashFlow(KMyMoneyRegister::CashFlowDirection dir)
{
  TQLabel* categoryLabel = dynamic_cast<TQLabel*>(haveWidget("category-label"));

  // tqDebug("Update cashflow to %d", dir);
  if(categoryLabel) {
    TabBar* tabbar = dynamic_cast<TabBar*>(haveWidget("tabbar"));
    if(categoryLabel->text() != i18n("Category")) {
      tabbar->setCurrentTab(KMyMoneyRegister::ActionTransfer);
      if(dir == KMyMoneyRegister::Deposit) {
        categoryLabel->setText(i18n("Transfer from"));
      } else {
        categoryLabel->setText(i18n("Transfer to"));
      }
    } else {
      if(dir == KMyMoneyRegister::Deposit)
        tabbar->setCurrentTab(KMyMoneyRegister::ActionDeposit);
      else
        tabbar->setCurrentTab(KMyMoneyRegister::ActionWithdrawal);
    }
  }
}

void StdTransactionEditor::slotUpdateCategory(const TQString& id)
{
  TQLabel *categoryLabel = dynamic_cast<TQLabel*>(haveWidget("category-label"));
  // tqDebug("Update category to %s", id.data());

  if(categoryLabel) {
    TabBar* tabbar = dynamic_cast<TabBar*>(haveWidget("tabbar"));
    kMyMoneyEdit* amount = dynamic_cast<kMyMoneyEdit*>(m_editWidgets["amount"]);
    MyMoneyMoney val = amount->value();

    if(categoryLabel->text() == i18n("Transfer from")) {
      val = -val;
    } else {
      val = val.abs();
    }

    if(tabbar) {
      tabbar->tab(KMyMoneyRegister::ActionTransfer)->setEnabled(true);
      tabbar->tab(KMyMoneyRegister::ActionDeposit)->setEnabled(true);
      tabbar->tab(KMyMoneyRegister::ActionWithdrawal)->setEnabled(true);
    }

    if(!id.isEmpty()) {
      MyMoneyAccount acc = MyMoneyFile::instance()->account(id);
      if(acc.isAssetLiability()
      || acc.accountGroup() == MyMoneyAccount::Equity) {
        if(tabbar) {
          tabbar->tab(KMyMoneyRegister::ActionDeposit)->setEnabled(false);
          tabbar->tab(KMyMoneyRegister::ActionWithdrawal)->setEnabled(false);
        }
        // only change the label if an amount is already filled in
        if(!val.isZero()) {
          if(val.isNegative())
            categoryLabel->setText(i18n("Transfer from"));
          else
            categoryLabel->setText(i18n("Transfer to"));
        }
      } else {
        if(tabbar)
          tabbar->tab(KMyMoneyRegister::ActionTransfer)->setEnabled(false);
        categoryLabel->setText(i18n("Category"));
      }
      updateAmount(val.abs());
    } else {
      KMyMoneyCategory* category = dynamic_cast<KMyMoneyCategory*>(m_editWidgets["category"]);
      if(!category->currentText().isEmpty() && tabbar)
        tabbar->tab(KMyMoneyRegister::ActionTransfer)->setEnabled(false);
      categoryLabel->setText(i18n("Category"));
    }
    if(tabbar)
      tabbar->update();
  }
  updateVAT(false);
}

void StdTransactionEditor::slotUpdatePayment(const TQString& txt)
{
  MyMoneyMoney val(txt);

  if(val.isNegative()) {
    dynamic_cast<kMyMoneyEdit*>(m_editWidgets["deposit"])->setValue(val.abs());
    dynamic_cast<kMyMoneyEdit*>(m_editWidgets["payment"])->clearText();
  } else {
    dynamic_cast<kMyMoneyEdit*>(m_editWidgets["deposit"])->clearText();
  }
  updateVAT();
}

void StdTransactionEditor::slotUpdateDeposit(const TQString& txt)
{
  MyMoneyMoney val(txt);
  if(val.isNegative()) {
    dynamic_cast<kMyMoneyEdit*>(m_editWidgets["payment"])->setValue(val.abs());
    dynamic_cast<kMyMoneyEdit*>(m_editWidgets["deposit"])->clearText();
  } else {
    dynamic_cast<kMyMoneyEdit*>(m_editWidgets["payment"])->clearText();
  }
  updateVAT();
}

void StdTransactionEditor::slotUpdateAmount(const TQString& txt)
{
  // tqDebug("Update amount to %s", txt.data());
  MyMoneyMoney val(txt);
  updateAmount(val);
  updateVAT(true);
}

void StdTransactionEditor::updateAmount(const MyMoneyMoney& val)
{
  TQLabel *categoryLabel = dynamic_cast<TQLabel*>(haveWidget("category-label"));
  if(categoryLabel) {
    KMyMoneyCashFlowCombo* cashflow = dynamic_cast<KMyMoneyCashFlowCombo*>(m_editWidgets["cashflow"]);

    if(val.isNegative()) {
      if(categoryLabel->text() != i18n("Category")) {
        if(categoryLabel->text() == i18n("Transfer from")) {
          categoryLabel->setText(i18n("Transfer to"));
          cashflow->setDirection(KMyMoneyRegister::Payment);
        } else {
          categoryLabel->setText(i18n("Transfer from"));
          cashflow->setDirection(KMyMoneyRegister::Deposit);
        }
      } else {
        if(cashflow->direction() == KMyMoneyRegister::Deposit)
          cashflow->setDirection(KMyMoneyRegister::Payment);
        else
          cashflow->setDirection(KMyMoneyRegister::Deposit);
        slotUpdateCashFlow(cashflow->direction());
      }
      dynamic_cast<kMyMoneyEdit*>(m_editWidgets["amount"])->setValue(val.abs());
    }
  }
}

void StdTransactionEditor::updateVAT(bool amountChanged)
{
  // make sure that we don't do this recursively
  if(m_inUpdateVat)
    return;

  // we don't do anything if we have multiple transactions selected
  if(isMultiSelection())
    return;

  // if auto vat assignment for this account is turned off
  // we don't care about taxes
  if(m_account.value("NoVat") == "Yes")
    return;

  // more splits than category and tax are not supported
  if(m_splits.count() > 2)
    return;

  // in order to do anything, we need an amount
  MyMoneyMoney amount, newAmount;
  bool amountOk;
  amount = amountFromWidget(&amountOk);
  if(!amountOk)
    return;

  // If the transaction has a tax and a category split, remove the tax split
  if(m_splits.count() == 2) {
    newAmount = removeVatSplit();
    if(m_splits.count() == 2)  // not removed?
      return;

  } else {
    // otherwise, we need a category
    KMyMoneyCategory* category = dynamic_cast<KMyMoneyCategory*>(m_editWidgets["category"]);
    if(category->selectedItem().isEmpty())
      return;

    // if no VAT account is associated with this category/account, then we bail out
    MyMoneyAccount cat = MyMoneyFile::instance()->account(category->selectedItem());
    if(cat.value("VatAccount").isEmpty())
      return;

    newAmount = amount;
  }

  // seems we have everything we need
  if(amountChanged)
    newAmount = amount;

  MyMoneyTransaction transaction;
  if(createTransaction(transaction, m_transaction, m_split)) {
    if(addVatSplit(transaction, newAmount)) {
      m_transaction = transaction;
      m_split = m_transaction.splits()[0];

      loadEditWidgets();

      // if we made this a split transaction, then move the
      // focus to the memo field
      if(tqApp->focusWidget() == haveWidget("category")) {
        TQWidget* w = haveWidget("memo");
        if(w)
          w->setFocus();
      }
    }
  }
}

bool StdTransactionEditor::addVatSplit(MyMoneyTransaction& tr, const MyMoneyMoney& amount)
{
  if(tr.splitCount() != 2)
    return false;

  bool rc = false;
  MyMoneyFile* file = MyMoneyFile::instance();

  try {
    MyMoneySplit cat;  // category
    MyMoneySplit tax;  // tax

    // extract the category split from the transaction
    MyMoneyAccount category = file->account(tr.splitByAccount(m_account.id(), false).accountId());
    if(category.value("VatAccount").isEmpty())
      return false;
    MyMoneyAccount vatAcc = file->account(category.value("VatAccount").latin1());
    const MyMoneySecurity& asec = file->security(m_account.currencyId());
    const MyMoneySecurity& csec = file->security(category.currencyId());
    const MyMoneySecurity& vsec = file->security(vatAcc.currencyId());
    if(asec.id() != csec.id() || asec.id() != vsec.id()) {
      tqDebug("Auto VAT assignment only works if all three accounts use the same currency.");
      return false;
    }

    MyMoneyMoney vatRate(vatAcc.value("VatRate"));
    MyMoneyMoney gv, nv;    // gross value, net value
    int fract = m_account.fraction();

    if(!vatRate.isZero()) {

      tax.setAccountId(vatAcc.id());

      // tqDebug("vat amount is '%s'", category.value("VatAmount").latin1());
      if(category.value("VatAmount").lower() != TQString("net")) {
        // split value is the gross value
        gv = amount;
        nv = gv / (MyMoneyMoney(1,1) + vatRate);
        MyMoneySplit catSplit = tr.splitByAccount(m_account.id(), false);
        catSplit.setShares(-nv.convert(fract));
        catSplit.setValue(catSplit.shares());
        tr.modifySplit(catSplit);

      } else {
        // split value is the net value
        nv = amount;
        gv = nv * (MyMoneyMoney(1,1) + vatRate);
        MyMoneySplit accSplit = tr.splitByAccount(m_account.id());
        accSplit.setValue(gv.convert(fract));
        accSplit.setShares(accSplit.value());
        tr.modifySplit(accSplit);
      }

      tax.setValue(-(gv - nv).convert(fract));
      tax.setShares(tax.value());
      tr.addSplit(tax);
      rc = true;
    }
  } catch(MyMoneyException *e) {
    delete e;
  }
  return rc;
}

MyMoneyMoney StdTransactionEditor::removeVatSplit(void)
{
  // we only deal with splits that have three splits
  if(m_splits.count() != 2)
    return amountFromWidget();

  MyMoneySplit c; // category split
  MyMoneySplit t; // tax split

  bool netValue = false;
  TQValueList<MyMoneySplit>::const_iterator it_s;
  for(it_s = m_splits.begin(); it_s != m_splits.end(); ++it_s) {
    MyMoneyAccount acc = MyMoneyFile::instance()->account((*it_s).accountId());
    if(!acc.value("VatAccount").isEmpty()) {
      netValue = (acc.value("VatAmount").lower() == "net");
      c = (*it_s);
    } else if(!acc.value("VatRate").isEmpty()) {
      t = (*it_s);
    }
  }

  // bail out if not all splits are setup
  if(c.id().isEmpty() || t.id().isEmpty())
    return amountFromWidget();

  MyMoneyMoney amount;
  // reduce the splits
  if(netValue) {
    amount = -c.shares();
  } else {
    amount = -(c.shares() + t.shares());
  }

  // remove tax split from the list, ...
  m_splits.clear();
  m_splits.append(c);

  // ... make sure that the widget is updated ...
  // block the signals to avoid popping up the split editor dialog
  // for nothing
  m_editWidgets["category"]->blockSignals(true);
  TQString id;
  setupCategoryWidget(id);
  m_editWidgets["category"]->blockSignals(false);

  // ... and return the updated amount
  return amount;
}

bool StdTransactionEditor::isComplete(TQString& reason) const
{
  reason = TQString();
  // reason.clear();  // for TQt4

  TQMap<TQString, TQWidget*>::const_iterator it_w;
  kMyMoneyDateInput* postDate = dynamic_cast<kMyMoneyDateInput*>(m_editWidgets["postdate"]);
  if(postDate) {
    postDate->markAsBadDate();
    TQToolTip::remove(postDate);
    if(postDate->date().isValid() && (postDate->date() < m_account.openingDate())) {
      postDate->markAsBadDate(true, KMyMoneyGlobalSettings::listNegativeValueColor());
      reason = i18n("Cannot enter transaction with postdate prior to account's opening date.");
      TQToolTip::add(postDate, reason);
      return false;
    }
  }

  for(it_w = m_editWidgets.begin(); it_w != m_editWidgets.end(); ++it_w) {
    KMyMoneyPayeeCombo* payee = dynamic_cast<KMyMoneyPayeeCombo*>(*it_w);
    KMyMoneyCategory* category = dynamic_cast<KMyMoneyCategory*>(*it_w);
    kMyMoneyEdit* amount = dynamic_cast<kMyMoneyEdit*>(*it_w);
    KMyMoneyReconcileCombo* reconcile = dynamic_cast<KMyMoneyReconcileCombo*>(*it_w);
    KMyMoneyCashFlowCombo* cashflow = dynamic_cast<KMyMoneyCashFlowCombo*>(*it_w);

    if(payee && !(payee->currentText().isEmpty()))
      break;

    if(category && !category->lineEdit()->text().isEmpty())
      break;

    if(amount && !(amount->value().isZero()))
      break;

    // the following two widgets are only checked if we are editing multiple transactions
    if(isMultiSelection()) {
      if(reconcile && reconcile->state() != MyMoneySplit::Unknown)
        break;

      if(cashflow && cashflow->direction() != KMyMoneyRegister::Unknown)
        break;
    }
  }
  return it_w != m_editWidgets.end();
}

void StdTransactionEditor::slotCreateCategory(const TQString& name, TQString& id)
{
  MyMoneyAccount acc, parent;
  acc.setName(name);

  KMyMoneyCashFlowCombo* cashflow = dynamic_cast<KMyMoneyCashFlowCombo*>(haveWidget("cashflow"));
  if(cashflow) {
    // form based input
    if(cashflow->direction() == KMyMoneyRegister::Deposit)
      parent = MyMoneyFile::instance()->income();
    else
      parent = MyMoneyFile::instance()->expense();

  } else if(haveWidget("deposit")) {
    // register based input
    kMyMoneyEdit* deposit = dynamic_cast<kMyMoneyEdit*>(m_editWidgets["deposit"]);
    if(deposit->value().isPositive())
      parent = MyMoneyFile::instance()->income();
    else
      parent = MyMoneyFile::instance()->expense();

  } else
    parent = MyMoneyFile::instance()->expense();

  // TODO extract possible first part of a hierarchy and check if it is one
  // of our top categories. If so, remove it and select the parent
  // according to this information.

  emit createCategory(acc, parent);

  // return id
  id = acc.id();
}

int StdTransactionEditor::slotEditSplits(void)
{
  int rc = TQDialog::Rejected;

  if(!m_openEditSplits) {
    // only get in here in a single instance
    m_openEditSplits = true;

    // force focus change to update all data
    TQWidget* w = dynamic_cast<KMyMoneyCategory*>(m_editWidgets["category"])->splitButton();
    if(w)
      w->setFocus();

    kMyMoneyEdit* amount = dynamic_cast<kMyMoneyEdit*>(haveWidget("amount"));
    kMyMoneyEdit* deposit = dynamic_cast<kMyMoneyEdit*>(haveWidget("deposit"));
    kMyMoneyEdit* payment = dynamic_cast<kMyMoneyEdit*>(haveWidget("payment"));
    KMyMoneyCashFlowCombo* cashflow = 0;
    KMyMoneyRegister::CashFlowDirection dir = KMyMoneyRegister::Unknown;
    bool isValidAmount = false;

    if(amount) {
      isValidAmount = amount->lineedit()->text().length() != 0;
      cashflow = dynamic_cast<KMyMoneyCashFlowCombo*>(haveWidget("cashflow"));
      if(cashflow)
        dir = cashflow->direction();

    } else {
      if(deposit) {
        if (deposit->lineedit()->text().length() != 0) {
          isValidAmount = true;
          dir = KMyMoneyRegister::Deposit;
        }
      }
      if(payment) {
        if (payment->lineedit()->text().length() != 0) {
          isValidAmount = true;
          dir = KMyMoneyRegister::Payment;
        }
      }
      if(!deposit || !payment) {
        tqDebug("Internal error: deposit(%p) & payment(%p) widgets not found but required", deposit, payment);
        return rc;
      }
    }

    if(dir == KMyMoneyRegister::Unknown)
      dir = KMyMoneyRegister::Payment;

    MyMoneyTransaction transaction;
    if(createTransaction(transaction, m_transaction, m_split)) {
      MyMoneyMoney value;

      KSplitTransactionDlg* dlg = new KSplitTransactionDlg(transaction,
                                                          transaction.splits()[0],
                                                          m_account,
                                                          isValidAmount,
                                                          dir == KMyMoneyRegister::Deposit,
                                                          0,
                                                          m_priceInfo,
                                                          m_regForm);
      connect(dlg, TQT_SIGNAL(objectCreation(bool)), this, TQT_SIGNAL(objectCreation(bool)));
      connect(dlg, TQT_SIGNAL(createCategory(MyMoneyAccount&, const MyMoneyAccount&)), this, TQT_SIGNAL(createCategory(MyMoneyAccount&, const MyMoneyAccount&)));

      if((rc = dlg->exec()) == TQDialog::Accepted) {
        m_transaction = dlg->transaction();
        m_split = m_transaction.splits()[0];
        loadEditWidgets();
      }

      delete dlg;
    }

    // focus jumps into the memo field
    if((w = haveWidget("memo")) != 0) {
      w->setFocus();
    }

    m_openEditSplits = false;
  }

  return rc;
}

void StdTransactionEditor::checkPayeeInSplit(MyMoneySplit& s, const TQString& payeeId)
{
  if(s.accountId().isEmpty())
    return;

  MyMoneyAccount acc = MyMoneyFile::instance()->account(s.accountId());
  if(acc.isIncomeExpense()) {
    s.setPayeeId(payeeId);
  } else {
    if(s.payeeId().isEmpty())
      s.setPayeeId(payeeId);
  }
}

MyMoneyMoney StdTransactionEditor::amountFromWidget(bool* update) const
{
  bool updateValue = false;
  MyMoneyMoney value;

  KMyMoneyCashFlowCombo* cashflow = dynamic_cast<KMyMoneyCashFlowCombo*>(haveWidget("cashflow"));
  if(cashflow) {
    // form based input
    kMyMoneyEdit* amount = dynamic_cast<kMyMoneyEdit*>(m_editWidgets["amount"]);
    // if both fields do not contain changes -> no need to update
    if(cashflow->direction() != KMyMoneyRegister::Unknown
    && !amount->lineedit()->text().isEmpty())
      updateValue = true;
    value = amount->value();
    if(cashflow->direction() == KMyMoneyRegister::Payment)
      value = -value;

  } else if(haveWidget("deposit")) {
    // register based input
    kMyMoneyEdit* deposit = dynamic_cast<kMyMoneyEdit*>(m_editWidgets["deposit"]);
    kMyMoneyEdit* payment = dynamic_cast<kMyMoneyEdit*>(m_editWidgets["payment"]);
    // if both fields do not contain text -> no need to update
    if(!(deposit->lineedit()->text().isEmpty() && payment->lineedit()->text().isEmpty()))
      updateValue = true;

    if(deposit->value().isPositive())
      value = deposit->value();
    else
      value = -(payment->value());
  }

  if(update)
    *update = updateValue;

  // determine the max fraction for this account and
  // adjust the value accordingly
  return value.convert(m_account.fraction());
}

bool StdTransactionEditor::createTransaction(MyMoneyTransaction& t, const MyMoneyTransaction& torig, const MyMoneySplit& sorig, bool skipPriceDialog)
{
  // extract price info from original transaction
  m_priceInfo.clear();
  TQValueList<MyMoneySplit>::const_iterator it_s;
  if(!torig.id().isEmpty()) {
    for(it_s = torig.splits().begin(); it_s != torig.splits().end(); ++it_s) {
      if((*it_s).id() != sorig.id()) {
        MyMoneyAccount cat = MyMoneyFile::instance()->account((*it_s).accountId());
        if(cat.currencyId() != m_account.currencyId()) {
          if(!(*it_s).shares().isZero() && !(*it_s).value().isZero()) {
            m_priceInfo[cat.currencyId()] = ((*it_s).shares() / (*it_s).value()).reduce();
          }
        }
      }
    }
  }

  t = torig;

  t.removeSplits();
  t.setCommodity(m_account.currencyId());

  kMyMoneyDateInput* postDate = dynamic_cast<kMyMoneyDateInput*>(m_editWidgets["postdate"]);
  if(postDate->date().isValid()) {
    t.setPostDate(postDate->date());
  }

  // we start with the previous values, make sure we can add them later on
  MyMoneySplit s0 = sorig;
  s0.clearId();

  // make sure we reference this account here
  s0.setAccountId(m_account.id());

  // memo and number field are special: if we have multiple transactions selected
  // and the edit field is empty, we treat it as "not modified".
  // FIXME a better approach would be to have a 'dirty' flag with the widgets
  //       which identifies if the originally loaded value has been modified
  //       by the user
  KTextEdit* memo = dynamic_cast<KTextEdit*>(m_editWidgets["memo"]);
  if(memo) {
    if(!isMultiSelection() || (isMultiSelection() && !memo->text().isEmpty() ) )
      s0.setMemo(memo->text());
  }

  kMyMoneyLineEdit* number = dynamic_cast<kMyMoneyLineEdit*>(haveWidget("number"));
  if(number) {
    if(!isMultiSelection() || (isMultiSelection() && !number->text().isEmpty() ) )
      s0.setNumber(number->text());
  }

  KMyMoneyPayeeCombo* payee = dynamic_cast<KMyMoneyPayeeCombo*>(m_editWidgets["payee"]);
  TQString payeeId;
  if(!isMultiSelection() || (isMultiSelection() && !payee->currentText().isEmpty())) {
    payeeId = payee->selectedItem();
    s0.setPayeeId(payeeId);
  }

  bool updateValue;
  MyMoneyMoney value = amountFromWidget(&updateValue);

  if(updateValue) {
    // for this account, the shares and value is the same
    s0.setValue(value);
    s0.setShares(value);
  } else {
    value = s0.value();
  }

  // if we mark the split reconciled here, we'll use today's date if no reconciliation date is given
  KMyMoneyReconcileCombo* status = dynamic_cast<KMyMoneyReconcileCombo*>(m_editWidgets["status"]);
  if(status->state() != MyMoneySplit::Unknown)
    s0.setReconcileFlag(status->state());

  if(s0.reconcileFlag() == MyMoneySplit::Reconciled && !s0.reconcileDate().isValid())
    s0.setReconcileDate(TQDate::currentDate());

  checkPayeeInSplit(s0, payeeId);

  // add the split to the transaction
  t.addSplit(s0);

  // if we have no other split we create it
  // if we have none or only one other split, we reconstruct it here
  // if we have more than one other split, we take them as they are
  // make sure to perform all those changes on a local copy
  TQValueList<MyMoneySplit> splits = m_splits;

  MyMoneySplit s1;
  if(splits.count() == 0) {
    s1.setMemo(s0.memo());
    splits.append(s1);

    // make sure we will fill the value and share fields later on
    updateValue = true;
  }

  // FIXME in multiSelection we currently only support transactions with one
  // or two splits. So we check the original transaction and extract the other
  // split or create it
  if(isMultiSelection()) {
    if(torig.splitCount() == 2) {
      TQValueList<MyMoneySplit>::const_iterator it_s;
      for(it_s = torig.splits().begin(); it_s != torig.splits().end(); ++it_s) {
        if((*it_s).id() == sorig.id())
          continue;
        s1 = *it_s;
        s1.clearId();
        break;
      }
    }
  } else {
    if(splits.count() == 1) {
      s1 = splits[0];
      s1.clearId();
    }
  }

  if(isMultiSelection() || splits.count() == 1) {
    KMyMoneyCategory* category = dynamic_cast<KMyMoneyCategory*>(m_editWidgets["category"]);
    if(!isMultiSelection() || (isMultiSelection() && !category->currentText().isEmpty())) {
      s1.setAccountId(category->selectedItem());
    }

    // if the first split has a memo but the second split is empty,
    // we just copy the memo text over
    if(memo) {
      if(!isMultiSelection() || (isMultiSelection() && !memo->text().isEmpty())) {
        // if the memo is filled, we check if the
        // account referenced by s1 is a regular account or a category.
        // in case of a regular account, we just leave the memo as is
        // in case of a category we simply copy the new value over the old.
        // in case we don't even have an account id, we just skip because
        // the split will be removed later on anyway.
        if(!s1.memo().isEmpty()) {
          if(!s1.accountId().isEmpty()) {
            MyMoneyAccount acc = MyMoneyFile::instance()->account(s1.accountId());
            if(acc.isIncomeExpense()) {
              s1.setMemo(s0.memo());
            }
          }
        } else {
          s1.setMemo(s0.memo());
        }
      }
    }

    if(updateValue && !s1.accountId().isEmpty()) {
      s1.setValue(-value);
      MyMoneyMoney shares;
      if(!skipPriceDialog) {
        if(!KCurrencyCalculator::setupSplitPrice(shares, t, s1, m_priceInfo, m_regForm))
          return false;
      } else {
        MyMoneyAccount cat = MyMoneyFile::instance()->account(s1.accountId());
        if(m_priceInfo.find(cat.currencyId()) != m_priceInfo.end()) {
          shares = (s1.value() * m_priceInfo[cat.currencyId()]).reduce().convert(cat.fraction());
        }
        else
          shares = s1.value();
      }
      s1.setShares(shares);
    }

    checkPayeeInSplit(s1, payeeId);

    if(!s1.accountId().isEmpty())
      t.addSplit(s1);

  } else {
    TQValueList<MyMoneySplit>::iterator it_s;
    for(it_s = splits.begin(); it_s != splits.end(); ++it_s) {
      s1 = *it_s;
      s1.clearId();
      checkPayeeInSplit(s1, payeeId);
      t.addSplit(s1);
    }
  }
  return true;
}

void StdTransactionEditor::setupFinalWidgets(void)
{
  addFinalWidget(haveWidget("deposit"));
  addFinalWidget(haveWidget("payment"));
  addFinalWidget(haveWidget("amount"));
  addFinalWidget(haveWidget("status"));
}

void StdTransactionEditor::slotUpdateAccount(const TQString& id)
{
  TransactionEditor::slotUpdateAccount(id);
  KMyMoneyCategory* category = dynamic_cast<KMyMoneyCategory*>(m_editWidgets["category"]);
  if(category && category->splitButton()) {
    category->splitButton()->setDisabled(id.isEmpty());
  }
}

#include "transactioneditor.moc"

