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

0001 /*
0002     SPDX-FileCopyrightText: 2006 Thomas Baumgart <ipwizard@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2017 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #ifndef KGLOBALLEDGERVIEW_P_H
0008 #define KGLOBALLEDGERVIEW_P_H
0009 
0010 #include "kgloballedgerview.h"
0011 
0012 // ----------------------------------------------------------------------------
0013 // QT Includes
0014 
0015 #include <QFrame>
0016 #include <QHBoxLayout>
0017 #include <QList>
0018 #include <QLabel>
0019 #include <QEvent>
0020 #include <QVBoxLayout>
0021 #include <QHeaderView>
0022 #include <QToolTip>
0023 #include <QMenu>
0024 #include <QWidgetAction>
0025 
0026 // ----------------------------------------------------------------------------
0027 // KDE Includes
0028 
0029 #include <KLocalizedString>
0030 #include <KMessageBox>
0031 #include <KToolBar>
0032 #include <KPassivePopup>
0033 
0034 // ----------------------------------------------------------------------------
0035 // Project Includes
0036 
0037 #include "accountsmodel.h"
0038 #include "fancydategroupmarkers.h"
0039 #include "icons.h"
0040 #include "kbalancewarning.h"
0041 #include "kendingbalancedlg.h"
0042 #include "kfindtransactiondlg.h"
0043 #include "kmergetransactionsdlg.h"
0044 #include "kmymoneyaccountcombo.h"
0045 #include "kmymoneyaccountselector.h"
0046 #include "kmymoneysettings.h"
0047 #include "kmymoneyutils.h"
0048 #include "kmymoneyviewbase_p.h"
0049 #include "menuenums.h"
0050 #include "modelenums.h"
0051 #include "models.h"
0052 #include "mymoneyaccount.h"
0053 #include "mymoneyenums.h"
0054 #include "mymoneyexception.h"
0055 #include "mymoneyfile.h"
0056 #include "mymoneymoney.h"
0057 #include "mymoneypayee.h"
0058 #include "mymoneyprice.h"
0059 #include "mymoneyschedule.h"
0060 #include "mymoneysecurity.h"
0061 #include "mymoneysplit.h"
0062 #include "mymoneytracer.h"
0063 #include "mymoneytransaction.h"
0064 #include "mymoneytransactionfilter.h"
0065 #include "register.h"
0066 #include "registersearchline.h"
0067 #include "scheduledtransaction.h"
0068 #include "selectedtransactions.h"
0069 #include "tabbar.h"
0070 #include "transaction.h"
0071 #include "transactioneditor.h"
0072 #include "transactionform.h"
0073 #include "transactionmatcher.h"
0074 #include "widgetenums.h"
0075 
0076 #include <config-kmymoney.h>
0077 #ifdef KMM_DEBUG
0078 #include "mymoneyutils.h"
0079 #endif
0080 
0081 using namespace eMenu;
0082 using namespace eMyMoney;
0083 
0084 /**
0085   * helper class implementing an event filter to detect mouse button press
0086   * events on widgets outside a given set of widgets. This is used internally
0087   * to detect when to leave the edit mode.
0088   */
0089 class MousePressFilter : public QObject
0090 {
0091     Q_OBJECT
0092 public:
0093     explicit MousePressFilter(QWidget* parent = nullptr) :
0094         QObject(parent),
0095         m_lastMousePressEvent(0),
0096         m_filterActive(true)
0097     {
0098     }
0099 
0100     /**
0101       * Add widget @p w to the list of possible parent objects. See eventFilter() how
0102       * they will be used.
0103       */
0104     void addWidget(QWidget* w)
0105     {
0106         m_parents.append(w);
0107     }
0108 
0109 public Q_SLOTS:
0110     /**
0111       * This slot allows to activate/deactivate the filter. By default the
0112       * filter is active.
0113       *
0114       * @param state Allows to activate (@a true) or deactivate (@a false) the filter
0115       */
0116     void setFilterActive(bool state = true)
0117     {
0118         m_filterActive = state;
0119     }
0120 
0121     /**
0122       * This slot allows to activate/deactivate the filter. By default the
0123       * filter is active.
0124       *
0125       * @param state Allows to deactivate (@a true) or activate (@a false) the filter
0126       */
0127     void setFilterDeactive(bool state = false) {
0128         setFilterActive(!state);
0129     }
0130 
0131 protected:
0132     /**
0133       * This method checks if the widget @p child is a child of
0134       * the widget @p parent and returns either @a true or @a false.
0135       *
0136       * @param child pointer to child widget
0137       * @param parent pointer to parent widget
0138       * @retval true @p child points to widget which has @p parent as parent or grand-parent
0139       * @retval false @p child points to a widget which is not related to @p parent
0140       */
0141     bool isChildOf(QWidget* child, QWidget* parent)
0142     {
0143         // QDialogs cannot be detected directly, but it can be assumed,
0144         // that events on a widget that do not have a parent widget within
0145         // our application are dialogs.
0146         if (!child->parentWidget())
0147             return true;
0148 
0149         while (child) {
0150             // if we are a child of the given parent, we have a match
0151             if (child == parent)
0152                 return true;
0153             // if we are at the application level, we don't have a match
0154             if (child->inherits("KMyMoneyApp"))
0155                 return false;
0156             // If one of the ancestors is a KPassivePopup or a KDialog or a popup widget then
0157             // it's as if it is a child of our own because these widgets could
0158             // appear during transaction entry (message boxes, completer widgets)
0159             if (dynamic_cast<KPassivePopup*>(child) ||
0160                     ((child->windowFlags() & Qt::Popup) && /*child != kmymoney*/
0161                      !child->parentWidget())) // has no parent, then it must be top-level window
0162                 return true;
0163             child = child->parentWidget();
0164         }
0165         return false;
0166     }
0167 
0168     /**
0169       * Reimplemented from base class. Sends out the mousePressedOnExternalWidget() signal
0170       * if object @p o points to an object which is not a child widget of any added previously
0171       * using the addWidget() method. The signal is sent out only once for each event @p e.
0172       *
0173       * @param o pointer to QObject
0174       * @param e pointer to QEvent
0175       * @return always returns @a false
0176       */
0177     bool eventFilter(QObject* o, QEvent* e) final override
0178     {
0179         if (m_filterActive) {
0180             if (e->type() == QEvent::MouseButtonPress && !m_lastMousePressEvent) {
0181                 QWidget* w = qobject_cast<QWidget*>(o);
0182                 if (!w) {
0183                     return QObject::eventFilter(o, e);
0184                 }
0185                 QList<QWidget*>::const_iterator it_w;
0186                 for (it_w = m_parents.constBegin(); it_w != m_parents.constEnd(); ++it_w) {
0187                     if (isChildOf(w, (*it_w))) {
0188                         m_lastMousePressEvent = e;
0189                         break;
0190                     }
0191                 }
0192                 if (it_w == m_parents.constEnd()) {
0193                     m_lastMousePressEvent = e;
0194                     bool rc = false;
0195                     emit mousePressedOnExternalWidget(rc);
0196                 }
0197             }
0198 
0199             if (e->type() != QEvent::MouseButtonPress) {
0200                 m_lastMousePressEvent = 0;
0201             }
0202         }
0203         return false;
0204     }
0205 
0206 Q_SIGNALS:
0207     void mousePressedOnExternalWidget(bool&);
0208 
0209 private:
0210     QList<QWidget*>      m_parents;
0211     QEvent*              m_lastMousePressEvent;
0212     bool                 m_filterActive;
0213 };
0214 
0215 class KGlobalLedgerViewPrivate : public KMyMoneyViewBasePrivate
0216 {
0217     Q_DECLARE_PUBLIC(KGlobalLedgerView)
0218 
0219 public:
0220     explicit KGlobalLedgerViewPrivate(KGlobalLedgerView *qq) :
0221         q_ptr(qq),
0222         m_mousePressFilter(0),
0223         m_registerSearchLine(0),
0224         m_precision(2),
0225         m_recursion(false),
0226         m_showDetails(false),
0227         m_action(eWidgets::eRegister::Action::None),
0228         m_filterProxyModel(0),
0229         m_accountComboBox(0),
0230         m_balanceIsApproximated(false),
0231         m_toolbarFrame(nullptr),
0232         m_registerFrame(nullptr),
0233         m_buttonFrame(nullptr),
0234         m_formFrame(nullptr),
0235         m_summaryFrame(nullptr),
0236         m_register(nullptr),
0237         m_buttonbar(nullptr),
0238         m_leftSummaryLabel(nullptr),
0239         m_centerSummaryLabel(nullptr),
0240         m_rightSummaryLabel(nullptr),
0241         m_form(nullptr),
0242         m_needLoad(true),
0243         m_newAccountLoaded(true),
0244         m_inEditMode(false),
0245         m_transactionEditor(nullptr),
0246         m_balanceWarning(nullptr),
0247         m_moveToAccountSelector(nullptr),
0248         m_endingBalanceDlg(nullptr),
0249         m_searchDlg(nullptr)
0250     {
0251     }
0252 
0253     ~KGlobalLedgerViewPrivate()
0254     {
0255         delete m_moveToAccountSelector;
0256         delete m_endingBalanceDlg;
0257         delete m_searchDlg;
0258     }
0259 
0260     void init()
0261     {
0262         Q_Q(KGlobalLedgerView);
0263         m_needLoad = false;
0264         auto vbox = new QVBoxLayout(q);
0265         q->setLayout(vbox);
0266         vbox->setSpacing(6);
0267         vbox->setMargin(0);
0268 
0269         m_mousePressFilter = new MousePressFilter((QWidget*)q);
0270         m_action = eWidgets::eRegister::Action::None;
0271 
0272         // the proxy filter model
0273         m_filterProxyModel = new AccountNamesFilterProxyModel(q);
0274         m_filterProxyModel->addAccountGroup(QVector<eMyMoney::Account::Type>{eMyMoney::Account::Type::Asset,
0275                                                                              eMyMoney::Account::Type::Liability,
0276                                                                              eMyMoney::Account::Type::Equity,
0277                                                                              eMyMoney::Account::Type::Income,
0278                                                                              eMyMoney::Account::Type::Expense});
0279         auto const model = Models::instance()->accountsModel();
0280         m_filterProxyModel->setSourceColumns(model->getColumns());
0281         m_filterProxyModel->setSourceModel(model);
0282         m_filterProxyModel->sort((int)eAccountsModel::Column::Account);
0283 
0284         // create the toolbar frame at the top of the view
0285         m_toolbarFrame = new QFrame();
0286         QHBoxLayout* toolbarLayout = new QHBoxLayout(m_toolbarFrame);
0287         toolbarLayout->setContentsMargins(0, 0, 0, 0);
0288         toolbarLayout->setSpacing(6);
0289 
0290         // the account selector widget
0291         m_accountComboBox = new KMyMoneyAccountCombo();
0292         m_accountComboBox->setModel(m_filterProxyModel);
0293         toolbarLayout->addWidget(m_accountComboBox);
0294 
0295         vbox->addWidget(m_toolbarFrame);
0296         toolbarLayout->setStretchFactor(m_accountComboBox, 60);
0297         // create the register frame
0298         m_registerFrame = new QFrame();
0299         QVBoxLayout* registerFrameLayout = new QVBoxLayout(m_registerFrame);
0300         registerFrameLayout->setContentsMargins(0, 0, 0, 0);
0301         registerFrameLayout->setSpacing(0);
0302         vbox->addWidget(m_registerFrame);
0303         vbox->setStretchFactor(m_registerFrame, 2);
0304         m_register = new KMyMoneyRegister::Register(m_registerFrame);
0305         m_register->setUsedWithEditor(true);
0306         registerFrameLayout->addWidget(m_register);
0307         m_register->installEventFilter(q);
0308         q->connect(m_register, &KMyMoneyRegister::Register::openContextMenu, q, &KGlobalLedgerView::slotTransactionsContextMenuRequested);
0309         q->connect(m_register, &KMyMoneyRegister::Register::transactionsSelected, q, &KGlobalLedgerView::slotUpdateSummaryLine);
0310         q->connect(m_register->horizontalHeader(), &QWidget::customContextMenuRequested, q, &KGlobalLedgerView::slotSortOptions);
0311         q->connect(m_register, &KMyMoneyRegister::Register::reconcileStateColumnClicked, q, &KGlobalLedgerView::slotToggleTransactionMark);
0312 
0313         // insert search line widget
0314 
0315         m_registerSearchLine = new KMyMoneyRegister::RegisterSearchLineWidget(m_register, m_toolbarFrame);
0316         toolbarLayout->addWidget(m_registerSearchLine);
0317         toolbarLayout->setStretchFactor(m_registerSearchLine, 100);
0318         // create the summary frame
0319         m_summaryFrame = new QFrame();
0320         QHBoxLayout* summaryFrameLayout = new QHBoxLayout(m_summaryFrame);
0321         summaryFrameLayout->setContentsMargins(0, 0, 0, 0);
0322         summaryFrameLayout->setSpacing(0);
0323         m_leftSummaryLabel = new QLabel(m_summaryFrame);
0324         m_centerSummaryLabel = new QLabel(m_summaryFrame);
0325         m_rightSummaryLabel = new QLabel(m_summaryFrame);
0326         summaryFrameLayout->addWidget(m_leftSummaryLabel);
0327         QSpacerItem* spacer = new QSpacerItem(20, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
0328         summaryFrameLayout->addItem(spacer);
0329         summaryFrameLayout->addWidget(m_centerSummaryLabel);
0330         spacer = new QSpacerItem(20, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
0331         summaryFrameLayout->addItem(spacer);
0332         summaryFrameLayout->addWidget(m_rightSummaryLabel);
0333         vbox->addWidget(m_summaryFrame);
0334 
0335         // create the button frame
0336         m_buttonFrame = new QFrame(q);
0337         QVBoxLayout* buttonLayout = new QVBoxLayout(m_buttonFrame);
0338         buttonLayout->setContentsMargins(0, 0, 0, 0);
0339         buttonLayout->setSpacing(0);
0340         vbox->addWidget(m_buttonFrame);
0341         m_buttonbar = new KToolBar(m_buttonFrame, 0, true);
0342         m_buttonbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0343         buttonLayout->addWidget(m_buttonbar);
0344 
0345         m_buttonbar->addAction(pActions[eMenu::Action::NewTransaction]);
0346         m_buttonbar->addAction(pActions[eMenu::Action::DeleteTransaction]);
0347         m_buttonbar->addAction(pActions[eMenu::Action::EditTransaction]);
0348         m_buttonbar->addAction(pActions[eMenu::Action::EnterTransaction]);
0349         m_buttonbar->addAction(pActions[eMenu::Action::CancelTransaction]);
0350         m_buttonbar->addAction(pActions[eMenu::Action::AcceptTransaction]);
0351         m_buttonbar->addAction(pActions[eMenu::Action::MatchTransaction]);
0352 
0353         // create the transaction form frame
0354         m_formFrame = new QFrame(q);
0355         QVBoxLayout* frameLayout = new QVBoxLayout(m_formFrame);
0356         frameLayout->setContentsMargins(5, 5, 5, 5);
0357         frameLayout->setSpacing(0);
0358         m_form = new KMyMoneyTransactionForm::TransactionForm(m_formFrame);
0359         frameLayout->addWidget(m_form->getTabBar(m_formFrame));
0360         frameLayout->addWidget(m_form);
0361         m_formFrame->setFrameShape(QFrame::Panel);
0362         m_formFrame->setFrameShadow(QFrame::Raised);
0363         vbox->addWidget(m_formFrame);
0364 
0365         q->connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, q, &KGlobalLedgerView::refresh);
0366         q->connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, q, &KGlobalLedgerView::slotUpdateMoveToAccountMenu);
0367         q->connect(m_register, static_cast<void (KMyMoneyRegister::Register::*)(KMyMoneyRegister::Transaction *)>(&KMyMoneyRegister::Register::focusChanged), m_form, &KMyMoneyTransactionForm::TransactionForm::slotSetTransaction);
0368         q->connect(m_register, static_cast<void (KMyMoneyRegister::Register::*)()>(&KMyMoneyRegister::Register::focusChanged), q, &KGlobalLedgerView::updateLedgerActionsInternal);
0369 //    q->connect(m_accountComboBox, &KMyMoneyAccountCombo::accountSelected, q, &KGlobalLedgerView::slotAccountSelected);
0370         q->connect(m_accountComboBox, &KMyMoneyAccountCombo::accountSelected, q, static_cast<void (KGlobalLedgerView::*)(const QString&)>(&KGlobalLedgerView::slotSelectAccount));
0371         q->connect(m_accountComboBox, &KMyMoneyAccountCombo::accountSelected, q, &KGlobalLedgerView::slotUpdateMoveToAccountMenu);
0372         q->connect(m_register, &KMyMoneyRegister::Register::transactionsSelected, q, &KGlobalLedgerView::slotTransactionsSelected);
0373         q->connect(m_register, &KMyMoneyRegister::Register::transactionsSelected, q, &KGlobalLedgerView::slotUpdateMoveToAccountMenu);
0374         q->connect(m_register, &KMyMoneyRegister::Register::editTransaction, q, &KGlobalLedgerView::slotEditTransaction);
0375         q->connect(m_register, &KMyMoneyRegister::Register::emptyItemSelected, q, &KGlobalLedgerView::slotNewTransaction);
0376         q->connect(m_register, &KMyMoneyRegister::Register::aboutToSelectItem, q, &KGlobalLedgerView::slotAboutToSelectItem);
0377         q->connect(m_mousePressFilter, &MousePressFilter::mousePressedOnExternalWidget, q, &KGlobalLedgerView::slotCancelOrEnterTransactions);
0378 
0379         q->connect(m_form, &KMyMoneyTransactionForm::TransactionForm::newTransaction, q, static_cast<void (KGlobalLedgerView::*)(eWidgets::eRegister::Action)>(&KGlobalLedgerView::slotNewTransactionForm));
0380 
0381         // setup mouse press filter
0382         m_mousePressFilter->addWidget(m_formFrame);
0383         m_mousePressFilter->addWidget(m_buttonFrame);
0384         m_mousePressFilter->addWidget(m_summaryFrame);
0385         m_mousePressFilter->addWidget(m_registerFrame);
0386 
0387         m_tooltipPosn = QPoint();
0388     }
0389 
0390     /**
0391       * This method reloads the account selection combo box of the
0392       * view with all asset and liability accounts from the engine.
0393       * If the account id of the current account held in @p m_accountId is
0394       * empty or if the referenced account does not exist in the engine,
0395       * the first account found in the list will be made the current account.
0396       */
0397     void loadAccounts()
0398     {
0399         const auto file = MyMoneyFile::instance();
0400 
0401         // check if the current account still exists and make it the
0402         // current account
0403         if (!m_lastSelectedAccountID.isEmpty()) {
0404             try {
0405                 m_currentAccount = file->account(m_lastSelectedAccountID);
0406             } catch (const MyMoneyException &) {
0407                 m_lastSelectedAccountID.clear();
0408                 m_currentAccount = MyMoneyAccount();
0409                 m_accountComboBox->setSelected(QString());
0410             }
0411         }
0412 
0413         // TODO: check why the invalidate is needed here
0414         m_filterProxyModel->invalidate();
0415         m_filterProxyModel->sort((int)eAccountsModel::Column::Account);
0416         m_filterProxyModel->setHideClosedAccounts(!KMyMoneySettings::showAllAccounts());
0417         m_filterProxyModel->setHideEquityAccounts(!KMyMoneySettings::expertMode());
0418         m_accountComboBox->expandAll();
0419 
0420         if (m_currentAccount.id().isEmpty()) {
0421             // find the first favorite account
0422             QModelIndexList list = m_filterProxyModel->match(m_filterProxyModel->index(0, 0),
0423                                    (int)eAccountsModel::Role::Favorite,
0424                                    QVariant(true),
0425                                    1,
0426                                    Qt::MatchFlags(Qt::MatchExactly | Qt::MatchCaseSensitive | Qt::MatchRecursive));
0427             if (list.count() > 0) {
0428                 QVariant accountId = list.front().data((int)eAccountsModel::Role::ID);
0429                 if (accountId.isValid()) {
0430                     m_currentAccount = file->account(accountId.toString());
0431                 }
0432             }
0433 
0434             if (m_currentAccount.id().isEmpty()) {
0435                 // there are no favorite accounts find any account
0436                 list = m_filterProxyModel->match(m_filterProxyModel->index(0, 0),
0437                                                  Qt::DisplayRole,
0438                                                  QVariant(QString("*")),
0439                                                  -1,
0440                                                  Qt::MatchFlags(Qt::MatchWildcard | Qt::MatchRecursive));
0441                 for (QModelIndexList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) {
0442                     if (!it->parent().isValid())
0443                         continue; // skip the top level accounts
0444                     QVariant accountId = (*it).data((int)eAccountsModel::Role::ID);
0445                     if (accountId.isValid()) {
0446                         MyMoneyAccount a = file->account(accountId.toString());
0447                         if (!a.isInvest() && !a.isClosed()) {
0448                             m_currentAccount = a;
0449                             break;
0450                         }
0451                     }
0452                 }
0453             }
0454         }
0455 
0456         if (!m_currentAccount.id().isEmpty()) {
0457             m_accountComboBox->setSelected(m_currentAccount.id());
0458             try {
0459                 m_precision = MyMoneyMoney::denomToPrec(m_currentAccount.fraction());
0460             } catch (const MyMoneyException &) {
0461                 qDebug("Security %s for account %s not found", qPrintable(m_currentAccount.currencyId()), qPrintable(m_currentAccount.name()));
0462                 m_precision = 2;
0463             }
0464         }
0465     }
0466 
0467     /**
0468       * This method clears the register, form, transaction list. See @sa m_register,
0469       * @sa m_transactionList
0470       */
0471     void clear()
0472     {
0473         // clear current register contents
0474         m_register->clear();
0475 
0476         // setup header font
0477         QFont font = KMyMoneySettings::listHeaderFontEx();
0478         QFontMetrics fm(font);
0479         int height = fm.lineSpacing() + 6;
0480         m_register->horizontalHeader()->setMinimumHeight(height);
0481         m_register->horizontalHeader()->setMaximumHeight(height);
0482         m_register->horizontalHeader()->setFont(font);
0483 
0484         // setup cell font
0485         font = KMyMoneySettings::listCellFontEx();
0486         m_register->setFont(font);
0487 
0488         // clear the form
0489         m_form->clear();
0490 
0491         // the selected transactions list
0492         m_transactionList.clear();
0493 
0494         // and the selected account in the combo box
0495         m_accountComboBox->setSelected(QString());
0496 
0497         // fraction defaults to two digits
0498         m_precision = 2;
0499     }
0500 
0501     void loadView()
0502     {
0503         MYMONEYTRACER(tracer);
0504         Q_Q(KGlobalLedgerView);
0505 
0506         // setup form visibility
0507         m_formFrame->setVisible(KMyMoneySettings::transactionForm());
0508 
0509         // no account selected
0510 //    emit q->objectSelected(MyMoneyAccount());
0511         // no transaction selected
0512         KMyMoneyRegister::SelectedTransactions list;
0513         emit q->selectByVariant(QVariantList {QVariant::fromValue(list)}, eView::Intent::SelectRegisterTransactions);
0514 
0515         QMap<QString, bool> isSelected;
0516         QString focusItemId;
0517         QString backUpFocusItemId;  // in case the focus item is removed
0518         QString anchorItemId;
0519         QString backUpAnchorItemId; // in case the anchor item is removed
0520 
0521         if (!m_newAccountLoaded) {
0522             // remember the current selected transactions
0523             KMyMoneyRegister::RegisterItem* item = m_register->firstItem();
0524             for (; item; item = item->nextItem()) {
0525                 if (item->isSelected()) {
0526                     isSelected[item->id()] = true;
0527                 }
0528             }
0529             // remember the item that has the focus
0530             storeId(m_register->focusItem(), focusItemId, backUpFocusItemId);
0531             // and the one that has the selection anchor
0532             storeId(m_register->anchorItem(), anchorItemId, backUpAnchorItemId);
0533         } else {
0534             m_registerSearchLine->searchLine()->clear();
0535         }
0536 
0537         // clear the current contents ...
0538         clear();
0539 
0540         // ... load the combobox widget and select current account ...
0541         loadAccounts();
0542 
0543         // ... setup the register columns ...
0544         m_register->setupRegister(m_currentAccount);
0545 
0546         // ... setup the form ...
0547         m_form->setupForm(m_currentAccount);
0548 
0549         if (m_currentAccount.id().isEmpty()) {
0550             // if we don't have an account we bail out
0551             q->setEnabled(false);
0552             return;
0553         }
0554         q->setEnabled(true);
0555 
0556         m_register->setUpdatesEnabled(false);
0557 
0558         // ... and recreate it
0559         KMyMoneyRegister::RegisterItem* focusItem = 0;
0560         KMyMoneyRegister::RegisterItem* anchorItem = 0;
0561         QMap<QString, MyMoneyMoney> actBalance, clearedBalance, futureBalance;
0562         QMap<QString, MyMoneyMoney>::iterator it_b;
0563         try {
0564             // setup the filter to select the transactions we want to display
0565             // and update the sort order
0566             QString sortOrder;
0567             QString key;
0568             QDate reconciliationDate = m_reconciliationDate;
0569 
0570             MyMoneyTransactionFilter filter(m_currentAccount.id());
0571             // if it's an investment account, we also take care of
0572             // the sub-accounts (stock accounts)
0573             if (m_currentAccount.accountType() == eMyMoney::Account::Type::Investment)
0574                 filter.addAccount(m_currentAccount.accountList());
0575 
0576             if (isReconciliationAccount()) {
0577                 key = "kmm-sort-reconcile";
0578                 sortOrder = KMyMoneySettings::sortReconcileView();
0579                 filter.addState((int)eMyMoney::TransactionFilter::State::NotReconciled);
0580                 filter.addState((int)eMyMoney::TransactionFilter::State::Cleared);
0581             } else {
0582                 filter.setDateFilter(KMyMoneySettings::startDate().date(), QDate());
0583                 key = "kmm-sort-std";
0584                 sortOrder = KMyMoneySettings::sortNormalView();
0585                 if (KMyMoneySettings::hideReconciledTransactions()
0586                         && !m_currentAccount.isIncomeExpense()) {
0587                     filter.addState((int)eMyMoney::TransactionFilter::State::NotReconciled);
0588                     filter.addState((int)eMyMoney::TransactionFilter::State::Cleared);
0589                 }
0590             }
0591             filter.setReportAllSplits(true);
0592 
0593             // check if we have an account override of the sort order
0594             if (!m_currentAccount.value(key).isEmpty())
0595                 sortOrder = m_currentAccount.value(key);
0596 
0597             // setup sort order
0598             m_register->setSortOrder(sortOrder);
0599 
0600             // retrieve the list from the engine
0601             MyMoneyFile::instance()->transactionList(m_transactionList, filter);
0602 
0603             emit q->slotStatusProgress(0, m_transactionList.count());
0604 
0605             // create the elements for the register
0606             QList<QPair<MyMoneyTransaction, MyMoneySplit> >::const_iterator it;
0607             QMap<QString, int>uniqueMap;
0608             int i = 0;
0609             for (it = m_transactionList.constBegin(); it != m_transactionList.constEnd(); ++it) {
0610                 uniqueMap[(*it).first.id()]++;
0611                 KMyMoneyRegister::Transaction* t = KMyMoneyRegister::Register::transactionFactory(m_register, (*it).first, (*it).second, uniqueMap[(*it).first.id()]);
0612                 actBalance[t->split().accountId()] = MyMoneyMoney();
0613                 emit q->slotStatusProgress(++i, 0);
0614                 // if we're in reconciliation and the state is cleared, we
0615                 // force the item to show in dimmed intensity to get a visual focus
0616                 // on those items, that we need to work on
0617                 if (isReconciliationAccount() && (*it).second.reconcileFlag() == eMyMoney::Split::State::Cleared) {
0618                     t->setReducedIntensity(true);
0619                 }
0620             }
0621 
0622             // create dummy entries for the scheduled transactions if sorted by postdate
0623             int period = KMyMoneySettings::schedulePreview();
0624             if (m_register->primarySortKey() == eWidgets::SortField::PostDate) {
0625                 // show scheduled transactions which have a scheduled postdate
0626                 // within the next 'period' days. In reconciliation mode, the
0627                 // period starts on the statement date.
0628                 QDate endDate = QDate::currentDate().addDays(period);
0629                 if (isReconciliationAccount())
0630                     endDate = reconciliationDate.addDays(period);
0631                 QList<MyMoneySchedule> scheduleList = MyMoneyFile::instance()->scheduleList(m_currentAccount.id());
0632                 while (!scheduleList.isEmpty()) {
0633                     MyMoneySchedule& s = scheduleList.first();
0634                     for (;;) {
0635                         if (s.isFinished() || s.adjustedNextDueDate() > endDate) {
0636                             break;
0637                         }
0638 
0639                         MyMoneyTransaction t(s.id(), KMyMoneyUtils::scheduledTransaction(s));
0640                         if (s.isOverdue() && !KMyMoneySettings::showPlannedScheduleDates()) {
0641                             // if the transaction is scheduled and overdue, it can't
0642                             // certainly be posted in the past. So we take today's date
0643                             // as the alternative
0644                             t.setPostDate(s.adjustedDate(QDate::currentDate(), s.weekendOption()));
0645                         } else {
0646                             t.setPostDate(s.adjustedNextDueDate());
0647                         }
0648                         foreach (const auto split, t.splits()) {
0649                             if (split.accountId() == m_currentAccount.id()) {
0650                                 new KMyMoneyRegister::StdTransactionScheduled(m_register, t, split, uniqueMap[t.id()]);
0651                             }
0652                         }
0653                         // keep track of this payment locally (not in the engine)
0654                         if (s.isOverdue() && !KMyMoneySettings::showPlannedScheduleDates()) {
0655                             s.setLastPayment(QDate::currentDate());
0656                         } else {
0657                             s.setLastPayment(s.nextDueDate());
0658                         }
0659 
0660                         // if this is a one time schedule, we can bail out here as we're done
0661                         if (s.occurrence() == eMyMoney::Schedule::Occurrence::Once)
0662                             break;
0663 
0664                         // for all others, we check if the next payment date is still 'in range'
0665                         QDate nextDueDate = s.nextPayment(s.nextDueDate());
0666                         if (nextDueDate.isValid()) {
0667                             s.setNextDueDate(nextDueDate);
0668                         } else {
0669                             break;
0670                         }
0671                     }
0672                     scheduleList.pop_front();
0673                 }
0674             }
0675 
0676             // add the group markers
0677             m_register->addGroupMarkers();
0678 
0679             // sort the transactions according to the sort setting
0680             m_register->sortItems();
0681 
0682             // remove trailing and adjacent markers
0683             m_register->removeUnwantedGroupMarkers();
0684 
0685             // add special markers for reconciliation now so that they do not get
0686             // removed by m_register->removeUnwantedGroupMarkers(). Needs resorting
0687             // of items but that's ok.
0688 
0689             KMyMoneyRegister::StatementGroupMarker* statement = 0;
0690             KMyMoneyRegister::StatementGroupMarker* dStatement = 0;
0691             KMyMoneyRegister::StatementGroupMarker* pStatement = 0;
0692 
0693             if (isReconciliationAccount()) {
0694                 switch (m_register->primarySortKey()) {
0695                 case eWidgets::SortField::PostDate:
0696                     statement = new KMyMoneyRegister::StatementGroupMarker(m_register, eWidgets::eRegister::CashFlowDirection::Deposit, reconciliationDate, i18n("Statement Details"));
0697                     m_register->sortItems();
0698                     break;
0699                 case eWidgets::SortField::Type:
0700                     dStatement = new KMyMoneyRegister::StatementGroupMarker(m_register, eWidgets::eRegister::CashFlowDirection::Deposit, reconciliationDate, i18n("Statement Deposit Details"));
0701                     pStatement = new KMyMoneyRegister::StatementGroupMarker(m_register, eWidgets::eRegister::CashFlowDirection::Payment, reconciliationDate, i18n("Statement Payment Details"));
0702                     m_register->sortItems();
0703                     break;
0704                 default:
0705                     break;
0706                 }
0707             }
0708 
0709             // we need at least the balance for the account we currently show
0710             actBalance[m_currentAccount.id()] = MyMoneyMoney();
0711 
0712             if (m_currentAccount.accountType() == eMyMoney::Account::Type::Investment)
0713                 foreach (const auto accountID, m_currentAccount.accountList())
0714                     actBalance[accountID] = MyMoneyMoney();
0715 
0716             // determine balances (actual, cleared). We do this by getting the actual
0717             // balance of all entered transactions from the engine and walk the list
0718             // of transactions backward. Also re-select a transaction if it was
0719             // selected before and setup the focus item.
0720 
0721             MyMoneyMoney factor(1, 1);
0722             if (m_currentAccount.accountGroup() == eMyMoney::Account::Type::Liability
0723                     || m_currentAccount.accountGroup() == eMyMoney::Account::Type::Equity)
0724                 factor = -factor;
0725 
0726             QMap<QString, int> deposits;
0727             QMap<QString, int> payments;
0728             QMap<QString, MyMoneyMoney> depositAmount;
0729             QMap<QString, MyMoneyMoney> paymentAmount;
0730             for (it_b = actBalance.begin(); it_b != actBalance.end(); ++it_b) {
0731                 MyMoneyMoney balance = MyMoneyFile::instance()->balance(it_b.key());
0732                 balance = balance * factor;
0733                 clearedBalance[it_b.key()] =
0734                     futureBalance[it_b.key()] =
0735                         (*it_b) = balance;
0736                 deposits[it_b.key()] = payments[it_b.key()] = 0;
0737                 depositAmount[it_b.key()] = MyMoneyMoney();
0738                 paymentAmount[it_b.key()] = MyMoneyMoney();
0739             }
0740 
0741             tracer.printf("total balance of %s = %s", qPrintable(m_currentAccount.name()), qPrintable(actBalance[m_currentAccount.id()].formatMoney("", 2)));
0742             tracer.printf("future balance of %s = %s", qPrintable(m_currentAccount.name()), qPrintable(futureBalance[m_currentAccount.id()].formatMoney("", 2)));
0743             tracer.printf("cleared balance of %s = %s", qPrintable(m_currentAccount.name()), qPrintable(clearedBalance[m_currentAccount.id()].formatMoney("", 2)));
0744 
0745             KMyMoneyRegister::RegisterItem* p = m_register->lastItem();
0746             focusItem = 0;
0747 
0748             // take care of possibly trailing scheduled transactions (bump up the future balance)
0749             while (p) {
0750                 if (p->isSelectable()) {
0751                     KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(p);
0752                     if (t && t->isScheduled()) {
0753                         MyMoneyMoney balance = futureBalance[t->split().accountId()];
0754                         const MyMoneySplit& split = t->split();
0755                         // if this split is a stock split, we can't just add the amount of shares
0756                         if (t->transaction().isStockSplit()) {
0757                             balance = MyMoneyFile::instance()->stockSplit(split.accountId(),
0758                                                                           balance,
0759                                                                           split.shares(),
0760                                                                           eMyMoney::StockSplitDirection::StockSplitForward);
0761                         } else {
0762                             balance += split.shares() * factor;
0763                         }
0764                         futureBalance[split.accountId()] = balance;
0765                     } else if (t && !focusItem)
0766                         focusItem = p;
0767                 }
0768                 p = p->prevItem();
0769             }
0770 
0771             p = m_register->lastItem();
0772             while (p) {
0773                 KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(p);
0774                 if (t) {
0775                     if (isSelected.contains(t->id()))
0776                         t->setSelected(true);
0777 
0778                     matchItemById(&focusItem, t, focusItemId, backUpFocusItemId);
0779                     matchItemById(&anchorItem, t, anchorItemId, backUpAnchorItemId);
0780 
0781                     const MyMoneySplit& split = t->split();
0782                     MyMoneyMoney balance = futureBalance[split.accountId()];
0783                     t->setBalance(balance);
0784 
0785                     // if this split is a stock split, we can't just add the amount of shares
0786                     if (t->transaction().isStockSplit()) {
0787                         balance =
0788                             MyMoneyFile::instance()->stockSplit(split.accountId(), balance, split.shares(), eMyMoney::StockSplitDirection::StockSplitBackward);
0789                     } else {
0790                         balance -= split.shares() * factor;
0791                     }
0792 
0793                     if (!t->isScheduled()) {
0794                         if (isReconciliationAccount() && t->transaction().postDate() <= reconciliationDate && split.reconcileFlag() == eMyMoney::Split::State::Cleared) {
0795                             if (split.shares().isNegative()) {
0796                                 payments[split.accountId()]++;
0797                                 paymentAmount[split.accountId()] += split.shares();
0798                             } else {
0799                                 deposits[split.accountId()]++;
0800                                 depositAmount[split.accountId()] += split.shares();
0801                             }
0802                         }
0803 
0804                         if (t->transaction().postDate() > QDate::currentDate()) {
0805                             tracer.printf("Reducing actual balance by %s because %s/%s(%s) is in the future", qPrintable((split.shares() * factor).formatMoney("", 2)), qPrintable(t->transaction().id()), qPrintable(split.id()), qPrintable(t->transaction().postDate().toString(Qt::ISODate)));
0806                             actBalance[split.accountId()] -= split.shares() * factor;
0807                         }
0808                     }
0809                     futureBalance[split.accountId()] = balance;
0810                 }
0811                 p = p->prevItem();
0812             }
0813 
0814             clearedBalance[m_currentAccount.id()] = MyMoneyFile::instance()->clearedBalance(m_currentAccount.id(), reconciliationDate);
0815 
0816             tracer.printf("total balance of %s = %s", qPrintable(m_currentAccount.name()), qPrintable(actBalance[m_currentAccount.id()].formatMoney("", 2)));
0817             tracer.printf("future balance of %s = %s", qPrintable(m_currentAccount.name()), qPrintable(futureBalance[m_currentAccount.id()].formatMoney("", 2)));
0818             tracer.printf("cleared balance of %s = %s", qPrintable(m_currentAccount.name()), qPrintable(clearedBalance[m_currentAccount.id()].formatMoney("", 2)));
0819 
0820             // update statement information
0821             if (statement) {
0822                 const QString aboutDeposits = i18np("%1 deposit (%2)", "%1 deposits (%2)",
0823                                                     deposits[m_currentAccount.id()], depositAmount[m_currentAccount.id()].abs().formatMoney(m_currentAccount.fraction()));
0824                 const QString aboutCharges = i18np("%1 charge (%2)", "%1 charges (%2)",
0825                                                    deposits[m_currentAccount.id()], depositAmount[m_currentAccount.id()].abs().formatMoney(m_currentAccount.fraction()));
0826                 const QString aboutPayments = i18np("%1 payment (%2)", "%1 payments (%2)",
0827                                                     payments[m_currentAccount.id()], paymentAmount[m_currentAccount.id()].abs().formatMoney(m_currentAccount.fraction()));
0828 
0829                 statement->setText(i18nc("%1 is a string, e.g. 7 deposits; %2 is a string, e.g. 4 payments", "%1, %2",
0830                                          m_currentAccount.accountType() == eMyMoney::Account::Type::CreditCard ? aboutCharges : aboutDeposits,
0831                                          aboutPayments));
0832             }
0833             if (pStatement) {
0834                 pStatement->setText(i18np("%1 payment (%2)", "%1 payments (%2)", payments[m_currentAccount.id()]
0835                                           , paymentAmount[m_currentAccount.id()].abs().formatMoney(m_currentAccount.fraction())));
0836             }
0837             if (dStatement) {
0838                 dStatement->setText(i18np("%1 deposit (%2)", "%1 deposits (%2)", deposits[m_currentAccount.id()]
0839                                           , depositAmount[m_currentAccount.id()].abs().formatMoney(m_currentAccount.fraction())));
0840             }
0841 
0842             // add a last empty entry for new transactions
0843             // leave some information about the current account
0844             MyMoneySplit split;
0845             split.setReconcileFlag(eMyMoney::Split::State::NotReconciled);
0846             // make sure to use the value specified in the option during reconciliation
0847             if (isReconciliationAccount())
0848                 split.setReconcileFlag(static_cast<eMyMoney::Split::State>(KMyMoneySettings::defaultReconciliationState()));
0849             MyMoneyTransaction emptyTransaction;
0850             emptyTransaction.setCommodity(m_currentAccount.currencyId());
0851             KMyMoneyRegister::Register::transactionFactory(m_register, emptyTransaction, split, 0);
0852 
0853             m_register->updateRegister(true);
0854 
0855             if (focusItem) {
0856                 // in case we have some selected items we just set the focus item
0857                 // in other cases, we make the focusitem also the selected item
0858                 if (anchorItem && (anchorItem != focusItem)) {
0859                     m_register->setFocusItem(focusItem);
0860                     m_register->setAnchorItem(anchorItem);
0861                 } else
0862                     m_register->selectItem(focusItem, true);
0863             } else {
0864                 // just use the empty line at the end if nothing else exists in the ledger
0865                 p = m_register->lastItem();
0866                 m_register->setFocusItem(p);
0867                 m_register->selectItem(p);
0868                 focusItem = p;
0869             }
0870 
0871             updateSummaryLine(actBalance, clearedBalance);
0872             emit q->slotStatusProgress(-1, -1);
0873 
0874         } catch (const MyMoneyException &) {
0875             m_currentAccount = MyMoneyAccount();
0876             clear();
0877         }
0878 
0879         m_showDetails = KMyMoneySettings::showRegisterDetailed();
0880 
0881         // and tell everyone what's selected
0882         emit q->selectByObject(m_currentAccount, eView::Intent::None);
0883         KMyMoneyRegister::SelectedTransactions actualSelection(m_register);
0884         emit q->selectByVariant(QVariantList {QVariant::fromValue(actualSelection)}, eView::Intent::SelectRegisterTransactions);
0885     }
0886 
0887     void selectTransaction(const QString& id)
0888     {
0889         if (!id.isEmpty()) {
0890             KMyMoneyRegister::RegisterItem* p = m_register->lastItem();
0891             while (p) {
0892                 KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(p);
0893                 if (t) {
0894                     if (t->transaction().id() == id) {
0895                         m_register->selectItem(t);
0896                         m_register->ensureItemVisible(t);
0897                         break;
0898                     }
0899                 }
0900                 p = p->prevItem();
0901             }
0902         }
0903     }
0904 
0905     /**
0906      * @brief selects transactions for processing with slots
0907      * @param list of transactions
0908      * @return false if only schedule is to be selected
0909      */
0910     bool selectTransactions(const KMyMoneyRegister::SelectedTransactions list)
0911     {
0912         Q_Q(KGlobalLedgerView);
0913         // list can either contain a list of transactions or a single selected scheduled transaction
0914         // in the latter case, the transaction id is actually the one of the schedule. In order
0915         // to differentiate between the two, we just ask for the schedule. If we don't find one - because
0916         // we passed the id of a real transaction - then we know that fact.  We use the schedule here,
0917         // because the list of schedules is kept in a cache by MyMoneyFile. This way, we save some trips
0918         // to the backend which we would have to do if we check for the transaction.
0919         m_selectedTransactions.clear();
0920         auto sch = MyMoneySchedule();
0921         auto ret = true;
0922 
0923         m_accountGoto.clear();
0924         m_payeeGoto.clear();
0925         if (!list.isEmpty() && !list.first().isScheduled()) {
0926             m_selectedTransactions = list;
0927             if (list.count() == 1) {
0928                 const MyMoneySplit& sp = m_selectedTransactions[0].split();
0929                 if (!sp.payeeId().isEmpty()) {
0930                     try {
0931                         auto payee = MyMoneyFile::instance()->payee(sp.payeeId());
0932                         if (!payee.name().isEmpty()) {
0933                             m_payeeGoto = payee.id();
0934                             auto name = payee.name();
0935                             name.replace(QRegExp("&(?!&)"), "&&");
0936                             pActions[Action::GoToPayee]->setText(i18n("Go to '%1'", name));
0937                         }
0938                     } catch (const MyMoneyException &) {
0939                     }
0940                 }
0941                 try {
0942                     const auto& t = m_selectedTransactions[0].transaction();
0943                     // search the first non-income/non-expense account and use it for the 'goto account'
0944                     const auto& selectedTransactionSplit = m_selectedTransactions[0].split();
0945                     foreach (const auto split, t.splits()) {
0946                         if (split.id() != selectedTransactionSplit.id()) {
0947                             auto acc = MyMoneyFile::instance()->account(split.accountId());
0948 
0949                             // set default icon
0950                             pActions[Action::GoToAccount]->setIcon(Icons::get(Icons::Icon::Accounts));
0951 
0952                             // for stock accounts we show the portfolio account
0953                             if (acc.isInvest()) {
0954                                 acc = MyMoneyFile::instance()->account(acc.parentAccountId());
0955                             } else if (acc.isIncomeExpense()) {
0956                                 pActions[Action::GoToAccount]->setIcon(Icons::get(Icons::Icon::FinancialCategories));
0957                             }
0958 
0959                             m_accountGoto = acc.id();
0960                             auto name = acc.name();
0961                             name.replace(QRegExp("&(?!&)"), "&&");
0962                             pActions[Action::GoToAccount]->setText(i18n("Go to '%1'", name));
0963 
0964                             break;
0965                         }
0966                     }
0967                 } catch (const MyMoneyException &) {
0968                 }
0969             }
0970         } else if (!list.isEmpty()) {
0971             sch = MyMoneyFile::instance()->schedule(list.first().scheduleId());
0972             m_selectedTransactions.append(list.first());
0973             ret = false;
0974         }
0975 
0976         emit q->selectByObject(sch, eView::Intent::None);
0977 
0978         // make sure, we show some neutral menu entry if we don't have an object
0979         if (m_payeeGoto.isEmpty())
0980             pActions[Action::GoToPayee]->setText(i18n("Go to payee"));
0981         if (m_accountGoto.isEmpty())
0982             pActions[Action::GoToAccount]->setText(i18n("Go to account"));
0983         return ret;
0984     }
0985 
0986     /**
0987       * Returns @a true if setReconciliationAccount() has been called for
0988       * the current loaded account.
0989       *
0990       * @retval true current account is in reconciliation mode
0991       * @retval false current account is not in reconciliation mode
0992       */
0993     bool isReconciliationAccount() const
0994     {
0995         return m_currentAccount.id() == m_reconciliationAccount.id();
0996     }
0997 
0998     /**
0999       * Updates the values on the summary line beneath the register with
1000       * the given values. The contents shown differs between reconciliation
1001       * mode and normal mode.
1002       *
1003       * @param actBalance map of account indexed values to be used as actual balance
1004       * @param clearedBalance map of account indexed values to be used as cleared balance
1005       */
1006     void updateSummaryLine(const QMap<QString, MyMoneyMoney>& actBalance, const QMap<QString, MyMoneyMoney>& clearedBalance)
1007     {
1008         Q_Q(KGlobalLedgerView);
1009         const auto file = MyMoneyFile::instance();
1010         m_leftSummaryLabel->show();
1011         m_centerSummaryLabel->show();
1012         m_rightSummaryLabel->show();
1013 
1014         if (isReconciliationAccount()) {
1015             if (m_currentAccount.accountType() != eMyMoney::Account::Type::Investment) {
1016                 m_leftSummaryLabel->setText(i18n("Statement: %1", m_endingBalance.formatMoney("", m_precision)));
1017                 m_centerSummaryLabel->setText(i18nc("Cleared balance", "Cleared: %1", clearedBalance[m_currentAccount.id()].formatMoney("", m_precision)));
1018                 m_totalBalance = clearedBalance[m_currentAccount.id()] - m_endingBalance;
1019             }
1020         } else {
1021             // update summary line in normal mode
1022             QDate reconcileDate = m_currentAccount.lastReconciliationDate();
1023 
1024             if (reconcileDate.isValid()) {
1025                 m_leftSummaryLabel->setText(i18n("Last reconciled: %1", QLocale().toString(reconcileDate, QLocale::ShortFormat)));
1026             } else {
1027                 m_leftSummaryLabel->setText(i18n("Never reconciled"));
1028             }
1029 
1030             QPalette palette = m_rightSummaryLabel->palette();
1031             palette.setColor(m_rightSummaryLabel->foregroundRole(), m_leftSummaryLabel->palette().color(q->foregroundRole()));
1032             if (m_currentAccount.accountType() != eMyMoney::Account::Type::Investment) {
1033                 m_centerSummaryLabel->setText(i18nc("Cleared balance", "Cleared: %1", clearedBalance[m_currentAccount.id()].formatMoney("", m_precision)));
1034                 m_totalBalance = actBalance[m_currentAccount.id()];
1035             } else {
1036                 m_centerSummaryLabel->hide();
1037                 MyMoneyMoney balance;
1038                 MyMoneySecurity base = file->baseCurrency();
1039                 QMap<QString, MyMoneyMoney>::const_iterator it_b;
1040                 // reset the approximated flag
1041                 m_balanceIsApproximated = false;
1042                 for (it_b = actBalance.begin(); it_b != actBalance.end(); ++it_b) {
1043                     MyMoneyAccount stock = file->account(it_b.key());
1044                     QString currencyId = stock.currencyId();
1045                     MyMoneySecurity sec = file->security(currencyId);
1046                     MyMoneyMoney rate(1, 1);
1047 
1048                     if (stock.isInvest()) {
1049                         currencyId = sec.tradingCurrency();
1050                         const MyMoneyPrice &priceInfo = file->price(sec.id(), currencyId);
1051                         m_balanceIsApproximated |= !priceInfo.isValid();
1052                         rate = priceInfo.rate(sec.tradingCurrency());
1053                     }
1054 
1055                     if (currencyId != base.id()) {
1056                         const MyMoneyPrice &priceInfo = file->price(sec.tradingCurrency(), base.id());
1057                         m_balanceIsApproximated |= !priceInfo.isValid();
1058                         rate = (rate * priceInfo.rate(base.id())).convertPrecision(sec.pricePrecision());
1059                     }
1060                     balance += ((*it_b) * rate).convert(base.smallestAccountFraction());
1061                 }
1062                 m_totalBalance = balance;
1063             }
1064             m_rightSummaryLabel->setPalette(palette);
1065         }
1066         // determine the number of selected transactions
1067         KMyMoneyRegister::SelectedTransactions selection;
1068         m_register->selectedTransactions(selection);
1069         q->slotUpdateSummaryLine(selection);
1070     }
1071 
1072     /**
1073       * setup the default action according to the current account type
1074       */
1075     void setupDefaultAction()
1076     {
1077         switch (m_currentAccount.accountType()) {
1078         case eMyMoney::Account::Type::Asset:
1079         case eMyMoney::Account::Type::AssetLoan:
1080         case eMyMoney::Account::Type::Savings:
1081             m_action = eWidgets::eRegister::Action::Deposit;
1082             break;
1083         default:
1084             m_action = eWidgets::eRegister::Action::Withdrawal;
1085             break;
1086         }
1087     }
1088 
1089     // used to store the id of an item and the id of an immediate unselected sibling
1090     void storeId(KMyMoneyRegister::RegisterItem *item, QString &id, QString &backupId) {
1091         if (item) {
1092             // the id of the item
1093             id = item->id();
1094             // the id of the item's previous/next unselected item
1095             for (KMyMoneyRegister::RegisterItem *it = item->prevItem(); it != 0 && backupId.isEmpty(); it = it->prevItem()) {
1096                 if (!it->isSelected()) {
1097                     backupId = it->id();
1098                 }
1099             }
1100             // if we didn't found previous unselected items search trough the next items
1101             for (KMyMoneyRegister::RegisterItem *it = item->nextItem(); it != 0 && backupId.isEmpty(); it = it->nextItem()) {
1102                 if (!it->isSelected()) {
1103                     backupId = it->id();
1104                 }
1105             }
1106         }
1107     }
1108 
1109     // use to match an item by it's id or a backup id which has a lower precedence
1110     void matchItemById(KMyMoneyRegister::RegisterItem **item, KMyMoneyRegister::Transaction* t, QString &id, QString &backupId) {
1111         if (!backupId.isEmpty() && t->id() == backupId)
1112             *item = t;
1113         if (!id.isEmpty() && t->id() == id) {
1114             // we found the real thing there's no need for the backup anymore
1115             backupId.clear();
1116             *item = t;
1117         }
1118     }
1119 
1120     bool canProcessTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const
1121     {
1122         if (m_register->focusItem() == 0)
1123             return false;
1124 
1125         if (!m_register->focusItem()->isSelected()) {
1126             tooltip = i18n("Cannot process transaction with focus if it is not selected.");
1127             showTooltip(tooltip);
1128             return false;
1129         }
1130         tooltip.clear();
1131         return !list.isEmpty();
1132     }
1133 
1134     void showTooltip(const QString msg) const
1135     {
1136         QToolTip::showText(m_tooltipPosn, msg);
1137     }
1138 
1139     bool createNewTransaction()
1140     {
1141         Q_Q(KGlobalLedgerView);
1142         auto rc = false;
1143         QString txt;
1144         if (q->canCreateTransactions(txt)) {
1145             rc = q->selectEmptyTransaction();
1146         }
1147         return rc;
1148     }
1149 
1150     TransactionEditor* startEdit(const KMyMoneyRegister::SelectedTransactions& list)
1151     {
1152         Q_Q(KGlobalLedgerView);
1153         TransactionEditor* editor = 0;
1154         QString txt;
1155         if (q->canEditTransactions(list, txt) || q->canCreateTransactions(txt)) {
1156             editor = q->startEdit(list);
1157         }
1158         return editor;
1159     }
1160 
1161     void doDeleteTransactions()
1162     {
1163         Q_Q(KGlobalLedgerView);
1164         KMyMoneyRegister::SelectedTransactions list = m_selectedTransactions;
1165         KMyMoneyRegister::SelectedTransactions::iterator it_t;
1166         int cnt = list.count();
1167         int i = 0;
1168         emit q->slotStatusProgress(0, cnt);
1169         MyMoneyFileTransaction ft;
1170         const auto file = MyMoneyFile::instance();
1171         try {
1172             it_t = list.begin();
1173             while (it_t != list.end()) {
1174                 const auto accountId = (*it_t).split().accountId();
1175                 const auto deletedNum = (*it_t).split().number();
1176                 const auto transactionId = (*it_t).transaction().id();
1177                 // only remove those transactions that do not reference a closed account
1178                 if (!file->referencesClosedAccount((*it_t).transaction())) {
1179                     file->removeTransaction((*it_t).transaction());
1180                     // remove all those references in the list of selected transactions
1181                     // that refer to the same transaction we just removed so that we
1182                     // will not be caught by an exception later on (see bko #285310)
1183                     while (it_t != list.end()) {
1184                         if (transactionId == (*it_t).transaction().id()) {
1185                             it_t = list.erase(it_t);
1186                             i++; // bump count of deleted transactions
1187                         } else {
1188                             ++it_t;
1189                         }
1190                     }
1191 
1192                 } else {
1193                     list.erase(it_t);
1194                 }
1195                 it_t = list.begin();
1196 
1197                 // need to ensure "nextCheckNumber" is still correct
1198                 auto acc = file->account(accountId);
1199 
1200                 // the "lastNumberUsed" might have been the txn number deleted
1201                 // so adjust it
1202                 if (deletedNum == acc.value("lastNumberUsed")) {
1203                     // decrement deletedNum and set new "lastNumberUsed"
1204                     QString num = KMyMoneyUtils::getAdjacentNumber(deletedNum, -1);
1205                     acc.setValue("lastNumberUsed", num);
1206                     file->modifyAccount(acc);
1207                 }
1208 
1209                 emit q->slotStatusProgress(i++, 0);
1210             }
1211             ft.commit();
1212 
1213         } catch (const MyMoneyException &e) {
1214             KMessageBox::detailedSorry(q, i18n("Unable to delete transaction(s)"), e.what());
1215         }
1216         emit q->slotStatusProgress(-1, -1);
1217     }
1218 
1219     void deleteTransactionEditor()
1220     {
1221         // make sure, we don't use the transaction editor pointer
1222         // anymore from now on
1223         auto p = m_transactionEditor;
1224         m_transactionEditor = nullptr;
1225         delete p;
1226     }
1227 
1228     void transactionUnmatch()
1229     {
1230         Q_Q(KGlobalLedgerView);
1231         KMyMoneyRegister::SelectedTransactions::const_iterator it;
1232         MyMoneyFileTransaction ft;
1233         try {
1234             for (it = m_selectedTransactions.constBegin(); it != m_selectedTransactions.constEnd(); ++it) {
1235                 if ((*it).split().isMatched()) {
1236                     TransactionMatcher matcher(m_currentAccount);
1237                     matcher.unmatch((*it).transaction(), (*it).split());
1238                 }
1239             }
1240             ft.commit();
1241 
1242         } catch (const MyMoneyException &e) {
1243             KMessageBox::detailedSorry(q, i18n("Unable to unmatch the selected transactions"), e.what());
1244         }
1245     }
1246 
1247     void transactionMatch()
1248     {
1249         Q_Q(KGlobalLedgerView);
1250         if (m_selectedTransactions.count() != 2)
1251             return;
1252 
1253         KMyMoneyRegister::SelectedTransaction toBeDeleted;
1254         KMyMoneyRegister::SelectedTransaction remaining;
1255         for (const auto& it : m_selectedTransactions) {
1256             if (it.transaction().isImported()) {
1257                 if (toBeDeleted.transaction().id().isEmpty()) {
1258                     toBeDeleted = it;
1259                 } else {
1260                     //This is a second imported transaction, we still want to merge
1261                     remaining = it;
1262                 }
1263             } else if (!it.split().isMatched()) {
1264                 if (remaining.transaction().id().isEmpty()) {
1265                     remaining = it;
1266                 } else {
1267                     toBeDeleted = it;
1268                 }
1269             }
1270         }
1271 
1272         // the user selected two transactions but they might be
1273         // selected in the wrong order. We check here, if the
1274         // other way around makes more sense and simply exchange
1275         // the two. See bko #435512
1276         if ((remaining.transaction().splitCount() == 1) && (toBeDeleted.transaction().splitCount() > 1)) {
1277             swap(remaining, toBeDeleted);
1278         }
1279 
1280         bool doMatch = true;
1281         // in case the transaction we are about to remove contains
1282         // more than one split, we ask the user if this is what
1283         // she really wants.
1284         if (toBeDeleted.transaction().splitCount() > 1) {
1285             KMergeTransactionsDlg dlg(m_currentAccount);
1286             dlg.addTransaction(remaining.transaction());
1287             dlg.addTransaction(toBeDeleted.transaction());
1288             doMatch = (dlg.exec() == QDialog::Accepted);
1289         }
1290         if (doMatch) {
1291             MyMoneyFileTransaction ft;
1292             try {
1293                 if (remaining.transaction().id().isEmpty())
1294                     throw MYMONEYEXCEPTION(QString::fromLatin1("No manually entered transaction selected for matching"));
1295                 if (toBeDeleted.transaction().id().isEmpty())
1296                     throw MYMONEYEXCEPTION(QString::fromLatin1("No imported transaction selected for matching"));
1297 
1298                 TransactionMatcher matcher(m_currentAccount);
1299                 matcher.match(remaining.transaction(), remaining.split(), toBeDeleted.transaction(), toBeDeleted.split(), true);
1300                 ft.commit();
1301             } catch (const MyMoneyException &e) {
1302                 KMessageBox::detailedSorry(q, i18n("Unable to match the selected transactions"), e.what());
1303             }
1304         }
1305     }
1306 
1307     /**
1308       * Mark the selected transactions as provided by @a flag. If
1309       * flag is @a MyMoneySplit::Unknown, the future state depends
1310       * on the current stat of the split's flag according to the
1311       * following table:
1312       *
1313       * - NotReconciled --> Cleared
1314       * - Cleared --> Reconciled
1315       * - Reconciled --> NotReconciled
1316       */
1317     void markTransaction(eMyMoney::Split::State flag)
1318     {
1319         Q_Q(KGlobalLedgerView);
1320         auto list = m_selectedTransactions;
1321         KMyMoneyRegister::SelectedTransactions::const_iterator it_t;
1322         auto cnt = list.count();
1323         auto i = 0;
1324         emit q->slotStatusProgress(0, cnt);
1325         MyMoneyFileTransaction ft;
1326         try {
1327             for (it_t = list.constBegin(); it_t != list.constEnd(); ++it_t) {
1328                 // turn on signals before we modify the last entry in the list
1329                 cnt--;
1330                 MyMoneyFile::instance()->blockSignals(cnt != 0);
1331 
1332                 // get a fresh copy
1333                 auto t = MyMoneyFile::instance()->transaction((*it_t).transaction().id());
1334                 auto sp = t.splitById((*it_t).split().id());
1335                 if (sp.reconcileFlag() != flag) {
1336                     if (flag == eMyMoney::Split::State::Unknown) {
1337                         if (m_reconciliationAccount.id().isEmpty()) {
1338                             // in normal mode we cycle through all states
1339                             switch (sp.reconcileFlag()) {
1340                             case eMyMoney::Split::State::NotReconciled:
1341                                 sp.setReconcileFlag(eMyMoney::Split::State::Cleared);
1342                                 break;
1343                             case eMyMoney::Split::State::Cleared:
1344                                 sp.setReconcileFlag(eMyMoney::Split::State::Reconciled);
1345                                 break;
1346                             case eMyMoney::Split::State::Reconciled:
1347                                 sp.setReconcileFlag(eMyMoney::Split::State::NotReconciled);
1348                                 break;
1349                             default:
1350                                 break;
1351                             }
1352                         } else {
1353                             // in reconciliation mode we skip the reconciled state
1354                             switch (sp.reconcileFlag()) {
1355                             case eMyMoney::Split::State::NotReconciled:
1356                                 sp.setReconcileFlag(eMyMoney::Split::State::Cleared);
1357                                 break;
1358                             case eMyMoney::Split::State::Cleared:
1359                                 sp.setReconcileFlag(eMyMoney::Split::State::NotReconciled);
1360                                 break;
1361                             default:
1362                                 break;
1363                             }
1364                         }
1365                     } else {
1366                         sp.setReconcileFlag(flag);
1367                     }
1368 
1369                     t.modifySplit(sp);
1370                     MyMoneyFile::instance()->modifyTransaction(t);
1371                 }
1372                 emit q->slotStatusProgress(i++, 0);
1373             }
1374             emit q->slotStatusProgress(-1, -1);
1375             ft.commit();
1376         } catch (const MyMoneyException &e) {
1377             KMessageBox::detailedSorry(q, i18n("Unable to modify transaction"), e.what());
1378         }
1379     }
1380 
1381     // move a stock transaction from one investment account to another
1382     void moveInvestmentTransaction(const QString& /*fromId*/,
1383                                    const QString& toId,
1384                                    const MyMoneyTransaction& tx)
1385     {
1386         MyMoneyAccount toInvAcc = MyMoneyFile::instance()->account(toId);
1387         MyMoneyTransaction t(tx);
1388         // first determine which stock we are dealing with.
1389         // fortunately, investment transactions have only one stock involved
1390         QString stockAccountId;
1391         QString stockSecurityId;
1392         MyMoneySplit s;
1393         foreach (const auto split, t.splits()) {
1394             stockAccountId = split.accountId();
1395             stockSecurityId =
1396                 MyMoneyFile::instance()->account(stockAccountId).currencyId();
1397             if (!MyMoneyFile::instance()->security(stockSecurityId).isCurrency()) {
1398                 s = split;
1399                 break;
1400             }
1401         }
1402         // Now check the target investment account to see if it
1403         // contains a stock with this id
1404         QString newStockAccountId;
1405         foreach (const auto sAccount, toInvAcc.accountList()) {
1406             if (MyMoneyFile::instance()->account(sAccount).currencyId() ==
1407                     stockSecurityId) {
1408                 newStockAccountId = sAccount;
1409                 break;
1410             }
1411         }
1412         // if it doesn't exist, we need to add it as a copy of the old one
1413         // no 'copyAccount()' function??
1414         if (newStockAccountId.isEmpty()) {
1415             MyMoneyAccount stockAccount =
1416                 MyMoneyFile::instance()->account(stockAccountId);
1417             MyMoneyAccount newStock;
1418             newStock.setName(stockAccount.name());
1419             newStock.setNumber(stockAccount.number());
1420             newStock.setDescription(stockAccount.description());
1421             newStock.setInstitutionId(stockAccount.institutionId());
1422             newStock.setOpeningDate(stockAccount.openingDate());
1423             newStock.setAccountType(stockAccount.accountType());
1424             newStock.setCurrencyId(stockAccount.currencyId());
1425             newStock.setClosed(stockAccount.isClosed());
1426             MyMoneyFile::instance()->addAccount(newStock, toInvAcc);
1427             newStockAccountId = newStock.id();
1428         }
1429         // now update the split and the transaction
1430         s.setAccountId(newStockAccountId);
1431         t.modifySplit(s);
1432         MyMoneyFile::instance()->modifyTransaction(t);
1433     }
1434 
1435     void createTransactionMoveMenu()
1436     {
1437         Q_Q(KGlobalLedgerView);
1438         if (!m_moveToAccountSelector) {
1439             auto menu = pMenus[eMenu::Menu::MoveTransaction];
1440             if (menu ) {
1441                 auto accountSelectorAction = new QWidgetAction(menu);
1442                 m_moveToAccountSelector = new KMyMoneyAccountSelector(menu, 0, false);
1443                 m_moveToAccountSelector->setObjectName("transaction_move_menu_selector");
1444                 accountSelectorAction->setDefaultWidget(m_moveToAccountSelector);
1445                 menu->addAction(accountSelectorAction);
1446                 q->connect(m_moveToAccountSelector, &QObject::destroyed, q, &KGlobalLedgerView::slotObjectDestroyed);
1447                 q->connect(m_moveToAccountSelector, &KMyMoneySelector::itemSelected, q, &KGlobalLedgerView::slotMoveToAccount);
1448             }
1449         }
1450     }
1451 
1452     QList<QPair<MyMoneyTransaction, MyMoneySplit> > automaticReconciliation(const MyMoneyAccount &account,
1453             const QList<QPair<MyMoneyTransaction, MyMoneySplit> > &transactions,
1454             const MyMoneyMoney &amount)
1455     {
1456         Q_Q(KGlobalLedgerView);
1457         static const int NR_OF_STEPS_LIMIT = 60000;
1458         static const int PROGRESSBAR_STEPS = 1000;
1459         QList<QPair<MyMoneyTransaction, MyMoneySplit> > result = transactions;
1460 
1461         // optimize the most common case - all transactions should be cleared
1462         QListIterator<QPair<MyMoneyTransaction, MyMoneySplit> > itTransactionSplitResult(result);
1463         MyMoneyMoney transactionsBalance;
1464         while (itTransactionSplitResult.hasNext()) {
1465             const QPair<MyMoneyTransaction, MyMoneySplit> &transactionSplit = itTransactionSplitResult.next();
1466             transactionsBalance += transactionSplit.second.shares();
1467         }
1468         if (amount == transactionsBalance) {
1469             result = transactions;
1470             return result;
1471         }
1472 
1473         // only one transaction is uncleared
1474         itTransactionSplitResult.toFront();
1475         int index = 0;
1476         while (itTransactionSplitResult.hasNext()) {
1477             const QPair<MyMoneyTransaction, MyMoneySplit> &transactionSplit = itTransactionSplitResult.next();
1478             if (transactionsBalance - transactionSplit.second.shares() == amount) {
1479                 result.removeAt(index);
1480                 return result;
1481             }
1482             index++;
1483         }
1484 
1485         // more than one transaction is uncleared - apply the algorithm
1486         result.clear();
1487 
1488         const auto& security = MyMoneyFile::instance()->security(account.currencyId());
1489         double precision = 0.1 / account.fraction(security);
1490 
1491         QList<MyMoneyMoney> sumList;
1492         sumList << MyMoneyMoney();
1493 
1494         QMap<MyMoneyMoney, QList<QPair<QString, QString> > > sumToComponentsMap;
1495 
1496         struct restoreStatusMsgHelper {
1497             restoreStatusMsgHelper(KGlobalLedgerView* qq)
1498                 : q(qq) {}
1499 
1500             ~restoreStatusMsgHelper()
1501             {
1502                 q->slotStatusMsg(QString());
1503                 q->slotStatusProgress(-1, -1);
1504             }
1505             KGlobalLedgerView* q;
1506         } restoreHelper(q);
1507 
1508         q->slotStatusMsg(i18n("Running automatic reconciliation"));
1509         q->slotStatusProgress(0, NR_OF_STEPS_LIMIT);
1510 
1511         // compute the possible matches
1512         QListIterator<QPair<MyMoneyTransaction, MyMoneySplit> > it_ts(transactions);
1513         while (it_ts.hasNext()) {
1514             const QPair<MyMoneyTransaction, MyMoneySplit> &transactionSplit = it_ts.next();
1515             QListIterator<MyMoneyMoney> itSum(sumList);
1516             QList<MyMoneyMoney> tempList;
1517             while (itSum.hasNext()) {
1518                 const MyMoneyMoney &sum = itSum.next();
1519                 QList<QPair<QString, QString> > splitIds;
1520                 splitIds << qMakePair<QString, QString>(transactionSplit.first.id(), transactionSplit.second.id());
1521                 if (sumToComponentsMap.contains(sum)) {
1522                     if (sumToComponentsMap.value(sum).contains(qMakePair<QString, QString>(transactionSplit.first.id(), transactionSplit.second.id()))) {
1523                         continue;
1524                     }
1525                     splitIds.append(sumToComponentsMap.value(sum));
1526                 }
1527                 tempList << transactionSplit.second.shares() + sum;
1528                 sumToComponentsMap[transactionSplit.second.shares() + sum] = splitIds;
1529                 int size = sumToComponentsMap.size();
1530                 if (size % PROGRESSBAR_STEPS == 0) {
1531                     q->slotStatusProgress(size, 0);
1532                 }
1533                 if (size > NR_OF_STEPS_LIMIT) {
1534                     return result; // it's taking too much resources abort the algorithm
1535                 }
1536             }
1537             QList<MyMoneyMoney> unionList;
1538             unionList.append(tempList);
1539             unionList.append(sumList);
1540             qSort(unionList);
1541             sumList.clear();
1542             MyMoneyMoney smallestSumFromUnion = unionList.first();
1543             sumList.append(smallestSumFromUnion);
1544             QListIterator<MyMoneyMoney> itUnion(unionList);
1545             while (itUnion.hasNext()) {
1546                 MyMoneyMoney sumFromUnion = itUnion.next();
1547                 if (smallestSumFromUnion < MyMoneyMoney(1 - precision / transactions.size())*sumFromUnion) {
1548                     smallestSumFromUnion = sumFromUnion;
1549                     sumList.append(sumFromUnion);
1550                 }
1551             }
1552         }
1553 
1554         q->slotStatusProgress(NR_OF_STEPS_LIMIT / PROGRESSBAR_STEPS, 0);
1555         if (sumToComponentsMap.contains(amount)) {
1556             QListIterator<QPair<MyMoneyTransaction, MyMoneySplit> > itTransactionSplit(transactions);
1557             while (itTransactionSplit.hasNext()) {
1558                 const QPair<MyMoneyTransaction, MyMoneySplit> &transactionSplit = itTransactionSplit.next();
1559                 const QList<QPair<QString, QString> > &splitIds = sumToComponentsMap.value(amount);
1560                 if (splitIds.contains(qMakePair<QString, QString>(transactionSplit.first.id(), transactionSplit.second.id()))) {
1561                     result.append(transactionSplit);
1562                 }
1563             }
1564         }
1565 
1566 #ifdef KMM_DEBUG
1567         qDebug("For the amount %s a number of %d possible sums where computed from the set of %d transactions: ",
1568                qPrintable(MyMoneyUtils::formatMoney(amount, security)), sumToComponentsMap.size(), transactions.size());
1569 #endif
1570 
1571         return result;
1572     }
1573 
1574     KGlobalLedgerView   *q_ptr;
1575     MousePressFilter    *m_mousePressFilter;
1576     KMyMoneyRegister::RegisterSearchLineWidget* m_registerSearchLine;
1577 //  QString              m_reconciliationAccount;
1578     QDate                m_reconciliationDate;
1579     MyMoneyMoney         m_endingBalance;
1580     int                  m_precision;
1581     bool                 m_recursion;
1582     bool                 m_showDetails;
1583     eWidgets::eRegister::Action m_action;
1584 
1585     // models
1586     AccountNamesFilterProxyModel *m_filterProxyModel;
1587 
1588     // widgets
1589     KMyMoneyAccountCombo* m_accountComboBox;
1590 
1591     MyMoneyMoney         m_totalBalance;
1592     bool                 m_balanceIsApproximated;
1593     // frames
1594     QFrame*                       m_toolbarFrame;
1595     QFrame*                       m_registerFrame;
1596     QFrame*                       m_buttonFrame;
1597     QFrame*                       m_formFrame;
1598     QFrame*                       m_summaryFrame;
1599 
1600     // widgets
1601     KMyMoneyRegister::Register*   m_register;
1602     KToolBar*                     m_buttonbar;
1603 
1604     /**
1605       * This member holds the currently selected account
1606       */
1607     MyMoneyAccount m_currentAccount;
1608     QString m_lastSelectedAccountID;
1609 
1610     MyMoneyAccount m_reconciliationAccount;
1611 
1612     /**
1613       * This member holds the transaction list
1614       */
1615     QList<QPair<MyMoneyTransaction, MyMoneySplit> >  m_transactionList;
1616 
1617     QLabel*                         m_leftSummaryLabel;
1618     QLabel*                         m_centerSummaryLabel;
1619     QLabel*                         m_rightSummaryLabel;
1620 
1621     KMyMoneyTransactionForm::TransactionForm* m_form;
1622 
1623     /**
1624       * This member holds the load state of page
1625       */
1626     bool                            m_needLoad;
1627 
1628     bool                            m_newAccountLoaded;
1629     bool                            m_inEditMode;
1630 
1631     QWidgetList                     m_tabOrderWidgets;
1632     QPoint                          m_tooltipPosn;
1633     KMyMoneyRegister::SelectedTransactions m_selectedTransactions;
1634     /**
1635       * This member keeps the date that was used as the last posting date.
1636       * It will be updated whenever the user modifies the post date
1637       * and is used to preset the posting date when new transactions are created.
1638       * This member is initialised to the current date when the program is started.
1639       */
1640     static QDate         m_lastPostDate;
1641     // pointer to the current transaction editor
1642     QPointer<TransactionEditor> m_transactionEditor;
1643 
1644     // id's that need to be remembered
1645     QString  m_accountGoto, m_payeeGoto;
1646     QString  m_lastPayeeEnteredId;
1647     QScopedPointer<KBalanceWarning> m_balanceWarning;
1648     KMyMoneyAccountSelector* m_moveToAccountSelector;
1649 
1650     // Reconciliation dialog
1651     KEndingBalanceDlg*    m_endingBalanceDlg;
1652     KFindTransactionDlg*  m_searchDlg;
1653 };
1654 
1655 #endif