/***************************************************************************
                             register.cpp  -  description
                             -------------------
    begin                : Fri Mar 10 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.                                   *
 *                                                                         *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <typeinfo>

// ----------------------------------------------------------------------------
// TQt Includes

#include <tqstring.h>
#include <tqtimer.h>
#include <tqapplication.h>
#include <tqeventloop.h>
#include <tqtooltip.h>
#include <tqimage.h>

// ----------------------------------------------------------------------------
// TDE Includes

#include <tdelocale.h>
#include <tdeglobal.h>
#include <kdebug.h>
#include <kurldrag.h>

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

#include <kmymoney/kmymoneydateinput.h>
#include <kmymoney/kmymoneyedit.h>
#include <kmymoney/kmymoneycategory.h>
#include <kmymoney/register.h>
#include <kmymoney/transactionform.h>
#include <kmymoney/stdtransactiondownloaded.h>
#include <kmymoney/stdtransactionmatched.h>
#include "scheduledtransaction.h"
#include "../kmymoneyglobalsettings.h"

const int LinesPerMemo = 3;

static TQString sortOrderText[] = {
  I18N_NOOP("Unknown"),
  I18N_NOOP("Post date"),
  I18N_NOOP("Date entered"),
  I18N_NOOP("Payee"),
  I18N_NOOP("Amount"),
  I18N_NOOP("Number"),
  I18N_NOOP("Entry order"),
  I18N_NOOP("Type"),
  I18N_NOOP("Category"),
  I18N_NOOP("Reconcile state"),
  I18N_NOOP("Security")
  // add new values above this comment line
  };

using namespace KMyMoneyRegister;

static unsigned char fancymarker_bg_image[] = {
/* 200x49 */
  0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,
  0x00,0x00,0x00,0x0D,0x49,0x48,0x44,0x52,
  0x00,0x00,0x00,0xC8,0x00,0x00,0x00,0x31,
  0x08,0x06,0x00,0x00,0x00,0x9F,0xC5,0xE6,
  0x4F,0x00,0x00,0x00,0x06,0x62,0x4B,0x47,
  0x44,0x00,0xFF,0x00,0xFF,0x00,0xFF,0xA0,
  0xBD,0xA7,0x93,0x00,0x00,0x00,0x09,0x70,
  0x48,0x59,0x73,0x00,0x00,0x0B,0x13,0x00,
  0x00,0x0B,0x13,0x01,0x00,0x9A,0x9C,0x18,
  0x00,0x00,0x00,0x86,0x49,0x44,0x41,0x54,
  0x78,0xDA,0xED,0xDD,0x31,0x0A,0x84,0x40,
  0x10,0x44,0xD1,0x1A,0x19,0x10,0xCF,0xE6,
  0xFD,0x4F,0xB2,0x88,0x08,0x22,0x9B,0x18,
  0x4E,0x1B,0x2C,0x1B,0x18,0xBC,0x07,0x7D,
  0x81,0x82,0x1F,0x77,0x4B,0xB2,0x06,0x18,
  0xEA,0x49,0x3E,0x66,0x00,0x81,0x80,0x40,
  0xE0,0xDF,0x81,0x6C,0x66,0x80,0x3A,0x90,
  0xDD,0x0C,0x50,0x07,0x72,0x98,0x01,0xEA,
  0x40,0x4E,0x33,0x40,0x1D,0xC8,0x65,0x06,
  0x18,0x6B,0xF7,0x01,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0xF0,0x16,0x3E,
  0x4C,0xC1,0x83,0x9E,0x64,0x32,0x03,0x08,
  0x04,0x7E,0x0A,0xA4,0x9B,0x01,0xEA,0x40,
  0x66,0x33,0x40,0x1D,0xC8,0x62,0x06,0x18,
  0xFB,0x02,0x05,0x87,0x08,0x55,0xFE,0xDE,
  0xA2,0x9D,0x00,0x00,0x00,0x00,0x49,0x45,
  0x4E,0x44,0xAE,0x42,0x60,0x82
};

TQPixmap* GroupMarker::m_bg = 0;
int GroupMarker::m_bgRefCnt = 0;

void ItemPtrVector::sort(void)
{
  if(count() > 0) {
    // get rid of 0 pointers in the list
    KMyMoneyRegister::ItemPtrVector::iterator it_l;
    RegisterItem *item;
    for(it_l = begin(); it_l != end(); ++it_l) {
      if(*it_l == 0) {
        item = last();
        *it_l = item;
        pop_back();
        --it_l;
      }
    }

    std::sort(begin(), end(), item_cmp);
  }
}

bool ItemPtrVector::item_cmp(RegisterItem* i1, RegisterItem* i2)
{
  const TQValueList<TransactionSortField>& sortOrder = i1->parent()->sortOrder();
  TQValueList<TransactionSortField>::const_iterator it;
  int rc = 0;
  bool ok1, ok2;
  TQ_ULLONG n1, n2;

  MyMoneyMoney tmp;

  for(it = sortOrder.begin(); it != sortOrder.end(); ++it) {
    TransactionSortField sortField = static_cast<TransactionSortField>(abs(*it));
    switch(sortField) {
      case PostDateSort:
        rc = i2->sortPostDate().daysTo(i1->sortPostDate());
        if(rc == 0) {
          rc = i1->sortSamePostDate() - i2->sortSamePostDate();
        }
        break;

      case EntryDateSort:
        rc = i2->sortEntryDate().daysTo(i1->sortEntryDate());
        break;

      case PayeeSort:
        rc = TQString::localeAwareCompare(i1->sortPayee(), i2->sortPayee());
        break;

      case ValueSort:
        tmp = i1->sortValue() - i2->sortValue();
        if(tmp.isZero())
          rc = 0;
        else if(tmp.isNegative())
          rc = -1;
        else
          rc = 1;
        break;

      case NoSort:
        // convert both values to numbers
        n1 = i1->sortNumber().toULongLong(&ok1);
        n2 = i2->sortNumber().toULongLong(&ok2);
        // the following four cases exist:
        // a) both are converted correct
        //    compare them directly
        // b) n1 is numeric, n2 is not
        //    numbers come first, so return -1
        // c) n1 is not numeric, n2 is
        //    numbers come first, so return 1
        // d) both are non numbers
        //    compare using localeAwareCompare
        if(ok1 && ok2) {  // case a)
          rc = (n1 > n2) ? 1 : ((n1 == n2 ) ? 0 : -1);
        } else if(ok1 && !ok2) {
          rc = -1;
        } else if(!ok1 && ok2) {
          rc = 1;
        } else
          rc = TQString::localeAwareCompare(i1->sortNumber(), i2->sortNumber());
        break;

      case EntryOrderSort:
        rc = TQString::compare(i1->sortEntryOrder(), i2->sortEntryOrder());
        break;

      case TypeSort:
        rc = i1->sortType() - i2->sortType();
        break;

      case CategorySort:
        rc = TQString::localeAwareCompare(i1->sortCategory(), i2->sortCategory());
        break;

      case ReconcileStateSort:
        rc = static_cast<int>(i1->sortReconcileState()) - static_cast<int>(i2->sortReconcileState());
        break;

      case SecuritySort:
        rc = TQString::localeAwareCompare(i1->sortSecurity(), i2->sortSecurity());
        break;

      default:
        tqDebug("Invalid sort key %d", *it);
        break;
    }

    // the items differ for this sort key so we can return a result
    if(rc != 0)
      return (*it < 0) ? rc >= 0 : rc < 0;
  }

  if(rc == 0) {
    rc = TQString::compare(i1->sortEntryOrder(), i2->sortEntryOrder());
  }

  return rc < 0;
}

GroupMarker::GroupMarker(Register *parent, const TQString& txt) :
  RegisterItem(parent),
  m_txt(txt),
  m_drawCounter(parent->drawCounter()-1),   // make sure we get painted the first time around
  m_showDate(false)
{
  int h;
  if(m_parent) {
    h = m_parent->rowHeightHint();
  } else {
    TQFontMetrics fm( KMyMoneyGlobalSettings::listCellFont() );
    h = fm.lineSpacing()+6;
  }

  if(m_bg && (m_bg->height() != h)) {
    delete m_bg;
    m_bg = 0;
  }

  // convert the backgroud once
  if(m_bg == 0) {
    m_bg = new TQPixmap;
    m_bg->loadFromData(fancymarker_bg_image, sizeof(fancymarker_bg_image));

    // for now, we can't simply resize the m_bg member as TQt does not support
    // alpha resizing. So we take the (slow) detour through a TQImage object
    // which is ok, since we do this only once.
    // m_bg->resize(m_bg->width(), h);
    TQImage img(m_bg->convertToImage());
    img = img.smoothScale(img.width(), h);
    m_bg->convertFromImage(img);
  }

  ++m_bgRefCnt;
}

GroupMarker::~GroupMarker()
{
  --m_bgRefCnt;
  if(!m_bgRefCnt) {
    delete m_bg;
    m_bg = 0;
  }
}

