/***************************************************************************
                          kmymoneysplittable.cpp  -  description
                             -------------------
    begin                : Thu Jan 10 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>
 ***************************************************************************/

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

#include <kdecompat.h>

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

#include <tqglobal.h>
#include <tqpainter.h>
#include <tqcursor.h>
#include <tqapplication.h>
#include <tqtimer.h>
#include <tqlayout.h>
#include <tqeventloop.h>

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

#include <tdeglobal.h>
#include <tdelocale.h>
#include <kiconloader.h>
#include <tdemessagebox.h>
#include <tdecompletionbox.h>
#include <kpushbutton.h>
#include <tdepopupmenu.h>
#include <tdestdaccel.h>
#include <tdeshortcut.h>

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

#include "kmymoneysplittable.h"
#include <kmymoney/mymoneyfile.h>
#include <kmymoney/kmymoneyedit.h>
#include <kmymoney/kmymoneycategory.h>
#include <kmymoney/kmymoneyaccountselector.h>
#include <kmymoney/kmymoneylineedit.h>
#include <kmymoney/mymoneysecurity.h>
#include <kmymoney/kmymoneyglobalsettings.h>

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

#include "../mymoney/mymoneyutils.h"

kMyMoneySplitTable::kMyMoneySplitTable(TQWidget *parent, const char *name ) :
  TQTable(parent,name),
  m_currentRow(0),
  m_maxRows(0),
  m_editMode(false),
  m_amountWidth(80),
  m_editCategory(0),
  m_editMemo(0),
  m_editAmount(0)
{
  // setup the transactions table
  setNumRows(1);
  setNumCols(3);
  horizontalHeader()->setLabel(0, i18n("Category"));
  horizontalHeader()->setLabel(1, i18n("Memo"));
  horizontalHeader()->setLabel(2, i18n("Amount"));
  setSelectionMode(TQTable::NoSelection);
  setLeftMargin(0);
  verticalHeader()->hide();
  setColumnStretchable(0, false);
  setColumnStretchable(1, false);
  setColumnStretchable(2, false);
  horizontalHeader()->setResizeEnabled(false);
  horizontalHeader()->setMovingEnabled(false);
  horizontalHeader()->setFont(KMyMoneyGlobalSettings::listHeaderFont());

  setVScrollBarMode(TQScrollView::AlwaysOn);
  // never show a horizontal scroll bar
  setHScrollBarMode(TQScrollView::AlwaysOff);

  // setup the context menu
  m_contextMenu = new TDEPopupMenu(this);
  TDEIconLoader *il = TDEGlobal::iconLoader();
  m_contextMenu->insertTitle(il->loadIcon("transaction", TDEIcon::MainToolbar), i18n("Split Options"));
  m_contextMenu->insertItem(il->loadIcon("edit", TDEIcon::Small), i18n("Edit..."), this, TQT_SLOT(slotStartEdit()));
  m_contextMenuDuplicate = m_contextMenu->insertItem(il->loadIcon("edit-copy", TDEIcon::Small), i18n("Duplicate"), this, TQT_SLOT(slotDuplicateSplit()));
  m_contextMenuDelete = m_contextMenu->insertItem(il->loadIcon("delete", TDEIcon::Small),
                        i18n("Delete ..."),
                        this, TQT_SLOT(slotDeleteSplit()));

  connect(this, TQT_SIGNAL(clicked(int, int, int, const TQPoint&)),
    this, TQT_SLOT(slotSetFocus(int, int, int, const TQPoint&)));

  connect(this, TQT_SIGNAL(transactionChanged(const MyMoneyTransaction&)),
    this, TQT_SLOT(slotUpdateData(const MyMoneyTransaction&)));
}

kMyMoneySplitTable::~kMyMoneySplitTable()
{
}

void kMyMoneySplitTable::setup(const TQMap<TQString, MyMoneyMoney>& priceInfo)
{
  m_priceInfo = priceInfo;
}

const TQColor kMyMoneySplitTable::rowBackgroundColor(const int row) const
{
  return (row % 2) ? KMyMoneyGlobalSettings::listColor() : KMyMoneyGlobalSettings::listBGColor();
}

