File indexing completed on 2024-05-12 16:43:49

0001 /*
0002     SPDX-FileCopyrightText: 2000-2001 Michael Edwardes <mte@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2004 Thomas Baumgart <ipwizard@users.sourceforge.net>
0004     SPDX-FileCopyrightText: 2017, 2018 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include <config-kmymoney.h>
0009 #include "kmymoneyview.h"
0010 
0011 // ----------------------------------------------------------------------------
0012 // Std Includes
0013 
0014 #include <memory>
0015 
0016 // ----------------------------------------------------------------------------
0017 // QT Includes
0018 
0019 #include <QFile>
0020 #include <QRegExp>
0021 #include <QLayout>
0022 #include <QList>
0023 #include <QByteArray>
0024 #include <QUrl>
0025 #include <QIcon>
0026 #include <QTemporaryFile>
0027 #include <QUrlQuery>
0028 
0029 // ----------------------------------------------------------------------------
0030 // KDE Includes
0031 
0032 #include <KMessageBox>
0033 #include <KTitleWidget>
0034 #include <KSharedConfig>
0035 #include <KBackup>
0036 #include <KActionCollection>
0037 #include <KIO/StoredTransferJob>
0038 #include <KJobWidgets>
0039 #include <KLocalizedString>
0040 
0041 // ----------------------------------------------------------------------------
0042 // Project Includes
0043 
0044 #ifdef ENABLE_UNFINISHEDFEATURES
0045 #include "simpleledgerview.h"
0046 #endif
0047 
0048 #include "kmymoneysettings.h"
0049 #include "kmymoneytitlelabel.h"
0050 #include "kcurrencyeditdlg.h"
0051 #include "mymoneyexception.h"
0052 #include "khomeview.h"
0053 #include "kaccountsview.h"
0054 #include "kcategoriesview.h"
0055 #include "kinstitutionsview.h"
0056 #include "kpayeesview.h"
0057 #include "ktagsview.h"
0058 #include "kscheduledview.h"
0059 #include "kgloballedgerview.h"
0060 #include "kinvestmentview.h"
0061 #include "models.h"
0062 #include "accountsmodel.h"
0063 #include "equitiesmodel.h"
0064 #include "securitiesmodel.h"
0065 #include "icons.h"
0066 #include "onlinejobadministration.h"
0067 #include "kmymoneyaccounttreeview.h"
0068 #include "accountsviewproxymodel.h"
0069 #include "mymoneymoney.h"
0070 #include "mymoneyprice.h"
0071 #include "mymoneyschedule.h"
0072 #include "mymoneysplit.h"
0073 #include "mymoneyaccount.h"
0074 #include "mymoneyinstitution.h"
0075 #include "mymoneytag.h"
0076 #include "mymoneyfile.h"
0077 #include "mymoneysecurity.h"
0078 #include "mymoneyreport.h"
0079 #include "kmymoneyplugin.h"
0080 #include "mymoneyenums.h"
0081 #include "menuenums.h"
0082 
0083 using namespace Icons;
0084 using namespace eMyMoney;
0085 
0086 typedef void(KMyMoneyView::*KMyMoneyViewFunc)();
0087 
0088 KMyMoneyView::KMyMoneyView()
0089     : KPageWidget(nullptr),
0090       m_header(0)
0091 {
0092     // this is a workaround for the bug in KPageWidget that causes the header to be shown
0093     // for a short while during page switch which causes a kind of bouncing of the page's
0094     // content and if the page's content is at it's minimum size then during a page switch
0095     // the main window's size is also increased to fit the header that is shown for a sort
0096     // period - reading the code in kpagewidget.cpp we know that the header should be at (1,1)
0097     // in a grid layout so if we find it there remove it for good to avoid the described issues
0098     QGridLayout* gridLayout =  qobject_cast<QGridLayout*>(layout());
0099     if (gridLayout) {
0100         QLayoutItem* headerItem = gridLayout->itemAtPosition(1, 1);
0101         // make sure that we remove only the header - we avoid surprises if the header is not at (1,1) in the layout
0102         if (headerItem && qobject_cast<KTitleWidget*>(headerItem->widget()) != NULL) {
0103             gridLayout->removeItem(headerItem);
0104             // after we remove the KPageWidget standard header replace it with our own title label
0105             m_header = new KMyMoneyTitleLabel(this);
0106             m_header->setObjectName("titleLabel");
0107             m_header->setMinimumSize(QSize(100, 30));
0108             m_header->setRightImageFile("pics/titlelabel_background.png");
0109             m_header->setVisible(KMyMoneySettings::showTitleBar());
0110             gridLayout->addWidget(m_header, 1, 1);
0111         }
0112     }
0113 
0114 //  newStorage();
0115     m_model = new KPageWidgetModel(this); // cannot be parentless, otherwise segfaults at exit
0116 
0117     viewBases[View::Home] = new KHomeView;
0118     viewBases[View::Institutions] = new KInstitutionsView;
0119     viewBases[View::Accounts] = new KAccountsView;
0120     viewBases[View::Schedules] = new KScheduledView;
0121     viewBases[View::Categories] = new KCategoriesView;
0122     viewBases[View::Tags] = new KTagsView;
0123     viewBases[View::Payees] = new KPayeesView;
0124     viewBases[View::Ledgers] = new KGlobalLedgerView;
0125     viewBases[View::Investments] = new KInvestmentView;
0126 #ifdef ENABLE_UNFINISHEDFEATURES
0127     viewBases[View::NewLedgers] = new SimpleLedgerView;
0128 #endif
0129 
0130     struct viewInfo
0131     {
0132         View id;
0133         QString name;
0134         Icon icon;
0135     };
0136 
0137     const QVector<viewInfo> viewsInfo
0138     {
0139         {View::Home,            i18n("Home"),                         Icon::Home},
0140         {View::Institutions,    i18n("Institutions"),                 Icon::Institutions},
0141         {View::Accounts,        i18n("Accounts"),                     Icon::Accounts},
0142         {View::Schedules,       i18n("Scheduled\ntransactions"),      Icon::Schedule},
0143         {View::Categories,      i18n("Categories"),                   Icon::FinancialCategories},
0144         {View::Tags,            i18n("Tags"),                         Icon::Tags},
0145         {View::Payees,          i18n("Payees"),                       Icon::Payees},
0146         {View::Ledgers,         i18n("Ledgers"),                      Icon::Ledger},
0147         {View::Investments,     i18n("Investments"),                  Icon::Investments},
0148 #ifdef ENABLE_UNFINISHEDFEATURES
0149         {View::NewLedgers,      i18n("New ledger"),                   Icon::DocumentProperties},
0150 #endif
0151     };
0152 
0153     for (const viewInfo& view : viewsInfo) {
0154         addView(viewBases[view.id], view.name, view.id, view.icon);
0155     }
0156 
0157     connect(Models::instance()->accountsModel(), &AccountsModel::netWorthChanged, this, &KMyMoneyView::slotSelectByVariant);
0158     connect(Models::instance()->accountsModel(), &AccountsModel::profitChanged, this, &KMyMoneyView::slotSelectByVariant);
0159     connect(Models::instance()->institutionsModel(), &AccountsModel::netWorthChanged, this, &KMyMoneyView::slotSelectByVariant);
0160     connect(Models::instance()->institutionsModel(), &AccountsModel::profitChanged, this, &KMyMoneyView::slotSelectByVariant);
0161 
0162     // set the model
0163     setModel(m_model);
0164     setCurrentPage(viewFrames[View::Home]);
0165     connect(this, SIGNAL(currentPageChanged(QModelIndex,QModelIndex)), this, SLOT(slotCurrentPageChanged(QModelIndex,QModelIndex)));
0166 
0167     updateViewType();
0168 }
0169 
0170 KMyMoneyView::~KMyMoneyView()
0171 {
0172 }
0173 
0174 void KMyMoneyView::slotFileOpened()
0175 {
0176     if (viewBases.contains(View::OnlineJobOutbox))
0177         viewBases[View::OnlineJobOutbox]->executeCustomAction(eView::Action::InitializeAfterFileOpen);
0178 
0179     if (viewBases.contains(View::Ledgers))
0180         viewBases[View::Ledgers]->executeCustomAction(eView::Action::InitializeAfterFileOpen);
0181 
0182 #ifdef ENABLE_UNFINISHEDFEATURES
0183     static_cast<SimpleLedgerView*>(viewBases[View::NewLedgers])->openFavoriteLedgers();
0184 #endif
0185     // delay the switchToDefaultView call until the event loop is running
0186     QMetaObject::invokeMethod(this, "switchToDefaultView", Qt::QueuedConnection);
0187     slotObjectSelected(MyMoneyAccount()); // in order to enable update all accounts on file reload
0188 }
0189 
0190 void KMyMoneyView::slotFileClosed()
0191 {
0192     slotShowHomePage();
0193     if (viewBases.contains(View::Home))
0194         viewBases[View::Home]->executeCustomAction(eView::Action::CleanupBeforeFileClose);
0195 
0196     if (viewBases.contains(View::Reports))
0197         viewBases[View::Reports]->executeCustomAction(eView::Action::CleanupBeforeFileClose);
0198 
0199     if (viewBases.contains(View::OnlineJobOutbox))
0200         viewBases[View::OnlineJobOutbox]->executeCustomAction(eView::Action::CleanupBeforeFileClose);
0201 
0202     if (viewBases.contains(View::Ledgers))
0203         viewBases[View::Ledgers]->executeCustomAction(eView::Action::CleanupBeforeFileClose);
0204 
0205 #ifdef ENABLE_UNFINISHEDFEATURES
0206     static_cast<SimpleLedgerView*>(viewBases[View::NewLedgers])->closeLedgers();
0207 #endif
0208 
0209     pActions[eMenu::Action::Print]->setEnabled(false);
0210     pActions[eMenu::Action::AccountCreditTransfer]->setEnabled(false);
0211     pActions[eMenu::Action::UpdateAllAccounts]->setEnabled(false);
0212 }
0213 
0214 void KMyMoneyView::slotShowHomePage()
0215 {
0216     showPageAndFocus(View::Home);
0217 }
0218 
0219 void KMyMoneyView::slotShowInstitutionsPage()
0220 {
0221     showPageAndFocus(View::Institutions);
0222 }
0223 
0224 void KMyMoneyView::slotShowAccountsPage()
0225 {
0226     showPageAndFocus(View::Accounts);
0227 }
0228 
0229 void KMyMoneyView::slotShowSchedulesPage()
0230 {
0231     showPageAndFocus(View::Schedules);
0232 }
0233 
0234 void KMyMoneyView::slotShowCategoriesPage()
0235 {
0236     showPageAndFocus(View::Categories);
0237 }
0238 
0239 void KMyMoneyView::slotShowTagsPage()
0240 {
0241     showPageAndFocus(View::Tags);
0242 }
0243 
0244 void KMyMoneyView::slotShowPayeesPage()
0245 {
0246     showPageAndFocus(View::Payees);
0247 }
0248 
0249 void KMyMoneyView::slotShowLedgersPage()
0250 {
0251     showPageAndFocus(View::Ledgers);
0252 }
0253 
0254 void KMyMoneyView::slotShowInvestmentsPage()
0255 {
0256     showPageAndFocus(View::Investments);
0257 }
0258 
0259 void KMyMoneyView::slotShowReportsPage()
0260 {
0261     showPageAndFocus(View::Reports);
0262 }
0263 
0264 void KMyMoneyView::slotShowBudgetPage()
0265 {
0266     showPageAndFocus(View::Budget);
0267 }
0268 
0269 void KMyMoneyView::slotShowForecastPage()
0270 {
0271     showPageAndFocus(View::Forecast);
0272 }
0273 
0274 void KMyMoneyView::slotShowOutboxPage()
0275 {
0276     showPageAndFocus(View::OnlineJobOutbox);
0277 }
0278 
0279 void KMyMoneyView::showTitleBar(bool show)
0280 {
0281     if (m_header)
0282         m_header->setVisible(show);
0283 }
0284 
0285 void KMyMoneyView::updateViewType()
0286 {
0287     // set the face type
0288     KPageView::FaceType faceType = KPageView::List;
0289     switch (KMyMoneySettings::viewType()) {
0290     case 0:
0291         faceType = KPageView::List;
0292         break;
0293     case 1:
0294         faceType = KPageView::Tree;
0295         break;
0296     case 2:
0297         faceType = KPageView::Tabbed;
0298         break;
0299     }
0300     if (faceType != KMyMoneyView::faceType()) {
0301         setFaceType(faceType);
0302         if (faceType == KPageView::Tree) {
0303             QList<QTreeView *> views = findChildren<QTreeView*>();
0304             foreach (QTreeView * view, views) {
0305                 if (view && (view->parent() == this)) {
0306                     view->setRootIsDecorated(false);
0307                     break;
0308                 }
0309             }
0310         }
0311     }
0312 }
0313 
0314 void KMyMoneyView::slotAccountTreeViewChanged(const eAccountsModel::Column column, const bool show)
0315 {
0316     QVector<AccountsViewProxyModel *> proxyModels
0317     {
0318         static_cast<KMyMoneyAccountsViewBase*>(viewBases[View::Institutions])->getProxyModel(),
0319         static_cast<KMyMoneyAccountsViewBase*>(viewBases[View::Accounts])->getProxyModel(),
0320         static_cast<KMyMoneyAccountsViewBase*>(viewBases[View::Categories])->getProxyModel()
0321     };
0322 
0323     if (viewBases.contains(View::Budget))
0324         proxyModels.append(static_cast<KMyMoneyAccountsViewBase*>(viewBases[View::Budget])->getProxyModel());
0325 
0326     for (auto i = proxyModels.count() - 1; i >= 0; --i) { // weed out unloaded views
0327         if (!proxyModels.at(i))
0328             proxyModels.removeAt(i);
0329     }
0330 
0331     QString question;
0332 
0333     if (show)
0334         question = i18n("Do you want to show <b>%1</b> column on every loaded view?", AccountsModel::getHeaderName(column));
0335     else
0336         question = i18n("Do you want to hide <b>%1</b> column on every loaded view?", AccountsModel::getHeaderName(column));
0337 
0338 
0339     if (proxyModels.count() == 1 || // no need to ask what to do with other views because they aren't loaded
0340             KMessageBox::questionYesNo(this,
0341                                        question,
0342                                        QString(),
0343                                        KStandardGuiItem::yes(), KStandardGuiItem::no(),
0344                                        QStringLiteral("ShowColumnOnEveryView")) == KMessageBox::Yes) {
0345         Models::instance()->accountsModel()->setColumnVisibility(column, show);
0346         Models::instance()->institutionsModel()->setColumnVisibility(column, show);
0347         foreach(AccountsViewProxyModel *proxyModel, proxyModels) {
0348             if (!proxyModel)
0349                 continue;
0350             proxyModel->setColumnVisibility(column, show);
0351             proxyModel->invalidate();
0352         }
0353     } else if(show) {
0354         // in case we need to show it, we have to make sure to set the visibility
0355         // in the base model as well. Otherwise, we don't see the column through the proxy model
0356         Models::instance()->accountsModel()->setColumnVisibility(column, show);
0357         Models::instance()->institutionsModel()->setColumnVisibility(column, show);
0358     }
0359 }
0360 
0361 void KMyMoneyView::setOnlinePlugins(QMap<QString, KMyMoneyPlugin::OnlinePlugin*>& plugins)
0362 {
0363     if (viewBases.contains(View::Accounts))
0364         viewBases[View::Accounts]->slotSelectByVariant(QVariantList {QVariant::fromValue(static_cast<void*>(&plugins))}, eView::Intent::SetOnlinePlugins);
0365 
0366     if (viewBases.contains(View::OnlineJobOutbox))
0367         viewBases[View::OnlineJobOutbox]->slotSelectByVariant(QVariantList {QVariant::fromValue(static_cast<void*>(&plugins))}, eView::Intent::SetOnlinePlugins);
0368 }
0369 
0370 eDialogs::ScheduleResultCode KMyMoneyView::enterSchedule(MyMoneySchedule& schedule, bool autoEnter, bool extendedKeys)
0371 {
0372     return static_cast<KScheduledView*>(viewBases[View::Schedules])->enterSchedule(schedule, autoEnter, extendedKeys);
0373 }
0374 
0375 void KMyMoneyView::addView(KMyMoneyViewBase* view, const QString& name, View idView, Icons::Icon icon)
0376 {
0377     /* There is a bug in
0378      *    static int layoutText(QTextLayout *layout, int maxWidth)
0379      *    from kpageview_p.cpp from kwidgetsaddons.
0380      *    The method doesn't break strings that are too long. Following line
0381      *    workarounds this by using LINE SEPARATOR character which is accepted by
0382      *    QTextLayout::createLine().*/
0383     auto adjustedName(name);
0384     adjustedName.replace(QLatin1Char('\n'), QString::fromUtf8("\xe2\x80\xa8"));
0385 
0386     auto isViewInserted = false;
0387     for (auto i = (int)idView; i < (int)View::None; ++i) {
0388         if (viewFrames.contains((View)i)) {
0389             viewFrames[idView] = m_model->insertPage(viewFrames[(View)i],view, adjustedName);
0390             isViewInserted = true;
0391             break;
0392         }
0393     }
0394 
0395     if (!isViewInserted)
0396         viewFrames[idView] = m_model->addPage(view, adjustedName);
0397 
0398     viewFrames[idView]->setIcon(Icons::get(icon));
0399     viewBases[idView] = view;
0400     connect(viewBases[idView], &KMyMoneyViewBase::selectByObject, this, &KMyMoneyView::slotSelectByObject);
0401     connect(viewBases[idView], &KMyMoneyViewBase::selectByVariant, this, &KMyMoneyView::slotSelectByVariant);
0402     connect(viewBases[idView], &KMyMoneyViewBase::customActionRequested, this, &KMyMoneyView::slotCustomActionRequested);
0403 }
0404 
0405 void KMyMoneyView::removeView(View idView)
0406 {
0407     if (!viewBases.contains(idView))
0408         return;
0409 
0410     disconnect(viewBases[idView], &KMyMoneyViewBase::selectByObject, this, &KMyMoneyView::slotSelectByObject);
0411     disconnect(viewBases[idView], &KMyMoneyViewBase::selectByVariant, this, &KMyMoneyView::slotSelectByVariant);
0412     disconnect(viewBases[idView], &KMyMoneyViewBase::customActionRequested, this, &KMyMoneyView::slotCustomActionRequested);
0413 
0414     m_model->removePage(viewFrames[idView]);
0415     viewFrames.remove(idView);
0416     viewBases.remove(idView);
0417 }
0418 
0419 QHash<eMenu::Action, QAction *> KMyMoneyView::actionsToBeConnected()
0420 {
0421     using namespace eMenu;
0422     // add fast switching of main views through Ctrl + NUM_X
0423     struct pageInfo {
0424         Action           view;
0425         KMyMoneyViewFunc callback;
0426         QString          text;
0427         QKeySequence     shortcut = QKeySequence();
0428     };
0429 
0430     const QVector<pageInfo> pageInfos {
0431         {Action::ShowHomeView,            &KMyMoneyView::slotShowHomePage,          i18n("Show home page"),                   Qt::CTRL + Qt::Key_1},
0432         {Action::ShowInstitutionsView,    &KMyMoneyView::slotShowInstitutionsPage,  i18n("Show institutions page"),           Qt::CTRL + Qt::Key_2},
0433         {Action::ShowAccountsView,        &KMyMoneyView::slotShowAccountsPage,      i18n("Show accounts page"),               Qt::CTRL + Qt::Key_3},
0434         {Action::ShowSchedulesView,       &KMyMoneyView::slotShowSchedulesPage,     i18n("Show scheduled transactions page"), Qt::CTRL + Qt::Key_4},
0435         {Action::ShowCategoriesView,      &KMyMoneyView::slotShowCategoriesPage,    i18n("Show categories page"),             Qt::CTRL + Qt::Key_5},
0436         {Action::ShowTagsView,            &KMyMoneyView::slotShowTagsPage,          i18n("Show tags page"),                   },
0437         {Action::ShowPayeesView,          &KMyMoneyView::slotShowPayeesPage,        i18n("Show payees page"),                 Qt::CTRL + Qt::Key_6},
0438         {Action::ShowLedgersView,         &KMyMoneyView::slotShowLedgersPage,       i18n("Show ledgers page"),                Qt::CTRL + Qt::Key_7},
0439         {Action::ShowInvestmentsView,     &KMyMoneyView::slotShowInvestmentsPage,   i18n("Show investments page"),            Qt::CTRL + Qt::Key_8},
0440         {Action::ShowReportsView,         &KMyMoneyView::slotShowReportsPage,       i18n("Show reports page"),                Qt::CTRL + Qt::Key_9},
0441         {Action::ShowBudgetView,          &KMyMoneyView::slotShowBudgetPage,        i18n("Show budget page"),                },
0442         {Action::ShowForecastView,        &KMyMoneyView::slotShowForecastPage,      i18n("Show forecast page"),              },
0443         {Action::ShowOnlineJobOutboxView, &KMyMoneyView::slotShowOutboxPage,        i18n("Show outbox page")                 },
0444     };
0445 
0446     QHash<Action, QAction *> lutActions;
0447     auto pageCount = 0;
0448     for (const pageInfo& info : pageInfos) {
0449         auto a = new QAction(this);
0450         // KActionCollection::addAction by name sets object name anyways,
0451         // so, as better alternative, set it here right from the start
0452         a->setObjectName(QString::fromLatin1("ShowPage%1").arg(QString::number(pageCount++)));
0453         a->setText(info.text);
0454         connect(a, &QAction::triggered, this, info.callback);
0455         lutActions.insert(info.view, a);  // store QAction's pointer for later processing
0456         if (!info.shortcut.isEmpty())
0457             a->setShortcut(info.shortcut);
0458     }
0459     return lutActions;
0460 }
0461 
0462 bool KMyMoneyView::showPageHeader() const
0463 {
0464     return false;
0465 }
0466 
0467 void KMyMoneyView::showPageAndFocus(View idView)
0468 {
0469     if (viewFrames.contains(idView)) {
0470         showPage(idView);
0471         viewBases[idView]->executeCustomAction(eView::Action::SetDefaultFocus);
0472     }
0473 }
0474 
0475 void KMyMoneyView::showPage(View idView)
0476 {
0477     if (!viewFrames.contains(idView) ||
0478             currentPage() == viewFrames[idView])
0479         return;
0480 
0481     resetViewSelection();
0482     setCurrentPage(viewFrames[idView]);
0483 }
0484 
0485 bool KMyMoneyView::canPrint()
0486 {
0487     return (MyMoneyFile::instance()->storageAttached() &&
0488             ((viewFrames.contains(View::Reports) && viewFrames[View::Reports] == currentPage()) ||
0489              (viewFrames.contains(View::Home) && viewFrames[View::Home] == currentPage()))
0490            );
0491 }
0492 
0493 void KMyMoneyView::enableViewsIfFileOpen(bool fileOpen)
0494 {
0495     // call set enabled only if the state differs to avoid widgets 'bouncing on the screen' while doing this
0496     Q_ASSERT_X(((int)(View::Home)+1) == (int)View::Institutions, "viewenums.h", "View::Home must be first and View::Institutions second entry");
0497 
0498     for (auto i = (int)View::Institutions; i < (int)View::None; ++i)
0499         if (viewFrames.contains(View(i)))
0500             if (viewFrames[View(i)]->isEnabled() != fileOpen)
0501                 viewFrames[View(i)]->setEnabled(fileOpen);
0502 
0503     emit viewStateChanged(fileOpen);
0504 }
0505 
0506 void KMyMoneyView::switchToDefaultView()
0507 {
0508     const auto idView = KMyMoneySettings::startLastViewSelected() ?
0509                         static_cast<View>(KMyMoneySettings::lastViewSelected()) :
0510                         View::Home;
0511     // if we currently see a different page, then select the right one
0512     if (viewFrames.contains(idView) && viewFrames[idView] != currentPage())
0513         showPage(idView);
0514 }
0515 
0516 void KMyMoneyView::slotPayeeSelected(const QString& payee, const QString& account, const QString& transaction)
0517 {
0518     showPage(View::Payees);
0519     static_cast<KPayeesView*>(viewBases[View::Payees])->slotSelectPayeeAndTransaction(payee, account, transaction);
0520 }
0521 
0522 void KMyMoneyView::slotTagSelected(const QString& tag, const QString& account, const QString& transaction)
0523 {
0524     showPage(View::Tags);
0525     static_cast<KTagsView*>(viewBases[View::Tags])->slotSelectTagAndTransaction(tag, account, transaction);
0526 }
0527 
0528 void KMyMoneyView::finishReconciliation(const MyMoneyAccount& /* account */)
0529 {
0530     Models::instance()->accountsModel()->slotReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
0531     static_cast<KGlobalLedgerView*>(viewBases[View::Ledgers])->slotSetReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
0532 }
0533 
0534 void KMyMoneyView::viewAccountList(const QString& /*selectAccount*/)
0535 {
0536     if (viewFrames[View::Accounts] != currentPage())
0537         showPage(View::Accounts);
0538     viewBases[View::Accounts]->show();
0539 }
0540 
0541 void KMyMoneyView::slotRefreshViews()
0542 {
0543     showTitleBar(KMyMoneySettings::showTitleBar());
0544 
0545     for (auto i = (int)View::Home; i < (int)View::None; ++i)
0546         if (viewBases.contains(View(i)))
0547             viewBases[View(i)]->executeCustomAction(eView::Action::Refresh);
0548 
0549     viewBases[View::Payees]->executeCustomAction(eView::Action::ClosePayeeIdentifierSource);
0550 }
0551 
0552 void KMyMoneyView::slotShowTransactionDetail(bool detailed)
0553 {
0554     KMyMoneySettings::setShowRegisterDetailed(detailed);
0555     slotRefreshViews();
0556 }
0557 
0558 void KMyMoneyView::slotCurrentPageChanged(const QModelIndex current, const QModelIndex previous)
0559 {
0560     // set the current page's title in the header
0561     if (m_header)
0562         m_header->setText(m_model->data(current, KPageModel::HeaderRole).toString());
0563 
0564     const auto view = currentPage();
0565     // remember the selected view if there is a real change
0566     if (previous.isValid()) {
0567         QHash<View, KPageWidgetItem*>::const_iterator it;
0568         for(it = viewFrames.cbegin(); it != viewFrames.cend(); ++it) {
0569             if ((*it) == view) {
0570                 emit viewActivated(it.key());
0571                 break;
0572             }
0573         }
0574     }
0575 
0576     if (viewBases.contains(View::Ledgers) && view != viewFrames.value(View::Ledgers))
0577         viewBases[View::Ledgers]->executeCustomAction(eView::Action::DisableViewDepenedendActions);
0578 
0579     pActions[eMenu::Action::Print]->setEnabled(canPrint());
0580     pActions[eMenu::Action::AccountCreditTransfer]->setEnabled(onlineJobAdministration::instance()->canSendCreditTransfer());
0581 }
0582 
0583 void KMyMoneyView::createSchedule(MyMoneySchedule newSchedule, MyMoneyAccount& newAccount)
0584 {
0585     // Add the schedule only if one exists
0586     //
0587     // Remember to modify the first split to reference the newly created account
0588     if (!newSchedule.name().isEmpty()) {
0589         MyMoneyFileTransaction ft;
0590         try {
0591             // We assume at least 2 splits in the transaction
0592             MyMoneyTransaction t = newSchedule.transaction();
0593             if (t.splitCount() < 2) {
0594                 throw MYMONEYEXCEPTION_CSTRING("Transaction for schedule has less than 2 splits!");
0595             }
0596             // now search the split that does not have an account reference
0597             // and set it up to be the one of the account we just added
0598             // to the account pool. Note: the schedule code used to leave
0599             // this always the first split, but the loan code leaves it as
0600             // the second one. So I thought, searching is a good alternative ....
0601             foreach (const auto split, t.splits()) {
0602                 if (split.accountId().isEmpty()) {
0603                     MyMoneySplit s = split;
0604                     s.setAccountId(newAccount.id());
0605                     t.modifySplit(s);
0606                     break;
0607                 }
0608             }
0609             newSchedule.setTransaction(t);
0610 
0611             MyMoneyFile::instance()->addSchedule(newSchedule);
0612 
0613             // in case of a loan account, we keep a reference to this
0614             // schedule in the account
0615             if (newAccount.isLoan()) {
0616                 newAccount.setValue("schedule", newSchedule.id());
0617                 MyMoneyFile::instance()->modifyAccount(newAccount);
0618             }
0619             ft.commit();
0620         } catch (const MyMoneyException &e) {
0621             KMessageBox::information(this, i18n("Unable to add schedule: %1", QString::fromLatin1(e.what())));
0622         }
0623     }
0624 }
0625 
0626 void KMyMoneyView::slotPrintView()
0627 {
0628     if (viewFrames.contains(View::Reports) && viewFrames[View::Reports] == currentPage())
0629         viewBases[View::Reports]->executeCustomAction(eView::Action::Print);
0630     else if (viewFrames.contains(View::Home) && viewFrames[View::Home] == currentPage())
0631         viewBases[View::Home]->executeCustomAction(eView::Action::Print);
0632 }
0633 
0634 void KMyMoneyView::resetViewSelection()
0635 {
0636     if (!MyMoneyFile::instance()->storageAttached())
0637         return;
0638     slotObjectSelected(MyMoneyAccount());
0639     slotObjectSelected(MyMoneyInstitution());
0640     slotObjectSelected(MyMoneySchedule());
0641     slotObjectSelected(MyMoneyTag());
0642     slotSelectByVariant(QVariantList {QVariant::fromValue(KMyMoneyRegister::SelectedTransactions())}, eView::Intent::SelectRegisterTransactions);
0643 }
0644 
0645 void KMyMoneyView::slotOpenObjectRequested(const MyMoneyObject& obj)
0646 {
0647     if (typeid(obj) == typeid(MyMoneyAccount)) {
0648         const auto& acc = static_cast<const MyMoneyAccount&>(obj);
0649         // check if we can open this account
0650         // currently it make's sense for asset and liability accounts
0651         if (!MyMoneyFile::instance()->isStandardAccount(acc.id()))
0652             if (viewBases.contains(View::Ledgers))
0653                 viewBases[View::Ledgers]->slotSelectByVariant(QVariantList {QVariant(acc.id()), QVariant(QString()) }, eView::Intent::ShowTransaction );
0654 
0655     } else if (typeid(obj) == typeid(MyMoneyInstitution)) {
0656 //    const auto& inst = static_cast<const MyMoneyInstitution&>(obj);
0657         if (viewBases.contains(View::Institutions))
0658             viewBases[View::Institutions]->executeCustomAction(eView::Action::EditInstitution);
0659     } else if (typeid(obj) == typeid(MyMoneySchedule)) {
0660         if (viewBases.contains(View::Schedules))
0661             viewBases[View::Schedules]->executeCustomAction(eView::Action::EditSchedule);
0662     } else if (typeid(obj) == typeid(MyMoneyReport)) {
0663 //    const auto& rep = static_cast<const MyMoneyReport&>(obj);
0664         showPage(View::Reports);
0665         if (viewBases.contains(View::Reports))
0666             viewBases[View::Reports]->slotSelectByObject(obj, eView::Intent::OpenObject);
0667     }
0668 }
0669 
0670 void KMyMoneyView::slotSelectByObject(const MyMoneyObject& obj, eView::Intent intent)
0671 {
0672     switch (intent) {
0673     case eView::Intent::None:
0674         slotObjectSelected(obj);
0675         break;
0676 
0677     case eView::Intent::SynchronizeAccountInInvestmentView:
0678         if (viewBases.contains(View::Investments))
0679             viewBases[View::Investments]->slotSelectByObject(obj, intent);
0680         break;
0681 
0682     case eView::Intent::SynchronizeAccountInLedgersView:
0683         if (viewBases.contains(View::Ledgers))
0684             viewBases[View::Ledgers]->slotSelectByObject(obj, intent);
0685         break;
0686 
0687     case eView::Intent::OpenObject:
0688         slotOpenObjectRequested(obj);
0689         break;
0690 
0691     case eView::Intent::OpenContextMenu:
0692         slotContextMenuRequested(obj);
0693         break;
0694 
0695     case eView::Intent::StartEnteringOverdueScheduledTransactions:
0696         if (viewBases.contains(View::Schedules))
0697             viewBases[View::Schedules]->slotSelectByObject(obj, intent);
0698         break;
0699 
0700     case eView::Intent::FinishEnteringOverdueScheduledTransactions:
0701         if (viewBases.contains(View::Ledgers)) {
0702             showPage(View::Ledgers);
0703             viewBases[View::Ledgers]->slotSelectByObject(obj, intent);
0704         }
0705         break;
0706 
0707     default:
0708         break;
0709     }
0710 }
0711 
0712 void KMyMoneyView::slotSelectByVariant(const QVariantList& variant, eView::Intent intent)
0713 {
0714     switch(intent) {
0715     case eView::Intent::ReportProgress:
0716         if (variant.count() == 2)
0717             emit statusProgress(variant.at(0).toInt(), variant.at(1).toInt());
0718         break;
0719 
0720     case eView::Intent::ReportProgressMessage:
0721         if (variant.count() == 1)
0722             emit statusMsg(variant.first().toString());
0723         break;
0724 
0725     case eView::Intent::UpdateNetWorth:
0726         if (viewBases.contains(View::Accounts))
0727             viewBases[View::Accounts]->slotSelectByVariant(variant, intent);
0728 
0729         if (viewBases.contains(View::Institutions))
0730             viewBases[View::Institutions]->slotSelectByVariant(variant, intent);
0731         break;
0732 
0733     case eView::Intent::UpdateProfit:
0734         if (viewBases.contains(View::Categories))
0735             viewBases[View::Categories]->slotSelectByVariant(variant, intent);
0736         break;
0737 
0738     case eView::Intent::ShowTransaction:
0739         if (viewBases.contains(View::Ledgers)) {
0740             showPage(View::Ledgers);
0741             viewBases[View::Ledgers]->slotSelectByVariant(variant, intent);
0742         }
0743         break;
0744 
0745     case eView::Intent::ToggleColumn:
0746         if (variant.count() == 2)
0747             slotAccountTreeViewChanged(variant.at(0).value<eAccountsModel::Column>(), variant.at(1).value<bool>());
0748         break;
0749 
0750     case eView::Intent::ShowPayee:
0751         if (viewBases.contains(View::Payees)) {
0752             showPage(View::Payees);
0753             viewBases[View::Payees]->slotSelectByVariant(variant, intent);
0754         }
0755         break;
0756 
0757     case eView::Intent::SelectRegisterTransactions:
0758         if (variant.count() == 1) {
0759             emit transactionsSelected(variant.at(0).value<KMyMoneyRegister::SelectedTransactions>()); // for plugins
0760             if (viewBases.contains(View::Ledgers))
0761                 viewBases[View::Ledgers]->slotSelectByVariant(variant, intent);
0762         }
0763         break;
0764 
0765     case eView::Intent::AccountReconciled:
0766         if (variant.count() == 5)
0767             emit accountReconciled(variant.at(0).value<MyMoneyAccount>(),
0768                                    variant.at(1).value<QDate>(),
0769                                    variant.at(2).value<MyMoneyMoney>(),
0770                                    variant.at(3).value<MyMoneyMoney>(),
0771                                    variant.at(4).value<QList<QPair<MyMoneyTransaction, MyMoneySplit>>>()); // for plugins
0772         break;
0773 
0774     default:
0775         break;
0776     }
0777 }
0778 
0779 void KMyMoneyView::slotCustomActionRequested(View view, eView::Action action)
0780 {
0781     switch (action) {
0782     case eView::Action::AboutToShow:
0783         resetViewSelection();
0784         break;
0785     case eView::Action::SwitchView:
0786         showPage(view);
0787         break;
0788     case eView::Action::ShowBalanceChart:
0789         if (viewBases.contains(View::Reports))
0790             viewBases[View::Reports]->executeCustomAction(action);
0791         break;
0792     default:
0793         break;
0794     }
0795 }
0796 
0797 void KMyMoneyView::slotObjectSelected(const MyMoneyObject& obj)
0798 {
0799     // carrying some slots over to views isn't easy for all slots...
0800     // ...so calls to kmymoney still must be here
0801     if (typeid(obj) == typeid(MyMoneyAccount)) {
0802         QVector<View> views {View::Investments, View::Categories, View::Accounts,
0803                              View::Ledgers, View::Reports, View::OnlineJobOutbox};
0804         for (const auto view : views)
0805             if (viewBases.contains(view))
0806                 viewBases[view]->slotSelectByObject(obj, eView::Intent::UpdateActions);
0807 
0808         // for plugin only
0809         const auto& acc = static_cast<const MyMoneyAccount&>(obj);
0810         if (!acc.isIncomeExpense() &&
0811                 !MyMoneyFile::instance()->isStandardAccount(acc.id()))
0812             emit accountSelected(acc);
0813     } else if (typeid(obj) == typeid(MyMoneyInstitution)) {
0814         viewBases[View::Institutions]->slotSelectByObject(obj, eView::Intent::UpdateActions);
0815     } else if (typeid(obj) == typeid(MyMoneySchedule)) {
0816         viewBases[View::Schedules]->slotSelectByObject(obj, eView::Intent::UpdateActions);
0817     }
0818 }
0819 
0820 void KMyMoneyView::slotContextMenuRequested(const MyMoneyObject& obj)
0821 {
0822     if (typeid(obj) == typeid(MyMoneyAccount)) {
0823         const auto& acc = static_cast<const MyMoneyAccount&>(obj);
0824         if (acc.isInvest())
0825             viewBases[View::Investments]->slotSelectByObject(obj, eView::Intent::OpenContextMenu);
0826         else if (acc.isIncomeExpense())
0827             viewBases[View::Categories]->slotSelectByObject(obj, eView::Intent::OpenContextMenu);
0828         else
0829             viewBases[View::Accounts]->slotSelectByObject(obj, eView::Intent::OpenContextMenu);
0830 
0831     } else if (typeid(obj) == typeid(MyMoneyInstitution)) {
0832         viewBases[View::Institutions]->slotSelectByObject(obj, eView::Intent::OpenContextMenu);
0833     } else if (typeid(obj) == typeid(MyMoneySchedule)) {
0834         viewBases[View::Schedules]->slotSelectByObject(obj, eView::Intent::OpenContextMenu);
0835     }
0836 }