void GroupMarker::paintRegisterCell(TQPainter* painter, int row, int /* col */, const TQRect& _r, bool /*selected*/, const TQColorGroup& _cg)
{
  // avoid painting the marker twice for the same update round
  unsigned int drawCounter = m_parent->drawCounter();
  if(m_drawCounter == drawCounter) {
    return;
  }
  m_drawCounter = drawCounter;

  TQRect r(_r);
  painter->save();
  painter->translate(-r.x(), -r.y());

  // the group marker always uses all cols
  r.setX(m_parent->columnPos(0));
  r.setWidth(m_parent->visibleWidth());
  painter->translate(r.x(), r.y());

  TQRect cellRect;
  cellRect.setX(0);
  cellRect.setY(0);
  cellRect.setWidth(m_parent->visibleWidth());
  cellRect.setHeight(m_parent->rowHeight(row + m_startRow));

  // clear out cell rectangle
  TQColorGroup cg(_cg);
  setupColors(cg);

  TQBrush backgroundBrush(cg.base());
  painter->fillRect(cellRect, backgroundBrush);
  painter->setPen(KMyMoneyGlobalSettings::listGridColor());
  painter->drawLine(cellRect.x(), cellRect.height()-1, cellRect.width(), cellRect.height()-1);

  // now write the text
  painter->setPen(cg.text());
  TQFont font = painter->font();
  font.setBold(true);
  painter->setFont(font);

  painter->drawText(cellRect, TQt::AlignVCenter | TQt::AlignCenter, m_txt);

  cellRect.setHeight(m_bg->height());
  int curWidth = m_bg->width();

  // if the background image is too small (not wide enough) we need to increase its width.
  if(curWidth < cellRect.width()) {
    TQPixmap* newPic = new TQPixmap(cellRect.width(), cellRect.height());
    int x = 0;
    while(x < cellRect.width()) {
      copyBlt(newPic, x, 0, m_bg, 0, 0, curWidth, m_bg->height());
      x += curWidth;
    }
    delete m_bg;
    m_bg = newPic;
  }

  // now it's time to draw the background
  painter->drawPixmap(cellRect, *m_bg);

  // translate back
  painter->translate(-r.x(), -r.y());

  // in case we need to show the date, we just paint it in col 1
  if(m_showDate) {
    r.setX(m_parent->columnPos(1));
    r.setWidth(m_parent->columnWidth(1));
    painter->translate(r.x(), r.y());

    cellRect.setX(0);
    cellRect.setY(0);
    cellRect.setWidth(m_parent->columnWidth(1));
    cellRect.setHeight(m_parent->rowHeight(row + m_startRow));

    font.setBold(false);
    painter->setFont(font);
    painter->drawText(cellRect, TQt::AlignVCenter | TQt::AlignCenter, TDEGlobal::locale()->formatDate(sortPostDate(), true));
  }

  painter->restore();
}

void GroupMarker::setupColors(TQColorGroup& cg)
{
  cg.setColor(TQColorGroup::Base, KMyMoneyGlobalSettings::groupMarkerColor());
}

int GroupMarker::rowHeightHint(void) const
{
  if(!m_visible)
    return 0;

  return m_bg->height();
}

StatementGroupMarker::StatementGroupMarker(Register* parent, CashFlowDirection dir, const TQDate& date, const TQString& txt) :
  FancyDateGroupMarker(parent, date, txt),
  m_dir(dir)
{
  m_showDate = true;
}

FancyDateGroupMarker::FancyDateGroupMarker(Register* parent, const TQDate& date, const TQString& txt) :
  GroupMarker(parent, txt),
  m_date(date)
{
}

FiscalYearGroupMarker::FiscalYearGroupMarker(Register* parent, const TQDate& date, const TQString& txt) :
    FancyDateGroupMarker(parent, date, txt)
{
}

void FiscalYearGroupMarker::setupColors(TQColorGroup& cg)
{
  cg.setColor(TQColorGroup::Base, KMyMoneyGlobalSettings::groupMarkerColor());
}


SimpleDateGroupMarker::SimpleDateGroupMarker(Register* parent, const TQDate& date, const TQString& txt) :
  FancyDateGroupMarker(parent, date, txt)
{
}

int SimpleDateGroupMarker::rowHeightHint(void) const
{
  if(!m_visible)
    return 0;

  return RegisterItem::rowHeightHint() / 2;
}

void SimpleDateGroupMarker::paintRegisterCell(TQPainter* painter, int row, int /*col*/, const TQRect& _r, bool /*selected*/, const TQColorGroup& _cg)
{
  TQRect r(_r);
  painter->save();
  painter->translate(-r.x(), -r.y());

  // the group marker always uses all cols
  r.setX(m_parent->columnPos(0));
  r.setWidth(m_parent->visibleWidth());
  painter->translate(r.x(), r.y());

  TQRect cellRect;
  cellRect.setX(0);
  cellRect.setY(0);
  cellRect.setWidth(m_parent->visibleWidth());
  cellRect.setHeight(m_parent->rowHeight(row + m_startRow));

  // clear out cell rectangle
  TQColorGroup cg(_cg);
  if(m_alternate)
    cg.setColor(TQColorGroup::Base, KMyMoneyGlobalSettings::listColor());
  else
    cg.setColor(TQColorGroup::Base, KMyMoneyGlobalSettings::listBGColor());
  TQBrush backgroundBrush(cg.base());
  // backgroundBrush.setStyle(TQt::DiagCrossPattern);
  backgroundBrush.setStyle(TQt::Dense5Pattern);
  backgroundBrush.setColor(KMyMoneyGlobalSettings::listGridColor());
  painter->eraseRect(cellRect);
  painter->fillRect(cellRect, backgroundBrush);
  painter->setPen(KMyMoneyGlobalSettings::listGridColor());
  painter->drawLine(cellRect.x(), cellRect.height()-1, cellRect.width(), cellRect.height()-1);

  painter->restore();
}

TypeGroupMarker::TypeGroupMarker(Register* parent, CashFlowDirection dir, MyMoneyAccount::accountTypeE accType) :
  GroupMarker(parent),
  m_dir(dir)
{
  switch(dir) {
    case Deposit:
      m_txt = i18n("Deposits onto account", "Deposits");
      if(accType == MyMoneyAccount::CreditCard) {
        m_txt = i18n("Payments towards credit card", "Payments");
      }
      break;
    case Payment:
      m_txt = i18n("Payments made from account", "Payments");
      if(accType == MyMoneyAccount::CreditCard) {
        m_txt = i18n("Payments made with credit card", "Charges");
      }
      break;
    default:
      tqDebug("Unknown CashFlowDirection %d for TypeGroupMarker constructor", dir);
      break;
  }
}

PayeeGroupMarker::PayeeGroupMarker(Register* parent, const TQString& name) :
  GroupMarker(parent, name)
{
}

CategoryGroupMarker::CategoryGroupMarker(Register* parent, const TQString& category) :
  GroupMarker(parent, category)
{
}

ReconcileGroupMarker::ReconcileGroupMarker(Register* parent, MyMoneySplit::reconcileFlagE state) :
  GroupMarker(parent),
  m_state(state)
{
  switch(state) {
    case MyMoneySplit::NotReconciled:
      m_txt = i18n("Reconcile state 'Not reconciled'", "Not reconciled");
      break;
    case MyMoneySplit::Cleared:
      m_txt = i18n("Reconcile state 'Cleared'", "Cleared");
      break;
    case MyMoneySplit::Reconciled:
      m_txt = i18n("Reconcile state 'Reconciled'", "Reconciled");
      break;
    case MyMoneySplit::Frozen:
      m_txt = i18n("Reconcile state 'Frozen'", "Frozen");
      break;
    default:
      m_txt = i18n("Unknown");
      break;
  }
}

class RegisterToolTip : public TQToolTip
{
public:
  RegisterToolTip(TQWidget* parent, Register* reg);
  void maybeTip(const TQPoint& pos);
  virtual ~RegisterToolTip() {}

private:
  Register* m_register;
};

RegisterToolTip::RegisterToolTip(TQWidget* parent, Register * reg) :
  TQToolTip(parent),
  m_register(reg)
{
}

void RegisterToolTip::maybeTip(const TQPoint& pos)
{
  // if we update the register, there's no need to show tooltips
  if(!m_register->isUpdatesEnabled())
    return;

  TQPoint cpos = m_register->viewportToContents(pos);
  // tqDebug("RegisterToolTip::mayBeTip(%d,%d)", cpos.x(), cpos.y());
  int row = m_register->rowAt(cpos.y());
  int col = m_register->columnAt(cpos.x());
  RegisterItem* item = m_register->itemAtRow(row);
  if(!item)
    return;

  TQPoint relPos(cpos.x() - m_register->columnPos(0), cpos.y() - m_register->rowPos(item->startRow()));
  row = row - item->startRow();

  // tqDebug("row = %d, col = %d", row, col);
  // tqDebug("relpos = %d,%d", relPos.x(), relPos.y());
  TQString msg;
  TQRect rect;
  if(!item->maybeTip(cpos, row, col, rect, msg))
    return;

  TQPoint tl(rect.topLeft());
  TQPoint br(rect.bottomRight());
  TQRect r = TQRect(m_register->contentsToViewport(tl), m_register->contentsToViewport(br));
  tip(r, msg);
  return;
}

