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 }