File indexing completed on 2024-05-12 16:42:05
0001 /* 0002 SPDX-FileCopyrightText: 2007-2012 Thomas Baumgart <tbaumgart@kde.org> 0003 SPDX-FileCopyrightText: 2017-2018 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com> 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kenterscheduledlg.h" 0008 0009 // ---------------------------------------------------------------------------- 0010 // QT Includes 0011 0012 #include <QTimer> 0013 #include <QWidget> 0014 #include <QLabel> 0015 #include <QPushButton> 0016 #include <QIcon> 0017 #include <QWindow> 0018 0019 // ---------------------------------------------------------------------------- 0020 // KDE Includes 0021 0022 #include <KMessageBox> 0023 #include <KHelpClient> 0024 #include <KLocalizedString> 0025 #include <KGuiItem> 0026 #include <KStandardGuiItem> 0027 #include <KSharedConfig> 0028 #include <KWindowConfig> 0029 #include <KConfigGroup> 0030 0031 // ---------------------------------------------------------------------------- 0032 // Project Includes 0033 0034 #include "ui_kenterscheduledlg.h" 0035 0036 #include "tabbar.h" 0037 #include "mymoneysplit.h" 0038 #include "mymoneytransaction.h" 0039 #include "mymoneyfile.h" 0040 #include "mymoneyaccount.h" 0041 #include "mymoneymoney.h" 0042 #include "mymoneyschedule.h" 0043 #include "register.h" 0044 #include "transactionform.h" 0045 #include "transaction.h" 0046 #include "selectedtransactions.h" 0047 #include "transactioneditor.h" 0048 #include "kmymoneyutils.h" 0049 #include "kmymoneylineedit.h" 0050 #include "kmymoneydateinput.h" 0051 #include "knewaccountdlg.h" 0052 #include "knewinvestmentwizard.h" 0053 #include "mymoneyexception.h" 0054 #include "icons/icons.h" 0055 #include "mymoneyenums.h" 0056 #include "dialogenums.h" 0057 #include "widgetenums.h" 0058 0059 using namespace Icons; 0060 0061 class KEnterScheduleDlgPrivate 0062 { 0063 Q_DISABLE_COPY(KEnterScheduleDlgPrivate) 0064 0065 public: 0066 KEnterScheduleDlgPrivate() : 0067 ui(new Ui::KEnterScheduleDlg), 0068 m_item(nullptr), 0069 m_showWarningOnce(true), 0070 m_extendedReturnCode(eDialogs::ScheduleResultCode::Cancel) 0071 { 0072 } 0073 0074 ~KEnterScheduleDlgPrivate() 0075 { 0076 delete ui; 0077 } 0078 0079 Ui::KEnterScheduleDlg *ui; 0080 MyMoneySchedule m_schedule; 0081 KMyMoneyRegister::Transaction* m_item; 0082 QWidgetList m_tabOrderWidgets; 0083 bool m_showWarningOnce; 0084 eDialogs::ScheduleResultCode m_extendedReturnCode; 0085 }; 0086 0087 KEnterScheduleDlg::KEnterScheduleDlg(QWidget *parent, const MyMoneySchedule& schedule) : 0088 QDialog(parent), 0089 d_ptr(new KEnterScheduleDlgPrivate) 0090 { 0091 Q_D(KEnterScheduleDlg); 0092 0093 // restore the last used dialog size 0094 KConfigGroup grp = KSharedConfig::openConfig()->group("KEnterScheduleDlg"); 0095 if (grp.isValid()) { 0096 KWindowConfig::restoreWindowSize(windowHandle(), grp); 0097 } 0098 // let the minimum size be 780x410 0099 resize(QSize(780, 410).expandedTo(windowHandle() ? windowHandle()->size() : QSize())); 0100 0101 // position the dialog centered on the application (for some reason without 0102 // a call to winId() the dialog is positioned in the upper left corner of 0103 // the screen, but winId() crashes on MS-Windows ... 0104 if (parent) 0105 move(parent->pos() + QPoint(parent->width()/2, parent->height()/2) - QPoint(width()/2, height()/2)); 0106 0107 d->ui->setupUi(this); 0108 d->m_schedule = schedule; 0109 d->m_extendedReturnCode = eDialogs::ScheduleResultCode::Enter; 0110 d->ui->buttonOk->setIcon(Icons::get(Icon::KeyEnter)); 0111 d->ui->buttonSkip->setIcon(Icons::get(Icon::SeekForward)); 0112 KGuiItem::assign(d->ui->buttonCancel, KStandardGuiItem::cancel()); 0113 KGuiItem::assign(d->ui->buttonHelp, KStandardGuiItem::help()); 0114 d->ui->buttonIgnore->setHidden(true); 0115 d->ui->buttonSkip->setHidden(true); 0116 0117 // make sure, we have a tabbar with the form 0118 KMyMoneyTransactionForm::TabBar* tabbar = d->ui->m_form->getTabBar(d->ui->m_form->parentWidget()); 0119 0120 // we never need to see the register 0121 d->ui->m_register->hide(); 0122 0123 // ... setup the form ... 0124 d->ui->m_form->setupForm(d->m_schedule.account()); 0125 0126 // ... and the register ... 0127 d->ui->m_register->clear(); 0128 0129 // ... now add the transaction to register and form ... 0130 MyMoneyTransaction t = transaction(); 0131 d->m_item = KMyMoneyRegister::Register::transactionFactory(d->ui->m_register, t, 0132 d->m_schedule.transaction().splits().isEmpty() ? MyMoneySplit() : d->m_schedule.transaction().splits().front(), 0); 0133 d->ui->m_register->selectItem(d->m_item); 0134 // show the account row 0135 d->m_item->setShowRowInForm(0, true); 0136 0137 d->ui->m_form->slotSetTransaction(d->m_item); 0138 0139 // no need to see the tabbar 0140 tabbar->hide(); 0141 0142 // setup name and type 0143 d->ui->m_scheduleName->setText(d->m_schedule.name()); 0144 d->ui->m_type->setText(KMyMoneyUtils::scheduleTypeToString(d->m_schedule.type())); 0145 0146 connect(d->ui->buttonHelp, &QAbstractButton::clicked, this, &KEnterScheduleDlg::slotShowHelp); 0147 connect(d->ui->buttonIgnore, &QAbstractButton::clicked, this, &KEnterScheduleDlg::slotIgnore); 0148 connect(d->ui->buttonSkip, &QAbstractButton::clicked, this, &KEnterScheduleDlg::slotSkip); 0149 } 0150 0151 KEnterScheduleDlg::~KEnterScheduleDlg() 0152 { 0153 Q_D(KEnterScheduleDlg); 0154 0155 // store the last used dialog size 0156 KConfigGroup grp = KSharedConfig::openConfig()->group("KEnterScheduleDlg"); 0157 if (grp.isValid()) { 0158 KWindowConfig::saveWindowSize(windowHandle(), grp); 0159 } 0160 0161 delete d; 0162 } 0163 0164 eDialogs::ScheduleResultCode KEnterScheduleDlg::resultCode() const 0165 { 0166 Q_D(const KEnterScheduleDlg); 0167 if (result() == QDialog::Accepted) 0168 return d->m_extendedReturnCode; 0169 return eDialogs::ScheduleResultCode::Cancel; 0170 } 0171 0172 void KEnterScheduleDlg::showExtendedKeys(bool visible) 0173 { 0174 Q_D(KEnterScheduleDlg); 0175 d->ui->buttonIgnore->setVisible(visible); 0176 d->ui->buttonSkip->setVisible(visible); 0177 } 0178 0179 void KEnterScheduleDlg::slotIgnore() 0180 { 0181 Q_D(KEnterScheduleDlg); 0182 d->m_extendedReturnCode = eDialogs::ScheduleResultCode::Ignore; 0183 accept(); 0184 } 0185 0186 void KEnterScheduleDlg::slotSkip() 0187 { 0188 Q_D(KEnterScheduleDlg); 0189 d->m_extendedReturnCode = eDialogs::ScheduleResultCode::Skip; 0190 accept(); 0191 } 0192 0193 MyMoneyTransaction KEnterScheduleDlg::transaction() 0194 { 0195 Q_D(KEnterScheduleDlg); 0196 auto t = d->m_schedule.transaction(); 0197 0198 try { 0199 if (d->m_schedule.type() == eMyMoney::Schedule::Type::LoanPayment) { 0200 KMyMoneyUtils::calculateAutoLoan(d->m_schedule, t, QMap<QString, MyMoneyMoney>()); 0201 } 0202 } catch (const MyMoneyException &e) { 0203 KMessageBox::detailedError(this, i18n("Unable to load schedule details"), QString::fromLatin1(e.what())); 0204 } 0205 0206 t.clearId(); 0207 t.setEntryDate(QDate()); 0208 return t; 0209 } 0210 0211 QDate KEnterScheduleDlg::date(const QDate& _date) const 0212 { 0213 Q_D(const KEnterScheduleDlg); 0214 auto date(_date); 0215 return d->m_schedule.adjustedDate(date, d->m_schedule.weekendOption()); 0216 } 0217 0218 void KEnterScheduleDlg::resizeEvent(QResizeEvent* ev) 0219 { 0220 Q_UNUSED(ev) 0221 Q_D(KEnterScheduleDlg); 0222 d->ui->m_register->resize((int)eWidgets::eTransaction::Column::Detail); 0223 d->ui->m_form->resize((int)eWidgets::eTransactionForm::Column::Value1); 0224 QDialog::resizeEvent(ev); 0225 } 0226 0227 void KEnterScheduleDlg::slotSetupSize() 0228 { 0229 resize(width(), minimumSizeHint().height()); 0230 } 0231 0232 int KEnterScheduleDlg::exec() 0233 { 0234 Q_D(KEnterScheduleDlg); 0235 if (d->m_showWarningOnce) { 0236 d->m_showWarningOnce = false; 0237 KMessageBox::information(parentWidget(), QString("<qt>") + i18n("<p>Please check that all the details in the following dialog are correct and press OK.</p><p>Editable data can be changed and can either be applied to just this occurrence or for all subsequent occurrences for this schedule. (You will be asked what you intend after pressing OK in the following dialog)</p>") + QString("</qt>"), i18n("Enter scheduled transaction"), "EnterScheduleDlgInfo"); 0238 } 0239 0240 // force the initial height to be as small as possible 0241 QTimer::singleShot(0, this, SLOT(slotSetupSize())); 0242 return QDialog::exec(); 0243 } 0244 0245 TransactionEditor* KEnterScheduleDlg::startEdit() 0246 { 0247 Q_D(KEnterScheduleDlg); 0248 KMyMoneyRegister::SelectedTransactions list(d->ui->m_register); 0249 auto editor = d->m_item->createEditor(d->ui->m_form, list, QDate()); 0250 if (editor) { 0251 editor->setScheduleInfo(d->m_schedule.name()); 0252 editor->setPaymentMethod(d->m_schedule.paymentType()); 0253 } 0254 0255 // check that we use the same transaction commodity in all selected transactions 0256 // if not, we need to update this in the editor's list. The user can also bail out 0257 // of this operation which means that we have to stop editing here. 0258 if (editor) { 0259 if (!editor->fixTransactionCommodity(d->m_schedule.account())) { 0260 // if the user wants to quit, we need to destroy the editor 0261 // and bail out 0262 delete editor; 0263 editor = 0; 0264 } 0265 } 0266 0267 if (editor) { 0268 connect(editor, &TransactionEditor::transactionDataSufficient, d->ui->buttonOk, &QWidget::setEnabled); 0269 connect(editor, &TransactionEditor::escapePressed, d->ui->buttonCancel, &QAbstractButton::animateClick); 0270 connect(editor, &TransactionEditor::returnPressed, d->ui->buttonOk, &QAbstractButton::animateClick); 0271 0272 connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, editor, &TransactionEditor::slotReloadEditWidgets); 0273 // connect(editor, SIGNAL(finishEdit(KMyMoneyRegister::SelectedTransactions)), this, SLOT(slotLeaveEditMode(KMyMoneyRegister::SelectedTransactions))); 0274 0275 // create the widgets, place them in the parent and load them with data 0276 // setup tab order 0277 d->m_tabOrderWidgets.clear(); 0278 eWidgets::eRegister::Action action = eWidgets::eRegister::Action::Withdrawal; 0279 switch (d->m_schedule.type()) { 0280 case eMyMoney::Schedule::Type::Transfer: 0281 action = eWidgets::eRegister::Action::Transfer; 0282 break; 0283 case eMyMoney::Schedule::Type::Deposit: 0284 action = eWidgets::eRegister::Action::Deposit; 0285 break; 0286 case eMyMoney::Schedule::Type::LoanPayment: 0287 switch (d->m_schedule.paymentType()) { 0288 case eMyMoney::Schedule::PaymentType::DirectDeposit: 0289 case eMyMoney::Schedule::PaymentType::ManualDeposit: 0290 action = eWidgets::eRegister::Action::Deposit; 0291 break; 0292 default: 0293 break; 0294 } 0295 break; 0296 default: 0297 break; 0298 } 0299 editor->setup(d->m_tabOrderWidgets, d->m_schedule.account(), action); 0300 0301 MyMoneyTransaction t = d->m_schedule.transaction(); 0302 QString num = t.splits().first().number(); 0303 QWidget* w = editor->haveWidget("number"); 0304 if (d->m_schedule.paymentType() == eMyMoney::Schedule::PaymentType::WriteChecque) { 0305 num = KMyMoneyUtils::nextFreeCheckNumber(d->m_schedule.account()); 0306 d->m_schedule.account().setValue("lastNumberUsed", num); 0307 if (w) 0308 if (auto numberWidget = dynamic_cast<KMyMoneyLineEdit*>(w)) 0309 numberWidget->loadText(num); 0310 } 0311 0312 Q_ASSERT(!d->m_tabOrderWidgets.isEmpty()); 0313 0314 // editor->setup() leaves the tabbar as the last widget in the stack, but we 0315 // need it as first here. So we move it around. 0316 w = editor->haveWidget("tabbar"); 0317 if (w) { 0318 int idx = d->m_tabOrderWidgets.indexOf(w); 0319 if (idx != -1) { 0320 d->m_tabOrderWidgets.removeAt(idx); 0321 d->m_tabOrderWidgets.push_front(w); 0322 } 0323 } 0324 0325 // don't forget our three buttons 0326 d->m_tabOrderWidgets.append(d->ui->buttonOk); 0327 d->m_tabOrderWidgets.append(d->ui->buttonCancel); 0328 d->m_tabOrderWidgets.append(d->ui->buttonHelp); 0329 0330 for (auto i = 0; i < d->m_tabOrderWidgets.size(); ++i) { 0331 w = d->m_tabOrderWidgets.at(i); 0332 if (w) { 0333 w->installEventFilter(this); 0334 w->installEventFilter(editor); 0335 } 0336 } 0337 // Check if the editor has some preference on where to set the focus 0338 // If not, set the focus to the first widget in the tab order 0339 QWidget* focusWidget = editor->firstWidget(); 0340 if (!focusWidget) 0341 focusWidget = d->m_tabOrderWidgets.first(); 0342 focusWidget->setFocus(); 0343 0344 // Make sure, we use the adjusted date 0345 if (auto dateEdit = dynamic_cast<KMyMoneyDateInput*>(editor->haveWidget("postdate"))) 0346 dateEdit->setDate(d->m_schedule.adjustedNextDueDate()); 0347 } 0348 0349 return editor; 0350 } 0351 0352 bool KEnterScheduleDlg::focusNextPrevChild(bool next) 0353 { 0354 Q_D(KEnterScheduleDlg); 0355 auto rc = false; 0356 0357 auto w = qApp->focusWidget(); 0358 int currentWidgetIndex = d->m_tabOrderWidgets.indexOf(w); 0359 while (w && currentWidgetIndex == -1) { 0360 // qDebug("'%s' not in list, use parent", w->className()); 0361 w = w->parentWidget(); 0362 currentWidgetIndex = d->m_tabOrderWidgets.indexOf(w); 0363 } 0364 0365 if (currentWidgetIndex != -1) { 0366 // if(w) qDebug("tab order is at '%s'", w->className()); 0367 currentWidgetIndex += next ? 1 : -1; 0368 if (currentWidgetIndex < 0) 0369 currentWidgetIndex = d->m_tabOrderWidgets.size() - 1; 0370 else if (currentWidgetIndex >= d->m_tabOrderWidgets.size()) 0371 currentWidgetIndex = 0; 0372 0373 w = d->m_tabOrderWidgets[currentWidgetIndex]; 0374 // qDebug("currentWidgetIndex = %d, w = %p", currentWidgetIndex, w); 0375 0376 if (((w->focusPolicy() & Qt::TabFocus) == Qt::TabFocus) && w->isVisible() && w->isEnabled()) { 0377 // qDebug("Selecting '%s' as focus", w->className()); 0378 w->setFocus(next ? Qt::TabFocusReason: Qt::BacktabFocusReason); 0379 rc = true; 0380 } 0381 } 0382 return rc; 0383 } 0384 0385 void KEnterScheduleDlg::slotShowHelp() 0386 { 0387 KHelpClient::invokeHelp("details.schedules.entering"); 0388 }