Register::Register(TQWidget *parent, const char *name ) :
  TransactionEditorContainer(parent, name),
  m_selectAnchor(0),
  m_focusItem(0),
  m_firstItem(0),
  m_lastItem(0),
  m_firstErronous(0),
  m_lastErronous(0),
  m_markErronousTransactions(0),
  m_rowHeightHint(0),
  m_ledgerLensForced(false),
  m_selectionMode(Multi),
  m_listsDirty(false),
  m_ignoreNextButtonRelease(false),
  m_needInitialColumnResize(false),
  m_buttonState(TQt::ButtonState(0)),
  m_drawCounter(0)
{
  m_tooltip = new RegisterToolTip(viewport(), this);

  setNumCols(MaxColumns);
  setCurrentCell(0, 1);
  // we do our own sorting
  setSorting(false);

  // keep the following list in sync with KMyMoneyRegister::Column in transaction.h
  horizontalHeader()->setLabel(NumberColumn, i18n("No."));
  horizontalHeader()->setLabel(DateColumn, i18n("Date"));
  horizontalHeader()->setLabel(AccountColumn, i18n("Account"));
  horizontalHeader()->setLabel(SecurityColumn, i18n("Security"));
  horizontalHeader()->setLabel(DetailColumn, i18n("Details"));
  horizontalHeader()->setLabel(ReconcileFlagColumn, i18n("C"));
  horizontalHeader()->setLabel(PaymentColumn, i18n("Payment"));
  horizontalHeader()->setLabel(DepositColumn, i18n("Deposit"));
  horizontalHeader()->setLabel(QuantityColumn, i18n("Quantity"));
  horizontalHeader()->setLabel(PriceColumn, i18n("Price"));
  horizontalHeader()->setLabel(ValueColumn, i18n("Value"));
  horizontalHeader()->setLabel(BalanceColumn, i18n("Balance"));

  setLeftMargin(0);
  verticalHeader()->hide();

  for(int i = 0; i < numCols(); ++i)
    setColumnStretchable(i, false);

  horizontalHeader()->setResizeEnabled(false);
  horizontalHeader()->setMovingEnabled(false);
  horizontalHeader()->setClickEnabled(false);

  horizontalHeader()->installEventFilter(this);

  // never show horizontal scroll bars
  setHScrollBarMode(TQScrollView::AlwaysOff);

  connect(this, TQ_SIGNAL(clicked(int, int, int, const TQPoint&)), this, TQ_SLOT(selectItem(int, int, int, const TQPoint&)));
  connect(this, TQ_SIGNAL(doubleClicked(int, int, int, const TQPoint&)), this, TQ_SLOT(slotDoubleClicked(int, int, int, const TQPoint&)));

  // double clicking the header turns on auto column sizing
  connect(horizontalHeader(), TQ_SIGNAL(sectionSizeChanged(int)), this, TQ_SLOT(slotAutoColumnSizing(int)));

  //DND
  setAcceptDrops(true);
}

// DND
Transaction* Register::dropTransaction(TQPoint cPos) const
{
  Transaction* t = 0;
  cPos -= TQPoint( verticalHeader()->width(), horizontalHeader()->height() );
  if(cPos.y() >= 0) {
    cPos += TQPoint(contentsX(), contentsY());
    int row = rowAt(cPos.y());
    t = dynamic_cast<Transaction*>(itemAtRow(row));
  }
  return t;
}

void Register::dragMoveEvent(TQDragMoveEvent* event)
{
  if ( KURLDrag::canDecode(event) ) {
    event->ignore();
    Transaction* t = dropTransaction(event->pos());
    if(t && !t->isScheduled()) {
      event->accept();
    }
  }
}

void Register::dropEvent(TQDropEvent* event)
{
  tqDebug("Register::dropEvent");
  if ( KURLDrag::canDecode(event) ) {
    event->ignore();
    Transaction* t = dropTransaction(event->pos());
    if(t && !t->isScheduled()) {
      tqDebug("Drop was ok");
      KURL::List urls;
      KURLDrag::decode(event, urls);
      tqDebug(TQString("List is '%s'").arg(urls.toStringList().join(";")));
      event->accept();
    }
  }
}
// DND end


Register::~Register()
{
  clear();
  delete m_tooltip;
  m_tooltip = 0;
}

void Register::slotAutoColumnSizing(int section)
{
  Q_UNUSED(section)
#if 0
  // this is some trial code to make the col sizes adjustable
  // there are some drawbacks though: what when we have a register
  // but no account? (ipwizard 2007-11-06)
  if(isUpdatesEnabled()) {
    int w = visibleWidth();
    TQString size;
    for(int i=0; i < numCols(); ++i) {
      if(i)
        size += ",";
      if(i == DetailColumn) {
        size += "0";
        continue;
      }
      size += TQString("%1").arg((columnWidth(i) * 100) / w);
    }
    tqDebug(TQString("size = %1").arg(size));
    m_account.setValue("kmm-ledger-column-width", size);
  }
#endif
}

bool Register::eventFilter(TQObject* o, TQEvent* e)
{
  if(o == horizontalHeader() && e->type() == TQEvent::MouseButtonPress) {
    TQMouseEvent *me = dynamic_cast<TQMouseEvent*>(e);
    if(me->button() == TQt::RightButton) {
      emit headerClicked();
    }
    // eat up left mouse button press for now
    return true;

  } else if(o == horizontalHeader() && e->type() == TQEvent::Paint) {
    // always show the header in bold (to suppress cell selection)
    TQFont f(horizontalHeader()->font());
    f.setBold(true);
    horizontalHeader()->setFont(f);

  } else if(o == this && e->type() == TQEvent::KeyPress) {
    TQKeyEvent* ke = dynamic_cast<TQKeyEvent*>(e);
    if(ke->key() == TQt::Key_Menu) {
      emit openContextMenu();
      return true;
    }
  }

  return TQTable::eventFilter(o, e);
}

void Register::setupRegister(const MyMoneyAccount& account, const TQValueList<Column>& cols)
{
  m_account = account;
  bool enabled = isUpdatesEnabled();
  setUpdatesEnabled(false);

  for(int i = 0; i < MaxColumns; ++i)
    hideColumn(i);

  m_needInitialColumnResize = true;

  m_lastCol = static_cast<Column>(0);
  TQValueList<Column>::const_iterator it_c;
  for(it_c = cols.begin(); it_c != cols.end(); ++it_c) {
    if((*it_c) > MaxColumns)
      continue;
    showColumn(*it_c);
    if(*it_c > m_lastCol)
      m_lastCol = *it_c;
  }

  setUpdatesEnabled(enabled);
}

void Register::setupRegister(const MyMoneyAccount& account, bool showAccountColumn)
{
  m_account = account;
  bool enabled = isUpdatesEnabled();
  setUpdatesEnabled(false);

  for(int i = 0; i < MaxColumns; ++i)
    hideColumn(i);

  horizontalHeader()->setLabel(PaymentColumn, i18n("Payment made from account", "Payment"));
  horizontalHeader()->setLabel(DepositColumn, i18n("Deposit into account", "Deposit"));

  if(account.id().isEmpty()) {
    setUpdatesEnabled(enabled);
    return;
  }

  m_needInitialColumnResize = true;

  // turn on standard columns
  showColumn(DateColumn);
  showColumn(DetailColumn);
  showColumn(ReconcileFlagColumn);

  // balance
  switch(account.accountType()) {
    case MyMoneyAccount::Stock:
      break;
    default:
      showColumn(BalanceColumn);
      break;
  }

  // Number column
  switch(account.accountType()) {
    case MyMoneyAccount::Savings:
    case MyMoneyAccount::Cash:
    case MyMoneyAccount::Loan:
    case MyMoneyAccount::AssetLoan:
    case MyMoneyAccount::Asset:
    case MyMoneyAccount::Liability:
    case MyMoneyAccount::Equity:
      if(KMyMoneyGlobalSettings::alwaysShowNrField())
        showColumn(NumberColumn);
      break;

    case MyMoneyAccount::Checkings:
    case MyMoneyAccount::CreditCard:
      showColumn(NumberColumn);
      break;

    default:
      hideColumn(NumberColumn);
      break;
  }

  switch(account.accountType()) {
    case MyMoneyAccount::Income:
    case MyMoneyAccount::Expense:
      showAccountColumn = true;
      break;
    default:
      break;
  }

  if(showAccountColumn)
    showColumn(AccountColumn);

  // Security, activity, payment, deposit, amount, price and value column
  switch(account.accountType()) {
    default:
      showColumn(PaymentColumn);
      showColumn(DepositColumn);
      break;

    case MyMoneyAccount::Investment:
      showColumn(SecurityColumn);
      showColumn(QuantityColumn);
      showColumn(PriceColumn);
      showColumn(ValueColumn);
      break;
  }

  // headings
  switch(account.accountType()) {
    case MyMoneyAccount::CreditCard:
      horizontalHeader()->setLabel(PaymentColumn, i18n("Payment made with credit card", "Charge"));
      horizontalHeader()->setLabel(DepositColumn, i18n("Payment towards credit card", "Payment"));
      break;
    case MyMoneyAccount::Asset:
    case MyMoneyAccount::AssetLoan:
      horizontalHeader()->setLabel(PaymentColumn, i18n("Decrease of asset/liability value", "Decrease"));
      horizontalHeader()->setLabel(DepositColumn, i18n("Increase of asset/liability value", "Increase"));
      break;
    case MyMoneyAccount::Liability:
    case MyMoneyAccount::Loan:
      horizontalHeader()->setLabel(PaymentColumn, i18n("Increase of asset/liability value", "Increase"));
      horizontalHeader()->setLabel(DepositColumn, i18n("Decrease of asset/liability value", "Decrease"));
      break;
    case MyMoneyAccount::Income:
    case MyMoneyAccount::Expense:
      horizontalHeader()->setLabel(PaymentColumn, i18n("Income"));
      horizontalHeader()->setLabel(DepositColumn, i18n("Expense"));
      break;

    default:
      break;
  }

  switch(account.accountType()) {
    default:
      m_lastCol = BalanceColumn;
      break;
  }

  setUpdatesEnabled(enabled);
}

bool Register::focusNextPrevChild(bool next)
{
  return TQFrame::focusNextPrevChild(next);
}

void Register::setSortOrder(const TQString& order)
{
  TQStringList orderList = TQStringList::split(",", order);
  TQStringList::const_iterator it;
  m_sortOrder.clear();
  for(it = orderList.begin(); it != orderList.end(); ++it) {
    m_sortOrder << static_cast<TransactionSortField>((*it).toInt());
  }
}