void kMyMoneySplitTable::paintCell(TQPainter *p, int row, int col, const TQRect& r, bool /*selected*/)
{
  TQColorGroup g = colorGroup();
  TQColor textColor;

  g.setColor(TQColorGroup::Base, rowBackgroundColor(row));

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

  TQString firsttext = text(row, col);
  TQString qstringCategory;
  TQString qstringMemo;

  int intPos = firsttext.find("|");
  if(intPos > -1)
  {
    qstringCategory = firsttext.left(intPos);
    qstringMemo = firsttext.mid(intPos + 1);
  }

  TQRect rr = r;
  TQRect rr2 = r;
  rr.setX(0);
  rr.setY(0);
  rr.setWidth(columnWidth(col));
  rr.setHeight(rowHeight(row));

  rr2.setX(2);
  rr2.setY(0);
  rr2.setWidth(columnWidth(col)-4);
  rr2.setHeight(rowHeight(row));


  if(row == m_currentRow) {
    TQBrush backgroundBrush(g.highlight());
    textColor = g.highlightedText();
    p->fillRect(rr,backgroundBrush);

  } else {
    TQBrush backgroundBrush(g.base());
    textColor = g.text();
    p->fillRect(rr,backgroundBrush);
  }

  if (KMyMoneyGlobalSettings::showGrid()) {
    p->setPen(KMyMoneyGlobalSettings::listGridColor());
    if(col != 0)
      p->drawLine(rr.x(), 0, rr.x(), rr.height()-1);    // left frame
    p->drawLine(rr.x(), rr.y(), rr.width(), 0);         // bottom frame
    p->setPen(textColor);
  }

  switch (col) {
    case 0:     // category
    case 1:     // memo
      p->drawText(rr2, TQt::AlignLeft | TQt::AlignVCenter, text(row, col));
      break;

    case 2:     // amount
      p->drawText(rr2, TQt::AlignRight | TQt::AlignVCenter,firsttext);
      break;
  }
}

/** Override the TQTable member function to avoid display of focus */
void kMyMoneySplitTable::paintFocus(TQPainter * /* p */, const TQRect & /*cr*/)
{
}

void kMyMoneySplitTable::columnWidthChanged(int col)
{
  for (int i=0; i<numRows(); i++)
    updateCell(i, col);
}

/** Override the TQTable member function to avoid confusion with our own functionality */
void kMyMoneySplitTable::endEdit(int /*row*/, int /*col*/, bool /*accept*/, bool /*replace*/ )
{
}

