File indexing completed on 2024-05-12 05:07:50
0001 /* 0002 SPDX-FileCopyrightText: 2015-2019 Thomas Baumgart <tbaumgart@kde.org> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 0007 #include "simpleledgerview.h" 0008 0009 // ---------------------------------------------------------------------------- 0010 // QT Includes 0011 0012 #include <QAction> 0013 #include <QDesktopServices> 0014 #include <QHeaderView> 0015 #include <QKeyEvent> 0016 #include <QLineEdit> 0017 #include <QTabBar> 0018 #include <QTimer> 0019 #include <QToolButton> 0020 #include <QTreeView> 0021 #include <QUrl> 0022 #include <QUuid> 0023 0024 // ---------------------------------------------------------------------------- 0025 // KDE Includes 0026 0027 #include <KMessageBox> 0028 #include <KPageWidgetItem> 0029 0030 // ---------------------------------------------------------------------------- 0031 // Project Includes 0032 0033 #include "accountsmodel.h" 0034 #include "icons.h" 0035 #include "institutionsmodel.h" 0036 #include "kmymoneyaccountcombo.h" 0037 #include "kmymoneysettings.h" 0038 #include "kmymoneyviewbase_p.h" 0039 #include "ledgerviewpage.h" 0040 #include "menuenums.h" 0041 #include "mymoneyaccount.h" 0042 #include "mymoneyenums.h" 0043 #include "mymoneyfile.h" 0044 #include "mymoneyinstitution.h" 0045 #include "reconciliationledgerviewpage.h" 0046 #include "selectedobjects.h" 0047 #include <ui_simpleledgerview.h> 0048 0049 using namespace Icons; 0050 0051 class SimpleLedgerViewPrivate : public KMyMoneyViewBasePrivate 0052 { 0053 Q_DECLARE_PUBLIC(SimpleLedgerView) 0054 0055 public: 0056 explicit SimpleLedgerViewPrivate(SimpleLedgerView* qq) 0057 : KMyMoneyViewBasePrivate(qq) 0058 , ui(new Ui_SimpleLedgerView) 0059 , accountsModel(nullptr) 0060 , newTabWidget(nullptr) 0061 , webSiteButton(nullptr) 0062 , accountCombo(nullptr) 0063 , lastIdx(-1) 0064 , inModelUpdate(false) 0065 , m_needInit(true) 0066 {} 0067 0068 ~SimpleLedgerViewPrivate() 0069 { 0070 delete ui; 0071 } 0072 0073 void removeCloseButton(int idx, const QString& tooltip = QString()) 0074 { 0075 Q_Q(SimpleLedgerView); 0076 // remove close button from new page 0077 QTabBar* bar = ui->ledgerTab->findChild<QTabBar*>(); 0078 if (bar) { 0079 QTabBar::ButtonPosition closeSide = 0080 (QTabBar::ButtonPosition)q->style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, ui->ledgerTab->widget(idx)); 0081 QWidget* w = bar->tabButton(idx, closeSide); 0082 bar->setTabButton(idx, closeSide, 0); 0083 bar->setTabToolTip(idx, tooltip); 0084 w->deleteLater(); 0085 } 0086 } 0087 0088 void init() 0089 { 0090 Q_Q(SimpleLedgerView); 0091 m_needInit = false; 0092 ui->setupUi(q); 0093 ui->ledgerTab->setTabIcon(0, Icons::get(Icon::ListAdd)); 0094 ui->ledgerTab->setTabText(0, QString()); 0095 newTabWidget = ui->ledgerTab->widget(0); 0096 0097 accountsModel = new AccountNamesFilterProxyModel(q); 0098 q->slotSettingsChanged(); 0099 0100 // remove close button from new page 0101 QTabBar* bar = ui->ledgerTab->findChild<QTabBar*>(); 0102 if(bar) { 0103 q->connect(bar, &QTabBar::tabMoved, q, &SimpleLedgerView::checkTabOrder); 0104 } 0105 removeCloseButton(0); 0106 0107 webSiteButton = new QToolButton; 0108 ui->ledgerTab->setCornerWidget(webSiteButton); 0109 q->connect(webSiteButton, &QToolButton::pressed, q, 0110 [=] { 0111 QDesktopServices::openUrl(webSiteUrl); 0112 }); 0113 0114 q->connect(ui->ledgerTab, &QTabWidget::currentChanged, q, &SimpleLedgerView::tabSelected); 0115 q->connect(ui->ledgerTab, &QTabWidget::tabBarClicked, q, &SimpleLedgerView::tabClicked); 0116 q->connect(ui->ledgerTab, &QTabWidget::tabCloseRequested, q, &SimpleLedgerView::closeLedger); 0117 0118 // we reload the icon if the institution data changed 0119 q->connect(MyMoneyFile::instance()->institutionsModel(), &InstitutionsModel::dataChanged, q, &SimpleLedgerView::setupCornerWidget); 0120 0121 accountsModel->addAccountGroup(QVector<eMyMoney::Account::Type> {eMyMoney::Account::Type::Asset, eMyMoney::Account::Type::Liability, eMyMoney::Account::Type::Equity}); 0122 0123 accountsModel->setHideEquityAccounts(!KMyMoneySettings::expertMode()); 0124 accountsModel->setHideZeroBalancedEquityAccounts(KMyMoneySettings::hideZeroBalanceEquities()); 0125 accountsModel->setHideZeroBalancedAccounts(KMyMoneySettings::hideZeroBalanceAccounts()); 0126 accountsModel->setShowAllEntries(KMyMoneySettings::showAllAccounts()); 0127 auto const model = MyMoneyFile::instance()->accountsModel(); 0128 accountsModel->setSourceModel(model); 0129 accountsModel->sort(AccountsModel::Column::AccountName); 0130 0131 accountCombo = new KMyMoneyAccountCombo(accountsModel, ui->ledgerTab); 0132 accountCombo->setEditable(true); 0133 accountCombo->setSplitActionVisible(false); 0134 accountCombo->hide(); 0135 q->connect(accountCombo, &KMyMoneyAccountCombo::accountSelected, q, &SimpleLedgerView::openLedger); 0136 0137 accountCombo->installEventFilter(q); 0138 accountCombo->popup()->installEventFilter(q); 0139 updateTitlePage(); 0140 0141 q->tabSelected(0); 0142 openLedgersAfterFileOpen(); 0143 } 0144 0145 void openLedgersAfterFileOpen() 0146 { 0147 if (m_needInit) 0148 return; 0149 0150 Q_Q(SimpleLedgerView); 0151 // get storage id without the enclosing braces 0152 const auto storageId = MyMoneyFile::instance()->storageId().toString(QUuid::WithoutBraces); 0153 KSharedConfigPtr config = KSharedConfig::openConfig(); 0154 KConfigGroup grp = config->group("OpenLedgers"); 0155 0156 // in case we have a previous setting, we open them 0157 const auto openLedgers = grp.readEntry(storageId, QStringList()); 0158 if (!openLedgers.isEmpty()) { 0159 for (const auto& id: qAsConst(openLedgers)) { 0160 auto thisId = id; 0161 openLedger(thisId.remove(QLatin1String("*")), id.endsWith(QLatin1String("*"))); 0162 } 0163 0164 // in case we have not opened any ledger, we proceed with the favorites 0165 if (ui->ledgerTab->count() > 1) { 0166 q->tabSelected(ui->ledgerTab->currentIndex()); 0167 return; 0168 } 0169 } 0170 0171 0172 AccountsModel* model = MyMoneyFile::instance()->accountsModel(); 0173 0174 const auto subtrees = QVector<QModelIndex> ({ model->favoriteIndex(), model->assetIndex(), model->liabilityIndex() }); 0175 0176 bool stopAfterFirstAccount = false; 0177 for (const auto& startIdx : subtrees) { 0178 // retrieve all items in the current subtree 0179 const auto indexes = model->match(model->index(0, 0, startIdx), Qt::DisplayRole, QString("*"), -1, Qt::MatchWildcard); 0180 0181 // indexes now has a list of favorite accounts 0182 for (const auto& idx : indexes) { 0183 openLedger(idx.data(eMyMoney::Model::Roles::IdRole).toString(), false); 0184 if (stopAfterFirstAccount) { 0185 break; 0186 } 0187 } 0188 0189 // if at least one account was found and opened 0190 // we stop processing 0191 if (!indexes.isEmpty()) { 0192 break; 0193 } 0194 stopAfterFirstAccount = true; 0195 } 0196 ui->ledgerTab->setCurrentIndex(0); 0197 } 0198 0199 void openLedger(QString accountId, bool makeCurrentLedger) 0200 { 0201 Q_Q(SimpleLedgerView); 0202 if(inModelUpdate || accountId.isEmpty()) 0203 return; 0204 0205 accountCombo->hide(); 0206 0207 const auto idx = tabIdByAccountId(accountId); 0208 if (idx != -1) { 0209 ui->ledgerTab->setCurrentIndex(idx); 0210 return; 0211 } 0212 0213 LedgerViewPage* view = nullptr; 0214 MyMoneyAccount acc = MyMoneyFile::instance()->accountsModel()->itemById(accountId); 0215 0216 // need a new tab, we insert it before the rightmost one 0217 if(!acc.id().isEmpty()) { 0218 0219 QString configGroupName; 0220 switch(acc.accountType()) { 0221 case eMyMoney::Account::Type::Investment: 0222 configGroupName = QStringLiteral("InvestmentLedger"); 0223 break; 0224 default: 0225 configGroupName = QStringLiteral("StandardLedger"); 0226 break; 0227 } 0228 // create new ledger view page 0229 view = new LedgerViewPage(q, configGroupName); 0230 0231 view->setAccount(acc); 0232 view->setShowEntryForNewTransaction(!acc.isClosed()); 0233 view->showTransactionForm(KMyMoneySettings::transactionForm()); 0234 0235 // insert new ledger view page in tab view 0236 int newIdx = ui->ledgerTab->insertTab(ui->ledgerTab->count()-1, view, acc.name()); 0237 if (makeCurrentLedger) { 0238 view->prepareToShow(); 0239 // selecting the last tab (the one with the +) and then the new one 0240 // makes sure that all signals about the new selection are emitted 0241 ui->ledgerTab->setCurrentIndex(ui->ledgerTab->count()-1); 0242 ui->ledgerTab->setCurrentIndex(newIdx); 0243 } 0244 0245 q->connect(view, &LedgerViewPage::requestSelectionChanged, q, &SimpleLedgerView::requestSelectionChange); 0246 q->connect(view, &LedgerViewPage::requestCustomContextMenu, q, &SimpleLedgerView::requestCustomContextMenu); 0247 0248 q->connect(q, &SimpleLedgerView::settingsChanged, view, &LedgerViewPage::slotSettingsChanged); 0249 q->connect(view, &LedgerViewPage::sectionResized, q, &SimpleLedgerView::sectionResized); 0250 q->connect(view, &LedgerViewPage::sectionMoved, q, &SimpleLedgerView::sectionMoved); 0251 0252 q->connect(q, &SimpleLedgerView::resizeSection, view, &LedgerViewPage::resizeSection); 0253 q->connect(q, &SimpleLedgerView::moveSection, view, &LedgerViewPage::moveSection); 0254 0255 q->connect(view, &LedgerViewPage::requestView, q, &SimpleLedgerView::requestView); 0256 } 0257 } 0258 0259 void openReconciliationLedger(QString accountId) 0260 { 0261 Q_Q(SimpleLedgerView); 0262 if (inModelUpdate || accountId.isEmpty()) 0263 return; 0264 0265 accountCombo->hide(); 0266 0267 // in case a stock account is selected, we switch to the parent which 0268 // is the investment account 0269 MyMoneyAccount acc = MyMoneyFile::instance()->accountsModel()->itemById(accountId); 0270 if (acc.isInvest()) { 0271 acc = MyMoneyFile::instance()->accountsModel()->itemById(acc.parentAccountId()); 0272 accountId = acc.id(); 0273 } 0274 0275 // check the position of the new tab 0276 auto newPos = ui->ledgerTab->count() - 1; 0277 LedgerViewPage* view = 0; 0278 // check if ledger is already opened 0279 for (int idx = 0; idx < ui->ledgerTab->count() - 1; ++idx) { 0280 view = qobject_cast<LedgerViewPage*>(ui->ledgerTab->widget(idx)); 0281 if (view) { 0282 if (accountId == view->accountId()) { 0283 newPos = idx; 0284 break; 0285 } 0286 } 0287 view = nullptr; // not found 0288 } 0289 0290 // need a new tab, we insert it before the rightmost one 0291 if (!acc.id().isEmpty()) { 0292 QString configGroupName; 0293 switch (acc.accountType()) { 0294 case eMyMoney::Account::Type::Investment: 0295 configGroupName = QStringLiteral("InvestmentLedger"); 0296 break; 0297 default: 0298 configGroupName = QStringLiteral("StandardLedger"); 0299 break; 0300 } 0301 0302 // create new reconciliation ledger view page 0303 auto reconciliationView = new ReconciliationLedgerViewPage(q, configGroupName); 0304 0305 reconciliationView->setAccount(acc); 0306 reconciliationView->setShowEntryForNewTransaction(); 0307 reconciledAccount = acc.id(); 0308 0309 // keep the current view so that we can get it back after reconciliation 0310 if (view) { 0311 reconciliationView->pushView(view); 0312 view->hide(); 0313 ui->ledgerTab->removeTab(newPos); 0314 reconciliationView->setSplitterSizes(view->splitterSizes()); 0315 } 0316 reconciliationView->showTransactionForm(KMyMoneySettings::transactionForm()); 0317 0318 // insert new ledger reconciliationView page in tab view 0319 int newIdx = ui->ledgerTab->insertTab(newPos, reconciliationView, acc.name()); 0320 0321 // we don't allow closing the tab without using the action buttons 0322 removeCloseButton( 0323 newIdx, 0324 i18nc("@info:tooltip", 0325 "The close button for this account has been removed because you are reconciling it. Finish, cancel or postpone the reconciliation " 0326 "to get it back.")); 0327 0328 q->connect(reconciliationView, &LedgerViewPage::requestSelectionChanged, q, &SimpleLedgerView::slotRequestSelectionChange); 0329 q->connect(reconciliationView, &LedgerViewPage::requestCustomContextMenu, q, &SimpleLedgerView::requestCustomContextMenu); 0330 0331 q->connect(q, &SimpleLedgerView::settingsChanged, reconciliationView, &LedgerViewPage::slotSettingsChanged); 0332 q->connect(reconciliationView, &LedgerViewPage::sectionResized, q, &SimpleLedgerView::sectionResized); 0333 q->connect(reconciliationView, &LedgerViewPage::sectionMoved, q, &SimpleLedgerView::sectionMoved); 0334 q->connect(q, &SimpleLedgerView::resizeSection, reconciliationView, &LedgerViewPage::resizeSection); 0335 q->connect(q, &SimpleLedgerView::moveSection, reconciliationView, &LedgerViewPage::moveSection); 0336 0337 // selecting the last tab (the one with the +) and then the new one 0338 // makes sure that all signal about the new selection are emitted 0339 reconciliationView->prepareToShow(); 0340 ui->ledgerTab->setCurrentIndex(ui->ledgerTab->count() - 1); 0341 ui->ledgerTab->setCurrentIndex(newIdx); 0342 } 0343 } 0344 0345 void closeLedgers() 0346 { 0347 Q_Q(SimpleLedgerView); 0348 if (m_needInit) 0349 return; 0350 0351 // get storage id without the enclosing braces 0352 const auto storageId = MyMoneyFile::instance()->storageId().toString(QUuid::WithoutBraces); 0353 KSharedConfigPtr config = KSharedConfig::openConfig(); 0354 KConfigGroup grp = config->group("OpenLedgers"); 0355 grp.deleteEntry(storageId); 0356 0357 // collect account ids of open ledgers 0358 QStringList openLedgers; 0359 LedgerViewPage* view = 0; 0360 for(int idx = 0; idx < ui->ledgerTab->count()-1; ++idx) { 0361 view = qobject_cast<LedgerViewPage*>(ui->ledgerTab->widget(idx)); 0362 if(view) { 0363 auto id = view->accountId(); 0364 if (idx == ui->ledgerTab->currentIndex()) { 0365 id.append(QLatin1String("*")); 0366 } 0367 openLedgers.append(id); 0368 } 0369 } 0370 // save the ones we have found 0371 if (!openLedgers.isEmpty()) { 0372 grp.writeEntry(storageId, openLedgers); 0373 } 0374 0375 auto tabCount = ui->ledgerTab->count(); 0376 // check that we have a least one tab that can be closed 0377 if(tabCount > 1) { 0378 // we keep the tab with the selector open at all times 0379 // which is located in the right most position 0380 --tabCount; 0381 do { 0382 --tabCount; 0383 q->closeLedger(tabCount); 0384 } while(tabCount > 0); 0385 } 0386 } 0387 0388 void propagateActionToViews(eMenu::Action action, const SelectedObjects& selections) 0389 { 0390 LedgerViewPage* view = 0; 0391 for (int idx = 0; idx < ui->ledgerTab->count() - 1; ++idx) { 0392 view = qobject_cast<LedgerViewPage*>(ui->ledgerTab->widget(idx)); 0393 if (view) { 0394 view->executeAction(action, selections); 0395 } 0396 } 0397 } 0398 0399 /** 0400 * This method returns the tab index for the given @a accountId 0401 * or -1 if there is no tab for it open or @a accountId is empty 0402 * 0403 * @note updates accountId to point to the investment account in case 0404 * @a accountId references a stock account 0405 */ 0406 int tabIdByAccountId(QString& accountId) 0407 { 0408 if (ui && ui->ledgerTab && !accountId.isEmpty()) { 0409 // in case a stock account is selected, we switch to the parent which 0410 // is the investment account 0411 MyMoneyAccount acc = MyMoneyFile::instance()->accountsModel()->itemById(accountId); 0412 if (acc.isInvest()) { 0413 acc = MyMoneyFile::instance()->accountsModel()->itemById(acc.parentAccountId()); 0414 accountId = acc.id(); 0415 } 0416 0417 // check if ledger is already opened 0418 for (int idx = 0; idx < ui->ledgerTab->count() - 1; ++idx) { 0419 const auto view = qobject_cast<LedgerViewPage*>(ui->ledgerTab->widget(idx)); 0420 if (view) { 0421 if (accountId == view->accountId()) { 0422 return idx; 0423 } 0424 } 0425 } 0426 } 0427 return -1; 0428 } 0429 0430 void updateTitlePage() 0431 { 0432 QString txt = QLatin1String("<html><head/><body><p>"); 0433 if (MyMoneyFile::instance()->accountsModel()->itemList().isEmpty()) { 0434 txt.append(i18nc("@label displayed when no ledger is open", 0435 "This page shows the accounts ledger. Currently, no accounts exist so this text is shown instead.")); 0436 } else { 0437 txt.append(i18nc("@label displayed when no ledger is open", 0438 "This page shows the accounts ledger. Currently, no accounts are selected so this text is shown instead.")); 0439 } 0440 txt.append(QLatin1String("</p></body></html>")); 0441 ui->l1->setText(txt); 0442 } 0443 0444 Ui_SimpleLedgerView* ui; 0445 AccountNamesFilterProxyModel* accountsModel; 0446 QWidget* newTabWidget; 0447 QToolButton* webSiteButton; 0448 KMyMoneyAccountCombo* accountCombo; 0449 QUrl webSiteUrl; 0450 QString reconciledAccount; 0451 int lastIdx; 0452 bool inModelUpdate; 0453 bool m_needInit; 0454 }; 0455 0456 0457 SimpleLedgerView::SimpleLedgerView(QWidget *parent) : 0458 KMyMoneyViewBase(*new SimpleLedgerViewPrivate(this), parent) 0459 { 0460 Q_D(SimpleLedgerView); 0461 d->m_sharedToolbarActions.insert(eMenu::Action::FileNew, pActions[eMenu::Action::NewTransaction]); 0462 0463 connect(MyMoneyFile::instance()->accountsModel(), &QAbstractItemModel::dataChanged, this, [&](const QModelIndex& topLeft, const QModelIndex& bottomRight) { 0464 Q_D(SimpleLedgerView); 0465 Q_ASSERT(topLeft.parent() == bottomRight.parent()); 0466 for (int row = topLeft.row(); row <= bottomRight.row(); ++row) { 0467 const auto idx = MyMoneyFile::instance()->accountsModel()->index(row, 0, topLeft.parent()); 0468 const auto modifiedAccountId = idx.data(eMyMoney::Model::IdRole).toString(); 0469 auto tabAccountId = modifiedAccountId; 0470 const auto tab = d->tabIdByAccountId(tabAccountId); 0471 // d->tabIdByAccountId() may return the tab for the parent in case 0472 // a stock account was changed. In this case it also updates 0473 // tabAccountId to contain the id of the parent account. We only 0474 // update the name if the parent was changed directly. 0475 if (tab != -1 && (tabAccountId == modifiedAccountId)) { 0476 d->ui->ledgerTab->setTabText(tab, idx.data(eMyMoney::Model::AccountNameRole).toString()); 0477 } 0478 } 0479 }); 0480 } 0481 0482 SimpleLedgerView::~SimpleLedgerView() 0483 { 0484 } 0485 0486 bool SimpleLedgerView::eventFilter(QObject* o, QEvent* e) 0487 { 0488 Q_D(SimpleLedgerView); 0489 0490 if (e->type() == QEvent::KeyPress) { 0491 if (o == d->accountCombo) { 0492 const auto kev = static_cast<QKeyEvent*>(e); 0493 if (kev->key() == Qt::Key_Escape) { 0494 d->accountCombo->hide(); 0495 return true; 0496 } 0497 } 0498 } 0499 return false; 0500 } 0501 0502 void SimpleLedgerView::openLedger(QString accountId) 0503 { 0504 Q_D(SimpleLedgerView); 0505 d->openLedger(accountId, true); 0506 } 0507 0508 void SimpleLedgerView::tabClicked(int idx) 0509 { 0510 Q_D(SimpleLedgerView); 0511 if (idx == (d->ui->ledgerTab->count()-1)) { 0512 const auto rect = d->ui->ledgerTab->tabBar()->tabRect(idx); 0513 if (!d->accountCombo->isVisible()) { 0514 d->accountCombo->lineEdit()->clear(); 0515 d->accountCombo->lineEdit()->setFocus(); 0516 // Using the QMetaObject::invokeMethod calls showPopup too early 0517 // so we delay it a bit (not recongnizable for the user) 0518 QTimer::singleShot(50, d->accountCombo, SLOT(showPopup())); 0519 } 0520 // make the combo box visible 0521 d->accountCombo->raise(); 0522 d->accountCombo->show(); 0523 // and adjust the size to the largest account 0524 // shown in the popup treeview 0525 const auto popupView = d->accountCombo->popup(); 0526 popupView->resizeColumnToContents(0); 0527 d->accountCombo->resize(popupView->header()->sectionSize(0), d->accountCombo->height()); 0528 // now place the combobox either left or right aligned to the tab button 0529 if (rect.left() + popupView->header()->sectionSize(0) < width()) { 0530 d->accountCombo->move(rect.left(), rect.bottom()); 0531 } else { 0532 d->accountCombo->move(rect.left()+rect.width()- popupView->header()->sectionSize(0), rect.bottom()); 0533 } 0534 } else { 0535 // make sure the view's model is initialized before 0536 // the view is shown 0537 const auto view = qobject_cast<LedgerViewPage*>(d->ui->ledgerTab->widget(idx)); 0538 if (view) { 0539 view->prepareToShow(); 0540 } 0541 d->accountCombo->hide(); 0542 } 0543 } 0544 0545 void SimpleLedgerView::tabSelected(int idx) 0546 { 0547 Q_D(SimpleLedgerView); 0548 // make sure that the ledger does not change 0549 // when the user accesses the account selection combo box 0550 if(idx != (d->ui->ledgerTab->count()-1)) { 0551 d->lastIdx = idx; 0552 0553 } else { 0554 d->ui->ledgerTab->setCurrentIndex(d->lastIdx); 0555 } 0556 0557 const auto view = qobject_cast<LedgerViewPage*>(d->ui->ledgerTab->widget(idx)); 0558 if (view) { 0559 d->m_selections = view->selections(); 0560 } 0561 slotRequestSelectionChange(d->m_selections); 0562 0563 setupCornerWidget(); 0564 } 0565 0566 void SimpleLedgerView::closeLedger(int idx) 0567 { 0568 Q_D(SimpleLedgerView); 0569 // don't react on the close request for the new ledger function 0570 // and non-existing tabs 0571 if ((idx >= 0) && (idx != (d->ui->ledgerTab->count() - 1))) { 0572 // if the currently selected ledger is closed, we 0573 // remove any selection 0574 d->m_selections.clearSelections(); 0575 Q_EMIT requestSelectionChange(d->m_selections); 0576 0577 auto tab = d->ui->ledgerTab->widget(idx); 0578 d->ui->ledgerTab->removeTab(idx); 0579 0580 auto view = qobject_cast<LedgerViewPage*>(tab); 0581 if (view) { 0582 view = view->popView(); 0583 if (view) { 0584 view->prepareToShow(); 0585 d->ui->ledgerTab->insertTab(idx, view, view->accountName()); 0586 d->ui->ledgerTab->setCurrentIndex(d->ui->ledgerTab->count() - 1); 0587 d->ui->ledgerTab->setCurrentIndex(idx); 0588 d->reconciledAccount.clear(); 0589 } 0590 } 0591 delete tab; 0592 0593 // make sure we always show an account 0594 if (d->ui->ledgerTab->currentIndex() == (d->ui->ledgerTab->count()-1)) { 0595 if (d->ui->ledgerTab->count() > 1) { 0596 d->ui->ledgerTab->setCurrentIndex((d->ui->ledgerTab->count()-2)); 0597 } 0598 } else { 0599 tabSelected(d->ui->ledgerTab->currentIndex()); 0600 } 0601 } 0602 } 0603 0604 void SimpleLedgerView::checkTabOrder(int from, int to) 0605 { 0606 Q_D(SimpleLedgerView); 0607 if(d->inModelUpdate) 0608 return; 0609 0610 QTabBar* bar = d->ui->ledgerTab->findChild<QTabBar*>(); 0611 if(bar) { 0612 const int rightMostIdx = d->ui->ledgerTab->count()-1; 0613 0614 if(from == rightMostIdx) { 0615 // someone tries to move the new account tab away from the rightmost position 0616 d->inModelUpdate = true; 0617 bar->moveTab(to, from); 0618 d->inModelUpdate = false; 0619 } 0620 } 0621 } 0622 0623 void SimpleLedgerView::showEvent(QShowEvent* event) 0624 { 0625 Q_D(SimpleLedgerView); 0626 if (d->m_needInit) { 0627 d->init(); 0628 0629 // close ledgers of accounts about to be removed 0630 connect(MyMoneyFile::instance()->accountsModel(), &AccountsModel::rowsAboutToBeRemoved, this, [&](const QModelIndex& parent, int first, int last) { 0631 Q_D(SimpleLedgerView); 0632 // we only need to react on real account removals not if the favorite 0633 // option is removed and the row is removed from the favorite subtree 0634 if (parent != MyMoneyFile::instance()->accountsModel()->favoriteIndex()) { 0635 for (int row = first; row <= last; ++row) { 0636 const auto modelIdx = parent.model()->index(row, 0, parent); 0637 auto accountId = modelIdx.data(eMyMoney::Model::IdRole).toString(); 0638 const auto idx = d->tabIdByAccountId(accountId); 0639 if (idx != -1) { 0640 closeLedger(idx); 0641 } 0642 } 0643 } 0644 }); 0645 } 0646 // don't forget base class implementation 0647 QWidget::showEvent(event); 0648 } 0649 0650 void SimpleLedgerView::setupCornerWidget() 0651 { 0652 Q_D(SimpleLedgerView); 0653 0654 // check if we already have the button, quit if not 0655 if (!d->webSiteButton) 0656 return; 0657 0658 d->webSiteButton->hide(); 0659 auto view = qobject_cast<LedgerViewPage*>(d->ui->ledgerTab->currentWidget()); 0660 if (view) { 0661 const auto accountsModel = MyMoneyFile::instance()->accountsModel(); 0662 auto index = accountsModel->indexById(view->accountId()); 0663 if(index.isValid()) { 0664 // get icon name and url via account and institution object 0665 const auto acc = accountsModel->itemByIndex(index); 0666 if (!acc.institutionId().isEmpty()) { 0667 const auto institutionsModel = MyMoneyFile::instance()->institutionsModel(); 0668 index = institutionsModel->indexById(acc.institutionId()); 0669 const auto institution = institutionsModel->itemByIndex(index); 0670 const auto url = institution.value(QStringLiteral("url")); 0671 const auto iconName = institution.value(QStringLiteral("icon")); 0672 if (!url.isEmpty() && !iconName.isEmpty()) { 0673 const auto favIcon = Icons::loadIconFromApplicationCache(iconName); 0674 if (!favIcon.isNull()) { 0675 d->webSiteButton->show(); 0676 d->webSiteButton->setIcon(favIcon); 0677 d->webSiteButton->setText(url); 0678 d->webSiteButton->setToolTip(i18n("Open website of <b>%1</b> in your browser.", institution.name())); 0679 0680 // we remove all parts of the URL that we provide ourselves first 0681 auto webUrl(url); 0682 auto pos = webUrl.indexOf(QLatin1String("://")); 0683 if (pos != -1) { 0684 webUrl = webUrl.mid(pos + 3); 0685 } 0686 // make sure it has at least one / 0687 pos = webUrl.indexOf(QLatin1Char('/')); 0688 if (pos != -1) { 0689 webUrl.push_back(QLatin1Char('/')); 0690 } 0691 d->webSiteUrl.setUrl(QString::fromLatin1("https://%1").arg(url)); 0692 } 0693 } 0694 } 0695 } 0696 } 0697 } 0698 0699 void SimpleLedgerView::aboutToShow() 0700 { 0701 Q_D(SimpleLedgerView); 0702 0703 d->m_selections.clearSelections(); 0704 0705 // we don't do anything special here if 0706 // we are not fully initialized 0707 if (!d->m_needInit) { 0708 // in case we have at least one account open 0709 if (d->ui->ledgerTab->count() > 1) { 0710 // use its current selection 0711 const auto view = qobject_cast<LedgerViewPage*>(d->ui->ledgerTab->currentWidget()); 0712 if (view) { 0713 d->m_selections = view->selections(); 0714 } 0715 } 0716 } 0717 0718 // don't forget base class logic 0719 KMyMoneyViewBase::aboutToShow(); 0720 } 0721 0722 void SimpleLedgerView::slotSettingsChanged() 0723 { 0724 Q_D(SimpleLedgerView); 0725 if (d->accountsModel) { 0726 d->accountsModel->setHideClosedAccounts(!KMyMoneySettings::showAllAccounts()); 0727 d->accountsModel->setHideEquityAccounts(!KMyMoneySettings::expertMode()); 0728 d->accountsModel->setHideZeroBalancedEquityAccounts(KMyMoneySettings::hideZeroBalanceEquities()); 0729 d->accountsModel->setHideZeroBalancedAccounts(KMyMoneySettings::hideZeroBalanceAccounts()); 0730 d->accountsModel->setShowAllEntries(KMyMoneySettings::showAllAccounts()); 0731 d->accountsModel->setHideFavoriteAccounts(false); 0732 } 0733 Q_EMIT settingsChanged(); 0734 } 0735 0736 void SimpleLedgerView::slotRequestSelectionChange(const SelectedObjects& selections) const 0737 { 0738 Q_D(const SimpleLedgerView); 0739 auto newSelections(selections); 0740 0741 if (!d->reconciledAccount.isEmpty()) { 0742 newSelections.setSelection(SelectedObjects::ReconciliationAccount, d->reconciledAccount); 0743 } 0744 Q_EMIT const_cast<SimpleLedgerView*>(this)->requestSelectionChange(newSelections); 0745 } 0746 0747 void SimpleLedgerView::updateActions(const SelectedObjects& selections) 0748 { 0749 Q_D(SimpleLedgerView); 0750 if (d->isActiveView()) { 0751 const auto reconciledAccount = selections.firstSelection(SelectedObjects::ReconciliationAccount); 0752 if (!reconciledAccount.isEmpty()) { 0753 if (reconciledAccount == selections.firstSelection(SelectedObjects::Account)) { 0754 pActions[eMenu::Action::PostponeReconciliation]->setEnabled(true); 0755 pActions[eMenu::Action::FinishReconciliation]->setEnabled(true); 0756 } 0757 pActions[eMenu::Action::CancelReconciliation]->setEnabled(true); 0758 } 0759 } 0760 if (!d->m_needInit) { 0761 d->updateTitlePage(); 0762 } 0763 } 0764 0765 void SimpleLedgerView::executeAction(eMenu::Action action, const SelectedObjects& selections) 0766 { 0767 Q_D(SimpleLedgerView); 0768 auto accountId = selections.firstSelection(SelectedObjects::Account); 0769 switch (action) { 0770 case eMenu::Action::GoToAccount: 0771 case eMenu::Action::NewTransaction: 0772 case eMenu::Action::OpenAccount: 0773 case eMenu::Action::EditTransaction: 0774 case eMenu::Action::EditSplits: 0775 case eMenu::Action::SelectAllTransactions: 0776 case eMenu::Action::EditTabOrder: 0777 case eMenu::Action::ShowTransaction: 0778 if (d->isActiveView() && !accountId.isEmpty()) { 0779 d->openLedger(accountId, true); 0780 auto view = qobject_cast<LedgerViewPage*>(d->ui->ledgerTab->currentWidget()); 0781 if (view) { 0782 view->executeAction(action, selections); 0783 } 0784 } 0785 break; 0786 case eMenu::Action::StartReconciliation: 0787 if (d->isActiveView() && !accountId.isEmpty()) { 0788 const auto reconciledAccountId = selections.firstSelection(SelectedObjects::ReconciliationAccount); 0789 if (reconciledAccountId.isEmpty()) 0790 d->openReconciliationLedger(accountId); 0791 else 0792 d->openLedger(accountId, true); 0793 auto view = qobject_cast<LedgerViewPage*>(d->ui->ledgerTab->currentWidget()); 0794 if (view) { 0795 view->executeAction(action, selections); 0796 } 0797 } 0798 break; 0799 case eMenu::Action::FinishReconciliation: 0800 case eMenu::Action::PostponeReconciliation: 0801 case eMenu::Action::CancelReconciliation: 0802 if (d->isActiveView() && !accountId.isEmpty()) { 0803 const auto reconciledAccountId = selections.firstSelection(SelectedObjects::ReconciliationAccount); 0804 d->openLedger(reconciledAccountId, true); 0805 auto view = qobject_cast<LedgerViewPage*>(d->ui->ledgerTab->currentWidget()); 0806 if (view) { 0807 if (view->executeAction(action, selections)) { 0808 closeLedger(d->ui->ledgerTab->currentIndex()); 0809 } 0810 } 0811 } 0812 break; 0813 0814 case eMenu::Action::MatchTransaction: 0815 d->propagateActionToViews(action, selections); 0816 break; 0817 0818 case eMenu::Action::FileNew: 0819 d->openLedgersAfterFileOpen(); 0820 break; 0821 0822 case eMenu::Action::FileClose: 0823 d->closeLedgers(); 0824 break; 0825 0826 case eMenu::Action::CloseAccount: 0827 // close the ledger in case it is shown 0828 closeLedger(d->tabIdByAccountId(accountId)); 0829 break; 0830 0831 default: 0832 break; 0833 } 0834 } 0835 0836 void SimpleLedgerView::sectionResized(QWidget* view, const QString& configGroupName, int section, int oldSize, int newSize) const 0837 { 0838 Q_EMIT const_cast<SimpleLedgerView*>(this)->resizeSection(view, configGroupName, section, oldSize, newSize); 0839 } 0840 0841 void SimpleLedgerView::sectionMoved(QWidget* view, int section, int oldIndex, int newIndex) const 0842 { 0843 Q_EMIT const_cast<SimpleLedgerView*>(this)->moveSection(view, section, oldIndex, newIndex); 0844 } 0845 0846 bool SimpleLedgerView::hasClosableView() const 0847 { 0848 Q_D(const SimpleLedgerView); 0849 return d->ui->ledgerTab->count() > 1; 0850 } 0851 0852 void SimpleLedgerView::closeCurrentView() 0853 { 0854 Q_D(SimpleLedgerView); 0855 const auto idx = d->ui->ledgerTab->currentIndex(); 0856 0857 // in case we're in reconciliation, we cannot simply close the 0858 // view but must use the finish/postpone reconciliation actions 0859 auto tab = d->ui->ledgerTab->widget(idx); 0860 auto view = qobject_cast<LedgerViewPage*>(tab); 0861 if (view) { 0862 if (view->hasPushedView()) { 0863 KMessageBox::information( 0864 this, 0865 i18n("You cannot close this view because you are reconciling the account. Finish, cancel or postpone the reconciliation first."), 0866 i18nc("@title:info In reconciliation", "View cannot be closed")); 0867 return; 0868 } 0869 } 0870 0871 // ok to close the view 0872 closeLedger(idx); 0873 }