void Register::sortItems(void)
{
  if(m_items.count() == 0)
    return;

  // sort the array of pointers to the transactions
  m_items.sort();

  // update the next/prev item chains
  RegisterItem* prev = 0;
  RegisterItem* item;
  m_firstItem = m_lastItem = 0;
  for(TQValueVector<RegisterItem*>::size_type i = 0; i < m_items.size(); ++i) {
    item = m_items[i];
    if(!item)
      continue;

    if(!m_firstItem)
      m_firstItem = item;
    m_lastItem = item;
    if(prev)
      prev->setNextItem(item);
    item->setPrevItem(prev);
    item->setNextItem(0);
    prev = item;
  }

  // update the balance visibility settings
  item = m_lastItem;
  bool showBalance = true;
  while(item) {
    Transaction* t = dynamic_cast<Transaction*>(item);
    if(t) {
      t->setShowBalance(showBalance);
      if(!t->isVisible()) {
        showBalance = false;
      }
    }
    item = item->prevItem();
  }

  // force update of the item index (row to item array)
  m_listsDirty = true;
}

TransactionSortField Register::primarySortKey(void) const
{
  if(!m_sortOrder.isEmpty())
    return static_cast<KMyMoneyRegister::TransactionSortField>(abs(m_sortOrder.first()));
  return UnknownSort;
}


void Register::clear(void)
{
  m_firstErronous = m_lastErronous = 0;
  m_ensureVisibleItem = 0;

  RegisterItem* p;
  while((p = firstItem()) != 0) {
    delete p;
  }
  m_items.clear();

  m_firstItem = m_lastItem = 0;

  m_listsDirty = true;
  m_selectAnchor = 0;
  m_focusItem = 0;

#ifndef KMM_DESIGNER
  // recalculate row height hint
  TQFontMetrics fm( KMyMoneyGlobalSettings::listCellFont() );
  m_rowHeightHint = fm.lineSpacing()+6;
#endif

  m_needInitialColumnResize = true;
}

void Register::insertItemAfter(RegisterItem*p, RegisterItem* prev)
{
  RegisterItem* next = 0;
  if(!prev)
    prev = lastItem();

  if(prev) {
    next = prev->nextItem();
    prev->setNextItem(p);
  }
  if(next)
    next->setPrevItem(p);

  p->setPrevItem(prev);
  p->setNextItem(next);

  if(!m_firstItem)
    m_firstItem = p;
  if(!m_lastItem)
    m_lastItem = p;

  if(prev == m_lastItem)
    m_lastItem = p;

  m_listsDirty = true;
}

void Register::addItem(RegisterItem* p)
{
  RegisterItem* q = lastItem();
  if(q)
    q->setNextItem(p);
  p->setPrevItem(q);
  p->setNextItem(0);

  m_items.append(p);

  if(!m_firstItem)
    m_firstItem = p;
  m_lastItem = p;
  m_listsDirty = true;
}

void Register::removeItem(RegisterItem* p)
{
  // remove item from list
  if(p->prevItem())
    p->prevItem()->setNextItem(p->nextItem());
  if(p->nextItem())
    p->nextItem()->setPrevItem(p->prevItem());

  // update first and last pointer if required
  if(p == m_firstItem)
    m_firstItem = p->nextItem();
  if(p == m_lastItem)
    m_lastItem = p->prevItem();

  // make sure we don't do it twice
  p->setNextItem(0);
  p->setPrevItem(0);

  // remove it from the m_items array
  for(TQValueVector<RegisterItem*>::size_type i = 0; i < m_items.size(); ++i) {
    RegisterItem* item = m_items[i];
    if(!item)
      continue;
    if(item == p) {
      m_items[i] = 0;
      break;
    }
  }
  m_listsDirty = true;
}

RegisterItem* Register::firstItem(void) const
{
  return m_firstItem;
}

RegisterItem* Register::lastItem(void) const
{
  return m_lastItem;
}

void Register::setupItemIndex(int rowCount)
{
  // setup index array
  m_itemIndex.clear();
  m_itemIndex.reserve(rowCount);

  // fill index array
  rowCount = 0;
  RegisterItem* prev = 0;
  m_firstItem = m_lastItem = 0;
  for(TQValueVector<RegisterItem*>::size_type i = 0; i < m_items.size(); ++i) {
    RegisterItem* item = m_items[i];
    if(!item)
      continue;
    if(!m_firstItem)
      m_firstItem = item;
    m_lastItem = item;
    if(prev)
      prev->setNextItem(item);
    item->setPrevItem(prev);
    item->setNextItem(0);
    prev = item;
    for(int j = item->numRowsRegister(); j; --j) {
      m_itemIndex.push_back(item);
    }
  }
}

void Register::drawContents( TQPainter *p, int cx, int cy, int cw, int ch )
{
  // the TQTable::drawContents() method does not honor the block update flag
  // so we take care of it here
  if ( testWState((TQt::WidgetState)(WState_Visible|WState_BlockUpdates)) != WState_Visible )
    return;

  if(m_listsDirty) {
    updateRegister(KMyMoneyGlobalSettings::ledgerLens() | !KMyMoneyGlobalSettings::transactionForm());
  }

  ++m_drawCounter;
  TQTable::drawContents(p, cx, cy, cw, ch);
}

void Register::updateAlternate(void) const
{
  bool alternate = false;
  for(TQValueVector<RegisterItem*>::size_type i = 0; i < m_items.size(); ++i) {
    RegisterItem* item = m_items[i];
    if(!item)
      continue;
    if(item->isVisible()) {
      item->setAlternate(alternate);
      alternate ^= true;
    }
  }
}

void Register::suppressAdjacentMarkers(void)
{
  bool lastWasGroupMarker = false;
  KMyMoneyRegister::RegisterItem* p = lastItem();
  KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(p);
  if(t && t->transaction().id().isEmpty()) {
    lastWasGroupMarker = true;
    p = p->prevItem();
  }
  while(p) {
    KMyMoneyRegister::GroupMarker* m = dynamic_cast<KMyMoneyRegister::GroupMarker*>(p);
    if(m) {
      // make adjacent group marker invisible except those that show statement information
      if(lastWasGroupMarker && (dynamic_cast<KMyMoneyRegister::StatementGroupMarker*>(m) == 0)) {
        m->setVisible(false);
      }
      lastWasGroupMarker = true;
    } else if(p->isVisible())
      lastWasGroupMarker = false;
    p = p->prevItem();
  }
}

void Register::updateRegister(bool forceUpdateRowHeight)
{
  ::timetrace("Update register");
  if(m_listsDirty || forceUpdateRowHeight) {
    // don't get in here recursively
    m_listsDirty = false;

    int rowCount = 0;
    // determine the number of rows we need to display all items
    // while going through the list, check for erronous transactions
    for(TQValueVector<RegisterItem*>::size_type i = 0; i < m_items.size(); ++i) {
      RegisterItem* item = m_items[i];
      if(!item)
        continue;
      item->setStartRow(rowCount);
      item->setNeedResize();
      rowCount += item->numRowsRegister();

      if(item->isErronous()) {
        if(!m_firstErronous)
          m_firstErronous = item;
        m_lastErronous = item;
      }
    }

    updateAlternate();

    // create item index
    setupItemIndex(rowCount);

    bool needUpdateHeaders = (numRows() != rowCount) | forceUpdateRowHeight;

    // setup TQTable.  Make sure to suppress screen updates for now
    bool updatesEnabled = isUpdatesEnabled();
    setUpdatesEnabled(false);
    setNumRows(rowCount);

    // if we need to update the headers, we do it now for all rows
    // again we make sure to suppress screen updates
    if(needUpdateHeaders) {
      // int height = rowHeightHint();

      verticalHeader()->setUpdatesEnabled(false);

      for(int i = 0; i < rowCount; ++i) {
        RegisterItem* item = itemAtRow(i);
        if(item->isVisible()) {
          showRow(i);
        } else {
          hideRow(i);
        }
        verticalHeader()->resizeSection(i, item->rowHeightHint());
      }
      verticalHeader()->setUpdatesEnabled(true);
    }

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

    setUpdatesEnabled(updatesEnabled);

    // force resizeing of the columns if necessary
    if(m_needInitialColumnResize) {
      TQTimer::singleShot(0, this, TQ_SLOT(resize()));
      m_needInitialColumnResize = false;
    } else {
      updateContents();

      // if the number of rows changed, we might need to resize the register
      // to make sure we reflect the current visibility of the scrollbars.
      if(needUpdateHeaders)
        TQTimer::singleShot(0, this, TQ_SLOT(resize()));
    }
  }
  ::timetrace("Done updateing register");
}

int Register::rowHeightHint(void) const
{
  if(!m_rowHeightHint) {
    tqDebug("Register::rowHeightHint(): m_rowHeightHint is zero!!");
  }
  return m_rowHeightHint;
}

void Register::paintCell(TQPainter* painter, int row, int col, const TQRect& r, bool selected, const TQColorGroup& cg)
{
  // determine the item that we need to paint in the row and call it's paintRegisterCell() method
  if((row < 0) || ((unsigned)row > m_itemIndex.size())) {
    tqDebug("Register::paintCell: row %d out of bounds %d", row, (int)m_itemIndex.size());
    return;
  }

  // tqDebug("paintCell(%d,%d)", row, col);
  RegisterItem* const item = m_itemIndex[row];
  item->paintRegisterCell(painter, row - item->startRow(), col, r, selected, cg);
}

void Register::focusInEvent(TQFocusEvent* ev)
{
  TQTable::focusInEvent(ev);
  if(m_focusItem) {
    m_focusItem->setFocus(true, false);
    repaintItems(m_focusItem);
  }
}

void Register::focusOutEvent(TQFocusEvent* ev)
{
  if(m_focusItem) {
    m_focusItem->setFocus(false, false);
    repaintItems(m_focusItem);
  }
  TQTable::focusOutEvent(ev);
}

void Register::resize(void)
{
  resize(DetailColumn);
}

