You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kmymoney/kmymoney2/dialogs/kmymoneysplittable.cpp

1000 lines
30 KiB

/***************************************************************************
kmymoneysplittable.cpp - description
-------------------
begin : Thu Jan 10 2002
copyright : (C) 2000-2002 by Michael Edwardes
email : mte@users.sourceforge.net
Javier Campos Morales <javi_c@users.sourceforge.net>
Felix Rodriguez <frodriguez@users.sourceforge.net>
John C <thetacoturtle@users.sourceforge.net>
Thomas Baumgart <ipwizard@users.sourceforge.net>
Kevin Tambascio <ktambascio@users.sourceforge.net>
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <kdecompat.h>
// ----------------------------------------------------------------------------
// QT Includes
#include <tqglobal.h>
#include <tqpainter.h>
#include <tqcursor.h>
#include <tqapplication.h>
#include <tqtimer.h>
#include <tqlayout.h>
#include <tqeventloop.h>
// ----------------------------------------------------------------------------
// KDE Includes
#include <tdeglobal.h>
#include <tdelocale.h>
#include <kiconloader.h>
#include <tdemessagebox.h>
#include <tdecompletionbox.h>
#include <kpushbutton.h>
#include <tdepopupmenu.h>
#include <tdestdaccel.h>
#include <tdeshortcut.h>
// ----------------------------------------------------------------------------
// Project Includes
#include "kmymoneysplittable.h"
#include <kmymoney/mymoneyfile.h>
#include <kmymoney/kmymoneyedit.h>
#include <kmymoney/kmymoneycategory.h>
#include <kmymoney/kmymoneyaccountselector.h>
#include <kmymoney/kmymoneylineedit.h>
#include <kmymoney/mymoneysecurity.h>
#include <kmymoney/kmymoneyglobalsettings.h>
#include "../dialogs/kcurrencycalculator.h"
#include "../mymoney/mymoneyutils.h"
kMyMoneySplitTable::kMyMoneySplitTable(TQWidget *parent, const char *name ) :
TQTable(parent,name),
m_currentRow(0),
m_maxRows(0),
m_editMode(false),
m_amountWidth(80),
m_editCategory(0),
m_editMemo(0),
m_editAmount(0)
{
// setup the transactions table
setNumRows(1);
setNumCols(3);
horizontalHeader()->setLabel(0, i18n("Category"));
horizontalHeader()->setLabel(1, i18n("Memo"));
horizontalHeader()->setLabel(2, i18n("Amount"));
setSelectionMode(TQTable::NoSelection);
setLeftMargin(0);
verticalHeader()->hide();
setColumnStretchable(0, false);
setColumnStretchable(1, false);
setColumnStretchable(2, false);
horizontalHeader()->setResizeEnabled(false);
horizontalHeader()->setMovingEnabled(false);
horizontalHeader()->setFont(KMyMoneyGlobalSettings::listHeaderFont());
setVScrollBarMode(TQScrollView::AlwaysOn);
// never show a horizontal scroll bar
setHScrollBarMode(TQScrollView::AlwaysOff);
// setup the context menu
m_contextMenu = new TDEPopupMenu(this);
TDEIconLoader *il = TDEGlobal::iconLoader();
m_contextMenu->insertTitle(il->loadIcon("transaction", TDEIcon::MainToolbar), i18n("Split Options"));
m_contextMenu->insertItem(il->loadIcon("edit", TDEIcon::Small), i18n("Edit..."), this, TQT_SLOT(slotStartEdit()));
m_contextMenuDuplicate = m_contextMenu->insertItem(il->loadIcon("edit-copy", TDEIcon::Small), i18n("Duplicate"), this, TQT_SLOT(slotDuplicateSplit()));
m_contextMenuDelete = m_contextMenu->insertItem(il->loadIcon("delete", TDEIcon::Small),
i18n("Delete ..."),
this, TQT_SLOT(slotDeleteSplit()));
connect(this, TQT_SIGNAL(clicked(int, int, int, const TQPoint&)),
this, TQT_SLOT(slotSetFocus(int, int, int, const TQPoint&)));
connect(this, TQT_SIGNAL(transactionChanged(const MyMoneyTransaction&)),
this, TQT_SLOT(slotUpdateData(const MyMoneyTransaction&)));
}
kMyMoneySplitTable::~kMyMoneySplitTable()
{
}
void kMyMoneySplitTable::setup(const TQMap<TQString, MyMoneyMoney>& priceInfo)
{
m_priceInfo = priceInfo;
}
const TQColor kMyMoneySplitTable::rowBackgroundColor(const int row) const
{
return (row % 2) ? KMyMoneyGlobalSettings::listColor() : KMyMoneyGlobalSettings::listBGColor();
}
void kMyMoneySplitTable::paintCell(TQPainter *p, int row, int col, const TQRect& r, bool /*selected*/)
{
TQColorGroup g = colorGroup();
TQColor textColor;
g.setColor(TQColorGroup::Base, rowBackgroundColor(row));
p->setFont(KMyMoneyGlobalSettings::listCellFont());
TQString firsttext = text(row, col);
TQString qstringCategory;
TQString qstringMemo;
int intPos = firsttext.find("|");
if(intPos > -1)
{
qstringCategory = firsttext.left(intPos);
qstringMemo = firsttext.mid(intPos + 1);
}
TQRect rr = r;
TQRect rr2 = r;
rr.setX(0);
rr.setY(0);
rr.setWidth(columnWidth(col));
rr.setHeight(rowHeight(row));
rr2.setX(2);
rr2.setY(0);
rr2.setWidth(columnWidth(col)-4);
rr2.setHeight(rowHeight(row));
if(row == m_currentRow) {
TQBrush backgroundBrush(g.highlight());
textColor = g.highlightedText();
p->fillRect(rr,backgroundBrush);
} else {
TQBrush backgroundBrush(g.base());
textColor = g.text();
p->fillRect(rr,backgroundBrush);
}
if (KMyMoneyGlobalSettings::showGrid()) {
p->setPen(KMyMoneyGlobalSettings::listGridColor());
if(col != 0)
p->drawLine(rr.x(), 0, rr.x(), rr.height()-1); // left frame
p->drawLine(rr.x(), rr.y(), rr.width(), 0); // bottom frame
p->setPen(textColor);
}
switch (col) {
case 0: // category
case 1: // memo
p->drawText(rr2, TQt::AlignLeft | TQt::AlignVCenter, text(row, col));
break;
case 2: // amount
p->drawText(rr2, TQt::AlignRight | TQt::AlignVCenter,firsttext);
break;
}
}
/** Override the TQTable member function to avoid display of focus */
void kMyMoneySplitTable::paintFocus(TQPainter * /* p */, const TQRect & /*cr*/)
{
}
void kMyMoneySplitTable::columnWidthChanged(int col)
{
for (int i=0; i<numRows(); i++)
updateCell(i, col);
}
/** Override the TQTable member function to avoid confusion with our own functionality */
void kMyMoneySplitTable::endEdit(int /*row*/, int /*col*/, bool /*accept*/, bool /*replace*/ )
{
}
bool kMyMoneySplitTable::eventFilter(TQObject *o, TQEvent *e)
{
// MYMONEYTRACER(tracer);
TQKeyEvent *k = TQT_TQKEYEVENT (e);
bool rc = false;
int row = currentRow();
int lines = visibleHeight()/rowHeight(0);
TQWidget* w;
if(e->type() == TQEvent::KeyPress && !isEditMode()) {
rc = true;
switch(k->key()) {
case TQt::Key_Up:
if(row)
slotSetFocus(row-1);
break;
case TQt::Key_Down:
if(row < static_cast<int> (m_transaction.splits().count()-1))
slotSetFocus(row+1);
break;
case TQt::Key_Home:
slotSetFocus(0);
break;
case TQt::Key_End:
slotSetFocus(m_transaction.splits().count()-1);
break;
case TQt::Key_PageUp:
if(lines) {
while(lines-- > 0 && row)
row--;
slotSetFocus(row);
}
break;
case TQt::Key_PageDown:
if(row < static_cast<int> (m_transaction.splits().count()-1)) {
while(lines-- > 0 && row < static_cast<int> (m_transaction.splits().count()-1))
row++;
slotSetFocus(row);
}
break;
case TQt::Key_Delete:
slotDeleteSplit();
break;
case TQt::Key_Return:
case TQt::Key_Enter:
if(row < static_cast<int> (m_transaction.splits().count()-1)
&& KMyMoneyGlobalSettings::enterMovesBetweenFields()) {
slotStartEdit();
} else
emit returnPressed();
break;
case TQt::Key_Escape:
emit escapePressed();
break;
case TQt::Key_F2:
slotStartEdit();
break;
default:
rc = true;
TDEShortcut copySplit(i18n("Duplicate split", "CTRL+c"));
TDEShortcut newSplit(TQKeySequence(TQt::CTRL | TQt::Key_Insert));
if(copySplit.contains(KKey(k))) {
slotDuplicateSplit();
} else if(newSplit.contains(KKey(k))) {
slotSetFocus(m_transaction.splits().count()-1);
slotStartEdit();
} else if ( k->text()[ 0 ].isPrint() ) {
w = slotStartEdit();
// make sure, the widget receives the key again
TQApplication::sendEvent(w, e);
}
break;
}
} else if(e->type() == TQEvent::KeyPress && isEditMode()) {
bool terminate = true;
rc = true;
switch(k->key()) {
// suppress the F2 functionality to start editing in inline edit mode
case TQt::Key_F2:
// suppress the cursor movement in inline edit mode
case TQt::Key_Up:
case TQt::Key_Down:
case TQt::Key_PageUp:
case TQt::Key_PageDown:
break;
case TQt::Key_Return:
case TQt::Key_Enter:
// we cannot call the slot directly, as it destroys the caller of
// this method :-( So we let the event handler take care of calling
// the respective slot using a timeout. For a KLineEdit derived object
// it could be, that at this point the user selected a value from
// a completion list. In this case, we close the completion list and
// do not end editing of the transaction.
if(o->inherits("KLineEdit")) {
KLineEdit* le = dynamic_cast<KLineEdit*> (o);
TDECompletionBox* box = le->completionBox(false);
if(box && box->isVisible()) {
terminate = false;
le->completionBox(false)->hide();
}
}
// in case we have the 'enter moves focus between fields', we need to simulate
// a TAB key when the object 'o' points to the category or memo field.
if(KMyMoneyGlobalSettings::enterMovesBetweenFields()) {
if(TQT_BASE_OBJECT(o) == TQT_BASE_OBJECT(m_editCategory->lineEdit()) || TQT_BASE_OBJECT(o) == TQT_BASE_OBJECT(m_editMemo)) {
terminate = false;
TQKeyEvent evt(e->type(),
Key_Tab, 0, k->state(), TQString(),
k->isAutoRepeat(), k->count());
TQApplication::sendEvent( o, &evt );
}
}
if(terminate) {
TQTimer::singleShot(0, this, TQT_SLOT(slotEndEditKeyboard()));
}
break;
case TQt::Key_Escape:
// we cannot call the slot directly, as it destroys the caller of
// this method :-( So we let the event handler take care of calling
// the respective slot using a timeout.
TQTimer::singleShot(0, this, TQT_SLOT(slotCancelEdit()));
break;
default:
rc = false;
break;
}
} else if(e->type() == TQEvent::KeyRelease && !isEditMode()) {
// for some reason, we only see a KeyRelease event of the Menu key
// here. In other locations (e.g. Register::eventFilter()) we see
// a KeyPress event. Strange. (ipwizard - 2008-05-10)
switch(k->key()) {
case TQt::Key_Menu:
// if the very last entry is selected, the delete
// operation is not available otherwise it is
m_contextMenu->setItemEnabled(m_contextMenuDelete,
row < static_cast<int> (m_transaction.splits().count()-1));
m_contextMenu->setItemEnabled(m_contextMenuDuplicate,
row < static_cast<int> (m_transaction.splits().count()-1));
m_contextMenu->exec(TQCursor::pos());
rc = true;
break;
default:
break;
}
}
// if the event has not been processed here, forward it to
// the base class implementation if it's not a key event
if(rc == false) {
if(e->type() != TQEvent::KeyPress
&& e->type() != TQEvent::KeyRelease) {
rc = TQTable::eventFilter(o, e);
}
}
return rc;
}
void kMyMoneySplitTable::slotSetFocus(int realrow, int /* col */, int button, const TQPoint& /* point */)
{
MYMONEYTRACER(tracer);
int row = realrow;
// adjust row to used area
if(row > static_cast<int> (m_transaction.splits().count()-1))
row = m_transaction.splits().count()-1;
if(row < 0)
row = 0;
// make sure the row will be on the screen
ensureCellVisible(row, 0);
if(button == Qt::LeftButton) { // left mouse button
if(isEditMode()) { // in edit mode?
if(KMyMoneyGlobalSettings::focusChangeIsEnter())
slotEndEdit();
else
slotCancelEdit();
}
if(row != static_cast<int> (currentRow())) {
// setup new current row and update visible selection
setCurrentCell(row, 0);
slotUpdateData(m_transaction);
}
} else if(button == Qt::RightButton) {
// context menu is only available when cursor is on
// an existing transaction or the first line after this area
if(row == realrow) {
// setup new current row and update visible selection
setCurrentCell(row, 0);
slotUpdateData(m_transaction);
// if the very last entry is selected, the delete
// operation is not available otherwise it is
m_contextMenu->setItemEnabled(m_contextMenuDelete,
row < static_cast<int> (m_transaction.splits().count()-1));
m_contextMenu->setItemEnabled(m_contextMenuDuplicate,
row < static_cast<int> (m_transaction.splits().count()-1));
m_contextMenu->exec(TQCursor::pos());
}
}
}
void kMyMoneySplitTable::contentsMousePressEvent( TQMouseEvent* e )
{
slotSetFocus( rowAt(e->pos().y()), columnAt(e->pos().x()), e->button(), e->pos() );
}
/* turn off TQTable behaviour */
void kMyMoneySplitTable::contentsMouseReleaseEvent( TQMouseEvent* /* e */ )
{
}
void kMyMoneySplitTable::contentsMouseDoubleClickEvent( TQMouseEvent *e )
{
MYMONEYTRACER(tracer);
int col = columnAt(e->pos().x());
slotSetFocus( rowAt(e->pos().y()), col, e->button(), e->pos() );
slotStartEdit();
KLineEdit* editWidget = 0;
switch(col) {
case 1:
editWidget = m_editMemo;
break;
case 2:
editWidget = dynamic_cast<KLineEdit*> (m_editAmount->focusWidget());
break;
default:
break;
}
if(editWidget) {
editWidget->setFocus();
editWidget->selectAll();
// we need to call setFocus on the edit widget from the
// main loop again to get the keyboard focus to the widget also
TQTimer::singleShot(0, editWidget, TQT_SLOT(setFocus()));
}
}
void kMyMoneySplitTable::setCurrentCell(int row, int /* col */)
{
MYMONEYTRACER(tracer);
if(row > m_maxRows)
row = m_maxRows;
m_currentRow = row;
TQTable::setCurrentCell(row, 0);
TQValueList<MyMoneySplit> list = getSplits(m_transaction);
if(row < static_cast<int>(list.count()))
m_split = list[row];
else
m_split = MyMoneySplit();
}
void kMyMoneySplitTable::setNumRows(int irows)
{
TQTable::setNumRows(irows);
// determine row height according to the edit widgets
// we use the category widget as the base
TQFontMetrics fm( KMyMoneyGlobalSettings::listCellFont() );
int height = fm.lineSpacing()+6;
#if 0
// recalculate row height hint
KMyMoneyCategory cat;
height = TQMAX(cat.sizeHint().height(), height);
#endif
verticalHeader()->setUpdatesEnabled(false);
for(int i = 0; i < irows; ++i)
verticalHeader()->resizeSection(i, height);
verticalHeader()->setUpdatesEnabled(true);
// add or remove scrollbars as required
updateScrollBars();
}
void kMyMoneySplitTable::setTransaction(const MyMoneyTransaction& t, const MyMoneySplit& s, const MyMoneyAccount& acc)
{
MYMONEYTRACER(tracer);
m_transaction = t;
m_account = acc;
m_hiddenSplit = s;
setCurrentCell(0, 0);
slotUpdateData(m_transaction);
}
const TQValueList<MyMoneySplit> kMyMoneySplitTable::getSplits(const MyMoneyTransaction& t) const
{
TQValueList<MyMoneySplit> list;
TQValueList<MyMoneySplit>::Iterator it;
// get list of splits
list = t.splits();
// and ignore the one that should be hidden
for(it = list.begin(); it != list.end(); ++it) {
if((*it).id() == m_hiddenSplit.id()) {
list.remove(it);
break;
}
}
return list;
}
void kMyMoneySplitTable::slotUpdateData(const MyMoneyTransaction& t)
{
MYMONEYTRACER(tracer);
unsigned long rowCount=0;
TQValueList<MyMoneySplit> list = getSplits(t);
updateTransactionTableSize();
// fill the part that is used by transactions
TQValueList<MyMoneySplit>::Iterator it;
kMyMoneyEdit* valfield = new kMyMoneyEdit();
for(it = list.begin(); it != list.end(); ++it) {
TQString colText;
MyMoneyMoney value = (*it).value();
if(!(*it).accountId().isEmpty()) {
try {
colText = MyMoneyFile::instance()->accountToCategory((*it).accountId());
} catch(MyMoneyException *e) {
tqDebug("Unexpected exception in kMyMoneySplitTable::slotUpdateData()");
delete e;
}
}
TQString amountTxt = value.formatMoney(m_account.fraction());
if(value == MyMoneyMoney::autoCalc) {
amountTxt = i18n("will be calculated");
}
if(colText.isEmpty() && (*it).memo().isEmpty() && value.isZero())
amountTxt = TQString();
unsigned width = fontMetrics().width(amountTxt);
valfield->setMinimumWidth(width);
width = valfield->minimumSizeHint().width();
if(width > m_amountWidth)
m_amountWidth = width;
setText(rowCount, 0, colText);
setText(rowCount, 1, (*it).memo());
setText(rowCount, 2, amountTxt);
rowCount++;
}
delete valfield;
// now clean out the remainder of the table
while(rowCount < static_cast<unsigned long> (numRows())) {
setText(rowCount, 0, "");
setText(rowCount, 1, "");
setText(rowCount, 2, "");
++rowCount;
}
}
void kMyMoneySplitTable::updateTransactionTableSize(void)
{
// get current size of transactions table
int rowHeight = cellGeometry(0, 0).height();
// add half a row to the height to avoid unnecessary toggling when
// changing the number of rows
int tableHeight = (height() + rowHeight/2);
int splitCount = m_transaction.splits().count()-1;
if(splitCount < 0)
splitCount = 0;
// see if we need some extra lines to fill the current size with the grid
int numExtraLines = (tableHeight / rowHeight) - splitCount;
if(numExtraLines < 2)
numExtraLines = 2;
setNumRows(splitCount + numExtraLines);
// setMaxRows(splitCount);
m_maxRows = splitCount;
}
void kMyMoneySplitTable::resizeEvent(TQResizeEvent* /* ev */)
{
int w = visibleWidth() - m_amountWidth;
// resize the columns
setColumnWidth(0, w/2);
setColumnWidth(1, w/2);
setColumnWidth(2, m_amountWidth);
updateTransactionTableSize();
}
void kMyMoneySplitTable::slotDuplicateSplit(void)
{
MYMONEYTRACER(tracer);
TQValueList<MyMoneySplit> list = getSplits(m_transaction);
if(m_currentRow < static_cast<int> (list.count())) {
MyMoneySplit split = list[m_currentRow];
split.clearId();
try {
m_transaction.addSplit(split);
emit transactionChanged(m_transaction);
} catch(MyMoneyException *e) {
tqDebug("Cannot duplicate split: %s", e->what().latin1());
delete e;
}
}
}
void kMyMoneySplitTable::slotDeleteSplit(void)
{
MYMONEYTRACER(tracer);
TQValueList<MyMoneySplit> list = getSplits(m_transaction);
if(m_currentRow < static_cast<int> (list.count())) {
if(KMessageBox::warningContinueCancel (this,
i18n("You are about to delete the selected split. "
"Do you really want to continue?"),
i18n("KMyMoney"),
i18n("Continue")
) == KMessageBox::Continue) {
try {
m_transaction.removeSplit(list[m_currentRow]);
// if we removed the last split, select the previous
if(m_currentRow && m_currentRow == static_cast<int>(list.count())-1)
setCurrentCell(m_currentRow-1, 0);
else
setCurrentCell(m_currentRow, 0);
emit transactionChanged(m_transaction);
} catch(MyMoneyException *e) {
tqDebug("Cannot remove split: %s", e->what().latin1());
delete e;
}
}
}
}
TQWidget* kMyMoneySplitTable::slotStartEdit(void)
{
MYMONEYTRACER(tracer);
return createEditWidgets();
}
void kMyMoneySplitTable::slotEndEdit(void)
{
endEdit(false);
}
void kMyMoneySplitTable::slotEndEditKeyboard(void)
{
endEdit(true);
}
void kMyMoneySplitTable::endEdit(bool keyBoardDriven)
{
// Don't proceed, if we're not in edit mode
if(!m_editCategory)
return;
MyMoneyFile* file = MyMoneyFile::instance();
MYMONEYTRACER(tracer);
MyMoneySplit s1 = m_split;
if (m_editCategory->selectedItem().isEmpty()) {
KMessageBox::information(this, i18n("You need to assign a category to this split before it can be entered."), i18n("Enter split"), "EnterSplitWithEmptyCategory");
m_editCategory->setFocus();
return;
}
bool needUpdate = false;
if(m_editCategory->selectedItem() != m_split.accountId()) {
s1.setAccountId(m_editCategory->selectedItem());
needUpdate = true;
}
if(m_editMemo->text() != m_split.memo()) {
s1.setMemo(m_editMemo->text());
needUpdate = true;
}
if(m_editAmount->value() != m_split.value()) {
s1.setValue(m_editAmount->value());
needUpdate = true;
}
if(needUpdate) {
if(!s1.value().isZero()) {
MyMoneyAccount cat = file->account(s1.accountId());
if(cat.currencyId() != m_transaction.commodity()) {
MyMoneySecurity fromCurrency, toCurrency;
MyMoneyMoney fromValue, toValue;
fromCurrency = file->security(m_transaction.commodity());
toCurrency = file->security(cat.currencyId());
// determine the fraction required for this category
int fract = toCurrency.smallestAccountFraction();
if(cat.accountType() == MyMoneyAccount::Cash)
fract = toCurrency.smallestCashFraction();
// display only positive values to the user
fromValue = s1.value().abs();
// if we had a price info in the beginning, we use it here
if(m_priceInfo.find(cat.currencyId()) != m_priceInfo.end()) {
toValue = (fromValue * m_priceInfo[cat.currencyId()]).convert(fract);
}
// if the shares are still 0, we need to change that
if(toValue.isZero()) {
MyMoneyPrice price = MyMoneyFile::instance()->price(fromCurrency.id(), toCurrency.id());
// if the price is valid calculate the shares. If it is invalid
// assume a conversion rate of 1.0
if(price.isValid()) {
toValue = (price.rate(toCurrency.id()) * fromValue).convert(fract);
} else {
toValue = fromValue;
}
}
// now present all that to the user
KCurrencyCalculator calc(fromCurrency,
toCurrency,
fromValue,
toValue,
m_transaction.postDate(),
fract,
this, "currencyCalculator");
if(calc.exec() == TQDialog::Rejected) {
return;
} else {
s1.setShares((s1.value() * calc.price()).convert(fract));
}
} else {
s1.setShares(s1.value());
}
} else
s1.setShares(s1.value());
m_split = s1;
try {
if(m_split.id().isEmpty()) {
m_transaction.addSplit(m_split);
} else {
m_transaction.modifySplit(m_split);
}
emit transactionChanged(m_transaction);
} catch(MyMoneyException *e) {
tqDebug("Cannot add/modify split: %s", e->what().latin1());
delete e;
}
}
this->setFocus();
destroyEditWidgets();
slotSetFocus(currentRow()+1);
// if we still have more splits, we start editing right away
// in case we have selected 'enter moves betweeen fields'
if(keyBoardDriven
&& currentRow() < static_cast<int> (m_transaction.splits().count()-1)
&& KMyMoneyGlobalSettings::enterMovesBetweenFields()) {
slotStartEdit();
}
}
void kMyMoneySplitTable::slotCancelEdit(void)
{
MYMONEYTRACER(tracer);
if(isEditMode()) {
destroyEditWidgets();
this->setFocus();
}
}
bool kMyMoneySplitTable::isEditMode(void) const
{
return m_editMode;
}
void kMyMoneySplitTable::destroyEditWidgets(void)
{
MYMONEYTRACER(tracer);
disconnect(MyMoneyFile::instance(), TQT_SIGNAL(dataChanged()), this, TQT_SLOT(slotLoadEditWidgets()));
clearCellWidget(m_currentRow, 0);
clearCellWidget(m_currentRow, 1);
clearCellWidget(m_currentRow, 2);
clearCellWidget(m_currentRow+1, 0);
m_editMode = false;
TQApplication::eventLoop()->processEvents(TQEventLoop::ExcludeUserInput, 100);
}
TQWidget* kMyMoneySplitTable::createEditWidgets(void)
{
MYMONEYTRACER(tracer);
TQFont cellFont = KMyMoneyGlobalSettings::listCellFont();
m_tabOrderWidgets.clear();
// create the widgets
m_editAmount = new kMyMoneyEdit(0);
m_editAmount->setFont(cellFont);
m_editAmount->setResetButtonVisible(false);
m_editCategory = new KMyMoneyCategory();
m_editCategory->setHint(i18n("Category"));
m_editCategory->setFont(cellFont);
connect(m_editCategory, TQT_SIGNAL(createItem(const TQString&, TQString&)), this, TQT_SIGNAL(createCategory(const TQString&, TQString&)));
connect(m_editCategory, TQT_SIGNAL(objectCreation(bool)), this, TQT_SIGNAL(objectCreation(bool)));
m_editMemo = new kMyMoneyLineEdit(0, 0, false, AlignLeft|AlignVCenter);
m_editMemo->setHint(i18n("Memo"));
m_editMemo->setFont(cellFont);
// create buttons for the mouse users
TDEIconLoader *il = TDEGlobal::iconLoader();
m_registerButtonFrame = new TQFrame(this, "buttonFrame");
TQPalette palette = m_registerButtonFrame->palette();
palette.setColor(TQColorGroup::Background, rowBackgroundColor(m_currentRow+1) );
m_registerButtonFrame->setPalette(palette);
TQHBoxLayout* l = new TQHBoxLayout(m_registerButtonFrame);
m_registerEnterButton = new KPushButton(il->loadIcon("button_ok", TDEIcon::Small, TDEIcon::SizeSmall), TQString(), m_registerButtonFrame, "EnterButton");
m_registerCancelButton = new KPushButton(il->loadIcon("button_cancel", TDEIcon::Small, TDEIcon::SizeSmall), TQString(), m_registerButtonFrame, "CancelButton");
l->addWidget(m_registerEnterButton);
l->addWidget(m_registerCancelButton);
l->addStretch(2);
connect(m_registerEnterButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotEndEdit()));
connect(m_registerCancelButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotCancelEdit()));
// setup tab order
addToTabOrder(m_editCategory);
addToTabOrder(m_editMemo);
addToTabOrder(m_editAmount);
addToTabOrder(m_registerEnterButton);
addToTabOrder(m_registerCancelButton);
if(!m_split.accountId().isEmpty()) {
m_editCategory->setSelectedItem(m_split.accountId());
} else {
// check if the transaction is balanced or not. If not,
// assign the remainder to the amount.
MyMoneyMoney diff;
TQValueList<MyMoneySplit> list = m_transaction.splits();
TQValueList<MyMoneySplit>::ConstIterator it_s;
for(it_s = list.begin(); it_s != list.end(); ++it_s) {
if(!(*it_s).accountId().isEmpty())
diff += (*it_s).value();
}
m_split.setValue(-diff);
}
m_editMemo->loadText(m_split.memo());
// don't allow automatically calculated values to be modified
if(m_split.value() == MyMoneyMoney::autoCalc) {
m_editAmount->setEnabled(false);
m_editAmount->loadText("will be calculated");
} else
m_editAmount->setValue(m_split.value());
setCellWidget(m_currentRow, 0, m_editCategory);
setCellWidget(m_currentRow, 1, m_editMemo);
setCellWidget(m_currentRow, 2, m_editAmount);
setCellWidget(m_currentRow+1, 0, m_registerButtonFrame);
// load e.g. the category widget with the account list
slotLoadEditWidgets();
connect(MyMoneyFile::instance(), TQT_SIGNAL(dataChanged()), this, TQT_SLOT(slotLoadEditWidgets()));
// setup the keyboard filter for all widgets
for(TQWidget* w = m_tabOrderWidgets.first(); w; w = m_tabOrderWidgets.next()) {
w->installEventFilter(this);
}
m_editCategory->setFocus();
m_editCategory->lineEdit()->selectAll();
m_editMode = true;
return m_editCategory->lineEdit();
}
void kMyMoneySplitTable::slotLoadEditWidgets(void)
{
// reload category widget
TQString categoryId = m_editCategory->selectedItem();
AccountSet aSet;
aSet.addAccountGroup(MyMoneyAccount::Asset);
aSet.addAccountGroup(MyMoneyAccount::Liability);
aSet.addAccountGroup(MyMoneyAccount::Income);
aSet.addAccountGroup(MyMoneyAccount::Expense);
if(KMyMoneyGlobalSettings::expertMode())
aSet.addAccountGroup(MyMoneyAccount::Equity);
// remove the accounts with invalid types at this point
aSet.removeAccountType(MyMoneyAccount::CertificateDep);
aSet.removeAccountType(MyMoneyAccount::Investment);
aSet.removeAccountType(MyMoneyAccount::Stock);
aSet.removeAccountType(MyMoneyAccount::MoneyMarket);
aSet.load(m_editCategory->selector());
// if an account is specified then remove it from the widget so that the user
// cannot create a transfer with from and to account being the same account
if(!m_account.id().isEmpty())
m_editCategory->selector()->removeItem(m_account.id());
if(!categoryId.isEmpty())
m_editCategory->setSelectedItem(categoryId);
}
void kMyMoneySplitTable::addToTabOrder(TQWidget* w)
{
if(w) {
while(w->focusProxy())
w = TQT_TQWIDGET(w->focusProxy());
m_tabOrderWidgets.append(w);
}
}
bool kMyMoneySplitTable::focusNextPrevChild(bool next)
{
MYMONEYTRACER(tracer);
bool rc = false;
if(m_editCategory) {
TQWidget *w = 0;
TQWidget *currentWidget;
m_tabOrderWidgets.find(tqApp->focusWidget());
currentWidget = m_tabOrderWidgets.current();
w = next ? m_tabOrderWidgets.next() : m_tabOrderWidgets.prev();
do {
if(!w) {
w = next ? m_tabOrderWidgets.first() : m_tabOrderWidgets.last();
}
if(w != currentWidget
&& ((w->focusPolicy() & TQ_TabFocus) == TQ_TabFocus)
&& w->isVisible() && w->isEnabled()) {
w->setFocus();
rc = true;
break;
}
w = next ? m_tabOrderWidgets.next() : m_tabOrderWidgets.prev();
} while(w != currentWidget);
} else
rc = TQTable::focusNextPrevChild(next);
return rc;
}
#include "kmymoneysplittable.moc"