diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-07-04 22:38:03 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-07-04 22:38:03 +0000 |
commit | dadc34655c3ab961b0b0b94a10eaaba710f0b5e8 (patch) | |
tree | 99e72842fe687baea16376a147619b6048d7e441 /kmymoney2/dialogs/kmymoneysplittable.cpp | |
download | kmymoney-dadc34655c3ab961b0b0b94a10eaaba710f0b5e8.tar.gz kmymoney-dadc34655c3ab961b0b0b94a10eaaba710f0b5e8.zip |
Added kmymoney
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kmymoney@1239792 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kmymoney2/dialogs/kmymoneysplittable.cpp')
-rw-r--r-- | kmymoney2/dialogs/kmymoneysplittable.cpp | 999 |
1 files changed, 999 insertions, 0 deletions
diff --git a/kmymoney2/dialogs/kmymoneysplittable.cpp b/kmymoney2/dialogs/kmymoneysplittable.cpp new file mode 100644 index 0000000..5b5f1af --- /dev/null +++ b/kmymoney2/dialogs/kmymoneysplittable.cpp @@ -0,0 +1,999 @@ +/*************************************************************************** + 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 <qglobal.h> +#include <qpainter.h> +#include <qcursor.h> +#include <qapplication.h> +#include <qtimer.h> +#include <qlayout.h> +#include <qeventloop.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(QWidget *parent, const char *name ) : + QTable(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(QTable::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(QScrollView::AlwaysOn); + // never show a horizontal scroll bar + setHScrollBarMode(QScrollView::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, SLOT(slotStartEdit())); + m_contextMenuDuplicate = m_contextMenu->insertItem(il->loadIcon("editcopy", KIcon::Small), i18n("Duplicate"), this, SLOT(slotDuplicateSplit())); + m_contextMenuDelete = m_contextMenu->insertItem(il->loadIcon("delete", KIcon::Small), + i18n("Delete ..."), + this, SLOT(slotDeleteSplit())); + + connect(this, SIGNAL(clicked(int, int, int, const QPoint&)), + this, SLOT(slotSetFocus(int, int, int, const QPoint&))); + + connect(this, SIGNAL(transactionChanged(const MyMoneyTransaction&)), + this, SLOT(slotUpdateData(const MyMoneyTransaction&))); +} + +kMyMoneySplitTable::~kMyMoneySplitTable() +{ +} + +void kMyMoneySplitTable::setup(const QMap<QString, MyMoneyMoney>& priceInfo) +{ + m_priceInfo = priceInfo; +} + +const QColor kMyMoneySplitTable::rowBackgroundColor(const int row) const +{ + return (row % 2) ? KMyMoneyGlobalSettings::listColor() : KMyMoneyGlobalSettings::listBGColor(); +} + +void kMyMoneySplitTable::paintCell(QPainter *p, int row, int col, const QRect& r, bool /*selected*/) +{ + QColorGroup g = colorGroup(); + QColor textColor; + + g.setColor(QColorGroup::Base, rowBackgroundColor(row)); + + p->setFont(KMyMoneyGlobalSettings::listCellFont()); + + QString firsttext = text(row, col); + QString qstringCategory; + QString qstringMemo; + + int intPos = firsttext.find("|"); + if(intPos > -1) + { + qstringCategory = firsttext.left(intPos); + qstringMemo = firsttext.mid(intPos + 1); + } + + QRect rr = r; + QRect 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) { + QBrush backgroundBrush(g.highlight()); + textColor = g.highlightedText(); + p->fillRect(rr,backgroundBrush); + + } else { + QBrush 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, Qt::AlignLeft | Qt::AlignVCenter, text(row, col)); + break; + + case 2: // amount + p->drawText(rr2, Qt::AlignRight | Qt::AlignVCenter,firsttext); + break; + } +} + +/** Override the QTable member function to avoid display of focus */ +void kMyMoneySplitTable::paintFocus(QPainter * /* p */, const QRect & /*cr*/) +{ +} + +void kMyMoneySplitTable::columnWidthChanged(int col) +{ + for (int i=0; i<numRows(); i++) + updateCell(i, col); +} + +/** Override the QTable member function to avoid confusion with our own functionality */ +void kMyMoneySplitTable::endEdit(int /*row*/, int /*col*/, bool /*accept*/, bool /*replace*/ ) +{ +} + +bool kMyMoneySplitTable::eventFilter(QObject *o, QEvent *e) +{ + // MYMONEYTRACER(tracer); + QKeyEvent *k = static_cast<QKeyEvent *> (e); + bool rc = false; + int row = currentRow(); + int lines = visibleHeight()/rowHeight(0); + QWidget* w; + + if(e->type() == QEvent::KeyPress && !isEditMode()) { + rc = true; + switch(k->key()) { + case Qt::Key_Up: + if(row) + slotSetFocus(row-1); + break; + + case Qt::Key_Down: + if(row < static_cast<int> (m_transaction.splits().count()-1)) + slotSetFocus(row+1); + break; + + case Qt::Key_Home: + slotSetFocus(0); + break; + + case Qt::Key_End: + slotSetFocus(m_transaction.splits().count()-1); + break; + + case Qt::Key_PageUp: + if(lines) { + while(lines-- > 0 && row) + row--; + slotSetFocus(row); + } + break; + + case Qt::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 Qt::Key_Delete: + slotDeleteSplit(); + break; + + case Qt::Key_Return: + case Qt::Key_Enter: + if(row < static_cast<int> (m_transaction.splits().count()-1) + && KMyMoneyGlobalSettings::enterMovesBetweenFields()) { + slotStartEdit(); + } else + emit returnPressed(); + break; + + case Qt::Key_Escape: + emit escapePressed(); + break; + + case Qt::Key_F2: + slotStartEdit(); + break; + + default: + rc = true; + KShortcut copySplit(i18n("Duplicate split", "CTRL+c")); + KShortcut newSplit(QKeySequence(Qt::CTRL | Qt::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 + QApplication::sendEvent(w, e); + } + break; + } + + } else if(e->type() == QEvent::KeyPress && isEditMode()) { + bool terminate = true; + rc = true; + switch(k->key()) { + // suppress the F2 functionality to start editing in inline edit mode + case Qt::Key_F2: + // suppress the cursor movement in inline edit mode + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_PageUp: + case Qt::Key_PageDown: + break; + + case Qt::Key_Return: + case Qt::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(o == m_editCategory->lineEdit() || o == m_editMemo) { + terminate = false; + QKeyEvent evt(e->type(), + Key_Tab, 0, k->state(), QString::null, + k->isAutoRepeat(), k->count()); + + QApplication::sendEvent( o, &evt ); + } + } + + if(terminate) { + QTimer::singleShot(0, this, SLOT(slotEndEditKeyboard())); + } + break; + + case Qt::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. + QTimer::singleShot(0, this, SLOT(slotCancelEdit())); + break; + + default: + rc = false; + break; + } + } else if(e->type() == QEvent::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 Qt::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(QCursor::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() != QEvent::KeyPress + && e->type() != QEvent::KeyRelease) { + rc = QTable::eventFilter(o, e); + } + } + + return rc; +} + +void kMyMoneySplitTable::slotSetFocus(int realrow, int /* col */, int button, const QPoint& /* 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(QCursor::pos()); + } + } +} + +void kMyMoneySplitTable::contentsMousePressEvent( QMouseEvent* e ) +{ + slotSetFocus( rowAt(e->pos().y()), columnAt(e->pos().x()), e->button(), e->pos() ); +} + +/* turn off QTable behaviour */ +void kMyMoneySplitTable::contentsMouseReleaseEvent( QMouseEvent* /* e */ ) +{ +} + +void kMyMoneySplitTable::contentsMouseDoubleClickEvent( QMouseEvent *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 + QTimer::singleShot(0, editWidget, SLOT(setFocus())); + } +} + +void kMyMoneySplitTable::setCurrentCell(int row, int /* col */) +{ + MYMONEYTRACER(tracer); + + if(row > m_maxRows) + row = m_maxRows; + m_currentRow = row; + QTable::setCurrentCell(row, 0); + QValueList<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) +{ + QTable::setNumRows(irows); + + // determine row height according to the edit widgets + // we use the category widget as the base + QFontMetrics fm( KMyMoneyGlobalSettings::listCellFont() ); + int height = fm.lineSpacing()+6; +#if 0 + // recalculate row height hint + KMyMoneyCategory cat; + height = QMAX(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 QValueList<MyMoneySplit> kMyMoneySplitTable::getSplits(const MyMoneyTransaction& t) const +{ + QValueList<MyMoneySplit> list; + QValueList<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; + + QValueList<MyMoneySplit> list = getSplits(t); + updateTransactionTableSize(); + + // fill the part that is used by transactions + QValueList<MyMoneySplit>::Iterator it; + kMyMoneyEdit* valfield = new kMyMoneyEdit(); + for(it = list.begin(); it != list.end(); ++it) { + QString colText; + MyMoneyMoney value = (*it).value(); + if(!(*it).accountId().isEmpty()) { + try { + colText = MyMoneyFile::instance()->accountToCategory((*it).accountId()); + } catch(MyMoneyException *e) { + qDebug("Unexpected exception in kMyMoneySplitTable::slotUpdateData()"); + delete e; + } + } + QString amountTxt = value.formatMoney(m_account.fraction()); + if(value == MyMoneyMoney::autoCalc) { + amountTxt = i18n("will be calculated"); + } + + if(colText.isEmpty() && (*it).memo().isEmpty() && value.isZero()) + amountTxt = QString(); + + 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(QResizeEvent* /* 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); + QValueList<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) { + qDebug("Cannot duplicate split: %s", e->what().latin1()); + delete e; + } + } +} + +void kMyMoneySplitTable::slotDeleteSplit(void) +{ + MYMONEYTRACER(tracer); + QValueList<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) { + qDebug("Cannot remove split: %s", e->what().latin1()); + delete e; + } + } + } +} + +QWidget* 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() == QDialog::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) { + qDebug("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(), SIGNAL(dataChanged()), this, SLOT(slotLoadEditWidgets())); + + clearCellWidget(m_currentRow, 0); + clearCellWidget(m_currentRow, 1); + clearCellWidget(m_currentRow, 2); + clearCellWidget(m_currentRow+1, 0); + m_editMode = false; + + QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput, 100); +} + +QWidget* kMyMoneySplitTable::createEditWidgets(void) +{ + MYMONEYTRACER(tracer); + + QFont 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, SIGNAL(createItem(const QString&, QString&)), this, SIGNAL(createCategory(const QString&, QString&))); + connect(m_editCategory, SIGNAL(objectCreation(bool)), this, 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 QFrame(this, "buttonFrame"); + QPalette palette = m_registerButtonFrame->palette(); + palette.setColor(QColorGroup::Background, rowBackgroundColor(m_currentRow+1) ); + m_registerButtonFrame->setPalette(palette); + + QHBoxLayout* l = new QHBoxLayout(m_registerButtonFrame); + m_registerEnterButton = new KPushButton(il->loadIcon("button_ok", KIcon::Small, KIcon::SizeSmall), QString(), m_registerButtonFrame, "EnterButton"); + + m_registerCancelButton = new KPushButton(il->loadIcon("button_cancel", KIcon::Small, KIcon::SizeSmall), QString(), m_registerButtonFrame, "CancelButton"); + + l->addWidget(m_registerEnterButton); + l->addWidget(m_registerCancelButton); + l->addStretch(2); + + connect(m_registerEnterButton, SIGNAL(clicked()), this, SLOT(slotEndEdit())); + connect(m_registerCancelButton, SIGNAL(clicked()), this, 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; + QValueList<MyMoneySplit> list = m_transaction.splits(); + QValueList<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(), SIGNAL(dataChanged()), this, SLOT(slotLoadEditWidgets())); + + // setup the keyboard filter for all widgets + for(QWidget* 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 + QString 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(QWidget* w) +{ + if(w) { + while(w->focusProxy()) + w = w->focusProxy(); + m_tabOrderWidgets.append(w); + } +} + +bool kMyMoneySplitTable::focusNextPrevChild(bool next) +{ + MYMONEYTRACER(tracer); + bool rc = false; + + if(m_editCategory) { + QWidget *w = 0; + QWidget *currentWidget; + + m_tabOrderWidgets.find(qApp->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() & TabFocus) == TabFocus) + && w->isVisible() && w->isEnabled()) { + w->setFocus(); + rc = true; + break; + } + w = next ? m_tabOrderWidgets.next() : m_tabOrderWidgets.prev(); + } while(w != currentWidget); + + } else + rc = QTable::focusNextPrevChild(next); + + return rc; +} + + + +#include "kmymoneysplittable.moc" |