void Register::resize(int col)
{
  bool enabled = isUpdatesEnabled();
  setUpdatesEnabled(false);

  // resize the register
  int w = visibleWidth();

  // TODO I was playing a bit with manual ledger resizing but could not get
  // a good solution. I just leave the code around, so that maybe others
  // pick it up again.  So far, it's not clear to me where to store the
  // size of the sections:
  //
  // a) with the account (as it is done now)
  // b) with the application for the specific account type
  // c) ????
  //
  // Ideas are welcome (ipwizard: 2007-07-19)
  // Note: currently there's no way to switch back to automatic
  // column sizing once the manual sizing option has been saved
#if 0
  if(m_account.value("kmm-ledger-column-width").isEmpty()) {
#endif

    // check which space we need
    if(columnWidth(NumberColumn))
      adjustColumn(NumberColumn);
    if(columnWidth(AccountColumn))
      adjustColumn(AccountColumn);
    if(columnWidth(PaymentColumn))
      adjustColumn(PaymentColumn);
    if(columnWidth(DepositColumn))
      adjustColumn(DepositColumn);
    if(columnWidth(BalanceColumn))
      adjustColumn(BalanceColumn);
    if(columnWidth(PriceColumn))
      adjustColumn(PriceColumn);
    if(columnWidth(ValueColumn))
      adjustColumn(ValueColumn);

    // make amount columns all the same size
    // only extend the entry columns to make sure they fit
    // the widget
    int dwidth = 0;
    int ewidth = 0;
    if(ewidth < columnWidth(PaymentColumn))
      ewidth = columnWidth(PaymentColumn);
    if(ewidth < columnWidth(DepositColumn))
      ewidth = columnWidth(DepositColumn);
    if(dwidth < columnWidth(BalanceColumn))
      dwidth = columnWidth(BalanceColumn);
    if(ewidth < columnWidth(PriceColumn))
      ewidth = columnWidth(PriceColumn);
    if(dwidth < columnWidth(ValueColumn))
      dwidth = columnWidth(ValueColumn);

    int swidth = columnWidth(SecurityColumn);
    if(swidth > 0) {
      adjustColumn(SecurityColumn);
      swidth = columnWidth(SecurityColumn);
    }

#ifndef KMM_DESIGNER
    // Resize the date and money fields to either
    // a) the size required by the input widget if no transaction form is shown
    // b) the adjusted value for the input widget if the transaction form is visible
    if(!KMyMoneyGlobalSettings::transactionForm()) {
      kMyMoneyDateInput* dateField = new kMyMoneyDateInput;
      kMyMoneyEdit* valField = new kMyMoneyEdit;

      dateField->setFont(KMyMoneyGlobalSettings::listCellFont());
      setColumnWidth(DateColumn, dateField->minimumSizeHint().width());
      valField->setMinimumWidth(ewidth);
      ewidth = valField->minimumSizeHint().width();

      if(swidth > 0) {
        swidth = columnWidth(SecurityColumn) + 40;
      }
      delete valField;
      delete dateField;
    } else {
      adjustColumn(DateColumn);
    }
#endif

    if(columnWidth(PaymentColumn))
      setColumnWidth(PaymentColumn, ewidth);
    if(columnWidth(DepositColumn))
      setColumnWidth(DepositColumn, ewidth);
    if(columnWidth(BalanceColumn))
      setColumnWidth(BalanceColumn, dwidth);
    if(columnWidth(PriceColumn))
      setColumnWidth(PriceColumn, ewidth);
    if(columnWidth(ValueColumn))
      setColumnWidth(ValueColumn, dwidth);

    if(columnWidth(ReconcileFlagColumn))
      setColumnWidth(ReconcileFlagColumn, 20);

    if(swidth > 0)
      setColumnWidth(SecurityColumn, swidth);
#if 0
  // see comment above
  } else {
    TQStringList colSizes = TQStringList::split(",", m_account.value("kmm-ledger-column-width"), true);
    for(int i; i < colSizes.count(); ++i) {
      int colWidth = colSizes[i].toInt();
      if(colWidth == 0)
        continue;
      setColumnWidth(i, w * colWidth / 100);
    }
  }
#endif

  for(int i = 0; i < numCols(); ++i) {
    if(i == col)
      continue;

    w -= columnWidth(i);
  }
  setColumnWidth(col, w);

  setUpdatesEnabled(enabled);
  updateContents();
}


void Register::adjustColumn(int col)
{
#ifdef KMM_DESIGNER
  Q_UNUSED(col)
#else
  TQString msg = "%1 adjusting column %2";
  ::timetrace((msg.arg("Start").arg(col)).ascii());
  TQHeader *topHeader = horizontalHeader();
  TQFontMetrics cellFontMetrics(KMyMoneyGlobalSettings::listCellFont());

  int w = topHeader->fontMetrics().width( topHeader->label( col ) ) + 10;
  if ( topHeader->iconSet( col ) )
    w += topHeader->iconSet( col )->pixmap().width();
  w = TQMAX( w, 20 );

  int maxWidth = 0;
  switch(col) {
    case NumberColumn:
      maxWidth = cellFontMetrics.width("0123456789");
      break;
    default:
      break;
  }

  // check for date column
  if(col == DateColumn) {
    TQString txt = TDEGlobal::locale()->formatDate(TQDate(6999,12,29), true);
    int nw = cellFontMetrics.width(txt+"  ");
    w = TQMAX( w, nw );
  } else {

    // scan through the transactions
    for(unsigned i = 0; i < m_items.size(); ++i) {
      RegisterItem* const item = m_items[i];
      if(!item)
        continue;
      Transaction* t = dynamic_cast<Transaction*>(item);
      if(t) {
        int nw = t->registerColWidth(col, cellFontMetrics);
        w = TQMAX( w, nw );
        if(maxWidth) {
          if(w > maxWidth) {
            w = maxWidth;
            break;
          }
        }
      }
    }
  }

  setColumnWidth( col, w );
#endif
}

void Register::repaintItems(RegisterItem* first, RegisterItem* last)
{
  if(first == 0 && last == 0) {
    first = firstItem();
    last = lastItem();
  }

  if(first == 0)
    return;

  if(last == 0)
    last = first;

  // tqDebug("repaintItems from row %d to row %d", first->startRow(), last->startRow()+last->numRowsRegister()-1);

  // the following code is based on code I found in
  // TQTable::cellGeometry() and TQTable::updateCell()  (ipwizard)
  TQRect cg(0,
           rowPos(first->startRow()),
           visibleWidth(),
           rowPos(last->startRow()+last->numRowsRegister()-1) - rowPos(first->startRow()) + rowHeight(last->startRow()+last->numRowsRegister()-1));

  TQRect r(contentsToViewport(TQPoint (cg.x() - 2, cg.y() - 2 )), TQSize(cg.width() + 4, cg.height() + 4 ));

  TQRect tmp = m_lastRepaintRect | r;
  if(abs(tmp.height()) > 3000) {
    // make sure that the previously triggered repaint has been done before we
    // trigger the next. Not having this used to cause some trouble when changing
    // the focus within a 2000 item ledger from the last to the first item.
    TQApplication::eventLoop()->processEvents(TQEventLoop::ExcludeUserInput, 10);
  }
  m_lastRepaintRect = r;
  TQApplication::postEvent( viewport(), new TQPaintEvent( r, FALSE ) );

}

void Register::clearSelection(void)
{
  unselectItems();
}

void Register::doSelectItems(int from, int to, bool selected)
{
  int start, end;
  // make sure start is smaller than end
  if(from <= to) {
    start = from;
    end = to;
  } else {
    start = to;
    end = from;
  }
  // make sure we stay in bounds
  if(start < 0)
    start = 0;
  if((end <= -1) || ((unsigned)end > (m_items.size()-1)))
    end = m_items.size()-1;

  RegisterItem* firstItem;
  RegisterItem* lastItem;
  firstItem = lastItem = 0;
  for(int i = start; i <= end; ++i) {
    RegisterItem* const item = m_items[i];
    if(item) {
      if(selected != item->isSelected()) {
        if(!firstItem)
          firstItem = item;
        item->setSelected(selected);
        lastItem = item;
      }
    }
  }

  // anything changed?
  if(firstItem || lastItem)
    repaintItems(firstItem, lastItem);
}

RegisterItem* Register::itemAtRow(int row) const
{
  if(row >= 0 && (unsigned)row < m_itemIndex.size()) {
    return m_itemIndex[row];
  }
  return 0;
}

int Register::rowToIndex(int row) const
{
  for(unsigned i = 0; i < m_items.size(); ++i) {
    RegisterItem* const item = m_items[i];
    if(!item)
      continue;
    if(row >= item->startRow() && row < (item->startRow() + item->numRowsRegister()))
      return i;
  }
  return -1;
}

void Register::selectedTransactions(SelectedTransactions& list) const
{
  if(m_focusItem && m_focusItem->isSelected() && m_focusItem->isVisible()) {
    Transaction* t = dynamic_cast<Transaction*>(m_focusItem);
    if(t) {
      TQString id;
      if(t->isScheduled())
        id = t->transaction().id();
      SelectedTransaction s(t->transaction(), t->split(), id);
      list << s;
    }
  }

  for(unsigned i = 0; i < m_items.size(); ++i) {
    RegisterItem* const item = m_items[i];
    // make sure, we don't include the focus item twice
    if(item == m_focusItem)
      continue;
    if(item && item->isSelected() && item->isVisible()) {
      Transaction* t = dynamic_cast<Transaction*>(item);
      if(t) {
        TQString id;
        if(t->isScheduled())
          id = t->transaction().id();
        SelectedTransaction s(t->transaction(), t->split(), id);
        list << s;
      }
    }
  }
}

TQValueList<RegisterItem*> Register::selectedItems(void) const
{
  TQValueList<RegisterItem*> list;

  RegisterItem* item = m_firstItem;
  while(item) {
    if(item && item->isSelected() && item->isVisible()) {
      list << item;
    }
    item = item->nextItem();
  }
  return list;
}

int Register::selectedItemsCount(void) const
{
  int cnt = 0;
  RegisterItem* item = m_firstItem;
  while(item) {
    if(item->isSelected() && item->isVisible())
      ++cnt;
    item = item->nextItem();
  }
  return cnt;
}

