File indexing completed on 2024-05-12 16:42:17
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-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "accountsproxymodel.h" 0008 #include "accountsproxymodel_p.h" 0009 0010 // ---------------------------------------------------------------------------- 0011 // QT Includes 0012 0013 // ---------------------------------------------------------------------------- 0014 // KDE Includes 0015 0016 // ---------------------------------------------------------------------------- 0017 // Project Includes 0018 0019 #include "modelenums.h" 0020 #include "mymoneyenums.h" 0021 #include "mymoneyinstitution.h" 0022 #include "mymoneyaccount.h" 0023 #include "mymoneymoney.h" 0024 0025 using namespace eAccountsModel; 0026 0027 #if QT_VERSION < QT_VERSION_CHECK(5,10,0) 0028 #define QSortFilterProxyModel KRecursiveFilterProxyModel 0029 #endif 0030 AccountsProxyModel::AccountsProxyModel(QObject *parent) : 0031 QSortFilterProxyModel(parent), 0032 d_ptr(new AccountsProxyModelPrivate) 0033 { 0034 setRecursiveFilteringEnabled(true); 0035 setDynamicSortFilter(true); 0036 setSortLocaleAware(true); 0037 setFilterCaseSensitivity(Qt::CaseInsensitive); 0038 } 0039 0040 AccountsProxyModel::AccountsProxyModel(AccountsProxyModelPrivate &dd, QObject *parent) : 0041 QSortFilterProxyModel(parent), d_ptr(&dd) 0042 { 0043 setRecursiveFilteringEnabled(true); 0044 } 0045 #undef QSortFilterProxyModel 0046 0047 AccountsProxyModel::~AccountsProxyModel() 0048 { 0049 } 0050 0051 /** 0052 * This function was re-implemented so we could have a special display order (favorites first) 0053 */ 0054 bool AccountsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const 0055 { 0056 Q_D(const AccountsProxyModel); 0057 if (!left.isValid() || !right.isValid()) 0058 return false; 0059 // different sorting based on the column which is being sorted 0060 switch (d->m_mdlColumns->at(left.column())) { 0061 // for the accounts column sort based on the DisplayOrderRole 0062 case Column::Account: { 0063 const auto leftData = sourceModel()->data(left, (int)Role::DisplayOrder); 0064 const auto rightData = sourceModel()->data(right, (int)Role::DisplayOrder); 0065 0066 if (leftData.toInt() == rightData.toInt()) { 0067 // sort items of the same display order alphabetically 0068 return QSortFilterProxyModel::lessThan(left, right); 0069 } 0070 return leftData.toInt() < rightData.toInt(); 0071 } 0072 // the total balance and value columns are sorted based on the value of the account 0073 case Column::TotalBalance: 0074 case Column::TotalValue: { 0075 const auto leftData = sourceModel()->data(sourceModel()->index(left.row(), (int)Column::Account, left.parent()), (int)Role::TotalValue); 0076 const auto rightData = sourceModel()->data(sourceModel()->index(right.row(), (int)Column::Account, right.parent()), (int)Role::TotalValue); 0077 return leftData.value<MyMoneyMoney>() < rightData.value<MyMoneyMoney>(); 0078 } 0079 default: 0080 break; 0081 } 0082 return QSortFilterProxyModel::lessThan(left, right); 0083 } 0084 0085 /** 0086 * This function was re-implemented to consider all the filtering aspects that we need in the application. 0087 */ 0088 bool AccountsProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const 0089 { 0090 const auto index = sourceModel()->index(source_row, (int)Column::Account, source_parent); 0091 return acceptSourceItem(index) && filterAcceptsRowOrChildRows(source_row, source_parent); 0092 } 0093 0094 /** 0095 * This function implements a recursive matching. It is used to match a row even if it's values 0096 * don't match the current filtering criteria but it has at least one child row that does match. 0097 */ 0098 bool AccountsProxyModel::filterAcceptsRowOrChildRows(int source_row, const QModelIndex &source_parent) const 0099 { 0100 if (QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent)) 0101 return true; 0102 0103 const auto index = sourceModel()->index(source_row, (int)Column::Account, source_parent); 0104 for (auto i = 0; i < sourceModel()->rowCount(index); ++i) { 0105 if (filterAcceptsRowOrChildRows(i, index)) 0106 return true; 0107 } 0108 return false; 0109 } 0110 0111 /** 0112 * Add the given account group to the filter. 0113 * @param group The account group to be added. 0114 * @see eMyMoney::Account 0115 */ 0116 void AccountsProxyModel::addAccountGroup(const QVector<eMyMoney::Account::Type> &groups) 0117 { 0118 Q_D(AccountsProxyModel); 0119 foreach (const auto group, groups) { 0120 switch (group) { 0121 case eMyMoney::Account::Type::Asset: 0122 d->m_typeList << eMyMoney::Account::Type::Checkings; 0123 d->m_typeList << eMyMoney::Account::Type::Savings; 0124 d->m_typeList << eMyMoney::Account::Type::Cash; 0125 d->m_typeList << eMyMoney::Account::Type::AssetLoan; 0126 d->m_typeList << eMyMoney::Account::Type::CertificateDep; 0127 d->m_typeList << eMyMoney::Account::Type::Investment; 0128 d->m_typeList << eMyMoney::Account::Type::Stock; 0129 d->m_typeList << eMyMoney::Account::Type::MoneyMarket; 0130 d->m_typeList << eMyMoney::Account::Type::Asset; 0131 d->m_typeList << eMyMoney::Account::Type::Currency; 0132 break; 0133 case eMyMoney::Account::Type::Liability: 0134 d->m_typeList << eMyMoney::Account::Type::CreditCard; 0135 d->m_typeList << eMyMoney::Account::Type::Loan; 0136 d->m_typeList << eMyMoney::Account::Type::Liability; 0137 break; 0138 case eMyMoney::Account::Type::Income: 0139 d->m_typeList << eMyMoney::Account::Type::Income; 0140 break; 0141 case eMyMoney::Account::Type::Expense: 0142 d->m_typeList << eMyMoney::Account::Type::Expense; 0143 break; 0144 case eMyMoney::Account::Type::Equity: 0145 d->m_typeList << eMyMoney::Account::Type::Equity; 0146 break; 0147 default: 0148 d->m_typeList << group; 0149 break; 0150 } 0151 } 0152 invalidateFilter(); 0153 } 0154 0155 /** 0156 * Add the given account type to the filter. 0157 * @param type The account type to be added. 0158 * @see eMyMoney::Account 0159 */ 0160 void AccountsProxyModel::addAccountType(eMyMoney::Account::Type type) 0161 { 0162 Q_D(AccountsProxyModel); 0163 d->m_typeList << type; 0164 invalidateFilter(); 0165 } 0166 0167 /** 0168 * Remove the given account type from the filter. 0169 * @param type The account type to be removed. 0170 * @see eMyMoney::Account 0171 */ 0172 void AccountsProxyModel::removeAccountType(eMyMoney::Account::Type type) 0173 { 0174 Q_D(AccountsProxyModel); 0175 if (d->m_typeList.removeAll(type) > 0) { 0176 invalidateFilter(); 0177 } 0178 } 0179 0180 /** 0181 * Use this to reset the filter. 0182 */ 0183 void AccountsProxyModel::clear() 0184 { 0185 Q_D(AccountsProxyModel); 0186 d->m_typeList.clear(); 0187 invalidateFilter(); 0188 } 0189 0190 /** 0191 * Implementation function that performs the actual filtering. 0192 */ 0193 bool AccountsProxyModel::acceptSourceItem(const QModelIndex &source) const 0194 { 0195 Q_D(const AccountsProxyModel); 0196 if (source.isValid()) { 0197 const auto data = sourceModel()->data(source, (int)Role::Account); 0198 if (data.isValid()) { 0199 if (data.canConvert<MyMoneyAccount>()) { 0200 const auto account = data.value<MyMoneyAccount>(); 0201 if ((hideClosedAccounts() && account.isClosed())) 0202 return false; 0203 0204 // we hide stock accounts if not in expert mode 0205 if (account.isInvest() && hideEquityAccounts()) 0206 return false; 0207 0208 // we hide equity accounts if not in expert mode 0209 if (account.accountType() == eMyMoney::Account::Type::Equity && hideEquityAccounts()) 0210 return false; 0211 0212 // we hide unused income and expense accounts if the specific flag is set 0213 if ((account.accountType() == eMyMoney::Account::Type::Income || account.accountType() == eMyMoney::Account::Type::Expense) && hideUnusedIncomeExpenseAccounts()) { 0214 const auto totalValue = sourceModel()->data(source, (int)Role::TotalValue); 0215 if (totalValue.isValid() && totalValue.value<MyMoneyMoney>().isZero()) { 0216 emit const_cast<AccountsProxyModel*>(this)->unusedIncomeExpenseAccountHidden(); 0217 return false; 0218 } 0219 } 0220 0221 if (d->m_typeList.contains(account.accountType())) 0222 return true; 0223 } else if (data.canConvert<MyMoneyInstitution>() && sourceModel()->rowCount(source) == 0) { 0224 return true; 0225 } 0226 // let the visibility of all other institutions (the ones with children) be controlled by the visibility of their children 0227 } 0228 0229 // all parents that have at least one visible child must be visible 0230 const auto rowCount = sourceModel()->rowCount(source); 0231 for (auto i = 0; i < rowCount; ++i) { 0232 const auto index = sourceModel()->index(i, (int)(int)Column::Account, source); 0233 if (acceptSourceItem(index)) 0234 return true; 0235 } 0236 } 0237 return false; 0238 } 0239 0240 /** 0241 * Set if closed accounts should be hidden or not. 0242 * @param hideClosedAccounts 0243 */ 0244 void AccountsProxyModel::setHideClosedAccounts(bool hideClosedAccounts) 0245 { 0246 Q_D(AccountsProxyModel); 0247 if (d->m_hideClosedAccounts != hideClosedAccounts) { 0248 d->m_hideClosedAccounts = hideClosedAccounts; 0249 invalidateFilter(); 0250 } 0251 } 0252 0253 /** 0254 * Check if closed accounts are hidden or not. 0255 */ 0256 bool AccountsProxyModel::hideClosedAccounts() const 0257 { 0258 Q_D(const AccountsProxyModel); 0259 return d->m_hideClosedAccounts; 0260 } 0261 0262 /** 0263 * Set if equity and investment accounts should be hidden or not. 0264 * @param hideEquityAccounts 0265 */ 0266 void AccountsProxyModel::setHideEquityAccounts(bool hideEquityAccounts) 0267 { 0268 Q_D(AccountsProxyModel); 0269 if (d->m_hideEquityAccounts != hideEquityAccounts) { 0270 d->m_hideEquityAccounts = hideEquityAccounts; 0271 invalidateFilter(); 0272 } 0273 } 0274 0275 /** 0276 * Check if equity and investment accounts are hidden or not. 0277 */ 0278 bool AccountsProxyModel::hideEquityAccounts() const 0279 { 0280 Q_D(const AccountsProxyModel); 0281 return d->m_hideEquityAccounts; 0282 } 0283 0284 /** 0285 * Set if empty categories should be hidden or not. 0286 * @param hideUnusedIncomeExpenseAccounts 0287 */ 0288 void AccountsProxyModel::setHideUnusedIncomeExpenseAccounts(bool hideUnusedIncomeExpenseAccounts) 0289 { 0290 Q_D(AccountsProxyModel); 0291 if (d->m_hideUnusedIncomeExpenseAccounts != hideUnusedIncomeExpenseAccounts) { 0292 d->m_hideUnusedIncomeExpenseAccounts = hideUnusedIncomeExpenseAccounts; 0293 invalidateFilter(); 0294 } 0295 } 0296 0297 /** 0298 * Check if empty categories are hidden or not. 0299 */ 0300 bool AccountsProxyModel::hideUnusedIncomeExpenseAccounts() const 0301 { 0302 Q_D(const AccountsProxyModel); 0303 return d->m_hideUnusedIncomeExpenseAccounts; 0304 } 0305 0306 /** 0307 * Returns the number of visible items after filtering. In case @a includeBaseAccounts 0308 * is set to @c true, the 5 base accounts (asset, liability, income, expense and equity) 0309 * will also be counted. The default is @c false. 0310 */ 0311 int AccountsProxyModel::visibleItems(bool includeBaseAccounts) const 0312 { 0313 auto rows = 0; 0314 for (auto i = 0; i < rowCount(QModelIndex()); ++i) { 0315 if(includeBaseAccounts) { 0316 ++rows; 0317 } 0318 const auto childIndex = index(i, 0); 0319 if (hasChildren(childIndex)) { 0320 rows += visibleItems(childIndex); 0321 } 0322 } 0323 return rows; 0324 } 0325 0326 /** 0327 * Returns the number of visible items under the given @a index. 0328 * The column of the @a index must be 0, otherwise no count will 0329 * be returned (returns 0). 0330 */ 0331 int AccountsProxyModel::visibleItems(const QModelIndex& index) const 0332 { 0333 auto rows = 0; 0334 if (index.isValid() && index.column() == (int)Column::Account) { // CAUTION! Assumption is being made that Account column number is always 0 0335 const auto *model = index.model(); 0336 const auto rowCount = model->rowCount(index); 0337 for (auto i = 0; i < rowCount; ++i) { 0338 ++rows; 0339 const auto childIndex = model->index(i, index.column(), index); 0340 if (model->hasChildren(childIndex)) 0341 rows += visibleItems(childIndex); 0342 } 0343 } 0344 return rows; 0345 } 0346 0347 void AccountsProxyModel::setSourceColumns(QList<eAccountsModel::Column> *columns) 0348 { 0349 Q_D(AccountsProxyModel); 0350 d->m_mdlColumns = columns; 0351 }