bool kMyMoneySplitTable::eventFilter(TQObject *o, TQEvent *e)
{
  // MYMONEYTRACER(tracer);
  TQKeyEvent *k = TQT_TQKEYEVENT (e);
  bool rc = false;
  int row = currentRow();
  int lines = visibleHeight()/rowHeight(0);
  TQWidget* w;

  if(e->type() == TQEvent::KeyPress && !isEditMode()) {
    rc = true;
    switch(k->key()) {
      case TQt::Key_Up:
        if(row)
          slotSetFocus(row-1);
        break;

      case TQt::Key_Down:
        if(row < static_cast<int> (m_transaction.splits().count()-1))
          slotSetFocus(row+1);
        break;

      case TQt::Key_Home:
        slotSetFocus(0);
        break;

      case TQt::Key_End:
        slotSetFocus(m_transaction.splits().count()-1);
        break;

      case TQt::Key_PageUp:
        if(lines) {
          while(lines-- > 0 && row)
            row--;
          slotSetFocus(row);
        }
        break;

      case TQt::Key_PageDown:
        if(row < static_cast<int> (m_transaction.splits().count()-1)) {
          while(lines-- > 0 && row < static_cast<int> (m_transaction.splits().count()-1))
            row++;
          slotSetFocus(row);
        }
        break;

      case TQt::Key_Delete:
        slotDeleteSplit();
        break;

      case TQt::Key_Return:
      case TQt::Key_Enter:
        if(row < static_cast<int> (m_transaction.splits().count()-1)
        && KMyMoneyGlobalSettings::enterMovesBetweenFields()) {
          slotStartEdit();
        } else
          emit returnPressed();
        break;

      case TQt::Key_Escape:
        emit escapePressed();
        break;

      case TQt::Key_F2:
        slotStartEdit();
        break;

      default:
        rc = true;
        TDEShortcut copySplit(i18n("Duplicate split", "CTRL+c"));
        TDEShortcut newSplit(TQKeySequence(TQt::CTRL | TQt::Key_Insert));
        if(copySplit.contains(KKey(k))) {
          slotDuplicateSplit();

        } else if(newSplit.contains(KKey(k))) {
          slotSetFocus(m_transaction.splits().count()-1);
          slotStartEdit();

        } else if ( k->text()[ 0 ].isPrint() ) {
          w = slotStartEdit();
          // make sure, the widget receives the key again
          TQApplication::sendEvent(w, e);
        }
        break;
    }

  } else if(e->type() == TQEvent::KeyPress && isEditMode()) {
    bool terminate = true;
    rc = true;
    switch(k->key()) {
      // suppress the F2 functionality to start editing in inline edit mode
      case TQt::Key_F2:
      // suppress the cursor movement in inline edit mode
      case TQt::Key_Up:
      case TQt::Key_Down:
      case TQt::Key_PageUp:
      case TQt::Key_PageDown:
        break;

      case TQt::Key_Return:
      case TQt::Key_Enter:
        // we cannot call the slot directly, as it destroys the caller of
        // this method :-(  So we let the event handler take care of calling
        // the respective slot using a timeout. For a KLineEdit derived object
        // it could be, that at this point the user selected a value from
        // a completion list. In this case, we close the completion list and
        // do not end editing of the transaction.
        if(o->inherits("KLineEdit")) {
          KLineEdit* le = dynamic_cast<KLineEdit*> (o);
          TDECompletionBox* box = le->completionBox(false);
          if(box && box->isVisible()) {
            terminate = false;
            le->completionBox(false)->hide();
          }
        }

        // in case we have the 'enter moves focus between fields', we need to simulate
        // a TAB key when the object 'o' points to the category or memo field.
        if(KMyMoneyGlobalSettings::enterMovesBetweenFields()) {
          if(TQT_BASE_OBJECT(o) == TQT_BASE_OBJECT(m_editCategory->lineEdit()) || TQT_BASE_OBJECT(o) == TQT_BASE_OBJECT(m_editMemo)) {
            terminate = false;
            TQKeyEvent evt(e->type(),
                          Key_Tab, 0, k->state(), TQString(),
                          k->isAutoRepeat(), k->count());

            TQApplication::sendEvent( o, &evt );
          }
        }

        if(terminate) {
          TQTimer::singleShot(0, this, TQT_SLOT(slotEndEditKeyboard()));
        }
        break;

      case TQt::Key_Escape:
        // we cannot call the slot directly, as it destroys the caller of
        // this method :-(  So we let the event handler take care of calling
        // the respective slot using a timeout.
        TQTimer::singleShot(0, this, TQT_SLOT(slotCancelEdit()));
        break;

      default:
        rc = false;
        break;
    }
  } else if(e->type() == TQEvent::KeyRelease && !isEditMode()) {
    // for some reason, we only see a KeyRelease event of the Menu key
    // here. In other locations (e.g. Register::eventFilter()) we see
    // a KeyPress event. Strange. (ipwizard - 2008-05-10)
    switch(k->key()) {
      case TQt::Key_Menu:
        // if the very last entry is selected, the delete
        // operation is not available otherwise it is
        m_contextMenu->setItemEnabled(m_contextMenuDelete,
              row < static_cast<int> (m_transaction.splits().count()-1));
        m_contextMenu->setItemEnabled(m_contextMenuDuplicate,
              row < static_cast<int> (m_transaction.splits().count()-1));

        m_contextMenu->exec(TQCursor::pos());
        rc = true;
        break;
      default:
        break;
    }
  }

  // if the event has not been processed here, forward it to
  // the base class implementation if it's not a key event
  if(rc == false) {
    if(e->type() != TQEvent::KeyPress
    && e->type() != TQEvent::KeyRelease) {
      rc = TQTable::eventFilter(o, e);
    }
  }

  return rc;
}

