File indexing completed on 2024-05-19 05:08:18
0001 /* 0002 SPDX-FileCopyrightText: 2002-2004 Kevin Tambascio <ktambascio@users.sourceforge.net> 0003 SPDX-FileCopyrightText: 2003-2021 Thomas Baumgart <tbaumgart@kde.org> 0004 SPDX-FileCopyrightText: 2004-2005 Ace Jones <acejones@users.sourceforge.net> 0005 SPDX-FileCopyrightText: 2009-2010 Alvaro Soliverez <asoliverez@kde.org> 0006 SPDX-FileCopyrightText: 2017 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com> 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "kinvestmentview_p.h" 0011 0012 // ---------------------------------------------------------------------------- 0013 // QT Includes 0014 0015 #include <QTimer> 0016 #include <QAction> 0017 #include <QMenu> 0018 #include <QBitArray> 0019 0020 // ---------------------------------------------------------------------------- 0021 // KDE Includes 0022 0023 #include <KMessageBox> 0024 0025 // ---------------------------------------------------------------------------- 0026 // Project Includes 0027 0028 #include "mymoneymoney.h" 0029 #include "mymoneyprice.h" 0030 #include "kequitypriceupdatedlg.h" 0031 #include "kcurrencycalculator.h" 0032 #include "knewinvestmentwizard.h" 0033 #include "kmymoneyutils.h" 0034 #include "menuenums.h" 0035 #include "storageenums.h" 0036 0037 using namespace Icons; 0038 0039 KInvestmentView::KInvestmentView(QWidget *parent) : 0040 KMyMoneyViewBase(*new KInvestmentViewPrivate(this), parent) 0041 { 0042 connect(pActions[eMenu::Action::NewInvestment], &QAction::triggered, this, &KInvestmentView::slotNewInvestment); 0043 connect(pActions[eMenu::Action::EditInvestment], &QAction::triggered, this, &KInvestmentView::slotEditInvestment); 0044 connect(pActions[eMenu::Action::DeleteInvestment], &QAction::triggered, this, &KInvestmentView::slotDeleteInvestment); 0045 connect(pActions[eMenu::Action::UpdatePriceOnline], &QAction::triggered, this, &KInvestmentView::slotUpdatePriceOnline); 0046 connect(pActions[eMenu::Action::UpdatePriceManually], &QAction::triggered, this, &KInvestmentView::slotUpdatePriceManually); 0047 connect(pActions[eMenu::Action::EditSecurity], &QAction::triggered, this, &KInvestmentView::slotEditSecurity); 0048 connect(pActions[eMenu::Action::DeleteSecurity], &QAction::triggered, this, &KInvestmentView::slotDeleteSecurity); 0049 } 0050 0051 KInvestmentView::~KInvestmentView() 0052 { 0053 } 0054 0055 void KInvestmentView::setDefaultFocus() 0056 { 0057 Q_D(KInvestmentView); 0058 auto tab = static_cast<eView::Investment::Tab>(d->ui->m_tab->currentIndex()); 0059 0060 switch (tab) { 0061 case eView::Investment::Tab::Equities: 0062 QMetaObject::invokeMethod(d->ui->m_equitiesTree, "setFocus", Qt::QueuedConnection); 0063 break; 0064 case eView::Investment::Tab::Securities: 0065 QMetaObject::invokeMethod(d->ui->m_securitiesTree, "setFocus", Qt::QueuedConnection); 0066 break; 0067 } 0068 } 0069 0070 void KInvestmentView::executeAction(eMenu::Action action, const SelectedObjects& selections) 0071 { 0072 Q_UNUSED(selections) 0073 Q_D(KInvestmentView); 0074 switch (action) { 0075 case eMenu::Action::FileNew: 0076 if (!d->m_needLoad) { 0077 d->ui->m_accountComboBox->expandAll(); 0078 d->m_equitiesProxyModel->invalidate(); 0079 d->m_securitiesProxyModel->invalidate(); 0080 d->selectDefaultInvestmentAccount(); 0081 } 0082 break; 0083 0084 case eMenu::Action::FileClose: 0085 d->m_idInvAcc.clear(); 0086 d->m_equitySelections.clearSelections(); 0087 d->m_securitySelections.clearSelections(); 0088 d->m_selections.clearSelections(); 0089 break; 0090 0091 default: 0092 break; 0093 } 0094 } 0095 void KInvestmentView::showEvent(QShowEvent* event) 0096 { 0097 Q_D(KInvestmentView); 0098 if (d->m_needLoad) { 0099 d->init(); 0100 0101 connect(d->ui->m_equitiesTree, &QWidget::customContextMenuRequested, this, [&](const QPoint& pos) { 0102 Q_D(KInvestmentView); 0103 Q_EMIT requestCustomContextMenu(eMenu::Menu::Investment, d->ui->m_equitiesTree->viewport()->mapToGlobal(pos)); 0104 }); 0105 0106 connect(d->ui->m_securitiesTree, &QWidget::customContextMenuRequested, this, [&](const QPoint& pos) { 0107 Q_D(KInvestmentView); 0108 Q_EMIT requestCustomContextMenu(eMenu::Menu::Security, d->ui->m_equitiesTree->viewport()->mapToGlobal(pos)); 0109 }); 0110 0111 connect(d->ui->m_equitiesTree->selectionModel(), 0112 &QItemSelectionModel::currentRowChanged, 0113 this, 0114 [&](const QModelIndex& current, const QModelIndex& previous) { 0115 Q_UNUSED(previous) 0116 Q_D(KInvestmentView); 0117 d->m_equitySelections.clearSelections(SelectedObjects::Account); 0118 // when closing equities, current may still reference a row that 0119 // is not valid any longer. For this reason, we set the row 0120 // to the last row in the model 0121 if (current.isValid()) { 0122 const auto rows = current.model()->rowCount(current.parent()); 0123 auto idx = current; 0124 if (idx.row() >= rows) { 0125 idx = idx.model()->index(rows - 1, idx.column(), idx.parent()); 0126 } 0127 if (idx.isValid()) { 0128 d->m_equitySelections.setSelection(SelectedObjects::Account, idx.data(eMyMoney::Model::IdRole).toString()); 0129 } 0130 } else { 0131 // suppress display if no more equities are shown 0132 d->m_equitiesProxyModel->setHideAllEntries(true); 0133 } 0134 if (d->ui->m_equitiesTree->isVisible()) { 0135 d->m_selections = d->m_equitySelections; 0136 Q_EMIT requestSelectionChange(d->m_selections); 0137 } 0138 }); 0139 0140 connect(d->ui->m_securitiesTree->selectionModel(), 0141 &QItemSelectionModel::currentRowChanged, 0142 this, 0143 [&](const QModelIndex& current, const QModelIndex& previous) { 0144 Q_UNUSED(previous) 0145 Q_D(KInvestmentView); 0146 d->m_securitySelections.setSelection(SelectedObjects::Security, current.data(eMyMoney::Model::IdRole).toString()); 0147 if (d->ui->m_securitiesTree->isVisible()) { 0148 d->m_selections = d->m_securitySelections; 0149 Q_EMIT requestSelectionChange(d->m_selections); 0150 } 0151 }); 0152 0153 connect(d->ui->m_equitiesTree, &QTreeView::doubleClicked, this, &KInvestmentView::slotEditInvestment); 0154 0155 // use a QueuedConnection here to suppress duplicate call (at least on Qt 5.12.7) 0156 connect( 0157 d->ui->m_tab, 0158 &QTabWidget::currentChanged, 0159 this, 0160 [&](int index) { 0161 Q_D(KInvestmentView); 0162 const auto tab = static_cast<eView::Investment::Tab>(index); 0163 0164 switch (tab) { 0165 case eView::Investment::Tab::Equities: 0166 d->m_selections = d->m_equitySelections; 0167 break; 0168 case eView::Investment::Tab::Securities: 0169 d->m_selections = d->m_securitySelections; 0170 break; 0171 } 0172 Q_EMIT requestSelectionChange(d->m_selections); 0173 }, 0174 Qt::QueuedConnection); 0175 0176 connect(d->ui->m_accountComboBox, &KMyMoneyAccountCombo::accountSelected, this, [&](const QString& accountId) { 0177 Q_D(KInvestmentView); 0178 d->loadAccount(accountId); 0179 }); 0180 0181 d->selectDefaultInvestmentAccount(); 0182 } 0183 0184 // don't forget base class implementation 0185 QWidget::showEvent(event); 0186 0187 // check if the last selected account was an investment account. 0188 // if so, then select it in this view as well. otherwise, we 0189 // leave the selection as is 0190 const auto accountId = d->m_externalSelections.firstSelection(SelectedObjects::Account); 0191 if (!accountId.isEmpty()) { 0192 const auto account = MyMoneyFile::instance()->account(accountId); 0193 if (account.accountType() == eMyMoney::Account::Type::Investment) { 0194 const auto indexes = d->m_accountsProxyModel->match(d->m_accountsProxyModel->index(0, 0), 0195 eMyMoney::Model::AccountTypeRole, 0196 static_cast<int>(eMyMoney::Account::Type::Investment), 0197 -1, 0198 Qt::MatchExactly | Qt::MatchRecursive); 0199 if (indexes.count()) { 0200 qDebug() << indexes.count(); 0201 d->ui->m_accountComboBox->setSelected(QString()); 0202 } 0203 d->ui->m_accountComboBox->setSelected(accountId); 0204 } 0205 } 0206 } 0207 0208 void KInvestmentView::updateActions(const SelectedObjects& selections) 0209 { 0210 Q_D(KInvestmentView); 0211 const auto equityId = selections.firstSelection(SelectedObjects::Account); 0212 const auto securityId = selections.firstSelection(SelectedObjects::Security); 0213 const auto file = MyMoneyFile::instance(); 0214 0215 pActions[eMenu::Action::NewInvestment]->setEnabled(false); 0216 pActions[eMenu::Action::EditInvestment]->setEnabled(false); 0217 pActions[eMenu::Action::DeleteInvestment]->setEnabled(false); 0218 pActions[eMenu::Action::UpdatePriceManually]->setEnabled(false); 0219 pActions[eMenu::Action::UpdatePriceOnline]->setEnabled(false); 0220 0221 pActions[eMenu::Action::EditSecurity]->setEnabled(false); 0222 pActions[eMenu::Action::DeleteSecurity]->setEnabled(false); 0223 0224 // check that the selected account (combobox) is an investment account 0225 auto idx = file->accountsModel()->indexById(d->m_idInvAcc); 0226 if (idx.data(eMyMoney::Model::AccountTypeRole).value<eMyMoney::Account::Type>() == eMyMoney::Account::Type::Investment) { 0227 pActions[eMenu::Action::NewInvestment]->setEnabled(true); 0228 } 0229 0230 if (!equityId.isEmpty()) { 0231 idx = file->accountsModel()->indexById(equityId); 0232 if (idx.data(eMyMoney::Model::AccountIsInvestRole).toBool()) { 0233 pActions[eMenu::Action::EditInvestment]->setEnabled(true); 0234 pActions[eMenu::Action::UpdatePriceManually]->setEnabled(true); 0235 pActions[eMenu::Action::DeleteInvestment]->setDisabled(file->isReferenced(equityId)); 0236 const auto secId = idx.data(eMyMoney::Model::AccountCurrencyIdRole).toString(); 0237 const auto sec = file->securitiesModel()->itemById(secId); 0238 pActions[eMenu::Action::UpdatePriceOnline]->setDisabled(sec.value("kmm-online-source").isEmpty()); 0239 } 0240 } 0241 if (!securityId.isEmpty()) { 0242 QBitArray skip((int)eStorage::Reference::Count); 0243 skip.fill(false); 0244 skip.setBit((int)eStorage::Reference::Price); 0245 pActions[eMenu::Action::EditSecurity]->setEnabled(true); 0246 pActions[eMenu::Action::DeleteSecurity]->setDisabled(file->isReferenced(securityId, skip)); 0247 } 0248 0249 d->m_externalSelections = selections; 0250 } 0251 0252 void KInvestmentView::slotNewInvestment() 0253 { 0254 Q_D(KInvestmentView); 0255 if (!isVisible()) 0256 KNewInvestmentWizard::newInvestment(d->currentEquity()); 0257 else 0258 KNewInvestmentWizard::newInvestment(MyMoneyFile::instance()->account(d->m_idInvAcc)); 0259 } 0260 0261 void KInvestmentView::slotEditInvestment() 0262 { 0263 Q_D(KInvestmentView); 0264 KNewInvestmentWizard::editInvestment(d->currentEquity()); 0265 } 0266 0267 void KInvestmentView::slotDeleteInvestment() 0268 { 0269 Q_D(KInvestmentView); 0270 if (KMessageBox::questionTwoActions(this, 0271 i18n("<p>Do you really want to delete the investment <b>%1</b>?</p>", d->currentEquity().name()), 0272 i18n("Delete investment"), 0273 KMMYesNo::yes(), 0274 KMMYesNo::no(), 0275 "DeleteInvestment") 0276 == KMessageBox::PrimaryAction) { 0277 auto file = MyMoneyFile::instance(); 0278 MyMoneyFileTransaction ft; 0279 try { 0280 file->removeAccount(d->currentEquity()); 0281 ft.commit(); 0282 } catch (const MyMoneyException &e) { 0283 KMessageBox::information(this, i18n("Unable to delete investment: %1", QString::fromLatin1(e.what()))); 0284 } 0285 } else { 0286 // we should not keep the 'no' setting because that can confuse people like 0287 // I have seen in some usability tests. So we just delete it right away. 0288 KSharedConfigPtr kconfig = KSharedConfig::openConfig(); 0289 if (kconfig) { 0290 kconfig->group(QLatin1String("Notification Messages")).deleteEntry(QLatin1String("DeleteInvestment")); 0291 } 0292 } 0293 } 0294 0295 void KInvestmentView::slotUpdatePriceOnline() 0296 { 0297 Q_D(KInvestmentView); 0298 if (!d->currentEquity().id().isEmpty()) { 0299 QPointer<KEquityPriceUpdateDlg> dlg = new KEquityPriceUpdateDlg(0, d->currentEquity().currencyId()); 0300 if ((dlg->exec() == QDialog::Accepted) && (dlg != nullptr)) 0301 dlg->storePrices(); 0302 delete dlg; 0303 } 0304 } 0305 0306 void KInvestmentView::slotUpdatePriceManually() 0307 { 0308 Q_D(KInvestmentView); 0309 if (!d->currentEquity().id().isEmpty()) { 0310 try { 0311 auto security = MyMoneyFile::instance()->security(d->currentEquity().currencyId()); 0312 auto currency = MyMoneyFile::instance()->security(security.tradingCurrency()); 0313 const auto& price = MyMoneyFile::instance()->price(security.id(), currency.id()); 0314 0315 QPointer<KCurrencyCalculator> calc = 0316 new KCurrencyCalculator(security, currency, MyMoneyMoney::ONE, 0317 price.rate(currency.id()), price.date(), 0318 MyMoneyMoney::precToDenom(security.pricePrecision())); 0319 calc->setupPriceEditor(); 0320 0321 // The dialog takes care of adding the price if necessary 0322 calc->exec(); 0323 delete calc; 0324 } catch (const MyMoneyException &e) { 0325 qDebug("Error in price update: %s", e.what()); 0326 } 0327 } 0328 } 0329 0330 void KInvestmentView::slotEditSecurity() 0331 { 0332 Q_D(KInvestmentView); 0333 auto sec = d->currentSecurity(); 0334 0335 if (!sec.id().isEmpty()) { 0336 QPointer<KNewInvestmentWizard> dlg = new KNewInvestmentWizard(sec, this); 0337 dlg->setObjectName("KNewInvestmentWizard"); 0338 if (dlg->exec() == QDialog::Accepted) 0339 dlg->createObjects(QString()); 0340 delete dlg; 0341 } 0342 } 0343 0344 void KInvestmentView::slotDeleteSecurity() 0345 { 0346 Q_D(KInvestmentView); 0347 auto sec = d->currentSecurity(); 0348 if (!sec.id().isEmpty()) 0349 KMyMoneyUtils::deleteSecurity(sec, this); 0350 } 0351 0352 void KInvestmentView::slotSettingsChanged() 0353 { 0354 Q_D(KInvestmentView); 0355 if (d->m_needLoad) { 0356 return; 0357 } 0358 0359 const bool showAllAccounts = KMyMoneySettings::showAllAccounts(); 0360 if (d->m_equitiesProxyModel->hideClosedAccounts() == showAllAccounts) { 0361 d->m_equitiesProxyModel->setHideClosedAccounts(!showAllAccounts); 0362 d->loadAccount(d->m_idInvAcc); 0363 } 0364 }