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.
1000 lines
30 KiB
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 <kglobal.h>
|
|
#include <klocale.h>
|
|
#include <kiconloader.h>
|
|
#include <kmessagebox.h>
|
|
#include <kcompletionbox.h>
|
|
#include <kpushbutton.h>
|
|
#include <kpopupmenu.h>
|
|
#include <kstdaccel.h>
|
|
#include <kshortcut.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 KPopupMenu(this);
|
|
KIconLoader *il = KGlobal::iconLoader();
|
|
m_contextMenu->insertTitle(il->loadIcon("transaction", KIcon::MainToolbar), i18n("Split Options"));
|
|
m_contextMenu->insertItem(il->loadIcon("edit", KIcon::Small), i18n("Edit..."), this, TQT_SLOT(slotStartEdit()));
|
|
m_contextMenuDuplicate = m_contextMenu->insertItem(il->loadIcon("editcopy", KIcon::Small), i18n("Duplicate"), this, TQT_SLOT(slotDuplicateSplit()));
|
|
m_contextMenuDelete = m_contextMenu->insertItem(il->loadIcon("delete", KIcon::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;
|
|
KShortcut copySplit(i18n("Duplicate split", "CTRL+c"));
|
|
KShortcut 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);
|
|
KCompletionBox* 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
|
|
KIconLoader *il = KGlobal::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", KIcon::Small, KIcon::SizeSmall), TQString(), m_registerButtonFrame, "EnterButton");
|
|
|
|
m_registerCancelButton = new KPushButton(il->loadIcon("button_cancel", KIcon::Small, KIcon::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"
|