/*************************************************************************** keditscheduledlg.cpp - description ------------------- begin : Mon Sep 3 2007 copyright : (C) 2007 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * 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 // ---------------------------------------------------------------------------- // TQt Includes #include #include #include #include #include #include // ---------------------------------------------------------------------------- // TDE Includes #include #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include #include #include #include #include #include #include #include #include #include "keditscheduledlg.h" #include "../kmymoney2.h" class KEditScheduleDlg::Private { public: MyMoneySchedule m_schedule; KMyMoneyRegister::Transaction* m_item; TQWidgetList m_tabOrderWidgets; TransactionEditor* m_editor; kMandatoryFieldGroup* m_requiredFields; }; KEditScheduleDlg::KEditScheduleDlg(const MyMoneySchedule& schedule, TQWidget *parent, const char *name) : KEditScheduleDlgDecl(parent, name, true), d(new Private) { d->m_schedule = schedule; d->m_editor = 0; buttonOk->setGuiItem(KStdGuiItem::ok()); buttonCancel->setGuiItem(KStdGuiItem::cancel()); buttonHelp->setGuiItem(KStdGuiItem::help()); d->m_requiredFields = new kMandatoryFieldGroup (this); d->m_requiredFields->setOkButton(buttonOk); // button to be enabled when all fields present // make sure, we have a tabbar with the form // insert it after the horizontal line m_paymentInformationLayout->insertWidget(2, m_form->tabBar(m_form->parentWidget())); // we never need to see the register m_register->hide(); // ... setup the form ... m_form->setupForm(d->m_schedule.account()); // ... and the register ... m_register->clear(); // ... now add the transaction to register and form ... MyMoneyTransaction t = transaction(); d->m_item = KMyMoneyRegister::Register::transactionFactory(m_register, t, d->m_schedule.transaction().splits()[0], 0); m_register->selectItem(d->m_item); // show the account row d->m_item->setShowRowInForm(0, true); m_form->slotSetTransaction(d->m_item); // setup widget contents m_nameEdit->setText(d->m_schedule.name()); m_frequencyEdit->setCurrentItem(d->m_schedule.occurencePeriod()); if(m_frequencyEdit->currentItem() == -1) m_frequencyEdit->setCurrentItem(MyMoneySchedule::OCCUR_MONTHLY); slotFrequencyChanged(m_frequencyEdit->currentItem()); m_frequencyNoEdit->setValue(d->m_schedule.occurenceMultiplier()); // load option widgets m_paymentMethodEdit->insertItem(i18n("Direct deposit"), MyMoneySchedule::STYPE_DIRECTDEPOSIT); m_paymentMethodEdit->insertItem(i18n("Manual deposit"), MyMoneySchedule::STYPE_MANUALDEPOSIT); m_paymentMethodEdit->insertItem(i18n("Direct debit"), MyMoneySchedule::STYPE_DIRECTDEBIT); m_paymentMethodEdit->insertItem(i18n("Standing order"), MyMoneySchedule::STYPE_STANDINGORDER); m_paymentMethodEdit->insertItem(i18n("Bank transfer"), MyMoneySchedule::STYPE_BANKTRANSFER); m_paymentMethodEdit->insertItem(i18n("Write check"), MyMoneySchedule::STYPE_WRITECHEQUE); m_paymentMethodEdit->insertItem(i18n("Other"), MyMoneySchedule::STYPE_OTHER); MyMoneySchedule::paymentTypeE method = d->m_schedule.paymentType(); if(method == MyMoneySchedule::STYPE_ANY) method = MyMoneySchedule::STYPE_OTHER; m_paymentMethodEdit->setCurrentItem(method); switch(d->m_schedule.weekendOption()) { case MyMoneySchedule::MoveNothing: m_weekendOptionEdit->setCurrentItem(0); break; case MyMoneySchedule::MoveFriday: m_weekendOptionEdit->setCurrentItem(1); break; case MyMoneySchedule::MoveMonday: m_weekendOptionEdit->setCurrentItem(2); break; } m_estimateEdit->setChecked(!d->m_schedule.isFixed()); m_autoEnterEdit->setChecked(d->m_schedule.autoEnter()); m_endSeriesEdit->setChecked(d->m_schedule.willEnd()); m_endOptionsFrame->setEnabled(d->m_schedule.willEnd()); if(d->m_schedule.willEnd()) { m_RemainingEdit->setValue(d->m_schedule.transactionsRemaining()); m_FinalPaymentEdit->setDate(d->m_schedule.endDate()); } connect(m_RemainingEdit, TQ_SIGNAL(valueChanged(int)), this, TQ_SLOT(slotRemainingChanged(int))); connect(m_FinalPaymentEdit, TQ_SIGNAL(dateChanged(const TQDate&)), this, TQ_SLOT(slotEndDateChanged(const TQDate&))); connect(m_frequencyEdit, TQ_SIGNAL(itemSelected(int)), this, TQ_SLOT(slotFrequencyChanged(int))); connect(m_frequencyNoEdit, TQ_SIGNAL(valueChanged(int)), this, TQ_SLOT(slotOccurenceMultiplierChanged(int))); connect(buttonHelp, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotShowHelp())); // force the initial height to be as small as possible TQTimer::singleShot(0, this, TQ_SLOT(slotSetupSize())); // we just hide the variation field for now and enable the logic // once we have a respective member in the MyMoneySchedule object m_variation->hide(); } KEditScheduleDlg::~KEditScheduleDlg() { delete d; } void KEditScheduleDlg::slotSetupSize(void) { resize(width(), minimumSizeHint().height()); } TransactionEditor* KEditScheduleDlg::startEdit(void) { KMyMoneyRegister::SelectedTransactions list(m_register); TransactionEditor* editor = d->m_item->createEditor(m_form, list, TQDate()); // check that we use the same transaction commodity in all selected transactions // if not, we need to update this in the editor's list. The user can also bail out // of this operation which means that we have to stop editing here. if(editor && !d->m_schedule.account().id().isEmpty()) { if(!editor->fixTransactionCommodity(d->m_schedule.account())) { // if the user wants to quit, we need to destroy the editor // and bail out delete editor; editor = 0; } } if(editor) { connect(editor, TQ_SIGNAL(transactionDataSufficient(bool)), buttonOk, TQ_SLOT(setEnabled(bool))); connect(editor, TQ_SIGNAL(escapePressed()), buttonCancel, TQ_SLOT(animateClick())); connect(editor, TQ_SIGNAL(returnPressed()), buttonOk, TQ_SLOT(animateClick())); connect(MyMoneyFile::instance(), TQ_SIGNAL(dataChanged()), editor, TQ_SLOT(slotReloadEditWidgets())); // connect(editor, TQ_SIGNAL(finishEdit(const KMyMoneyRegister::SelectedTransactions&)), this, TQ_SLOT(slotLeaveEditMode(const KMyMoneyRegister::SelectedTransactions&))); connect(editor, TQ_SIGNAL(createPayee(const TQString&, TQString&)), kmymoney2, TQ_SLOT(slotPayeeNew(const TQString&, TQString&))); connect(editor, TQ_SIGNAL(createCategory(MyMoneyAccount&, const MyMoneyAccount&)), kmymoney2, TQ_SLOT(slotCategoryNew(MyMoneyAccount&, const MyMoneyAccount&))); connect(editor, TQ_SIGNAL(createSecurity(MyMoneyAccount&, const MyMoneyAccount&)), kmymoney2, TQ_SLOT(slotInvestmentNew(MyMoneyAccount&, const MyMoneyAccount&))); connect(MyMoneyFile::instance(), TQ_SIGNAL(dataChanged()), editor, TQ_SLOT(slotReloadEditWidgets())); // create the widgets, place them in the parent and load them with data // setup tab order d->m_tabOrderWidgets.clear(); KMyMoneyRegister::Action action = KMyMoneyRegister::ActionWithdrawal; switch(d->m_schedule.type()) { case MyMoneySchedule::TYPE_DEPOSIT: action = KMyMoneyRegister::ActionDeposit; break; case MyMoneySchedule::TYPE_BILL: action = KMyMoneyRegister::ActionWithdrawal; break; case MyMoneySchedule::TYPE_TRANSFER: action = KMyMoneyRegister::ActionTransfer; break; default: // if we end up here, we don't have a known schedule type (yet). in this case, we just glimpse // into the transaction and determine the type. in case we don't have a transaction with splits // we stick with the default action already set up if(d->m_schedule.transaction().splits().count() > 0) { TQValueList::const_iterator it_s; bool isDeposit = false; bool isTransfer = false; for(it_s = d->m_schedule.transaction().splits().begin(); it_s != d->m_schedule.transaction().splits().end(); ++it_s) { if((*it_s).accountId() == d->m_schedule.account().id()) { isDeposit = !((*it_s).shares().isNegative()); } else { MyMoneyAccount acc = MyMoneyFile::instance()->account((*it_s).accountId()); if(acc.isAssetLiability() && d->m_schedule.transaction().splits().count() == 2) { isTransfer = true; } } } if(isTransfer) action = KMyMoneyRegister::ActionTransfer; else if(isDeposit) action = KMyMoneyRegister::ActionDeposit; } break; } editor->setup(d->m_tabOrderWidgets, d->m_schedule.account(), action); // if it's not a check, then we need to clear // a possibly assigned check number if(d->m_schedule.paymentType() != MyMoneySchedule::STYPE_WRITECHEQUE) { TQWidget* w = editor->haveWidget("number"); if(w) dynamic_cast(w)->loadText(TQString()); } Q_ASSERT(!d->m_tabOrderWidgets.isEmpty()); // don't forget our three buttons and additional widgets d->m_tabOrderWidgets.append(m_weekendOptionEdit); d->m_tabOrderWidgets.append(m_estimateEdit); d->m_tabOrderWidgets.append(m_variation); d->m_tabOrderWidgets.append(m_autoEnterEdit); d->m_tabOrderWidgets.append(m_endSeriesEdit); d->m_tabOrderWidgets.append(m_RemainingEdit); d->m_tabOrderWidgets.append(m_FinalPaymentEdit); d->m_tabOrderWidgets.append(buttonOk); d->m_tabOrderWidgets.append(buttonCancel); d->m_tabOrderWidgets.append(buttonHelp); d->m_tabOrderWidgets.append(m_nameEdit); d->m_tabOrderWidgets.append(m_frequencyNoEdit); d->m_tabOrderWidgets.append(m_frequencyEdit); d->m_tabOrderWidgets.append(m_paymentMethodEdit); d->m_tabOrderWidgets.append(m_form); // install event filter in all taborder widgets TQWidget* w; for(w = d->m_tabOrderWidgets.first(); w; w = d->m_tabOrderWidgets.next()) { w->installEventFilter(this); w->installEventFilter(editor); } // connect the postdate modification signal to our update routine kMyMoneyDateInput* dateEdit = dynamic_cast(editor->haveWidget("postdate")); if(dateEdit) connect(dateEdit, TQ_SIGNAL(dateChanged(const TQDate&)), this, TQ_SLOT(slotPostDateChanged(const TQDate&))); m_nameEdit->setFocus(); // add the required fields to the mandatory group d->m_requiredFields->add(m_nameEdit); d->m_requiredFields->add(editor->haveWidget("account")); d->m_requiredFields->add(editor->haveWidget("category")); // fix labels TQLabel* label = dynamic_cast(editor->haveWidget("date-label")); if(label) { label->setText(i18n("Next due date")); } d->m_editor = editor; slotSetPaymentMethod(d->m_schedule.paymentType()); connect(m_paymentMethodEdit, TQ_SIGNAL(itemSelected(int)), this, TQ_SLOT(slotSetPaymentMethod(int))); } return editor; } void KEditScheduleDlg::accept(void) { // Force the focus to be on the OK button. This will trigger creation // of any unknown objects (payees, categories etc.) buttonOk->setFocus(); // only accept if the button is really still enabled. We could end // up here, if the user filled all fields, the focus is on the category // field, but the category is not yet existant. When the user presses the // OK button in this context, he will be asked if he wants to create // the category or not. In case he decides no, we end up here with no // category filled in, so we don't run through the final acceptance. if(buttonOk->isEnabled()) KEditScheduleDlgDecl::accept(); } const MyMoneySchedule& KEditScheduleDlg::schedule(void) const { if(d->m_editor) { MyMoneyTransaction t = transaction(); if(d->m_schedule.nextDueDate() != t.postDate()) d->m_schedule.setNextDueDate(t.postDate()); d->m_schedule.setTransaction(t); d->m_schedule.setName(m_nameEdit->text()); d->m_schedule.setFixed(!m_estimateEdit->isChecked()); d->m_schedule.setOccurencePeriod(static_cast(m_frequencyEdit->currentItem())); d->m_schedule.setOccurenceMultiplier( m_frequencyNoEdit->value() ); switch(m_weekendOptionEdit->currentItem()) { case 0: d->m_schedule.setWeekendOption(MyMoneySchedule::MoveNothing); break; case 1: d->m_schedule.setWeekendOption(MyMoneySchedule::MoveFriday); break; case 2: d->m_schedule.setWeekendOption(MyMoneySchedule::MoveMonday); break; } d->m_schedule.setType(MyMoneySchedule::TYPE_BILL); KMyMoneyTransactionForm::TabBar* tabbar = dynamic_cast(d->m_editor->haveWidget("tabbar")); if(tabbar) { switch(static_cast(tabbar->currentTab())) { case KMyMoneyRegister::ActionDeposit: d->m_schedule.setType(MyMoneySchedule::TYPE_DEPOSIT); break; default: case KMyMoneyRegister::ActionWithdrawal: d->m_schedule.setType(MyMoneySchedule::TYPE_BILL); break; case KMyMoneyRegister::ActionTransfer: d->m_schedule.setType(MyMoneySchedule::TYPE_TRANSFER); break; } } else { tqDebug("No tabbar found in KEditScheduleDlg::schedule(). Defaulting type to BILL"); } d->m_schedule.setAutoEnter(m_autoEnterEdit->isChecked()); d->m_schedule.setPaymentType(static_cast(m_paymentMethodEdit->currentItem())); if(m_endSeriesEdit->isEnabled() && m_endSeriesEdit->isChecked()) { d->m_schedule.setEndDate(m_FinalPaymentEdit->date()); } else { d->m_schedule.setEndDate(TQDate()); } } return d->m_schedule; } MyMoneyTransaction KEditScheduleDlg::transaction(void) const { MyMoneyTransaction t = d->m_schedule.transaction(); if(d->m_editor) { d->m_editor->createTransaction(t, d->m_schedule.transaction(), d->m_schedule.transaction().splits()[0], false); } t.clearId(); t.setEntryDate(TQDate()); return t; } bool KEditScheduleDlg::focusNextPrevChild(bool next) { bool rc = false; // tqDebug("KEditScheduleDlg::focusNextPrevChild(editmode=%s)", m_inEditMode ? "true" : "false"); TQWidget *w = 0; TQWidget *currentWidget; w = tqApp->focusWidget(); while(w && d->m_tabOrderWidgets.find(w) == -1) { // tqDebug("'%s' not in list, use parent", w->className()); w = w->parentWidget(); } // if(w) tqDebug("tab order is at '%s'", w->className()); currentWidget = d->m_tabOrderWidgets.current(); w = next ? d->m_tabOrderWidgets.next() : d->m_tabOrderWidgets.prev(); do { if(!w) { w = next ? d->m_tabOrderWidgets.first() : d->m_tabOrderWidgets.last(); } if(w != currentWidget && ((w->focusPolicy() & TQWidget::TabFocus) == TQWidget::TabFocus) && w->isVisible() && w->isEnabled()) { // tqDebug("Selecting '%s' as focus", w->className()); w->setFocus(); rc = true; break; } w = next ? d->m_tabOrderWidgets.next() : d->m_tabOrderWidgets.prev(); } while(w != currentWidget); return rc; } void KEditScheduleDlg::resizeEvent(TQResizeEvent* ev) { m_register->resize(KMyMoneyRegister::DetailColumn); m_form->resize(KMyMoneyTransactionForm::ValueColumn1); KEditScheduleDlgDecl::resizeEvent(ev); } void KEditScheduleDlg::slotRemainingChanged(int value) { // Make sure the required fields are set kMyMoneyDateInput* dateEdit = dynamic_cast(d->m_editor->haveWidget("postdate")); d->m_schedule.setNextDueDate(dateEdit->date()); d->m_schedule.setOccurencePeriod(static_cast(m_frequencyEdit->currentItem())); d->m_schedule.setOccurenceMultiplier(m_frequencyNoEdit->value()); if(d->m_schedule.transactionsRemaining() != value) { m_FinalPaymentEdit->blockSignals(true); m_FinalPaymentEdit->setDate(d->m_schedule.dateAfter(value)); m_FinalPaymentEdit->blockSignals(false); } } void KEditScheduleDlg::slotEndDateChanged(const TQDate& date) { // Make sure the required fields are set kMyMoneyDateInput* dateEdit = dynamic_cast(d->m_editor->haveWidget("postdate")); d->m_schedule.setNextDueDate(dateEdit->date()); d->m_schedule.setOccurencePeriod(static_cast(m_frequencyEdit->currentItem())); d->m_schedule.setOccurenceMultiplier(m_frequencyNoEdit->value()); if(d->m_schedule.endDate() != date) { d->m_schedule.setEndDate(date); updateTransactionsRemaining(); } } void KEditScheduleDlg::slotPostDateChanged(const TQDate& date) { if(d->m_schedule.nextDueDate() != date) { if (m_endOptionsFrame->isEnabled()) { d->m_schedule.setNextDueDate(date); d->m_schedule.setOccurenceMultiplier(m_frequencyNoEdit->value()); d->m_schedule.setOccurencePeriod(static_cast(m_frequencyEdit->currentItem())); d->m_schedule.setEndDate(m_FinalPaymentEdit->date()); updateTransactionsRemaining(); } } } void KEditScheduleDlg::slotSetPaymentMethod(int item) { kMyMoneyLineEdit* dateEdit = dynamic_cast(d->m_editor->haveWidget("number")); if(dateEdit) { dateEdit->setShown(item == MyMoneySchedule::STYPE_WRITECHEQUE); // hiding the label does not work, because the label underneath will shine // through. So we either write the label or a blank TQLabel* label = dynamic_cast(d->m_editor->haveWidget("number-label")); if(label) { label->setText((item == MyMoneySchedule::STYPE_WRITECHEQUE) ? i18n("Number") : " "); } } } void KEditScheduleDlg::slotFrequencyChanged(int item) { m_endSeriesEdit->setEnabled(item != MyMoneySchedule::OCCUR_ONCE); bool isEndSeries = m_endSeriesEdit->isChecked(); if(isEndSeries ) m_endOptionsFrame->setEnabled(item != MyMoneySchedule::OCCUR_ONCE); switch( item ) { case MyMoneySchedule::OCCUR_DAILY: case MyMoneySchedule::OCCUR_WEEKLY: case MyMoneySchedule::OCCUR_EVERYHALFMONTH: case MyMoneySchedule::OCCUR_MONTHLY: case MyMoneySchedule::OCCUR_YEARLY: // Supports Frequency Number m_frequencyNoEdit->setEnabled(true); break; default: // Multiplier is always 1 m_frequencyNoEdit->setEnabled(false); m_frequencyNoEdit->setValue(1); break; } if ( isEndSeries && ( item != MyMoneySchedule::OCCUR_ONCE ) ) { // Changing the frequency changes the number // of remaining transactions kMyMoneyDateInput* dateEdit = dynamic_cast(d->m_editor->haveWidget("postdate")); d->m_schedule.setNextDueDate(dateEdit->date()); d->m_schedule.setOccurenceMultiplier(m_frequencyNoEdit->value()); d->m_schedule.setOccurencePeriod(static_cast(item)); d->m_schedule.setEndDate(m_FinalPaymentEdit->date()); updateTransactionsRemaining(); } } void KEditScheduleDlg::slotOccurenceMultiplierChanged(int multiplier) { // Make sure the required fields are set int oldOccurenceMultiplier = d->m_schedule.occurenceMultiplier(); if ( multiplier != oldOccurenceMultiplier ) { if (m_endOptionsFrame->isEnabled()) { kMyMoneyDateInput* dateEdit = dynamic_cast(d->m_editor->haveWidget("postdate")); d->m_schedule.setNextDueDate(dateEdit->date()); d->m_schedule.setOccurenceMultiplier(multiplier); d->m_schedule.setOccurencePeriod(static_cast(m_frequencyEdit->currentItem())); d->m_schedule.setEndDate(m_FinalPaymentEdit->date()); updateTransactionsRemaining(); } } } void KEditScheduleDlg::updateTransactionsRemaining(void) { int remain = d->m_schedule.transactionsRemaining(); if ( remain != m_RemainingEdit->value() ) { m_RemainingEdit->blockSignals(true); m_RemainingEdit->setValue(remain); m_RemainingEdit->blockSignals(false); } } void KEditScheduleDlg::slotShowHelp(void) { kapp->invokeHelp("details.schedules.intro"); } #include