void kMyMoneySplitTable::slotSetFocus(int realrow, int /* col */, int button, const TQPoint& /* point */)
{
  MYMONEYTRACER(tracer);
  int   row = realrow;

  // adjust row to used area
  if(row > static_cast<int> (m_transaction.splits().count()-1))
    row = m_transaction.splits().count()-1;
  if(row < 0)
    row = 0;

  // make sure the row will be on the screen
  ensureCellVisible(row, 0);

  if(button == Qt::LeftButton) {          // left mouse button
    if(isEditMode()) {                    // in edit mode?
      if(KMyMoneyGlobalSettings::focusChangeIsEnter())
        slotEndEdit();
      else
        slotCancelEdit();
    }
    if(row != static_cast<int> (currentRow())) {
      // setup new current row and update visible selection
      setCurrentCell(row, 0);
      slotUpdateData(m_transaction);
    }
  } else if(button == Qt::RightButton) {
    // context menu is only available when cursor is on
    // an existing transaction or the first line after this area
    if(row == realrow) {
      // setup new current row and update visible selection
      setCurrentCell(row, 0);
      slotUpdateData(m_transaction);

      // if the very last entry is selected, the delete
      // operation is not available otherwise it is
      m_contextMenu->setItemEnabled(m_contextMenuDelete,
            row < static_cast<int> (m_transaction.splits().count()-1));
      m_contextMenu->setItemEnabled(m_contextMenuDuplicate,
            row < static_cast<int> (m_transaction.splits().count()-1));

      m_contextMenu->exec(TQCursor::pos());
    }
  }
}

void kMyMoneySplitTable::contentsMousePressEvent( TQMouseEvent* e )
{
  slotSetFocus( rowAt(e->pos().y()), columnAt(e->pos().x()), e->button(), e->pos() );
}

/* turn off TQTable behaviour */
void kMyMoneySplitTable::contentsMouseReleaseEvent( TQMouseEvent* /* e */ )
{
}

void kMyMoneySplitTable::contentsMouseDoubleClickEvent( TQMouseEvent *e )
{
  MYMONEYTRACER(tracer);

  int col = columnAt(e->pos().x());
  slotSetFocus( rowAt(e->pos().y()), col, e->button(), e->pos() );
  slotStartEdit();

  KLineEdit* editWidget = 0;
  switch(col) {
    case 1:
      editWidget = m_editMemo;
      break;

    case 2:
      editWidget = dynamic_cast<KLineEdit*> (m_editAmount->focusWidget());
      break;

    default:
      break;
  }
  if(editWidget) {
    editWidget->setFocus();
    editWidget->selectAll();
    // we need to call setFocus on the edit widget from the
    // main loop again to get the keyboard focus to the widget also
    TQTimer::singleShot(0, editWidget, TQT_SLOT(setFocus()));
  }
}

void kMyMoneySplitTable::setCurrentCell(int row, int /* col */)
{
  MYMONEYTRACER(tracer);

  if(row > m_maxRows)
    row = m_maxRows;
  m_currentRow = row;
  TQTable::setCurrentCell(row, 0);
  TQValueList<MyMoneySplit> list = getSplits(m_transaction);
  if(row < static_cast<int>(list.count()))
    m_split = list[row];
  else
    m_split = MyMoneySplit();
}

void kMyMoneySplitTable::setNumRows(int irows)
{
  TQTable::setNumRows(irows);

  // determine row height according to the edit widgets
  // we use the category widget as the base
  TQFontMetrics fm( KMyMoneyGlobalSettings::listCellFont() );
  int height = fm.lineSpacing()+6;
#if 0
  // recalculate row height hint
  KMyMoneyCategory cat;
  height = TQMAX(cat.sizeHint().height(), height);
#endif

  verticalHeader()->setUpdatesEnabled(false);

  for(int i = 0; i < irows; ++i)
    verticalHeader()->resizeSection(i, height);

  verticalHeader()->setUpdatesEnabled(true);

  // add or remove scrollbars as required
  updateScrollBars();
}