void Register::contentsMouseReleaseEvent( TQMouseEvent *e )
{
  if(m_ignoreNextButtonRelease) {
    m_ignoreNextButtonRelease = false;
    return;
  }

  m_buttonState = e->state();
  TQTable::contentsMouseReleaseEvent(e);
}

void Register::selectItem(int row, int col, int button, const TQPoint& /* mousePos */)
{
  if(row >= 0 && (unsigned)row < m_itemIndex.size()) {
    RegisterItem* item = m_itemIndex[row];

    // don't support selecting when the item has an editor
    // or the item itself is not selectable
    if(item->hasEditorOpen() || !item->isSelectable())
      return;

    TQString id = item->id();
    selectItem(item);
    // selectItem() might have changed the pointers, so we
    // need to reconstruct it here
    item = itemById(id);
    Transaction* t = dynamic_cast<Transaction*>(item);
    if(t) {
      if(!id.isEmpty()) {
        switch(button & TQt::MouseButtonMask) {
          case TQt::RightButton:
            emit openContextMenu();
            break;

          case TQt::LeftButton:
            if(t && col == ReconcileFlagColumn && selectedItemsCount() == 1 && !t->isScheduled())
              emit reconcileStateColumnClicked(t);
            break;

          default:
            break;
        }
      } else {
        emit emptyItemSelected();
      }
    }
  }
}

void Register::setAnchorItem(RegisterItem* anchorItem)
{
  m_selectAnchor = anchorItem;
}

bool Register::setFocusItem(RegisterItem* focusItem)
{
  if(focusItem && focusItem->canHaveFocus()) {
    if(m_focusItem) {
      m_focusItem->setFocus(false);
      // issue a repaint here only if we move the focus
      if(m_focusItem != focusItem)
        repaintItems(m_focusItem);
    }
    Transaction* item = dynamic_cast<Transaction*>(focusItem);
    if(m_focusItem != focusItem && item) {
      emit focusChanged(item);
    }

    m_focusItem = focusItem;
    m_focusItem->setFocus(true);
    if(m_listsDirty)
      updateRegister(KMyMoneyGlobalSettings::ledgerLens() | !KMyMoneyGlobalSettings::transactionForm());
    ensureItemVisible(m_focusItem);
    repaintItems(m_focusItem);
    return true;
  } else
    return false;
}

bool Register::setFocusToTop(void)
{
  RegisterItem* rgItem=m_firstItem;
  while (rgItem) {
    if (setFocusItem(rgItem))
      return true;
    rgItem=rgItem->nextItem();
  }
  return false;
}

void Register::selectItem(RegisterItem* item, bool dontChangeSelections)
{
  if(!item)
    return;

  // kdDebug(2) << "Register::selectItem(" << item << "): type is " << typeid(*item).name() << endl;

  TQt::ButtonState buttonState = m_buttonState;
  m_buttonState = TQt::NoButton;

  if(m_selectionMode == NoSelection)
    return;

  if(item->isSelectable()) {
    TQString id = item->id();
    TQValueList<RegisterItem*> itemList = selectedItems();
    bool okToSelect = true;
    int cnt = itemList.count();
    bool sameEntryType = true;
    if(cnt > 0) {
      if(typeid(*itemList.begin()) != typeid(item))
        sameEntryType = false;
    }

    if(buttonState & TQt::LeftButton) {
      if(!(buttonState & (TQt::ShiftButton | TQt::ControlButton))) {
        if((cnt != 1) || ((cnt == 1) && !item->isSelected())) {
          emit aboutToSelectItem(item, okToSelect);
          if(okToSelect) {
            // pointer 'item' might have changed. reconstruct it.
            item = itemById(id);
            unselectItems();
            item->setSelected(true);
            setFocusItem(item);
          }
        }
        if(okToSelect)
          m_selectAnchor = item;
      }

      if(m_selectionMode == Multi) {
        switch(buttonState & (TQt::ShiftButton | TQt::ControlButton)) {
          case TQt::ControlButton:
            okToSelect = sameEntryType;
            if(typeid(*item) == typeid(StdTransactionScheduled))
              okToSelect = false;
            // toggle selection state of current item
            emit aboutToSelectItem(item, okToSelect);
            if(okToSelect) {
              // pointer 'item' might have changed. reconstruct it.
              item = itemById(id);
              item->setSelected(!item->isSelected());
              setFocusItem(item);
            }
            break;

          case TQt::ShiftButton:
            okToSelect = sameEntryType;
            if(typeid(*item) == typeid(StdTransactionScheduled))
              okToSelect = false;
            emit aboutToSelectItem(item, okToSelect);
            if(okToSelect) {
              // pointer 'item' might have changed. reconstruct it.
              item = itemById(id);
              unselectItems();
              selectItems(rowToIndex(m_selectAnchor->startRow()), rowToIndex(item->startRow()));
              setFocusItem(item);
            }
            break;
        }
      }
    } else if(buttonState & TQt::RightButton) {
      // if the right button is pressed then only change the
      // selection if none of the Shift/Ctrl button is pressed and
      // one of the following conditions is true:
      //
      // a) single transaction is selected
      // b) multiple transactions are selected and the one to be selected is not
      if(!(buttonState & (TQt::ShiftButton | TQt::ControlButton))) {
        if((cnt > 0) && (!item->isSelected())) {
          okToSelect = sameEntryType;
          emit aboutToSelectItem(item, okToSelect);
          if(okToSelect) {
            // pointer 'item' might have changed. reconstruct it.
            item = itemById(id);
            unselectItems();
            item->setSelected(true);
            setFocusItem(item);
          }
        }
        if(okToSelect)
          m_selectAnchor = item;
      }
    } else {
      // we get here when called by application logic
      emit aboutToSelectItem(item, okToSelect);
      if(okToSelect) {
        // pointer 'item' might have changed. reconstruct it.
        item = itemById(id);
        if(!dontChangeSelections)
          unselectItems();
        item->setSelected(true);
        setFocusItem(item);
        m_selectAnchor = item;
      }
    }
    if(okToSelect) {
      SelectedTransactions list(this);
      emit selectionChanged(list);
    }
  }
}

void Register::ensureItemVisible(RegisterItem* item)
{
  if(!item)
    return;

  m_ensureVisibleItem = item;
  TQTimer::singleShot(0, this, TQ_SLOT(slotEnsureItemVisible()));
}

void Register::slotDoubleClicked(int row, int, int, const TQPoint&)
{
  if(row >= 0 && (unsigned)row < m_itemIndex.size()) {
    RegisterItem* p = m_itemIndex[row];
    if(p->isSelectable()) {
      m_ignoreNextButtonRelease = true;
      // double click to start editing only works if the focus
      // item is among the selected ones
      if(!focusItem()) {
        setFocusItem(p);
        if(m_selectionMode != NoSelection)
          p->setSelected(true);
      }

      if(m_focusItem->isSelected()) {
        // don't emit the signal right away but wait until
        // we come back to the TQt main loop
        TQTimer::singleShot(0, this, TQ_SIGNAL(editTransaction()));
      }
    }
  }
}

void Register::slotEnsureItemVisible(void)
{
  // if clear() has been called since the timer was
  // started, we just ignore the call
  if(!m_ensureVisibleItem)
    return;

  // make sure to catch latest changes
  bool enabled = isUpdatesEnabled();
  setUpdatesEnabled(false);
  updateRegister();
  setUpdatesEnabled(enabled);

  RegisterItem* item = m_ensureVisibleItem;
  RegisterItem* prev = item->prevItem();
  while(prev && !prev->isVisible())
    prev = prev->prevItem();
  RegisterItem* next = item->nextItem();
  while(next && !next->isVisible())
    next = next->nextItem();

  int rowPrev, rowNext;
  rowPrev = item->startRow();
  rowNext = item->startRow() + item->numRowsRegister() - 1;

  if(prev)
    rowPrev = prev->startRow();
  if(next)
    rowNext = next->startRow() + next->numRowsRegister() - 1;

  if(rowPrev < 0)
    rowPrev = 0;
  if(rowNext >= numRows())
    rowNext = numRows()-1;

  int wt = contentsY();           // window top
  int wh = visibleHeight();       // window height
  int lt = rowPos(rowPrev);       // top of line above lens
  int lb = rowPos(rowNext)+rowHeight(rowNext);       // bottom of line below lens

  // only update widget, if the transaction is not fully visible
  if(lt < wt || lb >= (wt + wh)) {
    if(rowPrev >= 0) {
      ensureCellVisible(rowPrev, 0);
    }

    ensureCellVisible(item->startRow(), 0);

    if(rowNext < numRows()) {
      ensureCellVisible(rowNext, 0);
    }
  }
}

TransactionSortField KMyMoneyRegister::textToSortOrder(const TQString& text)
{
  for(int idx = 1; idx < static_cast<int>(MaxSortFields); ++idx) {
    if(text == i18n(sortOrderText[idx].utf8())) {
      return static_cast<TransactionSortField>(idx);
    }
  }
  return UnknownSort;
}

const TQString KMyMoneyRegister::sortOrderToText(TransactionSortField idx)
{
  if(idx < PostDateSort || idx >= MaxSortFields)
    idx = UnknownSort;
  return i18n(sortOrderText[idx].utf8());
}

TQString Register::text(int /*row*/, int /*col*/) const
{
  return TQString("a");
}

