diff options
Diffstat (limited to 'kmymoney2/widgets/transaction.cpp')
-rw-r--r-- | kmymoney2/widgets/transaction.cpp | 2189 |
1 files changed, 2189 insertions, 0 deletions
diff --git a/kmymoney2/widgets/transaction.cpp b/kmymoney2/widgets/transaction.cpp new file mode 100644 index 0000000..4c8a23b --- /dev/null +++ b/kmymoney2/widgets/transaction.cpp @@ -0,0 +1,2189 @@ +/*************************************************************************** + transaction.cpp - description + ------------------- + begin : Tue Jun 13 2006 + copyright : (C) 2000-2006 by Thomas Baumgart + email : Thomas Baumgart <ipwizard@users.sourceforge.net> + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qstring.h> +#include <qpainter.h> +#include <qwidgetlist.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klocale.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <kdebug.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/transaction.h> +#include <kmymoney/mymoneytransaction.h> +#include <kmymoney/mymoneysplit.h> +#include <kmymoney/mymoneyfile.h> +#include <kmymoney/mymoneypayee.h> +#include <kmymoney/register.h> +#include <kmymoney/kmymoneycategory.h> +#include <kmymoney/kmymoneydateinput.h> +#include <kmymoney/transactionform.h> +#include <kmymoney/kmymoneylineedit.h> +#include <kmymoney/kmymoneyedit.h> +#include <kmymoney/transactioneditor.h> +#include <kmymoney/investtransactioneditor.h> +#include <kmymoney/kmymoneyutils.h> + +#include "../kmymoneyglobalsettings.h" + +using namespace KMyMoneyRegister; +using namespace KMyMoneyTransactionForm; + +static unsigned char attentionSign[] = { + 0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A, + 0x00,0x00,0x00,0x0D,0x49,0x48,0x44,0x52, + 0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x14, + 0x08,0x06,0x00,0x00,0x00,0x8D,0x89,0x1D, + 0x0D,0x00,0x00,0x00,0x04,0x73,0x42,0x49, + 0x54,0x08,0x08,0x08,0x08,0x7C,0x08,0x64, + 0x88,0x00,0x00,0x00,0x19,0x74,0x45,0x58, + 0x74,0x53,0x6F,0x66,0x74,0x77,0x61,0x72, + 0x65,0x00,0x77,0x77,0x77,0x2E,0x69,0x6E, + 0x6B,0x73,0x63,0x61,0x70,0x65,0x2E,0x6F, + 0x72,0x67,0x9B,0xEE,0x3C,0x1A,0x00,0x00, + 0x02,0x05,0x49,0x44,0x41,0x54,0x38,0x8D, + 0xAD,0x95,0xBF,0x4B,0x5B,0x51,0x14,0xC7, + 0x3F,0x2F,0xBC,0x97,0x97,0x97,0x97,0x77, + 0xF3,0xF2,0x1C,0xA4,0x54,0x6B,0x70,0x10, + 0x44,0x70,0x2A,0x91,0x2E,0x52,0x02,0x55, + 0x8A,0xB5,0xA3,0xAB,0x38,0x08,0x66,0xCC, + 0xEE,0xE0,0xE2,0x20,0xB8,0x38,0xB8,0xB8, + 0xF8,0x1F,0x38,0x29,0xA5,0x29,0x74,0x90, + 0x0E,0x0D,0x0E,0x22,0x1D,0x44,0xA8,0xD0, + 0xD4,0xB4,0x58,0x4B,0x09,0xF9,0xF1,0x4A, + 0x3B,0xD4,0xD3,0xE1,0x55,0xD3,0x34,0xAF, + 0x49,0x6C,0x3D,0xF0,0x85,0x7B,0xCF,0xFD, + 0x9E,0xEF,0x3D,0xE7,0xFE,0xD4,0x44,0x84, + 0xDB,0xB4,0x48,0x2F,0xA4,0x94,0xAB,0xE5, + 0x52,0xAE,0x96,0xEB,0x49,0x51,0x44,0x3A, + 0x02,0x18,0x88,0xC7,0xF1,0xE3,0x71,0x7C, + 0x60,0xA0,0x1B,0xBF,0x6B,0x86,0x49,0xC5, + 0x46,0x3E,0x47,0x34,0x9F,0x23,0x9A,0x54, + 0x6C,0xFC,0x57,0x86,0x40,0xC6,0x4B,0xE1, + 0x37,0xCA,0x48,0xA3,0x8C,0x78,0x29,0x7C, + 0x20,0xD3,0x31,0xA6,0xD3,0xA0,0x52,0x1C, + 0x6D,0x6F,0x72,0xD9,0x28,0x23,0xFE,0x07, + 0x64,0x7B,0x93,0x4B,0xA5,0x38,0xFA,0x27, + 0x41,0x60,0x6E,0x74,0x84,0x7A,0xE5,0x1D, + 0x92,0x54,0x88,0xE7,0x22,0xD5,0x12,0x32, + 0x3A,0x42,0x1D,0x98,0xBB,0x91,0x20,0x60, + 0xDA,0x36,0x17,0xFB,0x7B,0xC8,0xC1,0x4B, + 0x04,0x02,0xBC,0x7E,0x81,0xEC,0xEF,0x21, + 0xB6,0xCD,0x05,0x60,0xF6,0x2C,0x68,0x9A, + 0x2C,0xCF,0x4C,0xE1,0x4B,0x05,0x39,0x3F, + 0x69,0x0A,0xBE,0x7F,0x83,0x48,0x05,0x99, + 0x99,0xC2,0x37,0x4D,0x96,0x7B,0x12,0x04, + 0xFA,0x2D,0x8B,0xC6,0xE9,0x61,0x10,0x2C, + 0x15,0xC4,0x8A,0x21,0x86,0x8E,0xFC,0xF8, + 0x12,0xF4,0x4F,0x0F,0x11,0xCB,0xA2,0x01, + 0xF4,0x77,0x3D,0x36,0x4E,0x82,0xF5,0xA5, + 0x05,0x8C,0xE1,0x74,0xD3,0x37,0x34,0x18, + 0x20,0xF2,0x8B,0x3D,0x9C,0x86,0xA5,0x05, + 0x0C,0x27,0xC1,0x7A,0xC7,0x63,0x03,0x8C, + 0x2B,0x07,0xBF,0x5A,0x6A,0x66,0x27,0x15, + 0x64,0x3A,0x8B,0x3C,0x7A,0xD8,0xEA,0xAB, + 0x96,0x10,0xE5,0xE0,0x03,0xE3,0x7F,0xCD, + 0x50,0x39,0x6C,0xAD,0xAD,0x10,0x53,0xAA, + 0x75,0xD2,0xF4,0xBD,0x00,0x2D,0x5C,0x05, + 0x6B,0x2B,0xC4,0x94,0xC3,0xD6,0xEF,0xFE, + 0x6B,0x41,0x4D,0xD3,0x66,0xFB,0x3C,0xC6, + 0x16,0xE7,0xDB,0x97,0x61,0xE2,0x3E,0x3C, + 0xC8,0xB4,0x15,0xC7,0xE2,0x3C,0x91,0x3E, + 0x8F,0x31,0x4D,0xD3,0x66,0x5B,0x4A,0x06, + 0x8C,0x84,0xCD,0x59,0x61,0xA7,0xB5,0xAC, + 0x2B,0x9C,0x1C,0x04,0x08,0x1B,0x2B,0xEC, + 0x20,0x09,0x9B,0x33,0xC0,0xB8,0xDE,0x65, + 0x43,0x27,0x9F,0x9D,0xA4,0x1E,0x16,0xF0, + 0xF9,0x6D,0xB0,0xC3,0x86,0x1E,0xB4,0xC3, + 0x38,0xD9,0x49,0xEA,0x86,0x4E,0xFE,0xEA, + 0x29,0xF4,0x2C,0x8B,0xDA,0x71,0x31,0x9C, + 0xFC,0xF5,0x23,0x32,0x34,0x88,0xDC,0xBD, + 0x13,0x5C,0xBF,0x30,0xCE,0x71,0x11,0xB1, + 0x2C,0x6A,0x80,0xA7,0xDB,0x36,0xAB,0x4F, + 0xA6,0x89,0xBA,0x49,0x38,0xFF,0xD4,0xBE, + 0x4E,0x00,0xAF,0x9E,0x81,0x08,0xD4,0xEA, + 0x01,0xFE,0x34,0x37,0x09,0x4F,0x1F,0x13, + 0xDD,0x7D,0xCE,0xAA,0x96,0x72,0x29,0x7C, + 0xFB,0xCE,0x44,0xB8,0xD4,0xCD,0x2C,0x66, + 0x52,0xD4,0x6E,0xFB,0x0B,0xF8,0x09,0x63, + 0x63,0x31,0xE4,0x85,0x76,0x2E,0x0E,0x00, + 0x00,0x00,0x00,0x49,0x45,0x4E,0x44,0xAE, + 0x42,0x60,0x82 +}; + +Transaction::Transaction(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : + RegisterItem(parent), + m_transaction(transaction), + m_split(split), + m_form(0), + m_uniqueId(m_transaction.id()), + m_formRowHeight(-1), + m_selected(false), + m_focus(false), + m_erronous(false), + m_inEdit(false), + m_inRegisterEdit(false), + m_showBalance(true), + m_reducedIntensity(false) +{ + MyMoneyFile* file = MyMoneyFile::instance(); + + // load the account + if(!m_split.accountId().isEmpty()) + m_account = file->account(m_split.accountId()); + + // load the payee + if(!m_split.payeeId().isEmpty()) { + m_payee = file->payee(m_split.payeeId()).name(); + } + if(parent->account().isIncomeExpense()) { + m_payeeHeader = m_split.shares().isNegative() ? i18n("From") : i18n("Pay to"); + } else { + m_payeeHeader = m_split.shares().isNegative() ? i18n("Pay to") : i18n("From"); + } + + // load the currency + if(!m_transaction.id().isEmpty()) + m_splitCurrencyId = m_account.currencyId(); + + // check if transaction is errnous or not + m_erronous = !m_transaction.splitSum().isZero(); + + if(!m_uniqueId.isEmpty()) { + m_uniqueId += "-"; + QString id; + id.setNum(uniqueId); + m_uniqueId += id.rightJustify(3, '0'); + } +} + +void Transaction::setFocus(bool focus, bool updateLens) +{ + if(focus != m_focus) { + m_focus = focus; + } + if(updateLens) { + if(KMyMoneyGlobalSettings::ledgerLens() + || !KMyMoneyGlobalSettings::transactionForm() + || KMyMoneyGlobalSettings::showRegisterDetailed() + || m_parent->m_ledgerLensForced) { + if(focus) + setNumRowsRegister(numRowsRegister(true)); + else + setNumRowsRegister(numRowsRegister(KMyMoneyGlobalSettings::showRegisterDetailed())); + } + } +} + +void Transaction::markAttachment(QPainter* painter, int /* row */, int /* col */, const QRect& r) +{ + static QPixmap clip; + + const int m = 2; // margin + int h = m_parent->rowHeightHint() - (2*m); + int lx = r.topRight().x() - h; + if(isErronous()) + lx -= h; + QRect cr(QPoint(lx - m, m), QSize(h, h)); + + painter->save(); + if(clip.isNull()) { + clip = KGlobal::iconLoader()->loadIcon("attach", KIcon::Small, KIcon::SizeSmall, KIcon::DefaultState); + if(clip.height() > h) { + clip.resize(h, h); + } + } + + painter->drawPixmap(QPoint(lx - m, m + (h - clip.height())/2 ), clip); + painter->restore(); +} + +void Transaction::markAsErronous(QPainter* painter, int /* row */, int /* col */, const QRect& r) +{ + const int m = 2; // margin + int h = m_parent->rowHeightHint() - (2*m); + QRect cr(QPoint(r.topRight().x() - h - m, m), QSize(h, h)); + + painter->save(); + QPixmap attention; + attention.loadFromData(attentionSign, sizeof(attentionSign), 0, 0); + + if(attention.height() > h) { + attention.resize(h, h); + } + painter->drawPixmap(QPoint(r.topRight().x() - h - m, m + (h - attention.height())/2 ), attention); + painter->restore(); + +} + +bool Transaction::paintRegisterCellSetup(QPainter* painter, int& row, int& col, QRect& cellRect, QRect& textRect, QColorGroup& cg, QBrush& brush) +{ + if(m_reducedIntensity) + cg = m_parent->palette().disabled(); + + if(m_alternate) + cg.setColor(QColorGroup::Base, KMyMoneyGlobalSettings::listColor()); + else + cg.setColor(QColorGroup::Base, KMyMoneyGlobalSettings::listBGColor()); + + cellRect.setX(0); + cellRect.setY(0); + cellRect.setWidth(m_parent->columnWidth(col)); + cellRect.setHeight(m_parent->rowHeight(m_startRow + row)); + + textRect = cellRect; + textRect.setX(2); + textRect.setWidth(textRect.width()-4); + + if(m_selected) { + brush = QBrush(cg.highlight()); + painter->setPen(cg.highlightedText()); + } else { + brush = QBrush(cg.base()); + painter->setPen(cg.text()); + } + + // do we need to switch to the error color? + if(m_erronous && m_parent->markErronousTransactions()) { + painter->setPen(KMyMoneyGlobalSettings::listErronousTransactionColor()); + } + + // do we need to switch to the negative balance color? + if(col == BalanceColumn) { + bool showNegative = m_balance.isNegative(); + if(m_account.accountGroup() == MyMoneyAccount::Liability && !m_balance.isZero() ) + showNegative = !showNegative; + if(showNegative) + painter->setPen(KMyMoneyGlobalSettings::listNegativeValueColor()); + } + return true; +} + +void Transaction::paintRegisterCellFocus(QPainter* painter, int row, int col, const QRect& r, const QColorGroup& cg) +{ + + if(m_focus) { + QPen oldPen = painter->pen(); + QPen newPen = oldPen; + newPen.setWidth(0); + + painter->setFont(KMyMoneyGlobalSettings::listCellFont()); + painter->setPen(newPen); + painter->setPen(cg.foreground()); + painter->setPen(Qt::DotLine); + // for the first Row, we need to paint the top + QPoint start, end; +#if 0 + if(row == 0) { + start = QPoint(r.x(), r.y() + 1); + end = QPoint(r.x() + r.width(), r.y() + 1); + if(col == 0) { + start.rx()++; + } else if(col == m_parent->lastCol()) { + end.rx()--; + } + // painter->drawLine(start, end); + painter->drawWinFocusRect(QRect(start, end)); + } + // for the last Row, we need to paint the bottom + if(row == numRows() - 1) { + start = QPoint(r.x(), r.y() + r.height() - 1); + end = QPoint(r.x() + r.width(), r.y() + r.height() - 1); + if(col == 0) { + start.rx()++; + } else if(col == m_parent->lastCol()) { + end.rx()--; + } + // painter->drawLine(start, end); + painter->drawWinFocusRect(QRect(start, end)); + } + // for the first col, we need to paint the left + if(col == 0) { + start = QPoint(r.x() + 1, r.y()); + end = QPoint(r.x() + 1, r.y() + r.height()); + if(row == 0) { + start.ry()++; + } else if(row == numRows()-1) { + end.ry()--; + } + //painter->drawLine(start, end); + painter->drawWinFocusRect(QRect(start, end)); + } + // for the last col, we need to paint the left + if(col == m_parent->lastCol()) { + start = QPoint(r.x() + r.width() - 1, r.y()); + end = QPoint(r.x() + r.width() - 1, r.y() + r.height()); + if(row == 0) { + start.ry()++; + } else if(row == numRows()-1) { + end.ry()--; + } + //painter->drawLine(start, end); + painter->drawWinFocusRect(QRect(start, end)); + } +#endif + if(row == 0) { + start = QPoint(r.x(), r.y()); + end = QPoint(r.x() + r.width(), r.y() + 1); + if(col == 0) { + start.rx()++; + } else if(col == m_parent->lastCol()) { + end.rx()--; + } + // painter->drawLine(start, end); + painter->drawWinFocusRect(QRect(start, end)); + } + // for the last Row, we need to paint the bottom + if(row == numRowsRegister() - 1) { + start = QPoint(r.x(), r.y() + r.height() - 2); + end = QPoint(r.x() + r.width(), r.y() + r.height() - 2); + if(col == 0) { + start.rx()++; + } else if(col == m_parent->lastCol()) { + end.rx()--; + } + // painter->drawLine(start, end); + painter->drawWinFocusRect(QRect(start, end)); + } + // for the first col, we need to paint the left + if(col == 0) { + start = QPoint(r.x() + 1, r.y()); + end = QPoint(r.x() + 1, r.y() + r.height()); + if(row == 0) { + start.ry()++; + } else if(row == numRowsRegister()-1) { + end.ry()--; + } + //painter->drawLine(start, end); + painter->drawWinFocusRect(QRect(start, end)); + } + // for the last col, we need to paint the left + if(col == m_parent->lastCol()) { + start = QPoint(r.x() + r.width() - 1, r.y()); + end = QPoint(r.x() + r.width() - 1, r.y() + r.height()); + if(row == 0) { + start.ry()++; + } else if(row == numRowsRegister()-1) { + end.ry()--; + } + //painter->drawLine(start, end); + painter->drawWinFocusRect(QRect(start, end)); + } + painter->setPen(oldPen); + } +} + +void Transaction::registerCellText(QString& txt, int row, int col) +{ + int align = 0; + registerCellText(txt, align, row, col, 0); +} + +void Transaction::paintRegisterCell(QPainter* painter, int row, int col, const QRect& r, bool /* selected */, const QColorGroup& _cg) +{ + QColorGroup cg(_cg); + QRect cellRect(r); + QRect textRect; + QBrush backgroundBrush; + + painter->save(); + + if(paintRegisterCellSetup(painter, row, col, cellRect, textRect, cg, backgroundBrush)) { + // construct the text for the cell + int align = Qt::AlignVCenter; + QString txt; + if(m_transaction != MyMoneyTransaction() && !m_inRegisterEdit) { + registerCellText(txt, align, row, col, painter); + } + + paintRegisterCellBackground(painter, row, col, cellRect, backgroundBrush); + + // and paint it + paintRegisterCellText(painter, row, col, textRect, cg, align, txt); + + // paint the grid + paintRegisterGrid(painter, row, col, cellRect, cg); + + // possible icons + paintRegisterIcons(painter, row, col, cellRect, cg); + + // and the focus + paintRegisterCellFocus(painter, row, col, cellRect, cg); + } + + painter->restore(); +} + +void Transaction::paintRegisterIcons(QPainter* painter, int row, int col, const QRect& /*r*/, const QColorGroup& /*cg*/) +{ + if(row == 0 && col == DetailColumn && painter) { + if(m_erronous || !m_transaction.value("kmm-attachment").isEmpty()) { + QRect cellRect; + cellRect.setX(0); + cellRect.setY(0); + cellRect.setWidth(m_parent->columnWidth(col)); + cellRect.setHeight(m_parent->rowHeight(m_startRow + row)); + if(m_erronous) { + markAsErronous(painter, row, col, cellRect); + } + if(!m_transaction.value("kmm-attachment").isEmpty()) { + markAttachment(painter, row, col, cellRect); + } + } + } +} +void Transaction::paintRegisterCellBackground(QPainter* painter, int row, int col, const QRect& r, const QBrush& backgroundBrush) +{ + Q_UNUSED(row); + Q_UNUSED(col); + + // fill the background + painter->fillRect(r, backgroundBrush); +} + +void Transaction::paintRegisterGrid(QPainter* painter, int row, int col, const QRect& r, const QColorGroup& _cg) const +{ + Q_UNUSED(_cg); + + // if a grid is selected, we paint it right away + if (KMyMoneyGlobalSettings::showGrid()) { + painter->setPen(KMyMoneyGlobalSettings::listGridColor()); + if(col != 0) + painter->drawLine(r.x(), 0, r.x(), r.height()-1); + if(row == numRowsRegister()-1) + painter->drawLine(r.x(), r.height()-1, r.width(), r.height()-1); + } +} + +void Transaction::paintRegisterCellText(QPainter* painter, int row, int col, const QRect& r, const QColorGroup& _cg, int align, const QString& txt) +{ + Q_UNUSED(row); + Q_UNUSED(col); + Q_UNUSED(r); + Q_UNUSED(_cg); + + // make sure, we clear the cell + if(txt.isEmpty()) + painter->drawText(r, align, " "); + else + painter->drawText(r, align, txt); +} + +int Transaction::formRowHeight(int /*row*/) +{ + if(m_formRowHeight < 0) { + m_formRowHeight = formRowHeight(); + } + return m_formRowHeight; +} + +int Transaction::formRowHeight(void) const +{ + if(m_formRowHeight < 0) { + // determine the height of the objects in the table + kMyMoneyDateInput dateInput; + KMyMoneyCategory category(0,0,true); + + return QMAX(dateInput.sizeHint().height(), category.sizeHint().height()); + } + return m_formRowHeight; +} + +void Transaction::setupForm(TransactionForm* form) +{ + m_form = form; + form->verticalHeader()->setUpdatesEnabled(false); + form->horizontalHeader()->setUpdatesEnabled(false); + + form->setNumRows(numRowsForm()); + form->setNumCols(numColsForm()); + + // Force all cells to have some text (so that paintCell is called for each cell) + for(int r = 0; r < numRowsForm(); ++r) { + for(int c = 0; c < numColsForm(); ++c) { + form->setText(r, c, "x"); + if(r == 0 && form->columnWidth(c) == 0) { + form->setColumnWidth(c, 10); + } + } + } + form->horizontalHeader()->setUpdatesEnabled(true); + form->verticalHeader()->setUpdatesEnabled(true); + + loadTab(form); +} + +void Transaction::paintFormCell(QPainter* painter, int row, int col, const QRect& /*r*/, bool /*selected*/, const QColorGroup& _cg) +{ + if(!m_form) + return; + + QRect cellRect = m_form->cellRect(row, col); + + QRect textRect(cellRect); + textRect.setX(1); + textRect.setY(1); + textRect.setWidth(textRect.width()-2); + textRect.setHeight(textRect.height()-2); + + painter->fillRect(cellRect, _cg.background()); + painter->setPen(_cg.text()); + + QString txt; + int align = Qt::AlignVCenter; + bool editField = formCellText(txt, align, row, col, painter); + + // if we have an editable field and don't currently edit the transaction + // show the background in a different color + if(editField && !m_inEdit) { + painter->fillRect(textRect, _cg.base()); + } + + // make sure, we clear the cell + // in case of an editable field and edit mode, we just clear the field + if(txt.isEmpty() || (editField && m_inEdit)) + painter->drawText(textRect, align, " "); + else + painter->drawText(textRect, align, txt); + +} + +void Transaction::setupPalette(const QPalette& palette, QMap<QString, QWidget*>& editWidgets) +{ + QMap<QString, QWidget*>::iterator it_w; + for(it_w = editWidgets.begin(); it_w != editWidgets.end(); ++it_w) { + if(*it_w) { + (*it_w)->setPalette(palette); + } + } +} + +void Transaction::setupFormPalette(QMap<QString, QWidget*>& editWidgets) +{ + setupPalette(m_parent->palette(), editWidgets); +} + +void Transaction::setupRegisterPalette(QMap<QString, QWidget*>& editWidgets) +{ + // make sure, we're using the right palette + QPalette palette = m_parent->palette(); + + // use the highlight color as background + palette.setColor(QPalette::Active, QColorGroup::Background, palette.color(QPalette::Active, QColorGroup::Highlight)); + + setupPalette(palette, editWidgets); +} + +QWidget* Transaction::focusWidget(QWidget* w) const +{ + if(w) { + while(w->focusProxy()) + w = w->focusProxy(); + } + return w; +} + +void Transaction::arrangeWidget(QTable* tbl, int row, int col, QWidget* w) const +{ + if(w) { + tbl->setCellWidget(row, col, w); + // remove the widget from the QTable's eventFilter so that all + // events will be directed to the edit widget + w->removeEventFilter(tbl); + } else + qDebug("No widget for %d,%d", row, col); +} + +bool Transaction::haveNumberField(void) const +{ + bool rc = true; + switch(m_account.accountType()) { + case MyMoneyAccount::Savings: + case MyMoneyAccount::Cash: + case MyMoneyAccount::Loan: + case MyMoneyAccount::AssetLoan: + case MyMoneyAccount::Asset: + case MyMoneyAccount::Liability: + case MyMoneyAccount::Equity: + rc = KMyMoneyGlobalSettings::alwaysShowNrField(); + break; + + case MyMoneyAccount::Checkings: + case MyMoneyAccount::CreditCard: + // the next case is used for the editor when the account + // is unknown (eg. when creating new schedules) + case MyMoneyAccount::UnknownAccountType: + break; + + default: + rc = false; + break; + } + return rc; +} + +bool Transaction::maybeTip(const QPoint& cpos, int row, int col, QRect& r, QString& msg) +{ + if(col != DetailColumn) + return false; + + if(!m_erronous && m_transaction.splitCount() < 3) + return false; + + int h = m_parent->rowHeightHint(); + + // check for detail column in row 0 of the transaction for a possible exclamation mark + r = m_parent->cellGeometry(m_startRow + 0, col); + // qDebug("r is %d,%d,%d,%d", r.x(), r.y(), r.width(), r.height()); + r.setBottomLeft(QPoint(r.x() + (r.width() - h), r.y() + h)); + // qDebug("r is %d,%d,%d,%d", r.x(), r.y(), r.width(), r.height()); + // qDebug("p is in r = %d", r.contains(cpos)); + if(r.contains(cpos) && m_erronous) { + if(m_transaction.splits().count() < 2) { + msg = QString("<qt>%1</qt>").arg(i18n("Transaction is missing a category assignment.")); + } else { + const MyMoneySecurity& sec = MyMoneyFile::instance()->security(m_account.currencyId()); + msg = QString("<qt>%1</qt>").arg(i18n("The transaction has a missing assignment of <b>%1</b>.").arg(m_transaction.splitSum().abs().formatMoney(m_account, sec))); + } + return true; + } + + // check for detail column in row 1 of the transaction for a possible exclamation mark + r = m_parent->cellGeometry(m_startRow + 1, col); + if(row == 1 && r.contains(cpos) && m_transaction.splitCount() > 2) { + MyMoneyFile* file = MyMoneyFile::instance(); + QValueList<MyMoneySplit>::const_iterator it_s; + QString txt; + const MyMoneySecurity& sec = file->security(m_transaction.commodity()); + MyMoneyMoney factor(1, 1); + if(!m_split.value().isNegative()) + factor = -factor; + + for(it_s = m_transaction.splits().begin(); it_s != m_transaction.splits().end(); ++it_s) { + if(*it_s == m_split) + continue; + const MyMoneyAccount& acc = file->account((*it_s).accountId()); + QString category = file->accountToCategory(acc.id()); + QString amount = ((*it_s).value() * factor).formatMoney(acc, sec); + + txt += QString("<tr><td><nobr>%1</nobr></td><td align=right><nobr>%2</nobr></td></tr>").arg(category, amount); + } + msg = QString("<table>%1</table>").arg(txt); + return true; + } + + return false; +} + +QString Transaction::reconcileState(bool text) const +{ + QString txt = KMyMoneyUtils::reconcileStateToString(m_split.reconcileFlag(), text); + + if((text == true) + && (txt == i18n("Unknown")) + && (m_transaction == MyMoneyTransaction())) + txt = QString(); + return txt; +} + +void Transaction::startEditMode(void) +{ + m_inEdit = true; + // only update the number of lines displayed if we edit inside the register + if(m_inRegisterEdit) + setNumRowsRegister(numRowsRegister(true)); +} + +void Transaction::leaveEditMode(void) +{ + m_inEdit = false; + m_inRegisterEdit = false; + setFocus(hasFocus(), true); +} + +void Transaction::singleLineMemo(QString& txt, const MyMoneySplit& split) const +{ + txt = split.memo(); + // remove empty lines + txt.replace("\n\n", "\n"); + // replace '\n' with ", " + txt.replace('\n', ", "); +} + +int Transaction::rowHeightHint(void) const +{ + return m_inEdit ? formRowHeight()-4 : RegisterItem::rowHeightHint(); +} + + +bool Transaction::matches(const QString& txt) const +{ + if(txt.isEmpty() || m_transaction.splitCount() == 0) + return true; + + MyMoneyFile* file = MyMoneyFile::instance(); + QString s(txt); + s.replace(MyMoneyMoney::thousandSeparator(), QString()); + + const QValueList<MyMoneySplit>&list = m_transaction.splits(); + QValueList<MyMoneySplit>::const_iterator it_s; + for(it_s = list.begin(); it_s != list.end(); ++it_s) { + // check if the text is contained in one of the fields + // memo, number, payee, account + if((*it_s).memo().contains(txt, false) + || (*it_s).number().contains(txt, false)) + return true; + + if(!(*it_s).payeeId().isEmpty()) { + const MyMoneyPayee& payee = file->payee((*it_s).payeeId()); + if(payee.name().contains(txt, false)) + return true; + } + const MyMoneyAccount& acc = file->account((*it_s).accountId()); + if(acc.name().contains(txt, false)) + return true; + + if(!s.isEmpty()) { + // check if any of the value field matches if a value has been entered + QString r = (*it_s).value().formatMoney(m_account.fraction(), false); + if(r.contains(s, false)) + return true; + const MyMoneyAccount& acc = file->account((*it_s).accountId()); + r = (*it_s).shares().formatMoney(acc.fraction(), false); + if(r.contains(s, false)) + return true; + } + } + + return false; +} + +void Transaction::setShowBalance(bool showBalance) +{ + m_showBalance = showBalance; +} + +void Transaction::setVisible(bool visible) +{ + if(visible != isVisible()) { + RegisterItem::setVisible(visible); + RegisterItem* p; + Transaction* t; + if(!visible) { + // if we are hidden, we need to inform all previous transactions + // about it so that they don't show the balance + p = prevItem(); + while(p) { + t = dynamic_cast<Transaction*>(p); + if(t) { + if(!t->m_showBalance) + break; + t->m_showBalance = false; + } + p = p->prevItem(); + } + } else { + // if we are shown, we need to check if the next transaction + // is visible and change the display of the balance + p = this; + do { + p = p->nextItem(); + t = dynamic_cast<Transaction*>(p); + } while(!t && p); + + // if the next transaction is visible or I am the last one + if((t && t->m_showBalance) || !t) { + m_showBalance = true; + p = prevItem(); + while(p && p->isVisible()) { + t = dynamic_cast<Transaction*>(p); + if(t) { + if(t->m_showBalance) + break; + t->m_showBalance = true; + } + p = p->prevItem(); + } + } + } + } +} + +void Transaction::setSelected(bool selected) +{ + if(!selected || (selected && isVisible())) + m_selected = selected; +} + +StdTransaction::StdTransaction(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : + Transaction(parent, transaction, split, uniqueId), + m_showAccountRow(false) +{ + try { + m_categoryHeader = i18n("Category"); + switch(transaction.splitCount()) { + default: + m_category = i18n("Split transaction (category replacement)", "Split transaction"); + break; + + case 0: // the empty transaction + case 1: + break; + + case 2: + setupFormHeader(m_transaction.splitByAccount(m_split.accountId(), false).accountId()); + break; + } + } catch(MyMoneyException *e) { + kdDebug(2) << "Problem determining the category for transaction '" << m_transaction.id() << "'. Reason: " << e->what() << "\n"; + delete e; + } + m_rowsForm = 6; + + if(KMyMoneyUtils::transactionType(m_transaction) == KMyMoneyUtils::InvestmentTransaction) { + MyMoneySplit split = KMyMoneyUtils::stockSplit(m_transaction); + m_payee = MyMoneyFile::instance()->account(split.accountId()).name(); + QString addon; + if(split.action() == MyMoneySplit::ActionBuyShares) { + if(split.value().isNegative()) { + addon = i18n("Sell"); + } else { + addon = i18n("Buy"); + } + } else if(split.action() == MyMoneySplit::ActionDividend) { + addon = i18n("Dividend"); + } else if(split.action() == MyMoneySplit::ActionYield) { + addon = i18n("Yield"); + } + if(!addon.isEmpty()) { + m_payee += QString(" (%1)").arg(addon); + } + m_payeeHeader = i18n("Activity"); + m_category = i18n("Investment transaction"); + } + + // setup initial size + setNumRowsRegister(numRowsRegister(KMyMoneyGlobalSettings::showRegisterDetailed())); + + emit parent->itemAdded(this); +} + +void StdTransaction::setupFormHeader(const QString& id) +{ + m_category = MyMoneyFile::instance()->accountToCategory(id); + switch(MyMoneyFile::instance()->account(id).accountGroup()) { + case MyMoneyAccount::Asset: + case MyMoneyAccount::Liability: + m_categoryHeader = m_split.shares().isNegative() ? i18n("Transfer to") : i18n("Transfer from"); + break; + + default: + m_categoryHeader = i18n("Category"); + break; + } +} + +KMyMoneyRegister::Action StdTransaction::actionType(void) const +{ + KMyMoneyRegister::Action action=ActionNone; + + // if at least one split is referencing an income or + // expense account, we will not call it a transfer + QValueList<MyMoneySplit>::const_iterator it_s; + + for(it_s = m_transaction.splits().begin(); it_s != m_transaction.splits().end(); ++it_s) { + if((*it_s).accountId() == m_split.accountId()) + continue; + MyMoneyAccount acc = MyMoneyFile::instance()->account((*it_s).accountId()); + if(acc.accountGroup() == MyMoneyAccount::Income + || acc.accountGroup() == MyMoneyAccount::Expense) { + // otherwise, we have to determine between deposit and withdrawal + action = m_split.shares().isNegative() ? ActionWithdrawal : ActionDeposit; + break; + } + } + // otherwise, it's a transfer + if(it_s == m_transaction.splits().end()) + action = ActionTransfer; + + return action; +} + +void StdTransaction::loadTab(TransactionForm* form) +{ + TabBar* bar = form->tabBar(); + bar->setSignalEmission(TabBar::SignalNever); + for(int i = 0; i < bar->count(); ++i) { + bar->setTabEnabled(bar->tabAt(i)->identifier(), true); + } + + if(m_transaction.splitCount() > 0) { + bar->setCurrentTab(actionType()); + } + bar->setSignalEmission(TabBar::SignalAlways); +} + +void StdTransaction::setupForm(TransactionForm* form) +{ + Transaction::setupForm(form); + + QTableItem* memo = form->item(3, ValueColumn1); + memo->setSpan(3, 1); +} + +bool StdTransaction::showRowInForm(int row) const +{ + return row == 0 ? m_showAccountRow : true; +} + +void StdTransaction::setShowRowInForm(int row, bool show) +{ + if(row == 0) + m_showAccountRow = show; +} + +bool StdTransaction::formCellText(QString& txt, int& align, int row, int col, QPainter* /* painter */) +{ + // if(m_transaction != MyMoneyTransaction()) { + switch(row) { + case 0: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = i18n("Account"); + break; + } + break; + + case 1: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = m_payeeHeader; + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + txt = m_payee; + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + if(haveNumberField()) + txt = i18n("Number"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if(haveNumberField()) + txt = m_split.number(); + break; + } + break; + + case 2: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = m_categoryHeader; + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + txt = m_category; + if(m_transaction != MyMoneyTransaction()) { + if(txt.isEmpty() && !m_split.value().isZero()) + txt = i18n("*** UNASSIGNED ***"); + } + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + txt = i18n("Date"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if(m_transaction != MyMoneyTransaction()) + txt = KGlobal::locale()->formatDate(m_transaction.postDate(), true); + break; + } + break; + + case 3: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = i18n("Memo"); + break; + + case ValueColumn1: + align &= ~Qt::AlignVCenter; + align |= Qt::AlignTop; + align |= Qt::AlignLeft; + if(m_transaction != MyMoneyTransaction()) + txt = m_split.memo().section('\n', 0, 2); + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + txt = i18n("Amount"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if(m_transaction != MyMoneyTransaction()) { + txt = (m_split.value(m_transaction.commodity(), m_splitCurrencyId).abs()).formatMoney(m_account.fraction()); + } + break; + } + break; + + case 5: + switch(col) { + case LabelColumn2: + align |= Qt::AlignLeft; + txt = i18n("Status"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + txt = reconcileState(); + break; + } + } + // } + if(col == ValueColumn2 && row == 1) { + return haveNumberField(); + } + return (col == ValueColumn1 && row < 4) || (col == ValueColumn2 && row > 0 && row != 4); +} + +void StdTransaction::registerCellText(QString& txt, int& align, int row, int col, QPainter* painter) +{ + switch(row) { + case 0: + switch(col) { + case NumberColumn: + align |= Qt::AlignLeft; + if(haveNumberField()) + txt = m_split.number(); + break; + + case DateColumn: + align |= Qt::AlignLeft; + txt = KGlobal::locale()->formatDate(m_transaction.postDate(), true); + break; + + case DetailColumn: + align |= Qt::AlignLeft; + txt = m_payee; + if(txt.isEmpty() && m_rowsRegister < 3) { + singleLineMemo(txt, m_split); + } + if(txt.isEmpty() && m_rowsRegister < 2) { + if(m_account.accountType() != MyMoneyAccount::Income + && m_account.accountType() != MyMoneyAccount::Expense) { + txt = m_category; + if(txt.isEmpty() && !m_split.value().isZero()) { + txt = i18n("*** UNASSIGNED ***"); + if(painter) + painter->setPen(KMyMoneyGlobalSettings::listErronousTransactionColor()); + } + } + } + break; + + case ReconcileFlagColumn: + align |= Qt::AlignHCenter; + txt = reconcileState(false); + break; + + case PaymentColumn: + align |= Qt::AlignRight; + if(m_split.value().isNegative()) { + txt = (-m_split.value(m_transaction.commodity(), m_splitCurrencyId)).formatMoney(m_account.fraction()); + } + break; + + case DepositColumn: + align |= Qt::AlignRight; + if(!m_split.value().isNegative()) { + txt = m_split.value(m_transaction.commodity(), m_splitCurrencyId).formatMoney(m_account.fraction()); + } + break; + + case BalanceColumn: + align |= Qt::AlignRight; + if(m_showBalance) + txt = m_balance.formatMoney(m_account.fraction()); + else + txt = "----"; + break; + + case AccountColumn: + // txt = m_objects->account(m_transaction.splits()[0].accountId()).name(); + txt = MyMoneyFile::instance()->account(m_split.accountId()).name(); + break; + + default: + break; + } + break; + + case 1: + switch(col) { + case DetailColumn: + align |= Qt::AlignLeft; + txt = m_category; + if(txt.isEmpty() && !m_split.value().isZero()) { + txt = i18n("*** UNASSIGNED ***"); + if(painter) + painter->setPen(KMyMoneyGlobalSettings::listErronousTransactionColor()); + } + break; + + default: + break; + } + break; + + case 2: + switch(col) { + case DetailColumn: + align |= Qt::AlignLeft; + singleLineMemo(txt, m_split); + break; + + default: + break; + } + break; + } +} + +int StdTransaction::registerColWidth(int col, const QFontMetrics& cellFontMetrics) +{ + QString txt; + int firstRow = 0, lastRow = 0; + + int nw = 0; + for(int i = firstRow; i <= lastRow; ++i) { + int align; + registerCellText(txt, align, i, col, 0); + int w = cellFontMetrics.width(txt+" "); + if(w > nw) + nw = w; + } + return nw; +} + +void StdTransaction::arrangeWidgetsInForm(QMap<QString, QWidget*>& editWidgets) +{ + if(!m_form || !m_parent) + return; + + setupFormPalette(editWidgets); + + arrangeWidget(m_form, 0, ValueColumn1, editWidgets["account"]); + arrangeWidget(m_form, 1, LabelColumn1, editWidgets["cashflow"]); + arrangeWidget(m_form, 1, ValueColumn1, editWidgets["payee"]); + arrangeWidget(m_form, 2, ValueColumn1, editWidgets["category"]->parentWidget()); + arrangeWidget(m_form, 3, ValueColumn1, editWidgets["memo"]); + if(haveNumberField()) { + arrangeWidget(m_form, 1, LabelColumn2, editWidgets["number-label"]); + arrangeWidget(m_form, 1, ValueColumn2, editWidgets["number"]); + } + arrangeWidget(m_form, 2, LabelColumn2, editWidgets["date-label"]); + arrangeWidget(m_form, 2, ValueColumn2, editWidgets["postdate"]); + arrangeWidget(m_form, 3, ValueColumn2, editWidgets["amount"]); + arrangeWidget(m_form, 5, ValueColumn2, editWidgets["status"]); + arrangeWidget(m_form, 2, LabelColumn1, editWidgets["category-label"]); + + // get rid of the hints. we don't need them for the form + QMap<QString, QWidget*>::iterator it; + for(it = editWidgets.begin(); it != editWidgets.end(); ++it) { + KMyMoneyCombo* combo = dynamic_cast<KMyMoneyCombo*>(*it); + kMyMoneyLineEdit* edit = dynamic_cast<kMyMoneyLineEdit*>(*it); + KMyMoneyPayeeCombo* payee = dynamic_cast<KMyMoneyPayeeCombo*>(*it); + if(combo) + combo->setHint(QString()); + if(edit) + edit->setHint(QString()); + if(payee) + payee->setHint(QString()); + } + + // drop the tabbar on top of the original + KMyMoneyTransactionForm::TransactionForm* form = dynamic_cast<KMyMoneyTransactionForm::TransactionForm*>(m_form); + TabBar* w = dynamic_cast<TabBar*>(editWidgets["tabbar"]); + if(w) { + w->reparent(form->tabBar(), QPoint(0, 0), true); + } +} + +void StdTransaction::tabOrderInForm(QWidgetList& tabOrderWidgets) const +{ + QStringList taborder = QStringList::split(",", KMyMoneyGlobalSettings::stdTransactionFormTabOrder()); + QStringList::const_iterator it_s = taborder.constBegin(); + QWidget* w; + while(it_s != taborder.constEnd()) { + if(*it_s == "account") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(0, ValueColumn1))); + } else if(*it_s == "cashflow") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(1, LabelColumn1))); + } else if(*it_s == "payee") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(1, ValueColumn1))); + } else if(*it_s == "category") { + // make sure to have the category field and the split button as seperate tab order widgets + // ok, we have to have some internal knowledge about the KMyMoneyCategory object, but + // it's one of our own widgets, so we actually don't care. Just make sure, that we don't + // go haywire when someone changes the KMyMoneyCategory object ... + w = m_form->cellWidget(2, ValueColumn1); + tabOrderWidgets.append(focusWidget(w)); + w = dynamic_cast<QWidget*>(w->child("splitButton")); + if(w) + tabOrderWidgets.append(w); + } else if(*it_s == "memo") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(3, ValueColumn1))); + } else if(*it_s == "number") { + if(haveNumberField()) { + if((w = focusWidget(m_form->cellWidget(1, ValueColumn2)))) + tabOrderWidgets.append(w); + } + } else if(*it_s == "date") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(2, ValueColumn2))); + } else if(*it_s == "amount") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(3, ValueColumn2))); + } else if(*it_s == "state") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(5, ValueColumn2))); + } + ++it_s; + } +} + +void StdTransaction::arrangeWidgetsInRegister(QMap<QString, QWidget*>& editWidgets) +{ + if(!m_parent) + return; + + setupRegisterPalette(editWidgets); + + if(haveNumberField()) + arrangeWidget(m_parent, m_startRow+0, NumberColumn, editWidgets["number"]); + arrangeWidget(m_parent, m_startRow + 0, DateColumn, editWidgets["postdate"]); + arrangeWidget(m_parent, m_startRow + 1, DateColumn, editWidgets["status"]); + arrangeWidget(m_parent, m_startRow + 0, DetailColumn, editWidgets["payee"]); + arrangeWidget(m_parent, m_startRow + 1, DetailColumn, editWidgets["category"]->parentWidget()); + arrangeWidget(m_parent, m_startRow + 2, DetailColumn, editWidgets["memo"]); + arrangeWidget(m_parent, m_startRow + 0, PaymentColumn, editWidgets["payment"]); + arrangeWidget(m_parent, m_startRow + 0, DepositColumn, editWidgets["deposit"]); + + // increase the height of the row containing the memo widget + m_parent->setRowHeight(m_startRow+2, m_parent->rowHeightHint() * 3); +} + +void StdTransaction::tabOrderInRegister(QWidgetList& tabOrderWidgets) const +{ + QStringList taborder = QStringList::split(",", KMyMoneyGlobalSettings::stdTransactionRegisterTabOrder()); + QStringList::const_iterator it_s = taborder.constBegin(); + QWidget* w; + while(it_s != taborder.constEnd()) { + if(*it_s == "number") { + if(haveNumberField()) { + if((w = focusWidget(m_parent->cellWidget(m_startRow + 0, NumberColumn)))) + tabOrderWidgets.append(w); + } + } else if(*it_s == "date") { + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, DateColumn))); + } else if(*it_s == "payee") { + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, DetailColumn))); + } else if(*it_s == "category") { + // make sure to have the category field and the split button as seperate tab order widgets + // ok, we have to have some internal knowledge about the KMyMoneyCategory object, but + // it's one of our own widgets, so we actually don't care. Just make sure, that we don't + // go haywire when someone changes the KMyMoneyCategory object ... + w = m_parent->cellWidget(m_startRow + 1, DetailColumn); + tabOrderWidgets.append(focusWidget(w)); + w = dynamic_cast<QWidget*>(w->child("splitButton")); + if(w) + tabOrderWidgets.append(w); + } else if(*it_s == "memo") { + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 2, DetailColumn))); + } else if(*it_s == "payment") { + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, PaymentColumn))); + } else if(*it_s == "deposit") { + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, DepositColumn))); + } else if(*it_s == "state") { + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 1, DateColumn))); + } + ++it_s; + } +} + +int StdTransaction::numRowsRegister(bool expanded) const +{ + int numRows = 1; + if(expanded) { + numRows = 3; + if(!m_inEdit) { + if(m_payee.isEmpty()) { + numRows--; + } + if(m_split.memo().isEmpty()) { + numRows--; + } + // For income and expense accounts that only have + // two splits we only show one line, because the + // account name is already contained in the account column. + if(m_account.accountType() == MyMoneyAccount::Income + || m_account.accountType() == MyMoneyAccount::Expense) { + if(numRows > 2 && m_transaction.splitCount() == 2) + numRows = 1; + } + } + } + return numRows; +} + +TransactionEditor* StdTransaction::createEditor(TransactionEditorContainer* regForm, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate) +{ + m_inRegisterEdit = regForm == m_parent; + return new StdTransactionEditor(regForm, this, list, lastPostDate); +} + +InvestTransaction::InvestTransaction(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : + Transaction(parent, transaction, split, uniqueId) +{ + // dissect the transaction into its type, splits, currency, security etc. + InvestTransactionEditor::dissectTransaction(m_transaction, m_split, + m_assetAccountSplit, + m_feeSplits, + m_interestSplits, + m_security, + m_currency, + m_transactionType); + + QValueList<MyMoneySplit>::ConstIterator it_s; + for(it_s = m_feeSplits.begin(); it_s != m_feeSplits.end(); ++it_s) { + m_feeAmount += (*it_s).value(); + } + for(it_s = m_interestSplits.begin(); it_s != m_interestSplits.end(); ++it_s) { + m_interestAmount += (*it_s).value(); + } + + // check the count of the fee splits and setup the text + switch(m_feeSplits.count()) { + case 0: + break; + + case 1: + m_feeCategory = MyMoneyFile::instance()->accountToCategory(m_feeSplits[0].accountId()); + break; + + default: + m_feeCategory = i18n("Split transaction (category replacement)", "Split transaction"); + break; + } + + // check the count of the interest splits and setup the text + switch(m_interestSplits.count()) { + case 0: + break; + + case 1: + m_interestCategory = MyMoneyFile::instance()->accountToCategory(m_interestSplits[0].accountId()); + break; + + default: + m_interestCategory = i18n("Split transaction (category replacement)", "Split transaction"); + break; + } + + m_rowsForm = 7; + + // setup initial size + setNumRowsRegister(numRowsRegister(KMyMoneyGlobalSettings::showRegisterDetailed())); + + emit parent->itemAdded(this); +} + +void InvestTransaction::setupForm(TransactionForm* form) +{ + Transaction::setupForm(form); + + QTableItem* memo = form->item(5, 1); + memo->setSpan(2, 1); +} + +void InvestTransaction::activity(QString& txt, MyMoneySplit::investTransactionTypeE type) const +{ + switch(type) { + case MyMoneySplit::AddShares: + txt = i18n("Add shares"); + break; + case MyMoneySplit::RemoveShares: + txt = i18n("Remove shares"); + break; + case MyMoneySplit::BuyShares: + txt = i18n("Buy shares"); + break; + case MyMoneySplit::SellShares: + txt = i18n("Sell shares"); + break; + case MyMoneySplit::Dividend: + txt = i18n("Dividend"); + break; + case MyMoneySplit::ReinvestDividend: + txt = i18n("Reinvest Dividend"); + break; + case MyMoneySplit::Yield: + txt = i18n("Yield"); + break; + case MyMoneySplit::SplitShares: + txt = i18n("Split shares"); + break; + default: + txt = i18n("Unknown"); + break; + } +} + +bool InvestTransaction::formCellText(QString& txt, int& align, int row, int col, QPainter* /* painter */) +{ + bool fieldEditable = false; + + switch(row) { + case 0: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = i18n("Activity"); + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + fieldEditable = true; + activity(txt, m_transactionType); + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + txt = i18n("Date"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + fieldEditable = true; + if(m_transaction != MyMoneyTransaction()) + txt = KGlobal::locale()->formatDate(m_transaction.postDate(), true); + break; + } + break; + + case 1: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = i18n("Security"); + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + fieldEditable = true; + if(m_account.isInvest()) + txt = m_security.name(); + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + if(haveShares()) { + txt = i18n("Shares"); + } else if(haveSplitRatio()) { + txt = i18n("Ratio"); + } + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if((fieldEditable = haveShares()) == true) { + txt = m_split.shares().abs().formatMoney("", MyMoneyMoney::denomToPrec(m_security.smallestAccountFraction())); + } else if(haveSplitRatio()) { + txt = QString("1 / %1").arg(m_split.shares().abs().formatMoney("", -1)); + } + break; + } + break; + + case 2: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + if(haveAssetAccount()) + txt = i18n("Account"); + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + if((fieldEditable = haveAssetAccount()) == true) { + txt = MyMoneyFile::instance()->accountToCategory(m_assetAccountSplit.accountId()); + } + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + if(havePrice()) + txt = i18n("Price/share"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if((fieldEditable = havePrice()) == true && !m_split.shares().isZero()) { + txt = m_split.price().formatMoney("", KMyMoneyGlobalSettings::pricePrecision()); + } + break; + } + break; + + case 3: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + if(haveFees()) + txt = i18n("Fees"); + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + if((fieldEditable = haveFees()) == true) { + txt = m_feeCategory; + } + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + if(haveFees() && !m_feeCategory.isEmpty()) + txt = i18n("Amount"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if(haveFees()) { + if((fieldEditable = !m_feeCategory.isEmpty()) == true) { + txt = m_feeAmount.formatMoney(m_currency); + } + } + break; + } + break; + + case 4: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + if(haveInterest()) + txt = i18n("Interest"); + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + if((fieldEditable = haveInterest()) == true) { + txt = m_interestCategory; + } + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + if(haveInterest() && !m_interestCategory.isEmpty()) + txt = i18n("Amount"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if(haveInterest()) { + if((fieldEditable = !m_interestCategory.isEmpty()) == true) { + txt = (-m_interestAmount).formatMoney(m_currency); + } + } + break; + } + break; + + case 5: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = i18n("Memo"); + break; + + case ValueColumn1: + align &= ~Qt::AlignVCenter; + align |= Qt::AlignTop; + align |= Qt::AlignLeft; + fieldEditable = true; + if(m_transaction != MyMoneyTransaction()) + txt = m_split.memo().section('\n', 0, 2); + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + if(haveAmount()) + txt = i18n("Total"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if((fieldEditable = haveAmount()) == true) { + txt = m_assetAccountSplit.value().abs().formatMoney(m_currency); + } + } + break; + + case 6: + switch(col) { + case LabelColumn2: + align |= Qt::AlignLeft; + txt = i18n("Status"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + fieldEditable = true; + txt = reconcileState(); + break; + } + } + + return fieldEditable; +} + +void InvestTransaction::registerCellText(QString& txt, int& align, int row, int col, QPainter* /* painter */) +{ + switch(row) { + case 0: + switch(col) { + case DateColumn: + align |= Qt::AlignLeft; + txt = KGlobal::locale()->formatDate(m_transaction.postDate(), true); + break; + + case DetailColumn: + align |= Qt::AlignLeft; + activity(txt, m_transactionType); + break; + + case SecurityColumn: + align |= Qt::AlignLeft; + if(m_account.isInvest()) + txt = m_security.name(); + break; + + case ReconcileFlagColumn: + align |= Qt::AlignHCenter; + txt = reconcileState(false); + break; + + case QuantityColumn: + align |= Qt::AlignRight; + if(haveShares()) + txt = m_split.shares().abs().formatMoney("", MyMoneyMoney::denomToPrec(m_security.smallestAccountFraction())); + else if(haveSplitRatio()) { + txt = QString("1 / %1").arg(m_split.shares().abs().formatMoney("", -1)); + } + break; + + case PriceColumn: + align |= Qt::AlignRight; + if(havePrice() && !m_split.shares().isZero()) { + txt = m_split.price().formatMoney(m_currency.tradingSymbol(), KMyMoneyGlobalSettings::pricePrecision()); + } + break; + + case ValueColumn: + align |= Qt::AlignRight; + if(haveAmount()) { + txt = m_assetAccountSplit.value().abs().formatMoney(m_currency); + + } else if(haveInterest()) { + txt = (-m_interestAmount).formatMoney(m_currency); + } + break; + + case BalanceColumn: + align |= Qt::AlignRight; + if(m_showBalance) + txt = m_balance.formatMoney("", MyMoneyMoney::denomToPrec(m_security.smallestAccountFraction())); + else + txt = "----"; + break; + + default: + break; + } + break; + + case 1: + switch(col) { + case DetailColumn: + align |= Qt::AlignLeft; + if(haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty()) { + txt = MyMoneyFile::instance()->accountToCategory(m_assetAccountSplit.accountId()); + } else if(haveInterest() && m_interestSplits.count()) { + txt = m_interestCategory; + } else if(haveFees() && m_feeSplits.count()) { + txt = m_feeCategory; + } else + singleLineMemo(txt, m_split); + break; + + case QuantityColumn: + align |= Qt::AlignRight; + if(haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty()) { + // txt = m_interestAmount.abs().formatMoney(m_currency); + } else if(haveInterest() && m_interestSplits.count()) { + txt = (-m_interestAmount).formatMoney(m_currency); + } else if(haveFees() && m_feeSplits.count()) { + txt = m_feeAmount.formatMoney(m_currency); + } + break; + + default: + break; + } + break; + + case 2: + switch(col) { + case DetailColumn: + align |= Qt::AlignLeft; + if(haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty() + && haveInterest() && m_interestSplits.count()) { + txt = m_interestCategory; + } else if(haveFees() && m_feeSplits.count()) { + txt = m_feeCategory; + } else + singleLineMemo(txt, m_split); + break; + + case QuantityColumn: + align |= Qt::AlignRight; + if(haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty() + && haveInterest() && m_interestSplits.count()) { + txt = (-m_interestAmount).formatMoney(m_currency); + } else if(haveFees() && m_feeSplits.count()) { + txt = m_feeAmount.formatMoney(m_currency); + } + break; + + default: + break; + } + break; + + case 3: + switch(col) { + case DetailColumn: + align |= Qt::AlignLeft; + if(haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty() + && haveInterest() && m_interestSplits.count() + && haveFees() && m_feeSplits.count()) { + txt = m_feeCategory; + } else + singleLineMemo(txt, m_split); + break; + + case QuantityColumn: + align |= Qt::AlignRight; + if(haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty() + && haveInterest() && m_interestSplits.count() + && haveFees() && m_feeSplits.count()) { + txt = m_feeAmount.formatMoney(m_currency); + } + break; + + default: + break; + } + break; + + case 4: + switch(col) { + case DetailColumn: + align |= Qt::AlignLeft; + singleLineMemo(txt, m_split); + break; + + default: + break; + } + break; + } +} + +int InvestTransaction::registerColWidth(int col, const QFontMetrics& cellFontMetrics) +{ + QString txt; + MyMoneyMoney amount; + int nw = 0; + + // for now just check all rows in that column + for(int row = 0; row < m_rowsRegister; ++row) { + int w; + Transaction::registerCellText(txt, row, col); + w = cellFontMetrics.width(txt+" "); + nw = QMAX(nw, w); + } + + // TODO the optimized way would be to base the size on the contents of a single row + // as we do it in StdTransaction::registerColWidth() +#if 0 + switch(col) { + default: + break; + + case PriceColumn: + if(havePrice()) { + txt = (m_split.value() / m_split.shares()).formatMoney("", KMyMoneyGlobalSettings::pricePrecision()); + nw = cellFontMetrics.width(txt+" "); + } + break; + } +#endif + return nw; +} + +void InvestTransaction::arrangeWidgetsInForm(QMap<QString, QWidget*>& editWidgets) +{ + if(!m_form || !m_parent) + return; + + setupFormPalette(editWidgets); + + // arrange the edit widgets + arrangeWidget(m_form, 0, ValueColumn1, editWidgets["activity"]); + arrangeWidget(m_form, 0, ValueColumn2, editWidgets["postdate"]); + arrangeWidget(m_form, 1, ValueColumn1, editWidgets["security"]); + arrangeWidget(m_form, 1, ValueColumn2, editWidgets["shares"]); + arrangeWidget(m_form, 2, ValueColumn1, editWidgets["asset-account"]); + arrangeWidget(m_form, 2, ValueColumn2, editWidgets["price"]); + arrangeWidget(m_form, 3, ValueColumn1, editWidgets["fee-account"]->parentWidget()); + arrangeWidget(m_form, 3, ValueColumn2, editWidgets["fee-amount"]); + arrangeWidget(m_form, 4, ValueColumn1, editWidgets["interest-account"]->parentWidget()); + arrangeWidget(m_form, 4, ValueColumn2, editWidgets["interest-amount"]); + arrangeWidget(m_form, 5, ValueColumn1, editWidgets["memo"]); + arrangeWidget(m_form, 5, ValueColumn2, editWidgets["total"]); + arrangeWidget(m_form, 6, ValueColumn2, editWidgets["status"]); + + // arrange dynamic labels + arrangeWidget(m_form, 1, LabelColumn2, editWidgets["shares-label"]); + arrangeWidget(m_form, 2, LabelColumn1, editWidgets["asset-label"]); + arrangeWidget(m_form, 2, LabelColumn2, editWidgets["price-label"]); + arrangeWidget(m_form, 3, LabelColumn1, editWidgets["fee-label"]); + arrangeWidget(m_form, 3, LabelColumn2, editWidgets["fee-amount-label"]); + arrangeWidget(m_form, 4, LabelColumn1, editWidgets["interest-label"]); + arrangeWidget(m_form, 4, LabelColumn2, editWidgets["interest-amount-label"]); + arrangeWidget(m_form, 5, LabelColumn2, editWidgets["total-label"]); + + // get rid of the hints. we don't need them for the form + QMap<QString, QWidget*>::iterator it; + for(it = editWidgets.begin(); it != editWidgets.end(); ++it) { + KMyMoneyCombo* combo = dynamic_cast<KMyMoneyCombo*>(*it); + kMyMoneyLineEdit* lineedit = dynamic_cast<kMyMoneyLineEdit*>(*it); + kMyMoneyEdit* edit = dynamic_cast<kMyMoneyEdit*>(*it); + KMyMoneyPayeeCombo* payee = dynamic_cast<KMyMoneyPayeeCombo*>(*it); + if(combo) + combo->setHint(QString()); + if(edit) + edit->setHint(QString()); + if(lineedit) + lineedit->setHint(QString()); + if(payee) + payee->setHint(QString()); + } +} + +void InvestTransaction::tabOrderInForm(QWidgetList& tabOrderWidgets) const +{ + // activity + tabOrderWidgets.append(focusWidget(m_form->cellWidget(0, ValueColumn1))); + + // date + tabOrderWidgets.append(focusWidget(m_form->cellWidget(0, ValueColumn2))); + + // security + tabOrderWidgets.append(focusWidget(m_form->cellWidget(1, ValueColumn1))); + + // shares + tabOrderWidgets.append(focusWidget(m_form->cellWidget(1, ValueColumn2))); + + // account + tabOrderWidgets.append(focusWidget(m_form->cellWidget(2, ValueColumn1))); + + // price + tabOrderWidgets.append(focusWidget(m_form->cellWidget(2, ValueColumn2))); + + // make sure to have the fee category field and the split button as seperate tab order widgets + // ok, we have to have some internal knowledge about the KMyMoneyCategory object, but + // it's one of our own widgets, so we actually don't care. Just make sure, that we don't + // go haywire when someone changes the KMyMoneyCategory object ... + QWidget* w = m_form->cellWidget(3, ValueColumn1); + tabOrderWidgets.append(focusWidget(w)); + w = dynamic_cast<QWidget*>(w->child("splitButton")); + if(w) + tabOrderWidgets.append(w); + + // fee amount + tabOrderWidgets.append(focusWidget(m_form->cellWidget(3, ValueColumn2))); + + // the same applies for the interest categories + w = m_form->cellWidget(4, ValueColumn1); + tabOrderWidgets.append(focusWidget(w)); + w = dynamic_cast<QWidget*>(w->child("splitButton")); + if(w) + tabOrderWidgets.append(w); + + // interest amount + tabOrderWidgets.append(focusWidget(m_form->cellWidget(4, ValueColumn2))); + + // memo + tabOrderWidgets.append(focusWidget(m_form->cellWidget(5, ValueColumn1))); + + // total + tabOrderWidgets.append(focusWidget(m_form->cellWidget(5, ValueColumn2))); + + // state + tabOrderWidgets.append(focusWidget(m_form->cellWidget(6, ValueColumn2))); +} + +void InvestTransaction::arrangeWidgetsInRegister(QMap<QString, QWidget*>& editWidgets) +{ + if(!m_parent) + return; + + setupRegisterPalette(editWidgets); + + arrangeWidget(m_parent, m_startRow + 0, DateColumn, editWidgets["postdate"]); + arrangeWidget(m_parent, m_startRow + 0, SecurityColumn, editWidgets["security"]); + arrangeWidget(m_parent, m_startRow + 0, DetailColumn, editWidgets["activity"]); + arrangeWidget(m_parent, m_startRow + 1, DetailColumn, editWidgets["asset-account"]); + arrangeWidget(m_parent, m_startRow + 2, DetailColumn, editWidgets["interest-account"]->parentWidget()); + arrangeWidget(m_parent, m_startRow + 3, DetailColumn, editWidgets["fee-account"]->parentWidget()); + arrangeWidget(m_parent, m_startRow + 4, DetailColumn, editWidgets["memo"]); + arrangeWidget(m_parent, m_startRow + 0, QuantityColumn, editWidgets["shares"]); + arrangeWidget(m_parent, m_startRow + 0, PriceColumn, editWidgets["price"]); + arrangeWidget(m_parent, m_startRow + 2, QuantityColumn, editWidgets["interest-amount"]); + arrangeWidget(m_parent, m_startRow + 3, QuantityColumn, editWidgets["fee-amount"]); + arrangeWidget(m_parent, m_startRow + 0, ValueColumn, editWidgets["total"]); + arrangeWidget(m_parent, m_startRow + 1, DateColumn, editWidgets["status"]); + + // increase the height of the row containing the memo widget + m_parent->setRowHeight(m_startRow+4, m_parent->rowHeightHint() * 3); +} + +void InvestTransaction::tabOrderInRegister(QWidgetList& tabOrderWidgets) const +{ + QWidget* w; + + // date + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, DateColumn))); + // security + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, SecurityColumn))); + // activity + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, DetailColumn))); + // shares + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, QuantityColumn))); + // price + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, PriceColumn))); + // asset account + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 1, DetailColumn))); + + // make sure to have the category fields and the split button as seperate tab order widgets + // ok, we have to have some internal knowledge about the KMyMoneyCategory object, but + // it's one of our own widgets, so we actually don't care. Just make sure, that we don't + // go haywire when someone changes the KMyMoneyCategory object ... + w = m_parent->cellWidget(m_startRow + 2, DetailColumn); // interest account + tabOrderWidgets.append(focusWidget(w)); + w = dynamic_cast<QWidget*>(w->child("splitButton")); + if(w) + tabOrderWidgets.append(w); + + // interest amount + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 2, QuantityColumn))); + + w = m_parent->cellWidget(m_startRow + 3, DetailColumn); // fee account + tabOrderWidgets.append(focusWidget(w)); + w = dynamic_cast<QWidget*>(w->child("splitButton")); + if(w) + tabOrderWidgets.append(w); + + // fee amount + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 3, QuantityColumn))); + + // memo + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 4, DetailColumn))); + + // status + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 1, DateColumn))); +} + +int InvestTransaction::numRowsRegister(bool expanded) const +{ + int numRows = 1; + if(expanded) { + if(!m_inEdit) { + if(haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty()) + ++numRows; + if(haveInterest() && m_interestSplits.count()) + ++numRows; + if(haveFees() && m_feeSplits.count()) + ++numRows; + if(!m_split.memo().isEmpty()) + ++numRows; + } else + numRows = 5; + } + return numRows; +} + +bool InvestTransaction::haveShares(void) const +{ + bool rc = true; + switch(m_transactionType) { + case MyMoneySplit::Dividend: + case MyMoneySplit::Yield: + case MyMoneySplit::SplitShares: + rc = false; + break; + + default: + break; + } + return rc; +} + +bool InvestTransaction::haveFees(void) const +{ + bool rc = true; + switch(m_transactionType) { + case MyMoneySplit::AddShares: + case MyMoneySplit::RemoveShares: + case MyMoneySplit::SplitShares: + rc = false; + break; + + default: + break; + } + return rc; +} + +bool InvestTransaction::haveInterest(void) const +{ + bool rc = false; + switch(m_transactionType) { + case MyMoneySplit::BuyShares: + case MyMoneySplit::SellShares: + case MyMoneySplit::Dividend: + case MyMoneySplit::ReinvestDividend: + case MyMoneySplit::Yield: + rc = true; + break; + + default: + break; + } + return rc; +} + +bool InvestTransaction::havePrice(void) const +{ + bool rc = false; + switch(m_transactionType) { + case MyMoneySplit::BuyShares: + case MyMoneySplit::SellShares: + case MyMoneySplit::ReinvestDividend: + rc = true; + break; + + default: + break; + } + return rc; +} + +bool InvestTransaction::haveAmount(void) const +{ + bool rc = false; + switch(m_transactionType) { + case MyMoneySplit::BuyShares: + case MyMoneySplit::SellShares: + case MyMoneySplit::Dividend: + case MyMoneySplit::Yield: + rc = true; + break; + + default: + break; + } + return rc; +} + +bool InvestTransaction::haveAssetAccount(void) const +{ + bool rc = true; + switch(m_transactionType) { + case MyMoneySplit::AddShares: + case MyMoneySplit::RemoveShares: + case MyMoneySplit::SplitShares: + case MyMoneySplit::ReinvestDividend: + rc = false; + break; + + default: + break; + } + return rc; +} + +bool InvestTransaction::haveSplitRatio(void) const +{ + return m_transactionType == MyMoneySplit::SplitShares; +} + +void InvestTransaction::splits(MyMoneySplit& assetAccountSplit, QValueList<MyMoneySplit>& interestSplits, QValueList<MyMoneySplit>& feeSplits) const +{ + assetAccountSplit = m_assetAccountSplit; + interestSplits = m_interestSplits; + feeSplits = m_feeSplits; +} + +TransactionEditor* InvestTransaction::createEditor(TransactionEditorContainer* regForm, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate) +{ + m_inRegisterEdit = regForm == m_parent; + return new InvestTransactionEditor(regForm, this, list, lastPostDate); +} + |