void kMyMoneySplitTable::setTransaction(const MyMoneyTransaction& t, const MyMoneySplit& s, const MyMoneyAccount& acc)
{
  MYMONEYTRACER(tracer);
  m_transaction = t;
  m_account = acc;
  m_hiddenSplit = s;
  setCurrentCell(0, 0);
  slotUpdateData(m_transaction);
}

const TQValueList<MyMoneySplit> kMyMoneySplitTable::getSplits(const MyMoneyTransaction& t) const
{
  TQValueList<MyMoneySplit> list;
  TQValueList<MyMoneySplit>::Iterator it;

  // get list of splits
  list = t.splits();

  // and ignore the one that should be hidden

  for(it = list.begin(); it != list.end(); ++it) {
    if((*it).id() == m_hiddenSplit.id()) {
      list.remove(it);
      break;
    }
  }
  return list;
}

void kMyMoneySplitTable::slotUpdateData(const MyMoneyTransaction& t)
{
  MYMONEYTRACER(tracer);
  unsigned long rowCount=0;

  TQValueList<MyMoneySplit> list = getSplits(t);
  updateTransactionTableSize();

  // fill the part that is used by transactions
  TQValueList<MyMoneySplit>::Iterator it;
  kMyMoneyEdit* valfield = new kMyMoneyEdit();
  for(it = list.begin(); it != list.end(); ++it) {
    TQString colText;
    MyMoneyMoney value = (*it).value();
    if(!(*it).accountId().isEmpty()) {
      try {
        colText = MyMoneyFile::instance()->accountToCategory((*it).accountId());
      } catch(MyMoneyException *e) {
        tqDebug("Unexpected exception in kMyMoneySplitTable::slotUpdateData()");
        delete e;
      }
    }
    TQString amountTxt = value.formatMoney(m_account.fraction());
    if(value == MyMoneyMoney::autoCalc) {
      amountTxt = i18n("will be calculated");
    }

    if(colText.isEmpty() && (*it).memo().isEmpty() && value.isZero())
      amountTxt = TQString();

    unsigned width = fontMetrics().width(amountTxt);
    valfield->setMinimumWidth(width);
    width = valfield->minimumSizeHint().width();

    if(width > m_amountWidth)
      m_amountWidth = width;

    setText(rowCount, 0, colText);
    setText(rowCount, 1, (*it).memo());
    setText(rowCount, 2, amountTxt);

    rowCount++;
  }
  delete valfield;

  // now clean out the remainder of the table
  while(rowCount < static_cast<unsigned long> (numRows())) {
    setText(rowCount, 0, "");
    setText(rowCount, 1, "");
    setText(rowCount, 2, "");
    ++rowCount;
  }
}

void kMyMoneySplitTable::updateTransactionTableSize(void)
{
  // get current size of transactions table
  int rowHeight = cellGeometry(0, 0).height();

  // add half a row to the height to avoid unnecessary toggling when
  // changing the number of rows
  int tableHeight = (height() + rowHeight/2);
  int splitCount = m_transaction.splits().count()-1;

  if(splitCount < 0)
    splitCount = 0;

  // see if we need some extra lines to fill the current size with the grid
  int numExtraLines = (tableHeight / rowHeight) - splitCount;
  if(numExtraLines < 2)
    numExtraLines = 2;

  setNumRows(splitCount + numExtraLines);
  // setMaxRows(splitCount);
  m_maxRows = splitCount;
}

void kMyMoneySplitTable::resizeEvent(TQResizeEvent* /* ev */)
{
  int w = visibleWidth() - m_amountWidth;

  // resize the columns
  setColumnWidth(0, w/2);
  setColumnWidth(1, w/2);
  setColumnWidth(2, m_amountWidth);

  updateTransactionTableSize();
}

void kMyMoneySplitTable::slotDuplicateSplit(void)
{
  MYMONEYTRACER(tracer);
  TQValueList<MyMoneySplit> list = getSplits(m_transaction);
  if(m_currentRow < static_cast<int> (list.count())) {
    MyMoneySplit split = list[m_currentRow];
    split.clearId();
    try {
      m_transaction.addSplit(split);
      emit transactionChanged(m_transaction);
    } catch(MyMoneyException *e) {
      tqDebug("Cannot duplicate split: %s", e->what().latin1());
      delete e;
    }
  }
}

