File indexing completed on 2024-05-12 16:43:41
0001 /* 0002 SPDX-FileCopyrightText: 2019-2020 Thomas Baumgart <tbaumgart@kde.org> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 0007 #include "investtransactioneditor.h" 0008 0009 // ---------------------------------------------------------------------------- 0010 // QT Includes 0011 0012 #include <QCompleter> 0013 #include <QStringList> 0014 #include <QDebug> 0015 #include <QGlobalStatic> 0016 #include <QAbstractItemView> 0017 #include <QStringListModel> 0018 #include <QSortFilterProxyModel> 0019 0020 0021 // ---------------------------------------------------------------------------- 0022 // KDE Includes 0023 0024 #include <KLocalizedString> 0025 #include <KConcatenateRowsProxyModel> 0026 #include <KDescendantsProxyModel> 0027 0028 // ---------------------------------------------------------------------------- 0029 // Project Includes 0030 0031 #include "ui_investtransactioneditor.h" 0032 #include "creditdebithelper.h" 0033 #include "mymoneyfile.h" 0034 #include "mymoneyaccount.h" 0035 #include "mymoneyexception.h" 0036 #include "kmymoneyutils.h" 0037 #include "kmymoneyaccountcombo.h" 0038 #include "accountsmodel.h" 0039 #include "journalmodel.h" 0040 #include "statusmodel.h" 0041 #include "splitmodel.h" 0042 #include "mymoneysplit.h" 0043 #include "mymoneytransaction.h" 0044 #include "splitdialog.h" 0045 #include "widgethintframe.h" 0046 #include "icons/icons.h" 0047 #include "modelenums.h" 0048 #include "mymoneyenums.h" 0049 #include "mymoneysecurity.h" 0050 #include "kcurrencycalculator.h" 0051 #include "investactivities.h" 0052 #include "kmymoneysettings.h" 0053 #include "mymoneyprice.h" 0054 #include "amounteditcurrencyhelper.h" 0055 0056 using namespace Icons; 0057 0058 class InvestTransactionEditor::Private 0059 { 0060 public: 0061 Private(InvestTransactionEditor* parent) 0062 : q(parent) 0063 , ui(new Ui_InvestTransactionEditor) 0064 , accountsModel(new AccountNamesFilterProxyModel(parent)) 0065 , feesModel(new AccountNamesFilterProxyModel(parent)) 0066 , interestModel(new AccountNamesFilterProxyModel(parent)) 0067 , activitiesModel(new QStringListModel(parent)) 0068 , securitiesModel(new QSortFilterProxyModel(parent)) 0069 , accountsListModel(new KDescendantsProxyModel(parent)) 0070 , currentActivity(nullptr) 0071 , feeSplitModel(new SplitModel(parent, &undoStack)) 0072 , interestSplitModel(new SplitModel(parent, &undoStack)) 0073 , accepted(false) 0074 , bypassPriceEditor(false) 0075 { 0076 accountsModel->setObjectName("InvestTransactionEditor::accountsModel"); 0077 feeSplitModel->setObjectName("FeesSplitModel"); 0078 interestSplitModel->setObjectName("InterestSplitModel"); 0079 0080 // keep in sync with eMyMoney::Split::InvestmentTransactionType 0081 QStringList activityItems{ 0082 i18nc("@item:inlistbox transaction type", "Buy shares"), 0083 i18nc("@item:inlistbox transaction type", "Sell shares"), 0084 i18nc("@item:inlistbox transaction type", "Dividend"), 0085 i18nc("@item:inlistbox transaction type", "Reinvest dividend"), 0086 i18nc("@item:inlistbox transaction type", "Yield"), 0087 i18nc("@item:inlistbox transaction type", "Add shares"), 0088 i18nc("@item:inlistbox transaction type", "Remove shares"), 0089 i18nc("@item:inlistbox transaction type", "Split shares"), 0090 i18nc("@item:inlistbox transaction type", "Interest Income"), 0091 }; 0092 0093 activitiesModel->setStringList(activityItems); 0094 } 0095 0096 ~Private() 0097 { 0098 delete ui; 0099 } 0100 0101 void dumpSplitModel(const QString& header, const QAbstractItemModel* model) 0102 { 0103 const auto rows = model->rowCount(); 0104 qDebug() << header; 0105 for (int row = 0; row < rows; ++row) { 0106 const auto idx = model->index(row, 0); 0107 qDebug() << row << idx.data(eMyMoney::Model::IdRole).toString() << idx.data(eMyMoney::Model::SplitAccountIdRole).toString() 0108 << idx.data(eMyMoney::Model::SplitSharesRole).value<MyMoneyMoney>().formatMoney(100) << idx.data(eMyMoney::Model::SplitValueRole).value<MyMoneyMoney>().formatMoney(100); 0109 } 0110 } 0111 void createStatusEntry(eMyMoney::Split::State status); 0112 bool isDatePostOpeningDate(const QDate& date, const QString& accountId); 0113 0114 bool postdateChanged(const QDate& date); 0115 bool categoryChanged(SplitModel* model, const QString& accountId, AmountEdit* widget, const MyMoneyMoney& factor); 0116 0117 void setSecurity(const MyMoneySecurity& sec); 0118 0119 bool valueChanged(const SplitModel* model, AmountEdit* widget); 0120 0121 void updateWidgetState(); 0122 0123 MyMoneyMoney getPrice(const SplitModel* model, const AmountEdit* widget); 0124 0125 void editSplits(SplitModel* splitModel, AmountEdit* editWidget, const MyMoneyMoney& factor); 0126 void removeUnusedSplits(MyMoneyTransaction& t, SplitModel* splitModel); 0127 void addSplits(MyMoneyTransaction& t, SplitModel* splitModel); 0128 void setupParentInvestmentAccount(const QString& accountId); 0129 QModelIndex adjustToSecuritySplitIdx(const QModelIndex& idx); 0130 0131 InvestTransactionEditor* q; 0132 Ui_InvestTransactionEditor* ui; 0133 0134 // models for UI elements 0135 AccountNamesFilterProxyModel* accountsModel; 0136 AccountNamesFilterProxyModel* feesModel; 0137 AccountNamesFilterProxyModel* interestModel; 0138 QStringListModel* activitiesModel; 0139 QSortFilterProxyModel* securitiesModel; 0140 KDescendantsProxyModel* accountsListModel; 0141 0142 QUndoStack undoStack; 0143 Invest::Activity* currentActivity; 0144 0145 QSet<AmountEditCurrencyHelper*> amountEditCurrencyHelpers; 0146 0147 // the selected security and the account holding it 0148 MyMoneySecurity security; 0149 // and its trading currency 0150 MyMoneySecurity currency; 0151 // and account holding the security 0152 MyMoneyAccount stockAccount; 0153 0154 MyMoneyAccount assetAccount; 0155 0156 // the containing investment account (parent of stockAccount) 0157 MyMoneyAccount parentAccount; 0158 0159 // the transaction 0160 MyMoneyTransaction transaction; 0161 0162 // the various splits 0163 MyMoneySplit stockSplit; 0164 MyMoneySplit assetSplit; 0165 SplitModel* feeSplitModel; 0166 SplitModel* interestSplitModel; 0167 0168 // exchange rate information for assetSplit 0169 MyMoneyPrice assetPrice; 0170 0171 bool accepted; 0172 bool bypassPriceEditor; 0173 }; 0174 0175 void InvestTransactionEditor::Private::removeUnusedSplits(MyMoneyTransaction& t, SplitModel* splitModel) 0176 { 0177 for (const auto& sp : qAsConst(t.splits())) { 0178 if (sp.id() == stockSplit.id()) { 0179 continue; 0180 } 0181 const auto rows = splitModel->rowCount(); 0182 int row; 0183 for (row = 0; row < rows; ++row) { 0184 const QModelIndex index = splitModel->index(row, 0); 0185 if (index.data(eMyMoney::Model::IdRole).toString() == sp.id()) { 0186 break; 0187 } 0188 } 0189 0190 // if the split is not in the model, we get rid of it 0191 if (splitModel->rowCount() == row) { 0192 t.removeSplit(sp); 0193 } 0194 } 0195 } 0196 0197 void InvestTransactionEditor::Private::addSplits(MyMoneyTransaction& t, SplitModel* splitModel) 0198 { 0199 for (int row = 0; row < splitModel->rowCount(); ++row) { 0200 const auto idx = splitModel->index(row, 0); 0201 MyMoneySplit s; 0202 const auto splitId = idx.data(eMyMoney::Model::IdRole).toString(); 0203 // Extract the split from the transaction if 0204 // it already exists. Otherwise it remains 0205 // an empty split and will be added later. 0206 try { 0207 s = t.splitById(splitId); 0208 } catch(const MyMoneyException&) { 0209 } 0210 s.setMemo(idx.data(eMyMoney::Model::SplitMemoRole).toString()); 0211 s.setAccountId(idx.data(eMyMoney::Model::SplitAccountIdRole).toString()); 0212 s.setShares(idx.data(eMyMoney::Model::SplitSharesRole).value<MyMoneyMoney>()); 0213 s.setValue(idx.data(eMyMoney::Model::SplitValueRole).value<MyMoneyMoney>()); 0214 0215 if (s.id().isEmpty() || splitModel->isNewSplitId(s.id())) { 0216 s.clearId(); 0217 t.addSplit(s); 0218 } else { 0219 t.modifySplit(s); 0220 } 0221 } 0222 } 0223 0224 bool InvestTransactionEditor::Private::isDatePostOpeningDate(const QDate& date, const QString& accountId) 0225 { 0226 bool rc = true; 0227 0228 try { 0229 MyMoneyAccount account = MyMoneyFile::instance()->account(accountId); 0230 0231 // we don't check for categories 0232 if (!account.isIncomeExpense()) { 0233 if (date < account.openingDate()) 0234 rc = false; 0235 } 0236 } catch (MyMoneyException& e) { 0237 qDebug() << "Ooops: invalid account id" << accountId << "in" << Q_FUNC_INFO; 0238 } 0239 return rc; 0240 } 0241 0242 bool InvestTransactionEditor::Private::postdateChanged(const QDate& date) 0243 { 0244 bool rc = true; 0245 WidgetHintFrame::hide(ui->dateEdit, i18n("The posting date of the transaction.")); 0246 0247 QStringList accountIds; 0248 0249 auto collectAccounts = [&](const SplitModel* model) { 0250 const auto rows = model->rowCount(); 0251 for (int row = 0; row < rows; ++row) { 0252 const auto index = model->index(row, 0); 0253 accountIds << index.data(eMyMoney::Model::SplitAccountIdRole).toString(); 0254 } 0255 }; 0256 0257 // collect all account ids 0258 accountIds << parentAccount.id(); 0259 if (currentActivity->feesRequired() != Invest::Activity::Unused) { 0260 collectAccounts(feeSplitModel); 0261 } 0262 if (currentActivity->interestRequired() != Invest::Activity::Unused) { 0263 collectAccounts(interestSplitModel); 0264 } 0265 0266 for (const auto& accountId : qAsConst(accountIds)) { 0267 if (!isDatePostOpeningDate(date, accountId)) { 0268 const auto account = MyMoneyFile::instance()->account(accountId); 0269 WidgetHintFrame::show(ui->dateEdit, i18n("The posting date is prior to the opening date of account <b>%1</b>.", account.name())); 0270 rc = false; 0271 break; 0272 } 0273 } 0274 return rc; 0275 } 0276 0277 bool InvestTransactionEditor::Private::categoryChanged(SplitModel* model, const QString& accountId, AmountEdit* widget, const MyMoneyMoney& factor) 0278 { 0279 bool rc = true; 0280 if (!accountId.isEmpty() && model->rowCount() <= 1) { 0281 try { 0282 MyMoneyAccount category = MyMoneyFile::instance()->account(accountId); 0283 0284 bool needValueSet = false; 0285 // make sure we have a split in the model 0286 if (model->rowCount() == 0) { 0287 // add an empty split 0288 MyMoneySplit s; 0289 model->addItem(s); 0290 needValueSet = true; 0291 } 0292 0293 const QModelIndex index = model->index(0, 0); 0294 if (!needValueSet) { 0295 // update the values only if the category changes. This prevents 0296 // the call of the currency calculator if not needed. 0297 needValueSet = (index.data(eMyMoney::Model::SplitAccountIdRole).toString().compare(accountId) != 0); 0298 } 0299 model->setData(index, accountId, eMyMoney::Model::SplitAccountIdRole); 0300 0301 if (!widget->value().isZero() && needValueSet) { 0302 model->setData(index, QVariant::fromValue<MyMoneyMoney>(factor * widget->value() * getPrice(model, widget)), eMyMoney::Model::SplitValueRole); 0303 model->setData(index, QVariant::fromValue<MyMoneyMoney>(factor * widget->value()), eMyMoney::Model::SplitSharesRole); 0304 } 0305 0306 } catch (MyMoneyException& e) { 0307 qDebug() << "Ooops: invalid account id" << accountId << "in" << Q_FUNC_INFO; 0308 } 0309 } 0310 return rc; 0311 } 0312 0313 void InvestTransactionEditor::Private::setSecurity(const MyMoneySecurity& sec) 0314 { 0315 if (sec.tradingCurrency() != security.tradingCurrency()) { 0316 for (const auto helper : qAsConst(amountEditCurrencyHelpers)) { 0317 helper->setCommodity(sec.tradingCurrency()); 0318 } 0319 transaction.setCommodity(sec.tradingCurrency()); 0320 currency = MyMoneyFile::instance()->currency(sec.tradingCurrency()); 0321 0322 auto haveValue = [&](const SplitModel* model) { 0323 const auto rows = model->rowCount(); 0324 for (int row = 0; row < rows; ++row) { 0325 const auto idx = model->index(row, 0); 0326 if (!idx.data(eMyMoney::Model::SplitValueRole).value<MyMoneyMoney>().isZero()) { 0327 return true; 0328 } 0329 } 0330 return false; 0331 }; 0332 0333 if (assetPrice.from() != currency.id()) { 0334 /// @todo collect exchange rate from user for asset account 0335 } 0336 0337 bool needWarning = !assetSplit.value().isZero(); 0338 if (currentActivity) { 0339 needWarning |= ((currentActivity->feesRequired() != Invest::Activity::Unused) && haveValue(feeSplitModel)); 0340 needWarning |= ((currentActivity->interestRequired() != Invest::Activity::Unused) && haveValue(interestSplitModel)); 0341 } 0342 0343 if (needWarning) { 0344 ui->infoMessage->setText(i18nc("@info:usagetip", "The transaction commodity has been changed which will possibly make all price information invalid. Please check them.")); 0345 if (!ui->infoMessage->isShowAnimationRunning()) { 0346 ui->infoMessage->animatedShow(); 0347 emit q->editorLayoutChanged(); 0348 } 0349 } 0350 } 0351 0352 security = sec; 0353 0354 // update the precision to that used by the new security 0355 ui->sharesAmountEdit->setPrecision(MyMoneyMoney::denomToPrec(security.smallestAccountFraction())); 0356 } 0357 0358 MyMoneyMoney InvestTransactionEditor::Private::getPrice(const SplitModel* model, const AmountEdit* widget) 0359 { 0360 auto result(MyMoneyMoney::ONE); 0361 const QModelIndex splitIdx = model->index(0, 0); 0362 if (splitIdx.isValid()) { 0363 const auto shares = splitIdx.data(eMyMoney::Model::SplitSharesRole).value<MyMoneyMoney>(); 0364 const auto value = splitIdx.data(eMyMoney::Model::SplitValueRole).value<MyMoneyMoney>(); 0365 if (!shares.isZero()) { 0366 result = value / shares; 0367 } 0368 } 0369 0370 if (!bypassPriceEditor && splitIdx.isValid()) { 0371 const auto categoryId = splitIdx.data(eMyMoney::Model::SplitAccountIdRole).toString(); 0372 const auto category = MyMoneyFile::instance()->accountsModel()->itemById(categoryId); 0373 if (!category.id().isEmpty()) { 0374 const auto sec = MyMoneyFile::instance()->security(category.currencyId()); 0375 /// @todo I think security in the following three occurrences needs to be replaced with currency 0376 if (sec.id() != security.id()) { 0377 if (result == MyMoneyMoney::ONE) { 0378 result = MyMoneyFile::instance()->price(sec.id(), security.id(), QDate()).rate(sec.id()); 0379 } 0380 0381 QPointer<KCurrencyCalculator> calc = 0382 new KCurrencyCalculator(sec, 0383 security, 0384 widget->value(), 0385 widget->value() / result, 0386 ui->dateEdit->date(), 0387 sec.smallestAccountFraction(), 0388 q); 0389 0390 if (calc->exec() == QDialog::Accepted && calc) { 0391 result = calc->price(); 0392 } 0393 delete calc; 0394 0395 } else { 0396 result = MyMoneyMoney::ONE; 0397 } 0398 } 0399 } 0400 return result; 0401 } 0402 0403 0404 bool InvestTransactionEditor::Private::valueChanged(const SplitModel* model, AmountEdit* widget) 0405 { 0406 bool rc = true; 0407 #if 0 0408 if (valueHelper->haveValue() && (feeSplitModel.rowCount() <= 1) && (amountHelper->value() != split.value())) { 0409 rc = false; 0410 try { 0411 MyMoneyMoney shares; 0412 if (feeSplitModel.rowCount() == 1) { 0413 const QModelIndex index = feeSplitModel.index(0, 0); 0414 feeSplitModel.setData(index, QVariant::fromValue<MyMoneyMoney>(-amountHelper->value()), eMyMoney::Model::SplitValueRole); 0415 feeSplitModel.setData(index, QVariant::fromValue<MyMoneyMoney>(-amountHelper->value() / getPrice(model)), eMyMoney::Model::SplitSharesRole); 0416 } 0417 rc = true; 0418 0419 } catch (MyMoneyException& e) { 0420 qDebug() << "Ooops: something went wrong in" << Q_FUNC_INFO; 0421 } 0422 } else { 0423 /// @todo ask what to do: if the rest of the splits is the same amount we could simply reverse the sign 0424 /// of all splits, otherwise we could ask if the user wants to start the split editor or anything else. 0425 } 0426 #endif 0427 return rc; 0428 } 0429 0430 void InvestTransactionEditor::Private::editSplits(SplitModel* sourceSplitModel, AmountEdit* editWidget, const MyMoneyMoney& transactionFactor) 0431 { 0432 SplitModel splitModel(q, nullptr, *sourceSplitModel); 0433 0434 // create an empty split at the end 0435 // used to create new splits 0436 splitModel.appendEmptySplit(); 0437 0438 QPointer<SplitDialog> splitDialog = new SplitDialog(parentAccount, security, MyMoneyMoney::autoCalc, transactionFactor, q); 0439 splitDialog->setModel(&splitModel); 0440 0441 int rc = splitDialog->exec(); 0442 0443 if (splitDialog && (rc == QDialog::Accepted)) { 0444 // remove that empty split again before we update the splits 0445 splitModel.removeEmptySplit(); 0446 0447 // copy the splits model contents 0448 *sourceSplitModel = splitModel; 0449 0450 // update the transaction amount 0451 editWidget->setValue(splitDialog->transactionAmount() * transactionFactor); 0452 0453 // the price might have been changed, so we have to update our copy 0454 // but only if there is one counter split 0455 if (sourceSplitModel->rowCount() == 1) { 0456 const auto splitIdx = sourceSplitModel->index(0, 0); 0457 const auto shares = splitIdx.data(eMyMoney::Model::SplitSharesRole).value<MyMoneyMoney>(); 0458 0459 // make sure to show the value in the widget 0460 // according to the currency presented 0461 editWidget->setValue(shares * transactionFactor); 0462 } 0463 0464 // bypass the currency calculator here, we have all info already 0465 bypassPriceEditor = true; 0466 updateWidgetState(); 0467 bypassPriceEditor = false; 0468 } 0469 0470 if (splitDialog) { 0471 splitDialog->deleteLater(); 0472 } 0473 } 0474 0475 void InvestTransactionEditor::Private::setupParentInvestmentAccount(const QString& accountId) 0476 { 0477 auto const file = MyMoneyFile::instance(); 0478 auto const model = file->accountsModel(); 0479 0480 // extract account information from model 0481 const auto index = model->indexById(accountId); 0482 parentAccount = model->itemByIndex(index); 0483 0484 // show child accounts in the combo box 0485 securitiesModel->setFilterFixedString(accountId); 0486 } 0487 0488 QModelIndex InvestTransactionEditor::Private::adjustToSecuritySplitIdx(const QModelIndex& index) 0489 { 0490 if (!index.isValid()) { 0491 return {}; 0492 } 0493 const auto first = MyMoneyFile::instance()->journalModel()->adjustToFirstSplitIdx(index); 0494 const auto id = first.data(eMyMoney::Model::IdRole).toString(); 0495 0496 const auto rows = first.data(eMyMoney::Model::TransactionSplitCountRole).toInt(); 0497 const auto endRow = first.row() + rows; 0498 for(int row = first.row(); row < endRow; ++row) { 0499 const auto idx = index.model()->index(row, 0); 0500 const auto accountId = idx.data(eMyMoney::Model::SplitAccountIdRole).toString(); 0501 const auto account = MyMoneyFile::instance()->accountsModel()->itemById(accountId); 0502 if (account.isInvest()) { 0503 return idx; 0504 } 0505 } 0506 return {}; 0507 } 0508 0509 void InvestTransactionEditor::Private::updateWidgetState() 0510 { 0511 WidgetHintFrame::hide(ui->feesCombo, i18nc("@info:tooltip", "Category for fees")); 0512 WidgetHintFrame::hide(ui->feesAmountEdit, i18nc("@info:tooltip", "Amount of fees")); 0513 WidgetHintFrame::hide(ui->interestCombo, i18nc("@info:tooltip", "Category for interest")); 0514 WidgetHintFrame::hide(ui->interestAmountEdit, i18nc("@info:tooltip", "Amount of interest")); 0515 WidgetHintFrame::hide(ui->assetAccountCombo, i18nc("@info:tooltip", "Asset or brokerage account")); 0516 WidgetHintFrame::hide(ui->priceAmountEdit, i18nc("@info:tooltip", "Price information for this transaction")); 0517 0518 // all the other logic needs a valid activity 0519 if (currentActivity == nullptr) { 0520 return; 0521 } 0522 0523 const auto widget = ui->sharesAmountEdit; 0524 switch(currentActivity->type()) { 0525 default: 0526 WidgetHintFrame::hide(widget, i18nc("@info:tooltip", "Number of shares")); 0527 if (widget->isVisible()) { 0528 if (widget->value().isZero()) { 0529 WidgetHintFrame::show(widget, i18nc("@info:tooltip", "Enter number of shares for this transaction")); 0530 } 0531 } 0532 break; 0533 case eMyMoney::Split::InvestmentTransactionType::SplitShares: 0534 WidgetHintFrame::hide(widget, i18nc("@info:tooltip", "Split ratio")); 0535 if (widget->isVisible()) { 0536 if (widget->value().isZero()) { 0537 WidgetHintFrame::show(widget, i18nc("@info:tooltip", "Enter the split ratio for this transaction")); 0538 } 0539 } 0540 break; 0541 } 0542 0543 switch(currentActivity->priceRequired()) { 0544 case Invest::Activity::Unused: 0545 break; 0546 case Invest::Activity::Optional: 0547 case Invest::Activity::Mandatory: 0548 if (ui->priceAmountEdit->value().isZero()) { 0549 WidgetHintFrame::show(ui->priceAmountEdit, i18nc("@info:tooltip", "Enter price information for this transaction")); 0550 } 0551 break; 0552 } 0553 0554 QString accountId; 0555 switch(currentActivity->assetAccountRequired()) { 0556 case Invest::Activity::Unused: 0557 break; 0558 case Invest::Activity::Optional: 0559 case Invest::Activity::Mandatory: 0560 accountId = ui->assetAccountCombo->getSelected(); 0561 if (MyMoneyFile::instance()->isStandardAccount(accountId)) { 0562 accountId.clear(); 0563 } 0564 if (accountId.isEmpty()) { 0565 WidgetHintFrame::show(ui->assetAccountCombo, i18nc("@info:tooltip", "Select account to balance the transaction")); 0566 } 0567 break; 0568 } 0569 0570 if (!currentActivity->haveFees(currentActivity->feesRequired())) { 0571 if (ui->feesCombo->currentText().isEmpty()) { 0572 WidgetHintFrame::show(ui->feesCombo, i18nc("@info:tooltip", "Enter category for fees")); 0573 } 0574 if (ui->feesAmountEdit->value().isZero()) { 0575 WidgetHintFrame::show(ui->feesAmountEdit, i18nc("@info:tooltip", "Enter amount of fees")); 0576 } 0577 } 0578 0579 if (!currentActivity->haveInterest(currentActivity->interestRequired())) { 0580 if (ui->interestCombo->currentText().isEmpty()) { 0581 WidgetHintFrame::show(ui->interestCombo, i18nc("@info:tooltip", "Enter category for interest")); 0582 } 0583 if (ui->interestAmountEdit->value().isZero()) { 0584 WidgetHintFrame::show(ui->interestAmountEdit, i18nc("@info:tooltip", "Enter amount of interest")); 0585 } 0586 } 0587 } 0588 0589 InvestTransactionEditor::InvestTransactionEditor(QWidget* parent, const QString& accountId) 0590 : TransactionEditorBase(parent, accountId) 0591 , d(new Private(this)) 0592 { 0593 d->ui->setupUi(this); 0594 0595 // initially, the info message is hidden 0596 d->ui->infoMessage->hide(); 0597 0598 d->ui->activityCombo->setModel(d->activitiesModel); 0599 0600 auto const model = MyMoneyFile::instance()->accountsModel(); 0601 d->accountsListModel->setSourceModel(model); 0602 d->securitiesModel->setSourceModel(d->accountsListModel); 0603 d->securitiesModel->setFilterRole(eMyMoney::Model::AccountParentIdRole); 0604 d->securitiesModel->setFilterKeyColumn(0); 0605 d->ui->securityAccountCombo->setModel(d->securitiesModel); 0606 d->ui->securityAccountCombo->lineEdit()->setReadOnly(true); 0607 0608 d->accountsModel->addAccountGroup(QVector<eMyMoney::Account::Type> { eMyMoney::Account::Type::Asset, eMyMoney::Account::Type::Liability } ); 0609 d->accountsModel->setHideEquityAccounts(false); 0610 d->accountsModel->setSourceModel(model); 0611 d->accountsModel->sort(AccountsModel::Column::AccountName); 0612 d->ui->assetAccountCombo->setModel(d->accountsModel); 0613 d->ui->assetAccountCombo->setSplitActionVisible(false); 0614 0615 d->feesModel->addAccountGroup(QVector<eMyMoney::Account::Type> { eMyMoney::Account::Type::Expense }); 0616 d->feesModel->setSourceModel(model); 0617 d->feesModel->sort(AccountsModel::Column::AccountName); 0618 d->ui->feesCombo->setModel(d->feesModel); 0619 auto helper = new KMyMoneyAccountComboSplitHelper(d->ui->feesCombo, d->feeSplitModel); 0620 connect(helper, &KMyMoneyAccountComboSplitHelper::accountComboDisabled, d->ui->feesAmountEdit, &AmountEdit::setReadOnly); 0621 0622 d->interestModel->addAccountGroup(QVector<eMyMoney::Account::Type> { eMyMoney::Account::Type::Income }); 0623 d->interestModel->setSourceModel(model); 0624 d->interestModel->sort(AccountsModel::Column::AccountName); 0625 d->ui->interestCombo->setModel(d->interestModel); 0626 helper = new KMyMoneyAccountComboSplitHelper(d->ui->interestCombo, d->interestSplitModel); 0627 connect(helper, &KMyMoneyAccountComboSplitHelper::accountComboDisabled, d->ui->interestAmountEdit, &AmountEdit::setReadOnly); 0628 0629 d->ui->enterButton->setIcon(Icons::get(Icon::DialogOK)); 0630 d->ui->cancelButton->setIcon(Icons::get(Icon::DialogCancel)); 0631 0632 d->ui->statusCombo->setModel(MyMoneyFile::instance()->statusModel()); 0633 0634 d->ui->dateEdit->setDisplayFormat(QLocale().dateFormat(QLocale::ShortFormat)); 0635 0636 d->ui->sharesAmountEdit->setAllowEmpty(true); 0637 d->ui->sharesAmountEdit->setCalculatorButtonVisible(true); 0638 connect(d->ui->sharesAmountEdit, &AmountEdit::textChanged, this, &InvestTransactionEditor::sharesChanged); 0639 0640 d->ui->priceAmountEdit->setAllowEmpty(true); 0641 d->ui->priceAmountEdit->setCalculatorButtonVisible(true); 0642 connect(d->ui->priceAmountEdit, &AmountEdit::textChanged, this, &InvestTransactionEditor::updateTotalAmount); 0643 0644 d->ui->feesAmountEdit->setAllowEmpty(true); 0645 d->ui->feesAmountEdit->setCalculatorButtonVisible(true); 0646 connect(d->ui->feesAmountEdit, &AmountEdit::textChanged, this, &InvestTransactionEditor::updateTotalAmount); 0647 0648 d->ui->interestAmountEdit->setAllowEmpty(true); 0649 d->ui->interestAmountEdit->setCalculatorButtonVisible(true); 0650 connect(d->ui->interestAmountEdit, &AmountEdit::textChanged, this, &InvestTransactionEditor::updateTotalAmount); 0651 0652 WidgetHintFrameCollection* frameCollection = new WidgetHintFrameCollection(this); 0653 frameCollection->addFrame(new WidgetHintFrame(d->ui->dateEdit)); 0654 frameCollection->addFrame(new WidgetHintFrame(d->ui->assetAccountCombo)); 0655 frameCollection->addFrame(new WidgetHintFrame(d->ui->sharesAmountEdit)); 0656 frameCollection->addFrame(new WidgetHintFrame(d->ui->priceAmountEdit)); 0657 frameCollection->addFrame(new WidgetHintFrame(d->ui->feesCombo)); 0658 frameCollection->addFrame(new WidgetHintFrame(d->ui->feesAmountEdit)); 0659 frameCollection->addFrame(new WidgetHintFrame(d->ui->interestCombo)); 0660 frameCollection->addFrame(new WidgetHintFrame(d->ui->interestAmountEdit)); 0661 frameCollection->addWidget(d->ui->enterButton); 0662 0663 0664 connect(d->ui->assetAccountCombo, &KMyMoneyAccountCombo::accountSelected, this, &InvestTransactionEditor::assetAccountChanged); 0665 connect(d->ui->dateEdit, &KMyMoneyDateEdit::dateChanged, this, &InvestTransactionEditor::postdateChanged); 0666 connect(d->ui->activityCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &InvestTransactionEditor::activityChanged); 0667 connect(d->ui->securityAccountCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &InvestTransactionEditor::securityAccountChanged); 0668 0669 connect(d->ui->feesCombo, &KMyMoneyAccountCombo::accountSelected, this, &InvestTransactionEditor::feeCategoryChanged); 0670 connect(d->ui->feesCombo, &KMyMoneyAccountCombo::splitDialogRequest, this, &InvestTransactionEditor::editFeeSplits, Qt::QueuedConnection); 0671 0672 connect(d->ui->interestCombo, &KMyMoneyAccountCombo::accountSelected, this, &InvestTransactionEditor::interestCategoryChanged); 0673 connect(d->ui->interestCombo, &KMyMoneyAccountCombo::splitDialogRequest, this, &InvestTransactionEditor::editInterestSplits, Qt::QueuedConnection); 0674 0675 /// @todo convert to new signal/slot syntax 0676 connect(d->ui->cancelButton, &QToolButton::clicked, this, [&]() { 0677 emit done(); 0678 } ); 0679 connect(d->ui->enterButton, &QToolButton::clicked, this, [&]() { 0680 d->accepted = true; 0681 emit done(); 0682 } ); 0683 0684 // handle some events in certain conditions different from default 0685 d->ui->activityCombo->installEventFilter(this); 0686 d->ui->statusCombo->installEventFilter(this); 0687 0688 d->ui->totalAmountEdit->setCalculatorButtonVisible(false); 0689 0690 d->setupParentInvestmentAccount(accountId); 0691 0692 d->amountEditCurrencyHelpers.insert(new AmountEditCurrencyHelper(d->ui->feesCombo, d->ui->feesAmountEdit, d->transaction.commodity())); 0693 d->amountEditCurrencyHelpers.insert(new AmountEditCurrencyHelper(d->ui->interestCombo, d->ui->interestAmountEdit, d->transaction.commodity())); 0694 0695 // setWindowFlags(Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint); 0696 } 0697 0698 InvestTransactionEditor::~InvestTransactionEditor() 0699 { 0700 } 0701 0702 bool InvestTransactionEditor::accepted() const 0703 { 0704 return d->accepted; 0705 } 0706 0707 void InvestTransactionEditor::updateTotalAmount() 0708 { 0709 d->updateWidgetState(); 0710 if (d->currentActivity) { 0711 const auto totalAmount = d->currentActivity->totalAmount(d->stockSplit, d->feeSplitModel, d->interestSplitModel); 0712 d->ui->totalAmountEdit->setValue(totalAmount.abs()); 0713 d->assetSplit.setValue(-totalAmount); 0714 d->assetSplit.setShares(d->assetSplit.value() / d->assetPrice.rate(d->assetAccount.currencyId())); 0715 } 0716 } 0717 0718 0719 void InvestTransactionEditor::loadTransaction(const QModelIndex& index) 0720 { 0721 d->ui->activityCombo->setCurrentIndex(-1); 0722 d->ui->securityAccountCombo->setCurrentIndex(-1); 0723 const auto file = MyMoneyFile::instance(); 0724 auto idx = d->adjustToSecuritySplitIdx(MyMoneyFile::baseModel()->mapToBaseSource(index)); 0725 if (!idx.isValid() || idx.data(eMyMoney::Model::IdRole).toString().isEmpty()) { 0726 d->transaction = MyMoneyTransaction(); 0727 d->transaction.setCommodity(d->parentAccount.currencyId()); 0728 d->currency = MyMoneyFile::instance()->baseCurrency(); 0729 d->security = MyMoneySecurity(); 0730 d->security.setTradingCurrency(d->currency.id()); 0731 d->stockSplit = MyMoneySplit(); 0732 d->assetSplit = MyMoneySplit(); 0733 d->assetAccount = MyMoneyAccount(); 0734 d->ui->activityCombo->setCurrentIndex(0); 0735 d->ui->securityAccountCombo->setCurrentIndex(0); 0736 const auto lastUsedPostDate = KMyMoneySettings::lastUsedPostDate(); 0737 if (lastUsedPostDate.isValid()) { 0738 d->ui->dateEdit->setDate(lastUsedPostDate.date()); 0739 } else { 0740 d->ui->dateEdit->setDate(QDate::currentDate()); 0741 } 0742 // select the associated brokerage account if it exists 0743 const auto brokerageAccount = file->accountsModel()->itemByName(d->parentAccount.brokerageName()); 0744 if (!brokerageAccount.id().isEmpty()) { 0745 d->ui->assetAccountCombo->setSelected(brokerageAccount.id()); 0746 } 0747 } else { 0748 // keep a copy of the transaction and split 0749 d->transaction = file->journalModel()->itemByIndex(idx).transaction(); 0750 d->stockSplit = file->journalModel()->itemByIndex(idx).split(); 0751 0752 QModelIndex assetAccountSplitIdx; 0753 eMyMoney::Split::InvestmentTransactionType transactionType; 0754 0755 KMyMoneyUtils::dissectInvestmentTransaction(idx, assetAccountSplitIdx, d->feeSplitModel, d->interestSplitModel, d->security, d->currency, transactionType); 0756 d->assetSplit = file->journalModel()->itemByIndex(assetAccountSplitIdx).split(); 0757 if (!d->assetSplit.id().isEmpty()) 0758 d->assetAccount = file->account(d->assetSplit.accountId()); 0759 0760 // extract conversion rate information for asset split before changing 0761 // the activity because that will need it (in updateTotalAmount() ) 0762 if (!(d->assetSplit.shares().isZero() || d->assetSplit.value().isZero())) { 0763 const auto rate = d->assetSplit.value() / d->assetSplit.shares(); 0764 d->assetPrice = MyMoneyPrice(d->currency.id(), d->assetAccount.currencyId(), d->transaction.postDate(), rate, QLatin1String("KMyMoney")); 0765 } 0766 0767 // load the widgets. setting activityCombo also initializes 0768 // d->currentActivity to have the right object 0769 d->ui->activityCombo->setCurrentIndex(static_cast<int>(transactionType)); 0770 d->ui->dateEdit->setDate(d->transaction.postDate()); 0771 0772 d->ui->memoEdit->setPlainText(d->stockSplit.memo()); 0773 0774 d->ui->assetAccountCombo->setSelected(d->assetSplit.accountId()); 0775 0776 d->ui->sharesAmountEdit->setPrecision(MyMoneyMoney::denomToPrec(d->security.smallestAccountFraction())); 0777 d->ui->sharesAmountEdit->setValue(d->stockSplit.shares() * d->currentActivity->sharesFactor()); 0778 0779 const auto indexes = d->securitiesModel->match(d->securitiesModel->index(0,0), eMyMoney::Model::IdRole, d->stockSplit.accountId(), 1, Qt::MatchFixedString); 0780 if (!indexes.isEmpty()) { 0781 d->ui->securityAccountCombo->setCurrentIndex(indexes.first().row()); 0782 d->stockAccount = file->account(d->stockSplit.accountId()); 0783 } 0784 0785 d->ui->feesAmountEdit->setValue(d->feeSplitModel->valueSum() * d->currentActivity->feesFactor()); 0786 d->ui->interestAmountEdit->setValue(d->interestSplitModel->valueSum() * d->currentActivity->interestFactor()); 0787 0788 d->currentActivity->loadPriceWidget(d->stockSplit); 0789 } 0790 0791 for (const auto helper : qAsConst(d->amountEditCurrencyHelpers)) { 0792 helper->setCommodity(d->transaction.commodity()); 0793 } 0794 0795 // delay update until next run of event loop so that all necessary widgets are visible 0796 QMetaObject::invokeMethod(this, "updateWidgets", Qt::QueuedConnection); 0797 0798 // set focus to date edit once we return to event loop 0799 QMetaObject::invokeMethod(d->ui->dateEdit, "setFocus", Qt::QueuedConnection); 0800 } 0801 0802 void InvestTransactionEditor::updateWidgets() 0803 { 0804 d->updateWidgetState(); 0805 } 0806 0807 void InvestTransactionEditor::securityAccountChanged(int index) 0808 { 0809 const auto idx = d->ui->securityAccountCombo->model()->index(index, 0); 0810 if (idx.isValid()) { 0811 const auto accountId = idx.data(eMyMoney::Model::IdRole).toString(); 0812 const auto securityId = idx.data(eMyMoney::Model::AccountCurrencyIdRole).toString(); 0813 try { 0814 const auto file = MyMoneyFile::instance(); 0815 const auto sec = file->security(securityId); 0816 0817 d->stockAccount = file->account(accountId); 0818 d->stockSplit.setAccountId(accountId); 0819 d->setSecurity(sec); 0820 0821 updateTotalAmount(); 0822 0823 } catch(MyMoneyException& e) { 0824 qDebug() << "Problem to find securityId" << accountId << "or" << securityId << "in InvestTransactionEditor::securityAccountChanged"; 0825 } 0826 } 0827 } 0828 0829 0830 void InvestTransactionEditor::activityChanged(int index) 0831 { 0832 const auto type = static_cast<eMyMoney::Split::InvestmentTransactionType>(index); 0833 if (!d->currentActivity || type != d->currentActivity->type()) { 0834 auto oldType = eMyMoney::Split::InvestmentTransactionType::UnknownTransactionType; 0835 if (d->currentActivity) { 0836 oldType = d->currentActivity->type(); 0837 } 0838 delete d->currentActivity; 0839 switch(type) { 0840 default: 0841 case eMyMoney::Split::InvestmentTransactionType::BuyShares: 0842 d->currentActivity = new Invest::Buy(this); 0843 break; 0844 case eMyMoney::Split::InvestmentTransactionType::SellShares: 0845 d->currentActivity = new Invest::Sell(this); 0846 break; 0847 case eMyMoney::Split::InvestmentTransactionType::Dividend: 0848 case eMyMoney::Split::InvestmentTransactionType::Yield: 0849 d->currentActivity = new Invest::Div(this); 0850 break; 0851 case eMyMoney::Split::InvestmentTransactionType::ReinvestDividend: 0852 d->currentActivity = new Invest::Reinvest(this); 0853 break; 0854 case eMyMoney::Split::InvestmentTransactionType::AddShares: 0855 d->currentActivity = new Invest::Add(this); 0856 break; 0857 case eMyMoney::Split::InvestmentTransactionType::RemoveShares: 0858 d->currentActivity = new Invest::Remove(this); 0859 break; 0860 case eMyMoney::Split::InvestmentTransactionType::SplitShares: 0861 d->currentActivity = new Invest::Split(this); 0862 break; 0863 case eMyMoney::Split::InvestmentTransactionType::InterestIncome: 0864 d->currentActivity = new Invest::IntInc(this); 0865 break; 0866 } 0867 d->currentActivity->showWidgets(); 0868 0869 if (type != eMyMoney::Split::InvestmentTransactionType::SplitShares && 0870 oldType == eMyMoney::Split::InvestmentTransactionType::SplitShares) { 0871 // switch to split 0872 d->stockSplit.setValue(MyMoneyMoney()); 0873 d->stockSplit.setPrice(MyMoneyMoney()); 0874 d->ui->sharesAmountEdit->setPrecision(-1); 0875 } else if (type == eMyMoney::Split::InvestmentTransactionType::SplitShares && 0876 oldType != eMyMoney::Split::InvestmentTransactionType::SplitShares) { 0877 // switch away from split 0878 d->stockSplit.setPrice(d->ui->priceAmountEdit->value()); 0879 d->stockSplit.setValue(d->stockSplit.shares() * d->stockSplit.price()); 0880 d->ui->sharesAmountEdit->setPrecision(MyMoneyMoney::denomToPrec(d->security.smallestAccountFraction())); 0881 } 0882 updateTotalAmount(); 0883 d->updateWidgetState(); 0884 emit editorLayoutChanged(); 0885 } 0886 } 0887 0888 void InvestTransactionEditor::sharesChanged() 0889 { 0890 if (d->currentActivity) { 0891 if (d->currentActivity->type() != eMyMoney::Split::InvestmentTransactionType::SplitShares) { 0892 updateTotalAmount(); 0893 } 0894 } 0895 } 0896 0897 void InvestTransactionEditor::assetAccountChanged(const QString& accountId) 0898 { 0899 const auto account = MyMoneyFile::instance()->account(accountId); 0900 if (account.currencyId() != d->assetAccount.currencyId()) { 0901 /// @todo update price rate information 0902 } 0903 d->postdateChanged(d->ui->dateEdit->date()); 0904 d->updateWidgetState(); 0905 } 0906 0907 void InvestTransactionEditor::feeCategoryChanged(const QString& accountId) 0908 { 0909 d->categoryChanged(d->feeSplitModel, accountId, d->ui->feesAmountEdit, MyMoneyMoney::ONE); 0910 d->updateWidgetState(); 0911 updateTotalAmount(); 0912 } 0913 0914 void InvestTransactionEditor::interestCategoryChanged(const QString& accountId) 0915 { 0916 d->categoryChanged(d->interestSplitModel, accountId, d->ui->interestAmountEdit, MyMoneyMoney::MINUS_ONE); 0917 d->updateWidgetState(); 0918 updateTotalAmount(); 0919 } 0920 0921 void InvestTransactionEditor::postdateChanged(const QDate& date) 0922 { 0923 d->postdateChanged(date); 0924 } 0925 0926 void InvestTransactionEditor::feesValueChanged() 0927 { 0928 d->valueChanged(d->feeSplitModel, d->ui->feesAmountEdit); 0929 d->updateWidgetState(); 0930 updateTotalAmount(); 0931 } 0932 0933 void InvestTransactionEditor::interestValueChanged() 0934 { 0935 d->valueChanged(d->interestSplitModel, d->ui->interestAmountEdit); 0936 d->updateWidgetState(); 0937 updateTotalAmount(); 0938 } 0939 0940 void InvestTransactionEditor::editFeeSplits() 0941 { 0942 d->editSplits(d->feeSplitModel, d->ui->feesAmountEdit, MyMoneyMoney::ONE); 0943 #if 0 0944 QWidget* next = d->ui->tagComboBox; 0945 next->setFocus(); 0946 #endif 0947 } 0948 0949 void InvestTransactionEditor::editInterestSplits() 0950 { 0951 d->editSplits(d->interestSplitModel, d->ui->interestAmountEdit, MyMoneyMoney::MINUS_ONE); 0952 0953 #if 0 0954 QWidget* next = d->ui->tagComboBox; 0955 next->setFocus(); 0956 #endif 0957 } 0958 0959 MyMoneyMoney InvestTransactionEditor::transactionAmount() const 0960 { 0961 #if 0 0962 return d->amountHelper->value(); 0963 #endif 0964 return {}; 0965 } 0966 0967 MyMoneyMoney InvestTransactionEditor::totalAmount() const 0968 { 0969 return d->assetSplit.value(); 0970 } 0971 0972 void InvestTransactionEditor::saveTransaction() 0973 { 0974 MyMoneyTransaction t; 0975 0976 if (!d->transaction.id().isEmpty()) { 0977 t = d->transaction; 0978 } else { 0979 // we keep the date when adding a new transaction 0980 // for the next new one 0981 KMyMoneySettings::setLastUsedPostDate(QDateTime(d->ui->dateEdit->date())); 0982 } 0983 0984 d->removeUnusedSplits(t, d->feeSplitModel); 0985 d->removeUnusedSplits(t, d->interestSplitModel); 0986 0987 // we start with the previous values, clear id to make sure 0988 // we can add them later on 0989 d->stockSplit.clearId(); 0990 0991 t.setCommodity(d->currency.id()); 0992 0993 t.removeSplits(); 0994 0995 t.setPostDate(d->ui->dateEdit->date()); 0996 d->stockSplit.setMemo(d->ui->memoEdit->toPlainText()); 0997 0998 d->currentActivity->adjustStockSplit(d->stockSplit); 0999 1000 QList<MyMoneySplit> resultSplits; // concatenates splits for easy processing 1001 1002 // now update and add what we have in the model(s) 1003 if (d->currentActivity->assetAccountRequired() != Invest::Activity::Unused) { 1004 resultSplits.append(d->assetSplit); 1005 } 1006 if (d->currentActivity->feesRequired() != Invest::Activity::Unused) { 1007 addSplitsFromModel(resultSplits, d->feeSplitModel); 1008 } 1009 if (d->currentActivity->interestRequired() != Invest::Activity::Unused) { 1010 addSplitsFromModel(resultSplits, d->interestSplitModel); 1011 } 1012 1013 AlkValue::RoundingMethod roundingMethod = AlkValue::RoundRound; 1014 if (d->security.roundingMethod() != AlkValue::RoundNever) 1015 roundingMethod = d->security.roundingMethod(); 1016 1017 int currencyFraction = d->currency.smallestAccountFraction(); 1018 int securityFraction = d->security.smallestAccountFraction(); 1019 1020 // assuming that all non-stock splits are monetary 1021 foreach (auto split, resultSplits) { 1022 split.clearId(); 1023 split.setShares(MyMoneyMoney(split.shares().convertDenominator(currencyFraction, roundingMethod))); 1024 split.setValue(MyMoneyMoney(split.value().convertDenominator(currencyFraction, roundingMethod))); 1025 t.addSplit(split); 1026 } 1027 1028 // Don't do any rounding on a split factor 1029 if (d->currentActivity->type() != eMyMoney::Split::InvestmentTransactionType::SplitShares) { 1030 // only the shares variable of a stock split isn't evaluated in currency 1031 d->stockSplit.setShares(MyMoneyMoney(d->stockSplit.shares().convertDenominator(securityFraction, roundingMethod))); 1032 d->stockSplit.setValue(MyMoneyMoney(d->stockSplit.value().convertDenominator(currencyFraction, roundingMethod))); 1033 } 1034 t.addSplit(d->stockSplit); 1035 1036 MyMoneyFileTransaction ft; 1037 try { 1038 const auto file = MyMoneyFile::instance(); 1039 if (t.id().isEmpty()) { 1040 file->addTransaction(t); 1041 } else { 1042 file->modifyTransaction(t); 1043 } 1044 ft.commit(); 1045 1046 } catch (const MyMoneyException& e) { 1047 qDebug() << Q_FUNC_INFO << "something went wrong" << e.what(); 1048 } 1049 } 1050 1051 bool InvestTransactionEditor::eventFilter(QObject* o, QEvent* e) 1052 { 1053 auto cb = qobject_cast<QComboBox*>(o); 1054 if (cb) { 1055 // filter out wheel events for combo boxes if the popup view is not visible 1056 if ((e->type() == QEvent::Wheel) && !cb->view()->isVisible()) { 1057 return true; 1058 } 1059 } 1060 return QFrame::eventFilter(o, e); 1061 } 1062 1063 void InvestTransactionEditor::keyPressEvent(QKeyEvent* e) 1064 { 1065 if (!e->modifiers() || ((e->modifiers() & Qt::KeypadModifier) && (e->key() == Qt::Key_Enter))) { 1066 switch (e->key()) { 1067 case Qt::Key_Enter: 1068 case Qt::Key_Return: { 1069 if (focusWidget() == d->ui->cancelButton) { 1070 d->ui->cancelButton->click(); 1071 } else { 1072 if (d->ui->enterButton->isEnabled()) { 1073 // move focus to enter button which 1074 // triggers update of widgets 1075 d->ui->enterButton->setFocus(); 1076 d->ui->enterButton->click(); 1077 } 1078 return; 1079 } 1080 } 1081 break; 1082 1083 case Qt::Key_Escape: 1084 d->ui->cancelButton->click(); 1085 break; 1086 1087 default: 1088 e->ignore(); 1089 return; 1090 } 1091 } else { 1092 e->ignore(); 1093 } 1094 } 1095 1096 1097 // kate: indent-mode cstyle; indent-width 4; replace-tabs on; remove-trailing-space on;remove-trailing-space-save on;