TQWidget* Register::cellWidget(int row, int col) const
{
  // separeted here in two if()s, because this method is called for each
  // event from TQTable::eventFilter and in the most cases it is -1, -1
  if(row < 0 || col < 0)
    return 0;

  if(row > numRows() - 1 || col > numCols() - 1) {
    if(numRows() && numCols())
      tqWarning("Register::cellWidget(%d,%d) out of bounds (%d,%d)", row, col, numRows(), numCols());
    return 0;
  }

  if(!m_cellWidgets.count())
    return 0;

  TQWidget* w = 0;
  TQPair<int, int> idx = qMakePair(row, col);
  TQMap<TQPair<int, int>, TQWidget*>::const_iterator it_w;

  it_w = m_cellWidgets.find(idx);
  if(it_w != m_cellWidgets.end())
    w = *it_w;
  return w;
}

void Register::insertWidget(int row, int col, TQWidget* w)
{
  if(row < 0 || col < 0 || row > numRows() - 1 || col > numCols() - 1) {
    tqWarning("Register::insertWidget(%d,%d) out of bounds", row, col);
    return;
  }

  TQPair<int, int> idx = qMakePair(row, col);
  m_cellWidgets[idx] = w;
}

void Register::clearCellWidget(int row, int col)
{
  if(row < 0 || col < 0 || row > numRows() - 1 || col > numCols() - 1) {
    tqWarning("Register::clearCellWidget(%d,%d) out of bounds", row, col);
    return;
  }

  TQPair<int, int> idx = qMakePair(row, col);
  TQMap<TQPair<int, int>, TQWidget*>::iterator it_w;

  it_w = m_cellWidgets.find(idx);
  if(it_w != m_cellWidgets.end()) {
    (*it_w)->deleteLater();
    m_cellWidgets.remove(it_w);
  }
}

TQWidget* Register::createEditor(int /*row*/, int /*col*/, bool /*initFromCell*/) const
{
  return 0;
}

void Register::setCellContentFromEditor(int /*row*/, int /*col*/)
{
}

void Register::endEdit(int /*row*/, int /*col*/, bool /*accept*/, bool /*replace*/)
{
}

void Register::arrangeEditWidgets(TQMap<TQString, TQWidget*>& editWidgets, KMyMoneyRegister::Transaction* t)
{
  t->arrangeWidgetsInRegister(editWidgets);
  ensureItemVisible(t);
  // updateContents();
}

void Register::tabOrder(TQWidgetList& tabOrderWidgets, KMyMoneyRegister::Transaction* t) const
{
  t->tabOrderInRegister(tabOrderWidgets);
}

void Register::removeEditWidgets(TQMap<TQString, TQWidget*>& editWidgets)
{
  // remove pointers from map
  TQMap<TQString, TQWidget*>::iterator it;
  for(it = editWidgets.begin(); it != editWidgets.end(); ) {
    if((*it)->parentWidget() == this) {
      editWidgets.remove(it);
      it = editWidgets.begin();
    } else
      ++it;
  }

  // now delete the widgets
  KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(focusItem());
  for(int row = t->startRow(); row < t->startRow() + t->numRowsRegister(true); ++row) {
    for(int col = 0; col < numCols(); ++col) {
      if(cellWidget(row, col))
        clearCellWidget(row, col);
    }
    // make sure to reduce the possibly size to what it was before editing started
    setRowHeight(row, t->rowHeightHint());
  }
}

void Register::slotToggleErronousTransactions(void)
{
  // toggle switch
  m_markErronousTransactions ^= 1;

  // check if anything needs to be redrawn
  KMyMoneyRegister::RegisterItem* p = m_firstErronous;
  while(p && p->prevItem() != m_lastErronous) {
    if(p->isErronous())
      repaintItems(p);
    p = p->nextItem();
  }

  // restart timer
  TQTimer::singleShot(500, this, TQ_SLOT(slotToggleErronousTransactions()));
}

RegisterItem* Register::itemById(const TQString& id) const
{
  if(id.isEmpty())
    return m_lastItem;

  for(TQValueVector<RegisterItem*>::size_type i = 0; i < m_items.size(); ++i) {
    RegisterItem* item = m_items[i];
    if(!item)
      continue;
    if(item->id() == id)
      return item;
  }
  return 0;
}

void Register::handleItemChange(RegisterItem* old, bool shift, bool control)
{
  if(m_selectionMode == Multi) {
    if(shift) {
      selectRange(m_selectAnchor ? m_selectAnchor : old,
                  m_focusItem, false, true, (m_selectAnchor && !control) ? true : false);
    } else if(!control) {
      selectItem(m_focusItem, false);
    }
  }
}

void Register::selectRange(RegisterItem* from, RegisterItem* to, bool invert, bool includeFirst, bool clearSel)
{
  if(!from || !to)
    return;
  if(from == to && !includeFirst)
    return;
  bool swap = false;
  if(to == from->prevItem())
    swap = true;

  RegisterItem* item;
  if(!swap && from != to && from != to->prevItem()) {
    bool found = false;
    for(item = from; item; item = item->nextItem()) {
      if(item == to) {
        found = true;
        break;
      }
    }
    if(!found)
      swap = true;
  }

  if(swap) {
    item = from;
    from = to;
    to = item;
    if(!includeFirst)
      to = to->prevItem();

  } else if(!includeFirst) {
    from = from->nextItem();
  }

  bool changed = false;
  if(clearSel) {
    for(item = firstItem(); item; item = item->nextItem()) {
      if(item->isSelected() && item->isVisible()) {
        item->setSelected(false);
        changed = true;
      }
    }
  }

  for(item = from; item; item = item->nextItem()) {
    if(item->isSelectable()) {
      if(!invert) {
        if(!item->isSelected() && item->isVisible()) {
          item->setSelected(true);
          changed = true;
        }
      } else {
        bool sel = !item->isSelected();
        if((item->isSelected() != sel) && (sel || !sel)) {
          if(item->isVisible()) {
            item->setSelected(sel);
            changed = true;
          }
        }
      }
    }
    if(item == to)
      break;
  }
}

void Register::scrollPage(int key, ButtonState state)
{
  RegisterItem* oldFocusItem = m_focusItem;

  // make sure we have a focus item
  if(!m_focusItem)
    setFocusItem(m_firstItem);
  if(!m_focusItem && m_firstItem)
    setFocusItem(m_firstItem->nextItem());
  if(!m_focusItem)
    return;

  RegisterItem* item = m_focusItem;
  int height = 0;

  switch(key) {
    case TQt::Key_PageUp:
      while(height < visibleHeight() && item->prevItem()) {
        do {
          item = item->prevItem();
          if(item->isVisible())
            height += item->rowHeightHint();
        } while((!item->isSelectable() || !item->isVisible()) && item->prevItem());
      }
      break;
    case TQt::Key_PageDown:
      while(height < visibleHeight() && item->nextItem()) {
        do {
          if(item->isVisible())
            height += item->rowHeightHint();
          item = item->nextItem();
        } while((!item->isSelectable() || !item->isVisible()) && item->nextItem());
      }
      break;

    case TQt::Key_Up:
      if(item->prevItem()) {
        do {
          item = item->prevItem();
        } while((!item->isSelectable() || !item->isVisible()) && item->prevItem());
      }
      break;

    case TQt::Key_Down:
      if(item->nextItem()) {
        do {
          item = item->nextItem();
        } while((!item->isSelectable() || !item->isVisible()) && item->nextItem());
      }
      break;

    case TQt::Key_Home:
      item = m_firstItem;
      while((!item->isSelectable() || !item->isVisible()) && item->nextItem())
        item = item->nextItem();
      break;

    case TQt::Key_End:
      item = m_lastItem;
      while((!item->isSelectable() || !item->isVisible()) && item->prevItem())
        item = item->prevItem();
      break;
  }

  // make sure to avoid selecting a possible empty transaction at the end
  Transaction* t = dynamic_cast<Transaction*>(item);
  if(t && t->transaction().id().isEmpty()) {
    if(t->prevItem()) {
      item = t->prevItem();
    }
  }

  if(!(state & ShiftButton) || !m_selectAnchor)
    m_selectAnchor = item;

  setFocusItem(item);

  if(item->isSelectable()) {
    handleItemChange(oldFocusItem, state & TQt::ShiftButton, state & TQt::ControlButton);
    // tell the world about the changes in selection
    SelectedTransactions list(this);
    emit selectionChanged(list);
  }

  if(m_focusItem && !m_focusItem->isSelected() && m_selectionMode == Single)
    selectItem(item);
}

void Register::keyPressEvent(TQKeyEvent* ev)
{
  switch(ev->key()) {
    case TQt::Key_Space:
      if(m_selectionMode != NoSelection) {
        // get the state out of the event ...
        m_buttonState = ev->state();
        // ... and pretend that we have pressed the left mouse button ;)
        m_buttonState = static_cast<TQt::ButtonState>(m_buttonState | TQt::LeftButton);
        selectItem(m_focusItem);
      }
      break;

    case TQt::Key_PageUp:
    case TQt::Key_PageDown:
    case TQt::Key_Home:
    case TQt::Key_End:
    case TQt::Key_Down:
    case TQt::Key_Up:
      scrollPage(ev->key(), ev->state());
      break;

    default:
      TQTable::keyPressEvent(ev);
      break;
  }
}