void kMyMoneySplitTable::slotDeleteSplit(void)
{
  MYMONEYTRACER(tracer);
  TQValueList<MyMoneySplit> list = getSplits(m_transaction);
  if(m_currentRow < static_cast<int> (list.count())) {
    if(KMessageBox::warningContinueCancel (this,
        i18n("You are about to delete the selected split. "
            "Do you really want to continue?"),
        i18n("KMyMoney"),
        i18n("Continue")
        ) == KMessageBox::Continue) {
      try {
        m_transaction.removeSplit(list[m_currentRow]);
        // if we removed the last split, select the previous
        if(m_currentRow && m_currentRow == static_cast<int>(list.count())-1)
          setCurrentCell(m_currentRow-1, 0);
        else
          setCurrentCell(m_currentRow, 0);
        emit transactionChanged(m_transaction);
      } catch(MyMoneyException *e) {
        tqDebug("Cannot remove split: %s", e->what().latin1());
        delete e;
      }
    }
  }
}

TQWidget* kMyMoneySplitTable::slotStartEdit(void)
{
  MYMONEYTRACER(tracer);
  return createEditWidgets();
}

void kMyMoneySplitTable::slotEndEdit(void)
{
  endEdit(false);
}

void kMyMoneySplitTable::slotEndEditKeyboard(void)
{
  endEdit(true);
}

void kMyMoneySplitTable::endEdit(bool keyBoardDriven)
{
  // Don't proceed, if we're not in edit mode
  if(!m_editCategory)
    return;

  MyMoneyFile* file = MyMoneyFile::instance();

  MYMONEYTRACER(tracer);
  MyMoneySplit s1 = m_split;

  if (m_editCategory->selectedItem().isEmpty()) {
    KMessageBox::information(this, i18n("You need to assign a category to this split before it can be entered."), i18n("Enter split"), "EnterSplitWithEmptyCategory");
    m_editCategory->setFocus();
    return;
  }

  bool needUpdate = false;
  if(m_editCategory->selectedItem() != m_split.accountId()) {
    s1.setAccountId(m_editCategory->selectedItem());
    needUpdate = true;
  }
  if(m_editMemo->text() != m_split.memo()) {
    s1.setMemo(m_editMemo->text());
    needUpdate = true;
  }
  if(m_editAmount->value() != m_split.value()) {
    s1.setValue(m_editAmount->value());
    needUpdate = true;
  }

  if(needUpdate) {
    if(!s1.value().isZero()) {
      MyMoneyAccount cat = file->account(s1.accountId());
      if(cat.currencyId() != m_transaction.commodity()) {

        MyMoneySecurity fromCurrency, toCurrency;
        MyMoneyMoney fromValue, toValue;
        fromCurrency = file->security(m_transaction.commodity());
        toCurrency = file->security(cat.currencyId());

        // determine the fraction required for this category
        int fract = toCurrency.smallestAccountFraction();
        if(cat.accountType() == MyMoneyAccount::Cash)
          fract = toCurrency.smallestCashFraction();

        // display only positive values to the user
        fromValue = s1.value().abs();

        // if we had a price info in the beginning, we use it here
        if(m_priceInfo.find(cat.currencyId()) != m_priceInfo.end()) {
          toValue = (fromValue * m_priceInfo[cat.currencyId()]).convert(fract);
        }

        // if the shares are still 0, we need to change that
        if(toValue.isZero()) {
          MyMoneyPrice price = MyMoneyFile::instance()->price(fromCurrency.id(), toCurrency.id());
          // if the price is valid calculate the shares. If it is invalid
          // assume a conversion rate of 1.0
          if(price.isValid()) {
            toValue = (price.rate(toCurrency.id()) * fromValue).convert(fract);
          } else {
            toValue = fromValue;
          }
        }

        // now present all that to the user
        KCurrencyCalculator calc(fromCurrency,
                                toCurrency,
                                fromValue,
                                toValue,
                                m_transaction.postDate(),
                                fract,
                                this, "currencyCalculator");

        if(calc.exec() == TQDialog::Rejected) {
          return;
        } else {
          s1.setShares((s1.value() * calc.price()).convert(fract));
        }

      } else {
        s1.setShares(s1.value());
      }
    } else
      s1.setShares(s1.value());

    m_split = s1;
    try {
      if(m_split.id().isEmpty()) {
        m_transaction.addSplit(m_split);
      } else {
        m_transaction.modifySplit(m_split);
      }
      emit transactionChanged(m_transaction);
    } catch(MyMoneyException *e) {
      tqDebug("Cannot add/modify split: %s", e->what().latin1());
      delete e;
    }
  }
  this->setFocus();
  destroyEditWidgets();
  slotSetFocus(currentRow()+1);

  // if we still have more splits, we start editing right away
  // in case we have selected 'enter moves betweeen fields'
  if(keyBoardDriven
  && currentRow() < static_cast<int> (m_transaction.splits().count()-1)
  && KMyMoneyGlobalSettings::enterMovesBetweenFields()) {
    slotStartEdit();
  }

}

