/*************************************************************************** kbudgetview.cpp --------------- begin : Thu Jan 10 2006 copyright : (C) 2006 by Darren Gould email : darren_gould@gmx.de ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include #include #include #include #include #include "../widgets/kmymoneyaccounttreebudget.h" #include "kbudgetview.h" #include "../dialogs/knewbudgetdlg.h" #include "../kmymoney2.h" // *** KBudgetListItem Implementation *** KBudgetListItem::KBudgetListItem(KListView *tqparent, const MyMoneyBudget& budget) : KListViewItem(tqparent), m_budget(budget) { setText(0, budget.name()); setText(1, TQString("%1").tqarg(budget.budgetStart().year())); // allow in column rename setRenameEnabled(0, true); } KBudgetListItem::~KBudgetListItem() { } void KBudgetListItem::paintCell(TQPainter *p, const TQColorGroup & cg, int column, int width, int align) { p->setFont(KMyMoneyGlobalSettings::listCellFont()); TQColorGroup cg2(cg); if (isAlternate()) cg2.setColor(TQColorGroup::Base, KMyMoneyGlobalSettings::listColor()); else cg2.setColor(TQColorGroup::Base, KMyMoneyGlobalSettings::listBGColor()); TQListViewItem::paintCell(p, cg2, column, width, align); } // *** KBudgetView Implementation *** const int KBudgetView::m_iBudgetYearsAhead = 5; const int KBudgetView::m_iBudgetYearsBack = 3; KBudgetView::KBudgetView(TQWidget *tqparent, const char *name ) : KBudgetViewDecl(tqparent,name), m_needReload(false), m_inSelection(false) { m_accountTree->setSorting(-1); m_budgetList->setSorting(0); KIconLoader* il = KGlobal::iconLoader(); KGuiItem newButtenItem( TQString(""), TQIconSet(il->loadIcon("file_new", KIcon::Small, KIcon::SizeSmall)), i18n("Creates a new budget"), i18n("Use this to create a new empty budget.")); m_newButton->setGuiItem(newButtenItem); TQToolTip::add(m_newButton, newButtenItem.toolTip()); KGuiItem renameButtenItem( TQString(""), TQIconSet(il->loadIcon("editpaste", KIcon::Small, KIcon::SizeSmall)), i18n("Rename the current selected budget"), i18n("Use this to start renaming the selected budget.")); m_renameButton->setGuiItem(renameButtenItem); TQToolTip::add(m_renameButton, renameButtenItem.toolTip()); KGuiItem deleteButtenItem( TQString(""), TQIconSet(il->loadIcon("editdelete", KIcon::Small, KIcon::SizeSmall)), i18n("Delete the current selected budget"), i18n("Use this to delete the selected budget.")); m_deleteButton->setGuiItem(deleteButtenItem); TQToolTip::add(m_deleteButton, deleteButtenItem.toolTip()); KGuiItem updateButtenItem( TQString(""), TQIconSet(il->loadIcon("button_ok", KIcon::Small, KIcon::SizeSmall)), i18n("Accepts the entered values and stores the budget"), i18n("Use this to store the modified data.")); m_updateButton->setGuiItem(updateButtenItem); TQToolTip::add(m_updateButton, updateButtenItem.toolTip()); KGuiItem resetButtenItem( TQString(""), TQIconSet(il->loadIcon("undo", KIcon::Small, KIcon::SizeSmall)), i18n("Revert budget to last saved state"), i18n("Use this to discard the modified data.")); m_resetButton->setGuiItem(resetButtenItem); TQToolTip::add(m_resetButton, resetButtenItem.toolTip()); connect(m_budgetList, TQT_SIGNAL(contextMenu(KListView*, TQListViewItem* , const TQPoint&)), this, TQT_SLOT(slotOpenContextMenu(KListView*, TQListViewItem*, const TQPoint&))); connect(m_budgetList, TQT_SIGNAL(itemRenamed(TQListViewItem*,int,const TQString&)), this, TQT_SLOT(slotRenameBudget(TQListViewItem*,int,const TQString&))); connect(m_budgetList, TQT_SIGNAL(selectionChanged()), this, TQT_SLOT(slotSelectBudget())); connect(m_cbBudgetSubaccounts, TQT_SIGNAL(clicked()), this, TQT_SLOT(cb_includesSubaccounts_clicked())); connect(m_accountTree, TQT_SIGNAL(selectionChanged(TQListViewItem*)), this, TQT_SLOT(slotSelectAccount(TQListViewItem*))); connect(m_accountTree, TQT_SIGNAL(valueChanged()), this, TQT_SLOT(slotRefreshHideUnusedButton())); // connect the buttons to the actions. Make sure the enabled state // of the actions is reflected by the buttons connect(kmymoney2->action("budget_new"), TQT_SIGNAL(enabled(bool)), m_newButton, TQT_SLOT(setEnabled(bool))); connect(m_renameButton, TQT_SIGNAL(clicked()), kmymoney2->action("budget_rename"), TQT_SLOT(activate())); connect(kmymoney2->action("budget_rename"), TQT_SIGNAL(enabled(bool)), m_renameButton, TQT_SLOT(setEnabled(bool))); connect(m_deleteButton, TQT_SIGNAL(clicked()), kmymoney2->action("budget_delete"), TQT_SLOT(activate())); connect(kmymoney2->action("budget_delete"), TQT_SIGNAL(enabled(bool)), m_deleteButton, TQT_SLOT(setEnabled(bool))); connect(m_budgetValue, TQT_SIGNAL(valuesChanged()), this, TQT_SLOT(slotBudgetedAmountChanged())); connect(m_newButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotNewBudget())); connect(m_updateButton, TQT_SIGNAL(pressed()), this, TQT_SLOT(slotUpdateBudget())); connect(m_resetButton, TQT_SIGNAL(pressed()), this, TQT_SLOT(slotResetBudget())); connect(m_hideUnusedButton, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(slotHideUnused(bool))); // setup initial state m_newButton->setEnabled(kmymoney2->action("budget_new")->isEnabled()); m_renameButton->setEnabled(kmymoney2->action("budget_rename")->isEnabled()); m_deleteButton->setEnabled(kmymoney2->action("budget_delete")->isEnabled()); connect(MyMoneyFile::instance(), TQT_SIGNAL(dataChanged()), this, TQT_SLOT(slotRefreshView())); } KBudgetView::~KBudgetView() { } void KBudgetView::show() { TQTimer::singleShot(50, this, TQT_SLOT(slotRearrange())); TQWidget::show(); if(m_needReload) { slotRefreshView(); } } void KBudgetView::polish() { KBudgetViewDecl::polish(); m_accountTree->restoreLayout("Budget Account View Settings"); } void KBudgetView::slotRearrange(void) { resizeEvent(0); } void KBudgetView::resizeEvent(TQResizeEvent* ev) { // resize the register KBudgetViewDecl::resizeEvent(ev); } void KBudgetView::slotReloadView(void) { ::timetrace("Start KBudgetView::slotReloadView"); slotRearrange(); ::timetrace("Done KBudgetView::slotReloadView"); } void KBudgetView::loadBudgets(void) { TQString id; ::timetrace("Start KBudgetView::loadBudgets"); // remember which item is currently selected id = m_budget.id(); // remember the upper left corner of the viewport TQPoint startPoint = m_budgetList->viewportToContents(TQPoint(0, 0)); // turn off updates to avoid flickering during reload m_budgetList->setUpdatesEnabled(false); // clear the budget list m_budgetList->clear(); m_budgetValue->clear(); // add the correct years to the drop down list TQDate date = TQDate::tqcurrentDate(Qt::LocalTime); int iStartYear = date.year() - m_iBudgetYearsBack; m_yearList.clear(); for (int i=0; i list = MyMoneyFile::instance()->budgetList(); TQValueList::ConstIterator it; for (it = list.begin(); it != list.end(); ++it) { KBudgetListItem* item = new KBudgetListItem(m_budgetList, *it); // create a list of unique years if (m_yearList.tqfindIndex(TQString::number((*it).budgetStart().year())) == -1) m_yearList += TQString::number((*it).budgetStart().year()); if(item->budget().id() == id) { m_budget = (*it); currentItem = item; item->setSelected(true); } } m_yearList.sort(); if (currentItem) { m_budgetList->setCurrentItem(currentItem); } // reposition viewport m_budgetList->setContentsPos(startPoint.x(), startPoint.y()); // turn updates back on m_budgetList->setUpdatesEnabled(true); m_budgetList->tqrepaintContents(); // reset the status of the buttons m_updateButton->setEnabled(false); m_resetButton->setEnabled(false); // make sure the world around us knows what we have selected slotSelectBudget(); ::timetrace("End KBudgetView::loadBudgets"); } void KBudgetView::ensureBudgetVisible(const TQString& id) { for (TQListViewItem * item = m_budgetList->firstChild(); item; item = item->itemBelow()) { KBudgetListItem* p = dynamic_cast(item); if(p && p->budget().id() == id) { if(p->itemAbove()) m_budgetList->ensureItemVisible(p->itemAbove()); if(p->itemBelow()) m_budgetList->ensureItemVisible(p->itemBelow()); m_budgetList->setCurrentItem(p); // active item and deselect all others m_budgetList->setSelected(p, true); // and select it m_budgetList->ensureItemVisible(p); break; } } } void KBudgetView::slotRefreshView(void) { if(isVisible()) { if(m_inSelection) TQTimer::singleShot(0, this, TQT_SLOT(slotRefreshView())); else { loadBudgets(); m_needReload = false; } } else { m_needReload = true; } } void KBudgetView::loadAccounts(void) { TQMap isOpen; ::timetrace("start load budget account view"); // if no budgets are selected, don't load the accounts // and clear out the previously shown list if (m_budget.id().isEmpty()) { m_accountTree->clear(); m_budgetValue->clear(); m_updateButton->setEnabled(false); m_resetButton->setEnabled(false); ::timetrace("done load budgets view"); return; } // 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_transactionCountMap.clear(); // make sure, the pointers are not pointing to some deleted object m_incomeItem = m_expenseItem = 0; MyMoneyFile* file = MyMoneyFile::instance(); m_transactionCountMap = file->transactionCountMap(); m_accountTree->setBaseCurrency(file->baseCurrency()); bool haveUnusedBudgets = false; // create the items try { const MyMoneySecurity& security = file->baseCurrency(); m_accountTree->setBaseCurrency(security); const MyMoneyAccount& income = file->income(); TQStringList incSubAcctList = income.accountList(); m_incomeItem = new KMyMoneyAccountTreeBudgetItem(m_accountTree, income, m_budget, security, i18n("Income")); haveUnusedBudgets |= loadSubAccounts(m_incomeItem, incSubAcctList, m_budget); m_incomeItem->setSelectable(false); const MyMoneyAccount& expense = file->expense(); TQStringList expSubAcctList = expense.accountList(); m_expenseItem = new KMyMoneyAccountTreeBudgetItem(m_accountTree, expense, m_budget, security, i18n("Expense")); haveUnusedBudgets |= loadSubAccounts(m_expenseItem, expSubAcctList, m_budget); m_expenseItem->setSelectable(false); } catch(MyMoneyException *e) { kdDebug(2) << "Problem in budgetview: " << 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.tqfind(item->id()) != isOpen.end()) item->setOpen(true); } ++it_lvi; } // reposition viewport m_accountTree->setContentsPos(startPoint.x(), startPoint.y()); // turn updates back on m_accountTree->setUpdatesEnabled(true); m_accountTree->tqrepaintContents(); m_updateButton->setEnabled(!(selectedBudget() == m_budget)); m_resetButton->setEnabled(!(selectedBudget() == m_budget)); ::timetrace("done load budgets view"); } bool KBudgetView::loadSubAccounts(KMyMoneyAccountTreeBudgetItem* tqparent, TQStringList& accountList, const MyMoneyBudget& budget) { MyMoneyFile* file = MyMoneyFile::instance(); bool unused = false; //sort the subaccount list //FIXME this is just a hack to order the accounts if ( !accountList.isEmpty() ) { TQMap accountMap; TQValueList alist; file->accountList ( alist, accountList ); accountList.clear(); TQValueList::const_iterator it_ac; for ( it_ac = alist.begin(); it_ac != alist.end(); ++it_ac ) { accountMap[(*it_ac).name()] = *it_ac; } TQMap::const_iterator it_am; for ( it_am = accountMap.begin(); it_am != accountMap.end(); ++it_am ) { accountList.prepend((*it_am).id()); //use prepend instead of append otherwise account show up in ascending order } } 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 = file->security(acc.currencyId()); prices += file->price(acc.currencyId(), security.tradingCurrency()); if(security.tradingCurrency() != file->baseCurrency().id()) { MyMoneySecurity sec = file->security(security.tradingCurrency()); prices += file->price(sec.id(), file->baseCurrency().id()); } } else if(acc.currencyId() != file->baseCurrency().id()) { if(acc.currencyId() != file->baseCurrency().id()) { security = file->security(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; } TQStringList subAcctList = acc.accountList(); KMyMoneyAccountTreeBudgetItem *item = new KMyMoneyAccountTreeBudgetItem(tqparent, acc, budget, prices, security); unused |= loadSubAccounts(item, subAcctList, budget); // no child accounts and no value assigned to this account bool thisUnused = (!item->firstChild()) && (!budget.tqcontains(acc.id())); // In case of a budget which is unused and we are requested to suppress // the display of those, if(acc.accountGroup() == MyMoneyAccount::Income || acc.accountGroup() == MyMoneyAccount::Expense) { if(m_hideUnusedButton->isEnabled() && m_hideUnusedButton->isChecked() && thisUnused) { unused = true; delete item; } } } return unused; } void KBudgetView::askSave(void) { // check if the content of a currently selected budget was modified // and ask to store the data if (m_updateButton->isEnabled()) { if (KMessageBox::questionYesNo(this, TQString("%1").tqarg( i18n("Do you want to save the changes for %1").tqarg(m_budget.name())), i18n("Save changes")) == KMessageBox::Yes) { m_inSelection = true; slotUpdateBudget(); m_inSelection = false; } } } void KBudgetView::slotRefreshHideUnusedButton(void) { m_hideUnusedButton->setDisabled(m_budget.getaccounts().isEmpty()); } void KBudgetView::slotSelectBudget(void) { askSave(); KBudgetListItem* item; if (m_budget.id().isEmpty()) { item = dynamic_cast(m_budgetList->firstChild()); if(item) { m_budgetList->blockSignals(true); m_budgetList->setSelected(item, true); // WRTODO das auch beim NewBudget machen m_budgetList->blockSignals(false); } } m_accountTree->setEnabled(false); m_assignmentBox->setEnabled(false); m_budget = MyMoneyBudget(); TQListViewItemIterator it_l(m_budgetList, TQListViewItemIterator::Selected); item = dynamic_cast(it_l.current()); if(item) { m_budget = item->budget(); m_accountTree->setEnabled(true); } slotRefreshHideUnusedButton(); loadAccounts(); TQValueList budgetList; if(!m_budget.id().isEmpty()) budgetList << m_budget; emit selectObjects(budgetList); } void KBudgetView::slotHideUnused(bool toggled) { // make sure we show all items for an empty budget bool prevState=!toggled; slotRefreshHideUnusedButton(); if (prevState!=m_hideUnusedButton->isChecked()) loadAccounts(); } const MyMoneyBudget& KBudgetView::selectedBudget(void) const { static MyMoneyBudget nullBudget; TQListViewItemIterator it_l(m_budgetList, TQListViewItemIterator::Selected); KBudgetListItem* item = dynamic_cast(it_l.current()); if(item) { return item->budget(); } return nullBudget; } KMyMoneyAccountTreeBudgetItem* KBudgetView::selectedAccount(void) const { TQListViewItemIterator it_l(m_accountTree, TQListViewItemIterator::Selected); KMyMoneyAccountTreeBudgetItem* item = dynamic_cast(it_l.current()); return item; } void KBudgetView::slotOpenContextMenu(KListView* lv, TQListViewItem* i, const TQPoint& p) { Q_UNUSED(lv); Q_UNUSED(p); m_accountTree->setUpdatesEnabled(false); KBudgetListItem* item = dynamic_cast(i); if (item) emit openContextMenu(item->budget()); else emit openContextMenu(MyMoneyBudget()); m_accountTree->setUpdatesEnabled(true); } void KBudgetView::slotStartRename(void) { TQListViewItemIterator it_l(m_budgetList, TQListViewItemIterator::Selected); TQListViewItem* it_v; if((it_v = it_l.current()) != 0) { it_v->startRename(0); } } // This variant is only called when a single budget is selected and renamed. void KBudgetView::slotRenameBudget(TQListViewItem* p , int /*col*/, const TQString& txt) { KBudgetListItem *pBudget = dynamic_cast (p); if (!pBudget) return; //kdDebug() << "[KPayeesView::slotRenamePayee]" << endl; // create a copy of the new name without appended whitespaces TQString new_name = txt.stripWhiteSpace(); if (pBudget->budget().name() != new_name) { MyMoneyFileTransaction ft; try { // check if we already have a budget with the new name try { // this function call will throw an exception, if the budget // hasn't been found. MyMoneyFile::instance()->budgetByName(new_name); // the name already exists, ask the user whether he's sure to keep the name if (KMessageBox::questionYesNo(this, i18n("A budget with the name '%1' already exists. It is not advisable to have " "multiple budgets with the same identification name. Are you sure you would like " "to rename the budget?").tqarg(new_name)) != KMessageBox::Yes) { p->setText(0,pBudget->budget().name()); return; } } catch(MyMoneyException *e) { // all ok, the name is unique delete e; } MyMoneyBudget b = pBudget->budget(); b.setName(new_name); // don't use pBudget beyond this point as it will change due to call to modifyBudget pBudget = 0; MyMoneyFile::instance()->modifyBudget(b); // the above call to modifyBudget will reload the view so // all references and pointers to the view have to be // re-established. You cannot use pBudget beyond this point!!! ft.commit(); } catch(MyMoneyException *e) { KMessageBox::detailedSorry(0, i18n("Unable to modify budget"), (e->what() + " " + i18n("thrown in") + " " + e->file()+ ":%1").tqarg(e->line())); delete e; } } else { pBudget->setText(0, new_name); } } void KBudgetView::slotSelectAccount(TQListViewItem* item) { if(item->listView() == m_accountTree) { m_assignmentBox->setEnabled(false); KMyMoneyAccountTreeBudgetItem *account = selectedAccount(); m_assignmentBox->setEnabled(account != 0); if(account) { if (m_budget.id().isEmpty() ) return; TQString id = account->id(); m_leAccounts->setText(MyMoneyFile::instance()->accountToCategory(id)); m_cbBudgetSubaccounts->setChecked(m_budget.account(id).budgetSubaccounts()); m_accountTotal->setValue(m_budget.account(id).totalBalance()); MyMoneyBudget::AccountGroup budgetAccount = m_budget.account( id ); if ( id != budgetAccount.id() ) { budgetAccount.setBudgetLevel(MyMoneyBudget::AccountGroup::eMonthly); } m_budgetValue->setBudgetValues(m_budget, budgetAccount); } } } void KBudgetView::slotBudgetedAmountChanged(void) { if (m_budget.id().isEmpty()) return; KMyMoneyAccountTreeBudgetItem *account; if ((account=selectedAccount()) == NULL) return; MyMoneyBudget::AccountGroup accountGroup = m_budget.account(account->id()); accountGroup.setId( account->id() ); m_budgetValue->budgetValues(m_budget, accountGroup); m_budget.setAccount(accountGroup, account->id()); account->setBudget(m_budget); m_accountTotal->setValue(accountGroup.totalBalance()); m_updateButton->setEnabled(!(selectedBudget() == m_budget)); m_resetButton->setEnabled(!(selectedBudget() == m_budget)); } void KBudgetView::AccountEnter() { if (m_budget.id().isEmpty()) return; //(ace) kCategoryWidget not currently defined KMyMoneyAccountTreeBudgetItem *item = NULL; //dynamic_cast (m_accountTree->findItem(m_leAccounts->selectedAccountId())); if (item) { m_accountTree->setCurrentItem(item); m_accountTree->setOpen(item, true); } } void KBudgetView::cb_includesSubaccounts_clicked() { if (m_budget.id().isEmpty()) return; if(selectedAccount() != 0) { TQString accountID = selectedAccount()->id(); // now, we get a reference to the accountgroup, to mofify its atribute, // and then put the resulting account group instead of the original MyMoneyBudget::AccountGroup auxAccount = m_budget.account(accountID); auxAccount.setBudgetSubaccounts( m_cbBudgetSubaccounts->isChecked()); m_budget.setAccount( auxAccount, accountID); loadAccounts(); } } void KBudgetView::slotNewBudget(void) { askSave(); kmymoney2->action("budget_new")->activate(); } void KBudgetView::slotResetBudget(void) { try { m_budget = MyMoneyFile::instance()->budget(m_budget.id()); loadAccounts(); } catch(MyMoneyException *e) { KMessageBox::detailedSorry(0, i18n("Unable to reset budget"), (e->what() + " " + i18n("thrown in") + " " + e->file()+ ":%1").tqarg(e->line())); delete e; } } void KBudgetView::slotUpdateBudget(void) { MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->modifyBudget(m_budget); ft.commit(); slotRefreshHideUnusedButton(); } catch(MyMoneyException *e) { KMessageBox::detailedSorry(0, i18n("Unable to modify budget"), (e->what() + " " + i18n("thrown in") + " " + e->file()+ ":%1").tqarg(e->line())); delete e; } } void KBudgetView::languageChange(void) { KBudgetViewDecl::languageChange(); m_newButton->setText(TQString()); m_renameButton->setText(TQString()); m_deleteButton->setText(TQString()); m_updateButton->setText(TQString()); m_resetButton->setText(TQString()); } #include "kbudgetview.moc"