/*************************************************************************** kaccountsview.cpp ------------------- copyright : (C) 2005 by Thomas Baumgart email : ipwizard@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include #include "kaccountsview.h" #include "kmymoneyview.h" #include "../widgets/tdelistviewsearchline.h" #include "../kmymoneyglobalsettings.h" #include "../kmymoney2.h" KMyMoneyAccountIconItem::KMyMoneyAccountIconItem(TQIconView *parent, const MyMoneyAccount& account) : TDEIconViewItem(parent, account.name()), m_account(account), m_reconcileFlag(false) { updateAccount(account); } KMyMoneyAccountIconItem::~KMyMoneyAccountIconItem() { } void KMyMoneyAccountIconItem::setReconciliation(bool on) { if(m_reconcileFlag == on) return; m_reconcileFlag = on; updateAccount(m_account); } void KMyMoneyAccountIconItem::updateAccount(const MyMoneyAccount& account) { setPixmap(account.accountPixmap(m_reconcileFlag)); } KAccountsView::KAccountsView(TQWidget *parent, const char *name) : KAccountsViewDecl(parent,name), m_assetItem(0), m_liabilityItem(0) { // create the searchline widget // and insert it into the existing layout m_searchWidget = new TDEListViewSearchLineWidget(m_accountTree, m_accountTree->parentWidget()); TQVBoxLayout* layout = dynamic_cast(m_accountTree->parentWidget()->layout()); if(layout) { layout->insertWidget(0, m_searchWidget); } // setup icons for collapse and expand button TDEIconLoader *ic = TDEGlobal::iconLoader(); KGuiItem collapseGuiItem("", TQIconSet(ic->loadIcon("zoom-out", TDEIcon::Small, TDEIcon::SizeSmall)), TQString(), TQString()); KGuiItem expandGuiItem("", TQIconSet(ic->loadIcon("zoom-in", TDEIcon::Small, TDEIcon::SizeSmall)), TQString(), TQString()); m_collapseButton->setGuiItem(collapseGuiItem); m_expandButton->setGuiItem(expandGuiItem); for(int i=0; i < MaxViewTabs; ++i) m_needReload[i] = false; TDEConfig *config = TDEGlobal::config(); config->setGroup("Last Use Settings"); m_tab->setCurrentPage(config->readNumEntry("KAccountsView_LastType", 0)); connect(m_tab, TQ_SIGNAL(currentChanged(TQWidget*)), this, TQ_SLOT(slotTabChanged(TQWidget*))); connect(m_accountTree, TQ_SIGNAL(selectObject(const MyMoneyObject&)), this, TQ_SIGNAL(selectObject(const MyMoneyObject&))); connect(m_accountTree, TQ_SIGNAL(openContextMenu(const MyMoneyObject&)), this, TQ_SIGNAL(openContextMenu(const MyMoneyObject&))); connect(m_accountTree, TQ_SIGNAL(valueChanged(void)), this, TQ_SLOT(slotUpdateNetWorth(void))); connect(m_accountTree, TQ_SIGNAL(openObject(const MyMoneyObject&)), this, TQ_SIGNAL(openObject(const MyMoneyObject&))); connect(m_accountTree, TQ_SIGNAL(reparent(const MyMoneyAccount&, const MyMoneyAccount&)), this, TQ_SIGNAL(reparent(const MyMoneyAccount&, const MyMoneyAccount&))); connect(m_accountIcons, TQ_SIGNAL(selectionChanged(TQIconViewItem*)), this, TQ_SLOT(slotSelectIcon(TQIconViewItem*))); connect(m_accountIcons, TQ_SIGNAL(rightButtonClicked(TQIconViewItem*, const TQPoint&)), this, TQ_SLOT(slotOpenContext(TQIconViewItem*))); connect(m_accountIcons, TQ_SIGNAL(executed(TQIconViewItem*)), this, TQ_SLOT(slotOpenObject(TQIconViewItem*))); connect(MyMoneyFile::instance(), TQ_SIGNAL(dataChanged()), this, TQ_SLOT(slotLoadAccounts())); connect(m_collapseButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotExpandCollapse())); connect(m_expandButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotExpandCollapse())); } KAccountsView::~KAccountsView() { } void KAccountsView::slotExpandCollapse(void) { if(sender()) { KMyMoneyGlobalSettings::setShowAccountsExpanded(sender() == m_expandButton); } } void KAccountsView::slotLoadAccounts(void) { m_needReload[ListView] = true; m_needReload[IconView] = true; if(isVisible()) slotTabChanged(m_tab->currentPage()); } void KAccountsView::slotTabChanged(TQWidget* _tab) { AccountsViewTab tab = static_cast(m_tab->indexOf(_tab)); // remember this setting for startup TDEConfig *config = TDEGlobal::config(); config->setGroup("Last Use Settings"); config->writeEntry("KAccountsView_LastType", tab); loadAccounts(tab); switch(tab) { case ListView: // update the hint if categories are hidden m_hiddenCategories->setShown(m_haveUnusedCategories); break; case IconView: m_hiddenCategories->hide(); break; default: break; } KMyMoneyAccountTreeBaseItem* treeItem = m_accountTree->selectedItem(); KMyMoneyAccountIconItem* iconItem = selectedIcon(); emit selectObject(MyMoneyAccount()); switch(static_cast(m_tab->indexOf(m_tab->currentPage()))) { case ListView: // if we have a selected account, let the application know about it if(treeItem) { emit selectObject(treeItem->itemObject()); } break; case IconView: if(iconItem) { emit selectObject(iconItem->itemObject()); } break; default: break; } } void KAccountsView::show(void) { // don't forget base class implementation KAccountsViewDecl::show(); slotTabChanged(m_tab->currentPage()); } void KAccountsView::polish(void) { // don't forget base class implementation KAccountsViewDecl::polish(); m_accountTree->setResizeMode(TQListView::LastColumn); m_accountTree->restoreLayout("Account View Settings"); } void KAccountsView::loadAccounts(AccountsViewTab tab) { if(m_needReload[tab]) { switch(tab) { case ListView: loadListView(); break; case IconView: loadIconView(); break; default: break; } m_needReload[tab] = false; } } void KAccountsView::loadIconView(void) { ::timetrace("start load accounts icon view"); // remember the positions of the icons TQMap posMap; KMyMoneyAccountIconItem* p = dynamic_cast(m_accountIcons->firstItem()); for(;p; p = dynamic_cast(p->nextItem())) posMap[p->itemObject().id()] = p->pos(); // turn off updates to avoid flickering during reload m_accountIcons->setAutoArrange(true); // clear the current contents and recreate it m_accountIcons->clear(); TQMap accountMap; MyMoneyFile* file = MyMoneyFile::instance(); // get account list and sort by name TQValueList alist; file->accountList(alist); TQValueList::const_iterator it_a; for(it_a = alist.begin(); it_a != alist.end(); ++it_a) { accountMap[TQString("%1-%2").arg((*it_a).name()).arg((*it_a).id())] = *it_a; } bool showClosedAccounts = kmymoney2->toggleAction("view_show_all_accounts")->isChecked() || !KMyMoneyGlobalSettings::hideClosedAccounts(); bool existNewIcons = false; // parse list and add all asset and liability accounts TQMap::const_iterator it; for(it = accountMap.begin(); it != accountMap.end(); ++it) { TQPoint loc; if((*it).isClosed() && !showClosedAccounts) continue; const TQString& pos = (*it).value("kmm-iconpos"); KMyMoneyAccountIconItem* item; switch((*it).accountGroup()) { case MyMoneyAccount::Equity: if(!KMyMoneyGlobalSettings::expertMode()) continue; // tricky fall through here case MyMoneyAccount::Asset: case MyMoneyAccount::Liability: // don't show stock accounts if((*it).isInvest()) continue; // if we have a position stored with the object and no other // idea of it's current position, then take the one // stored inside the object. Also, turn off auto arrangement if(!pos.isEmpty() && posMap[(*it).id()] == TQPoint()) { posMap[(*it).id()] = point(pos); } loc = posMap[(*it).id()]; if(loc == TQPoint()) { existNewIcons = true; } else { m_accountIcons->setAutoArrange(false); } item = new KMyMoneyAccountIconItem(m_accountIcons, *it); if((*it).id() == m_reconciliationAccount.id()) item->setReconciliation(true); if(loc != TQPoint()) { item->move(loc); } break; default: break; } } // clear the current contents m_securityMap.clear(); m_transactionCountMap.clear(); if(existNewIcons) { m_accountIcons->arrangeItemsInGrid(true); } m_accountIcons->setAutoArrange(false); ::timetrace("done load accounts icon view"); } void KAccountsView::loadListView(void) { TQMap isOpen; ::timetrace("start load accounts list view"); // remember the id of the current selected item KMyMoneyAccountTreeBaseItem *item = m_accountTree->selectedItem(); TQString selectedItemId = (item) ? item->id() : TQString(); // keep a map of all 'expanded' accounts TQListViewItemIterator it_lvi(m_accountTree); while(it_lvi.current()) { item = dynamic_cast(it_lvi.current()); if(item && item->isOpen()) { isOpen[item->id()] = true; } ++it_lvi; } // remember the upper left corner of the viewport TQPoint startPoint = m_accountTree->viewportToContents(TQPoint(0, 0)); // turn off updates to avoid flickering during reload m_accountTree->setUpdatesEnabled(false); // clear the current contents and recreate it m_accountTree->clear(); m_securityMap.clear(); m_transactionCountMap.clear(); // make sure, the pointers are not pointing to some deleted object m_assetItem = m_liabilityItem = 0; MyMoneyFile* file = MyMoneyFile::instance(); TQValueList slist = file->currencyList(); slist += file->securityList(); TQValueList::const_iterator it_s; for(it_s = slist.begin(); it_s != slist.end(); ++it_s) { m_securityMap[(*it_s).id()] = *it_s; } m_transactionCountMap = file->transactionCountMap(); m_haveUnusedCategories = false; // create the items try { const MyMoneySecurity security = file->baseCurrency(); m_accountTree->setBaseCurrency(security); const MyMoneyAccount& asset = file->asset(); m_assetItem = new KMyMoneyAccountTreeItem(m_accountTree, asset, security, i18n("Asset")); loadSubAccounts(m_assetItem, asset.accountList()); const MyMoneyAccount& liability = file->liability(); m_liabilityItem = new KMyMoneyAccountTreeItem(m_accountTree, liability, security, i18n("Liability")); loadSubAccounts(m_liabilityItem, liability.accountList()); const MyMoneyAccount& income = file->income(); KMyMoneyAccountTreeItem *incomeItem = new KMyMoneyAccountTreeItem(m_accountTree, income, security, i18n("Income")); m_haveUnusedCategories |= loadSubAccounts(incomeItem, income.accountList()); const MyMoneyAccount& expense = file->expense(); KMyMoneyAccountTreeItem *expenseItem = new KMyMoneyAccountTreeItem(m_accountTree, expense, security, i18n("Expense")); m_haveUnusedCategories |= loadSubAccounts(expenseItem, expense.accountList()); if(KMyMoneyGlobalSettings::expertMode()) { const MyMoneyAccount equity = file->equity(); KMyMoneyAccountTreeItem *equityItem = new KMyMoneyAccountTreeItem(m_accountTree, equity, security, i18n("Equity")); loadSubAccounts(equityItem, equity.accountList()); } } catch(MyMoneyException *e) { kdDebug(2) << "Problem in accounts list view: " << e->what(); delete e; } // scan through the list of accounts and re-expand those that were // expanded and re-select the one that was probably selected before it_lvi = TQListViewItemIterator(m_accountTree); while(it_lvi.current()) { item = dynamic_cast(it_lvi.current()); if(item) { if(item->id() == selectedItemId) m_accountTree->setSelected(item, true); if(isOpen.find(item->id()) != isOpen.end()) item->setOpen(true); } ++it_lvi; } // reposition viewport m_accountTree->setContentsPos(startPoint.x(), startPoint.y()); m_searchWidget->searchLine()->updateSearch(TQString()); // turn updates back on m_accountTree->setUpdatesEnabled(true); m_accountTree->repaintContents(); // and in case we need to show things expanded, we'll do so if(KMyMoneyGlobalSettings::showAccountsExpanded()) m_accountTree->slotExpandAll(); // clear the current contents m_securityMap.clear(); m_transactionCountMap.clear(); ::timetrace("done load accounts list view"); } bool KAccountsView::loadSubAccounts(KMyMoneyAccountTreeItem* parent, const TQStringList& accountList) { MyMoneyFile* file = MyMoneyFile::instance(); bool unused = false; bool showClosedAccounts = kmymoney2->toggleAction("view_show_all_accounts")->isChecked() || !KMyMoneyGlobalSettings::hideClosedAccounts(); TQStringList::const_iterator it_a; for(it_a = accountList.begin(); it_a != accountList.end(); ++it_a) { const MyMoneyAccount& acc = file->account(*it_a); TQValueList prices; MyMoneySecurity security = file->baseCurrency(); try { if(acc.isInvest()) { security = m_securityMap[acc.currencyId()]; prices += file->price(acc.currencyId(), security.tradingCurrency()); if(security.tradingCurrency() != file->baseCurrency().id()) { MyMoneySecurity sec = m_securityMap[security.tradingCurrency()]; prices += file->price(sec.id(), file->baseCurrency().id()); } } else if(acc.currencyId() != file->baseCurrency().id()) { if(acc.currencyId() != file->baseCurrency().id()) { security = m_securityMap[acc.currencyId()]; prices += file->price(acc.currencyId(), file->baseCurrency().id()); } } } catch(MyMoneyException *e) { kdDebug(2) << __PRETTY_FUNCTION__ << " caught exception while adding " << acc.name() << "[" << acc.id() << "]: " << e->what(); delete e; } KMyMoneyAccountTreeItem* item = new KMyMoneyAccountTreeItem(parent, acc, prices, security); if(acc.id() == m_reconciliationAccount.id()) item->setReconciliation(true); unused |= loadSubAccounts(item, acc.accountList()); // no child accounts and no transactions in this account means 'unused' bool thisUnused = (!item->firstChild()) && (m_transactionCountMap[acc.id()] == 0); // In case of a category which is unused and we are requested to suppress // the display of those, if(acc.isIncomeExpense()) { if(KMyMoneyGlobalSettings::hideUnusedCategory() && thisUnused) { unused = true; delete item; } } // if the account is closed and we should not show it, we delete the item if(acc.isClosed() && !showClosedAccounts) { delete item; } } return unused; } void KAccountsView::slotReconcileAccount(const MyMoneyAccount& acc, const TQDate& reconciliationDate, const MyMoneyMoney& endingBalance) { Q_UNUSED(reconciliationDate); Q_UNUSED(endingBalance); // scan through the list of accounts and mark all non // expanded and re-select the one that was probably selected before TQListViewItemIterator it_lvi(m_accountTree); KMyMoneyAccountTreeItem* item; while(it_lvi.current()) { item = dynamic_cast(it_lvi.current()); if(item) { item->setReconciliation(false); } ++it_lvi; } // scan trough the icon list and do the same thing KMyMoneyAccountIconItem* icon = dynamic_cast(m_accountIcons->firstItem()); for(;icon; icon = dynamic_cast(icon->nextItem())) { icon->setReconciliation(false); } m_reconciliationAccount = acc; if(!acc.id().isEmpty()) { // scan through the list of accounts and mark // the one that is currently reconciled it_lvi = TQListViewItemIterator(m_accountTree); while(it_lvi.current()) { item = dynamic_cast(it_lvi.current()); if(item && item->itemObject().id() == acc.id()) { item->setReconciliation(true); break; } ++it_lvi; } // scan trough the icon list and do the same thing icon = dynamic_cast(m_accountIcons->firstItem()); for(;icon; icon = dynamic_cast(icon->nextItem())) { if(icon->itemObject().id() == acc.id()) { icon->setReconciliation(true); break; } } } } void KAccountsView::slotUpdateNetWorth(void) { if(!m_assetItem || !m_liabilityItem) return; MyMoneyMoney netWorth = m_assetItem->totalValue() - m_liabilityItem->totalValue(); TQString s(i18n("Net Worth: ")); // FIXME figure out how to deal with the approximate // if(!(file->totalValueValid(assetAccount.id()) & file->totalValueValid(liabilityAccount.id()))) // s += "~ "; s.replace(TQString(" "), TQString(" ")); if(netWorth.isNegative()) { s += ""; } const MyMoneySecurity& sec = MyMoneyFile::instance()->baseCurrency(); TQString v(netWorth.formatMoney(sec)); s += v.replace(TQString(" "), TQString(" ")); if(netWorth.isNegative()) { s += ""; } m_totalProfitsLabel->setFont(KMyMoneyGlobalSettings::listCellFont()); m_totalProfitsLabel->setText(s); } KMyMoneyAccountIconItem* KAccountsView::selectedIcon(void) const { return dynamic_cast(m_accountIcons->currentItem()); } void KAccountsView::slotSelectIcon(TQIconViewItem* item) { KMyMoneyAccountIconItem* p = dynamic_cast(item); if(p) emit selectObject(p->itemObject()); } void KAccountsView::slotOpenContext(TQIconViewItem* item) { KMyMoneyAccountIconItem* p = dynamic_cast(item); if(p) emit openContextMenu(p->itemObject()); } void KAccountsView::slotOpenObject(TQIconViewItem* item) { KMyMoneyAccountIconItem* p = dynamic_cast(item); if(p) emit openObject(p->itemObject()); } TQString KAccountsView::point(const TQPoint& val) const { return TQString("%1;%2").arg(val.x()).arg(val.y()); } TQPoint KAccountsView::point(const TQString& val) const { TQRegExp exp("(\\d+);(\\d+)"); int x = 0; int y = 0; if(exp.search(val) != -1) { x = exp.cap(1).toInt(); y = exp.cap(2).toInt(); } return TQPoint(x, y); } void KAccountsView::slotUpdateIconPos(unsigned int action) { if(action != KMyMoneyView::preSave) return; MyMoneyFileTransaction ft; KMyMoneyAccountIconItem* p = dynamic_cast(m_accountIcons->firstItem()); for(;p; p = dynamic_cast(p->nextItem())) { const MyMoneyAccount& acc = dynamic_cast(p->itemObject()); if(acc.value("kmm-iconpos") != point(p->pos())) { MyMoneyAccount a(acc); a.setValue("kmm-iconpos", point(p->pos())); try { MyMoneyFile::instance()->modifyAccount(a); } catch(MyMoneyException* e) { kdDebug(2) << "Unable to update icon pos: " << e->what(); delete e; } } } ft.commit(); } #include "kaccountsview.moc"