void kMyMoneySplitTable::slotCancelEdit(void)
{
  MYMONEYTRACER(tracer);
  if(isEditMode()) {
    destroyEditWidgets();
    this->setFocus();
  }
}

bool kMyMoneySplitTable::isEditMode(void) const
{
  return m_editMode;
}

void kMyMoneySplitTable::destroyEditWidgets(void)
{
  MYMONEYTRACER(tracer);

  disconnect(MyMoneyFile::instance(), TQT_SIGNAL(dataChanged()), this, TQT_SLOT(slotLoadEditWidgets()));

  clearCellWidget(m_currentRow, 0);
  clearCellWidget(m_currentRow, 1);
  clearCellWidget(m_currentRow, 2);
  clearCellWidget(m_currentRow+1, 0);
  m_editMode = false;

  TQApplication::eventLoop()->processEvents(TQEventLoop::ExcludeUserInput, 100);
}

TQWidget* kMyMoneySplitTable::createEditWidgets(void)
{
  MYMONEYTRACER(tracer);

  TQFont cellFont = KMyMoneyGlobalSettings::listCellFont();
  m_tabOrderWidgets.clear();

  // create the widgets
  m_editAmount = new kMyMoneyEdit(0);
  m_editAmount->setFont(cellFont);
  m_editAmount->setResetButtonVisible(false);

  m_editCategory = new KMyMoneyCategory();
  m_editCategory->setHint(i18n("Category"));
  m_editCategory->setFont(cellFont);
  connect(m_editCategory, TQT_SIGNAL(createItem(const TQString&, TQString&)), this, TQT_SIGNAL(createCategory(const TQString&, TQString&)));
  connect(m_editCategory, TQT_SIGNAL(objectCreation(bool)), this, TQT_SIGNAL(objectCreation(bool)));

  m_editMemo = new kMyMoneyLineEdit(0, 0, false, AlignLeft|AlignVCenter);
  m_editMemo->setHint(i18n("Memo"));
  m_editMemo->setFont(cellFont);

  // create buttons for the mouse users
  TDEIconLoader *il = TDEGlobal::iconLoader();
  m_registerButtonFrame = new TQFrame(this, "buttonFrame");
  TQPalette palette = m_registerButtonFrame->palette();
  palette.setColor(TQColorGroup::Background, rowBackgroundColor(m_currentRow+1) );
  m_registerButtonFrame->setPalette(palette);

  TQHBoxLayout* l = new TQHBoxLayout(m_registerButtonFrame);
  m_registerEnterButton = new KPushButton(il->loadIcon("button_ok", TDEIcon::Small, TDEIcon::SizeSmall), TQString(), m_registerButtonFrame, "EnterButton");

  m_registerCancelButton = new KPushButton(il->loadIcon("button_cancel", TDEIcon::Small, TDEIcon::SizeSmall), TQString(), m_registerButtonFrame, "CancelButton");

  l->addWidget(m_registerEnterButton);
  l->addWidget(m_registerCancelButton);
  l->addStretch(2);

  connect(m_registerEnterButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotEndEdit()));
  connect(m_registerCancelButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotCancelEdit()));

  // setup tab order
  addToTabOrder(m_editCategory);
  addToTabOrder(m_editMemo);
  addToTabOrder(m_editAmount);
  addToTabOrder(m_registerEnterButton);
  addToTabOrder(m_registerCancelButton);

  if(!m_split.accountId().isEmpty()) {
    m_editCategory->setSelectedItem(m_split.accountId());
  } else {
    // check if the transaction is balanced or not. If not,
    // assign the remainder to the amount.
    MyMoneyMoney diff;
    TQValueList<MyMoneySplit> list = m_transaction.splits();
    TQValueList<MyMoneySplit>::ConstIterator it_s;
    for(it_s = list.begin(); it_s != list.end(); ++it_s) {
      if(!(*it_s).accountId().isEmpty())
        diff += (*it_s).value();
    }
    m_split.setValue(-diff);
  }

  m_editMemo->loadText(m_split.memo());
  // don't allow automatically calculated values to be modified
  if(m_split.value() == MyMoneyMoney::autoCalc) {
    m_editAmount->setEnabled(false);
    m_editAmount->loadText("will be calculated");
  } else
    m_editAmount->setValue(m_split.value());

  setCellWidget(m_currentRow, 0, m_editCategory);
  setCellWidget(m_currentRow, 1, m_editMemo);
  setCellWidget(m_currentRow, 2, m_editAmount);
  setCellWidget(m_currentRow+1, 0, m_registerButtonFrame);

  // load e.g. the category widget with the account list
  slotLoadEditWidgets();
  connect(MyMoneyFile::instance(), TQT_SIGNAL(dataChanged()), this, TQT_SLOT(slotLoadEditWidgets()));

  // setup the keyboard filter for all widgets
  for(TQWidget* w = m_tabOrderWidgets.first(); w; w = m_tabOrderWidgets.next()) {
    w->installEventFilter(this);
  }

  m_editCategory->setFocus();
  m_editCategory->lineEdit()->selectAll();
  m_editMode = true;

  return m_editCategory->lineEdit();
}