Transaction* Register::transactionFactory(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId)
{
  Transaction* t = 0;
  MyMoneySplit s = split;

  if(parent->account() == MyMoneyAccount()) {
    t = new KMyMoneyRegister::StdTransaction(parent, transaction, s, uniqueId);
    return t;
  }

  switch(parent->account().accountType()) {
    case MyMoneyAccount::Checkings:
    case MyMoneyAccount::Savings:
    case MyMoneyAccount::Cash:
    case MyMoneyAccount::CreditCard:
    case MyMoneyAccount::Loan:
    case MyMoneyAccount::Asset:
    case MyMoneyAccount::Liability:
    case MyMoneyAccount::Currency:
    case MyMoneyAccount::Income:
    case MyMoneyAccount::Expense:
    case MyMoneyAccount::AssetLoan:
    case MyMoneyAccount::Equity:
      if(s.accountId().isEmpty())
        s.setAccountId(parent->account().id());
      if(s.isMatched())
        t = new KMyMoneyRegister::StdTransactionMatched(parent, transaction, s, uniqueId);
      else if(transaction.isImported())
        t = new KMyMoneyRegister::StdTransactionDownloaded(parent, transaction, s, uniqueId);
      else
        t = new KMyMoneyRegister::StdTransaction(parent, transaction, s, uniqueId);
      break;

    case MyMoneyAccount::Investment:
      if(s.isMatched())
        t = new KMyMoneyRegister::InvestTransaction/* Matched */(parent, transaction, s, uniqueId);
      else if(transaction.isImported())
        t = new KMyMoneyRegister::InvestTransactionDownloaded(parent, transaction, s, uniqueId);
      else
        t = new KMyMoneyRegister::InvestTransaction(parent, transaction, s, uniqueId);
      break;

    case MyMoneyAccount::CertificateDep:
    case MyMoneyAccount::MoneyMarket:
    case MyMoneyAccount::Stock:
    default:
      tqDebug("Register::transactionFactory: invalid accountTypeE %d", parent->account().accountType());
      break;
  }
  return t;
}

void Register::addGroupMarkers(void)
{
  TQMap<TQString, int> list;
  TQMap<TQString, int>::const_iterator it;
  KMyMoneyRegister::RegisterItem* p = firstItem();
  KMyMoneyRegister::Transaction* t;
  TQString name;
  TQDate today;
  TQDate yesterday, thisWeek, lastWeek;
  TQDate thisMonth, lastMonth;
  TQDate thisYear;
  int weekStartOfs;

  switch(primarySortKey()) {
    case KMyMoneyRegister::PostDateSort:
    case KMyMoneyRegister::EntryDateSort:
      today = TQDate::currentDate();
      thisMonth.setYMD(today.year(), today.month(), 1);
      lastMonth = thisMonth.addMonths(-1);
      yesterday = today.addDays(-1);
      // a = TQDate::dayOfWeek()      todays weekday (1 = Monday, 7 = Sunday)
      // b = TDELocale::weekStartDay() first day of week (1 = Monday, 7 = Sunday)
      weekStartOfs = today.dayOfWeek() - TDEGlobal::locale()->weekStartDay();
      if(weekStartOfs < 0) {
        weekStartOfs = 7 + weekStartOfs;
      }
      thisWeek = today.addDays(-weekStartOfs);
      lastWeek = thisWeek.addDays(-7);
      thisYear.setYMD(today.year(), 1, 1);
      if(KMyMoneyGlobalSettings::startDate().date() != TQDate(1900,1,1))
        new KMyMoneyRegister::FancyDateGroupMarker(this, KMyMoneyGlobalSettings::startDate().date(), i18n("Prior transactions possibly filtered"));

      if(KMyMoneyGlobalSettings::showFancyMarker()) {
        if(m_account.lastReconciliationDate().isValid())
          new KMyMoneyRegister::StatementGroupMarker(this, KMyMoneyRegister::Deposit, m_account.lastReconciliationDate(), i18n("Last reconciliation"));

        if(!m_account.value("lastImportedTransactionDate").isEmpty()
        && !m_account.value("lastStatementBalance").isEmpty()) {
          MyMoneyMoney balance(m_account.value("lastStatementBalance"));
          if(m_account.accountGroup() == MyMoneyAccount::Liability)
            balance = -balance;
          TQString txt = i18n("Online Statement Balance: %1").arg(balance.formatMoney(m_account.fraction()));
          new KMyMoneyRegister::StatementGroupMarker(this, KMyMoneyRegister::Deposit, TQDate::fromString(m_account.value("lastImportedTransactionDate"), TQt::ISODate), txt);
        }

        new KMyMoneyRegister::FancyDateGroupMarker(this, thisYear, i18n("This year"));
        new KMyMoneyRegister::FancyDateGroupMarker(this, lastMonth, i18n("Last month"));
        new KMyMoneyRegister::FancyDateGroupMarker(this, thisMonth, i18n("This month"));
        new KMyMoneyRegister::FancyDateGroupMarker(this, lastWeek, i18n("Last week"));
        new KMyMoneyRegister::FancyDateGroupMarker(this, thisWeek, i18n("This week"));
        new KMyMoneyRegister::FancyDateGroupMarker(this, yesterday, i18n("Yesterday"));
        new KMyMoneyRegister::FancyDateGroupMarker(this, today, i18n("Today"));
        new KMyMoneyRegister::FancyDateGroupMarker(this, today.addDays(1), i18n("Future transactions"));
        new KMyMoneyRegister::FancyDateGroupMarker(this, thisWeek.addDays(7), i18n("Next week"));
        new KMyMoneyRegister::FancyDateGroupMarker(this, thisMonth.addMonths(1), i18n("Next month"));

      } else {
        new KMyMoneyRegister::SimpleDateGroupMarker(this, today.addDays(1), i18n("Future transactions"));
      }
      if(KMyMoneyGlobalSettings::showFiscalMarker()) {
        TQDate currentFiscalYear(TQDate::currentDate().year(), KMyMoneyGlobalSettings::firstFiscalMonth(), KMyMoneyGlobalSettings::firstFiscalDay());

        if(TQDate::currentDate() < currentFiscalYear)
          currentFiscalYear = currentFiscalYear.addYears(-1);
        TQDate previousFiscalYear = currentFiscalYear.addYears(-1);
        new KMyMoneyRegister::FiscalYearGroupMarker(this, currentFiscalYear, i18n("Current fiscal year"));
        new KMyMoneyRegister::FiscalYearGroupMarker(this, currentFiscalYear.addYears(-1), i18n("Previous fiscal year"));
        new KMyMoneyRegister::FiscalYearGroupMarker(this, currentFiscalYear.addYears(1), i18n("Next fiscal year"));
      }
      break;

    case KMyMoneyRegister::TypeSort:
      if(KMyMoneyGlobalSettings::showFancyMarker()) {
        new KMyMoneyRegister::TypeGroupMarker(this, KMyMoneyRegister::Deposit, m_account.accountType());
        new KMyMoneyRegister::TypeGroupMarker(this, KMyMoneyRegister::Payment, m_account.accountType());
      }
      break;

    case KMyMoneyRegister::ReconcileStateSort:
      if(KMyMoneyGlobalSettings::showFancyMarker()) {
        new KMyMoneyRegister::ReconcileGroupMarker(this, MyMoneySplit::NotReconciled);
        new KMyMoneyRegister::ReconcileGroupMarker(this, MyMoneySplit::Cleared);
        new KMyMoneyRegister::ReconcileGroupMarker(this, MyMoneySplit::Reconciled);
        new KMyMoneyRegister::ReconcileGroupMarker(this, MyMoneySplit::Frozen);
      }
      break;

    case KMyMoneyRegister::PayeeSort:
      if(KMyMoneyGlobalSettings::showFancyMarker()) {
        while(p) {
          t = dynamic_cast<KMyMoneyRegister::Transaction*>(p);
          if(t) {
            list[t->sortPayee()] = 1;
          }
          p = p->nextItem();
        }
        for(it = list.begin(); it != list.end(); ++it) {
          name = it.key();
          if(name.isEmpty()) {
            name = i18n("Unknown payee", "Unknown");
          }
          new KMyMoneyRegister::PayeeGroupMarker(this, name);
        }
      }
      break;

    case KMyMoneyRegister::CategorySort:
      if(KMyMoneyGlobalSettings::showFancyMarker()) {
        while(p) {
          t = dynamic_cast<KMyMoneyRegister::Transaction*>(p);
          if(t) {
            list[t->sortCategory()] = 1;
          }
          p = p->nextItem();
        }
        for(it = list.begin(); it != list.end(); ++it) {
          name = it.key();
          if(name.isEmpty()) {
            name = i18n("Unknown category", "Unknown");
          }
          new KMyMoneyRegister::CategoryGroupMarker(this, name);
        }
      }
      break;

    case KMyMoneyRegister::SecuritySort:
      if(KMyMoneyGlobalSettings::showFancyMarker()) {
        while(p) {
          t = dynamic_cast<KMyMoneyRegister::InvestTransaction*>(p);
          if(t) {
            list[t->sortSecurity()] = 1;
          }
          p = p->nextItem();
        }
        for(it = list.begin(); it != list.end(); ++it) {
          name = it.key();
          if(name.isEmpty()) {
            name = i18n("Unknown security", "Unknown");
          }
          new KMyMoneyRegister::CategoryGroupMarker(this, name);
        }
      }
      break;

      default: // no markers supported
        break;
  }
}

void Register::removeUnwantedGroupMarkers(void)
{
  // remove all trailing group markers except statement markers
  KMyMoneyRegister::RegisterItem* q;
  KMyMoneyRegister::RegisterItem* p = lastItem();
  while(p) {
    q = p;
    if(dynamic_cast<KMyMoneyRegister::Transaction*>(p)
    || dynamic_cast<KMyMoneyRegister::StatementGroupMarker*>(p))
      break;

    p = p->prevItem();
    delete q;
  }

  // remove all adjacent group markers
  bool lastWasGroupMarker = false;
  p = lastItem();
  while(p) {
    q = p;
    KMyMoneyRegister::GroupMarker* m = dynamic_cast<KMyMoneyRegister::GroupMarker*>(p);
    p = p->prevItem();
    if(m) {
      m->markVisible(true);
      // make adjacent group marker invisible except those that show statement information
      if(lastWasGroupMarker && (dynamic_cast<KMyMoneyRegister::StatementGroupMarker*>(m) == 0)) {
        m->markVisible(false);
      }
      lastWasGroupMarker = true;
    } else if(q->isVisible())
      lastWasGroupMarker = false;
  }
}


#include "register.moc"
