File indexing completed on 2024-05-12 05:07:56

0001 /*
0002     SPDX-FileCopyrightText: 2010-2014 Cristian Oneț <onet.cristian@gmail.com>
0003     SPDX-FileCopyrightText: 2017-2018 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
0004     SPDX-FileCopyrightText: 2019 Thomas Baumgart <tbaumgart@kde.org>
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "kmymoneyaccounttreeview.h"
0009 
0010 // ----------------------------------------------------------------------------
0011 // QT Includes
0012 
0013 #include <QObject>
0014 #include <QHeaderView>
0015 #include <QMouseEvent>
0016 #include <QPoint>
0017 
0018 // ----------------------------------------------------------------------------
0019 // KDE Includes
0020 
0021 #include <KConfigGroup>
0022 #include <KSharedConfig>
0023 
0024 // ----------------------------------------------------------------------------
0025 // Project Includes
0026 
0027 #include "mymoneyfile.h"
0028 #include "mymoneyinstitution.h"
0029 #include "mymoneyaccount.h"
0030 #include "accountsmodel.h"
0031 #include "institutionsmodel.h"
0032 #include "accountsproxymodel.h"
0033 #include "menuenums.h"
0034 #include "selectedobjects.h"
0035 
0036 class KMyMoneyAccountTreeViewPrivate
0037 {
0038     Q_DECLARE_PUBLIC(KMyMoneyAccountTreeView)
0039     Q_DISABLE_COPY_MOVE(KMyMoneyAccountTreeViewPrivate)
0040 
0041 public:
0042     KMyMoneyAccountTreeViewPrivate(KMyMoneyAccountTreeView *qq)
0043         : q_ptr(qq),
0044           proxyModel(new AccountsProxyModel(qq))
0045     {}
0046 
0047     void openIndex(const QModelIndex &index)
0048     {
0049         Q_Q(KMyMoneyAccountTreeView);
0050         if (index.isValid()) {
0051             QModelIndex baseIdx = MyMoneyFile::baseModel()->mapToBaseSource(index);
0052             // baseIdx could point into the accountsModel or the institutionsModel.
0053             // in case of the institutionsModel it is unclear if it is an account or
0054             // an institution. So we simply extract the id and check where we find
0055             // the object.
0056             const auto objId = baseIdx.data(eMyMoney::Model::IdRole).toString();
0057             auto idx = MyMoneyFile::instance()->accountsModel()->indexById(objId);
0058             if (idx.isValid() || (objId == MyMoneyAccount::stdAccName(eMyMoney::Account::Standard::Favorite))) {
0059                 // In case we should open an account or category we do it
0060                 // For the top level accounts we switch between expanded and collapsed view
0061                 if (index.parent().isValid()) {
0062                     Q_EMIT q->requestActionTrigger(eMenu::Action::OpenAccount);
0063                 } else {
0064                     q->setExpanded(index, !q->isExpanded(index));
0065                 }
0066             } else {
0067                 idx = MyMoneyFile::instance()->institutionsModel()->indexById(objId);
0068                 if (idx.isValid()) {
0069                     Q_EMIT q->requestActionTrigger(eMenu::Action::EditInstitution);
0070                 }
0071             }
0072         }
0073     }
0074 
0075     KMyMoneyAccountTreeView*  q_ptr;
0076     AccountsProxyModel*       proxyModel;
0077 };
0078 
0079 KMyMoneyAccountTreeView::KMyMoneyAccountTreeView(QWidget *parent)
0080     : KMyMoneyTreeView(parent)
0081     , d_ptr(new KMyMoneyAccountTreeViewPrivate(this))
0082 {
0083     setContextMenuPolicy(Qt::CustomContextMenu);            // allow context menu to be opened on tree items
0084     connect(this, &QWidget::customContextMenuRequested, this, &KMyMoneyAccountTreeView::customContextMenuRequested);
0085     setAllColumnsShowFocus(true);
0086     setAlternatingRowColors(true);
0087     setIconSize(QSize(22, 22));
0088     setSortingEnabled(true);
0089     QTreeView::header()->setStretchLastSection(false);
0090 
0091     connect(this, &KMyMoneyTreeView::startEdit, this, [&](const QModelIndex& idx) {
0092         Q_D(KMyMoneyAccountTreeView);
0093         d->openIndex(idx);
0094     });
0095 }
0096 
0097 KMyMoneyAccountTreeView::~KMyMoneyAccountTreeView()
0098 {
0099 }
0100 
0101 
0102 void KMyMoneyAccountTreeView::setModel(QAbstractItemModel* model)
0103 {
0104     Q_D(KMyMoneyAccountTreeView);
0105     d->proxyModel->setSourceModel(model);
0106     QTreeView::setModel(d->proxyModel);
0107 
0108     for (int column = 0; column < model->columnCount(); column++) {
0109         QVariant headerData = model->headerData(column, Qt::Horizontal, Qt::UserRole);
0110         QHeaderView::ResizeMode mode = QHeaderView::ResizeMode(headerData.toInt());
0111         QTreeView::header()->setSectionResizeMode(column, mode);
0112     }
0113 }
0114 
0115 void KMyMoneyAccountTreeView::setProxyModel(AccountsProxyModel* model)
0116 {
0117     Q_D(KMyMoneyAccountTreeView);
0118     // unlink a possible sourceModel
0119     QAbstractItemModel* sourceModel = d->proxyModel->sourceModel();
0120     if (sourceModel) {
0121         d->proxyModel->setSourceModel(nullptr);
0122     }
0123 
0124     // delete the old proxy
0125     d->proxyModel->deleteLater();
0126 
0127     // reparent the new proxy
0128     model->setParent(this);
0129 
0130     // and insert it into the chain
0131     d->proxyModel = model;
0132     d->proxyModel->setSourceModel(sourceModel);
0133     QTreeView::setModel(d->proxyModel);
0134 
0135 }
0136 
0137 AccountsProxyModel* KMyMoneyAccountTreeView::proxyModel() const
0138 {
0139     Q_D(const KMyMoneyAccountTreeView);
0140     return d->proxyModel;
0141 }
0142 
0143 void KMyMoneyAccountTreeView::customContextMenuRequested(const QPoint pos)
0144 {
0145     if (currentIndex().isValid() && (model()->flags(currentIndex()) & Qt::ItemIsSelectable)) {
0146         auto objId = currentIndex().data(eMyMoney::Model::IdRole).toString();
0147         const auto account = MyMoneyFile::instance()->accountsModel()->itemById(objId);
0148         if (!account.id().isEmpty()) {
0149             if (account.isIncomeExpense()) {
0150                 Q_EMIT requestCustomContextMenu(eMenu::Menu::Category, viewport()->mapToGlobal(pos));
0151             } else {
0152                 Q_EMIT requestCustomContextMenu(eMenu::Menu::Account, viewport()->mapToGlobal(pos));
0153             }
0154         }
0155         const auto institution = MyMoneyFile::instance()->institutionsModel()->itemById(objId);
0156         if (!institution.id().isEmpty()) {
0157             // the institutions model also reports accounts as institutions at this point.
0158             // We can differentiate between the two objects by looking at the parent of
0159             // the index. If it is valid, we have been called via an account,
0160             // if it is invalid the source is an institution.
0161             if (!currentIndex().parent().isValid()) {
0162                 Q_EMIT requestCustomContextMenu(eMenu::Menu::Institution, viewport()->mapToGlobal(pos));
0163             }
0164         }
0165     }
0166 }
0167 
0168 void KMyMoneyAccountTreeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
0169 {
0170     SelectedObjects selections;
0171 
0172     QTreeView::selectionChanged(selected, deselected);
0173     if (!selected.isEmpty()) {
0174         QModelIndexList idxList = selected.indexes();
0175         if (!idxList.isEmpty()) {
0176             auto objId = selected.indexes().front().data(eMyMoney::Model::IdRole).toString();
0177             const auto account = MyMoneyFile::instance()->accountsModel()->itemById(objId);
0178             if (!account.id().isEmpty()) {
0179                 selections.addSelection(SelectedObjects::Account, account.id());
0180                 if (!account.institutionId().isEmpty()) {
0181                     selections.addSelection(SelectedObjects::Institution, account.institutionId());
0182                 }
0183             } else {
0184 
0185                 const auto institution = MyMoneyFile::instance()->institutionsModel()->itemById(objId);
0186                 if (!institution.id().isEmpty()) {
0187                     selections.addSelection(SelectedObjects::Institution, institution.id());
0188                 }
0189             }
0190         }
0191     }
0192     // since no object was selected reset the object selection
0193     Q_EMIT requestSelectionChange(selections);
0194 }