void kMyMoneySplitTable::slotLoadEditWidgets(void)
{
  // reload category widget
  TQString categoryId = m_editCategory->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);

  // remove the accounts with invalid types at this point
  aSet.removeAccountType(MyMoneyAccount::CertificateDep);
  aSet.removeAccountType(MyMoneyAccount::Investment);
  aSet.removeAccountType(MyMoneyAccount::Stock);
  aSet.removeAccountType(MyMoneyAccount::MoneyMarket);

  aSet.load(m_editCategory->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())
    m_editCategory->selector()->removeItem(m_account.id());

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

}

void kMyMoneySplitTable::addToTabOrder(TQWidget* w)
{
  if(w) {
    while(w->focusProxy())
      w = TQT_TQWIDGET(w->focusProxy());
    m_tabOrderWidgets.append(w);
  }
}

bool kMyMoneySplitTable::focusNextPrevChild(bool next)
{
  MYMONEYTRACER(tracer);
  bool  rc = false;

  if(m_editCategory) {
    TQWidget *w = 0;
    TQWidget *currentWidget;

    m_tabOrderWidgets.find(tqApp->focusWidget());
    currentWidget = m_tabOrderWidgets.current();
    w = next ? m_tabOrderWidgets.next() : m_tabOrderWidgets.prev();

    do {
      if(!w) {
        w = next ? m_tabOrderWidgets.first() : m_tabOrderWidgets.last();
      }

      if(w != currentWidget
      && ((w->focusPolicy() & TQ_TabFocus) == TQ_TabFocus)
      && w->isVisible() && w->isEnabled()) {
        w->setFocus();
        rc = true;
        break;
      }
      w = next ? m_tabOrderWidgets.next() : m_tabOrderWidgets.prev();
    } while(w != currentWidget);

  } else
    rc = TQTable::focusNextPrevChild(next);

  return rc;
}



#include "kmymoneysplittable.moc"
