File indexing completed on 2024-05-19 05:08:32
0001 /* 0002 SPDX-FileCopyrightText: 2003-2018 Thomas Baumgart <tbaumgart@kde.org> 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 "kmymoneyaccountselector.h" 0008 #include "kmymoneyselector_p.h" 0009 0010 // ---------------------------------------------------------------------------- 0011 // QT Includes 0012 0013 #include <QIcon> 0014 #include <QList> 0015 #include <QPixmapCache> 0016 #include <QPushButton> 0017 #include <QVBoxLayout> 0018 0019 // ---------------------------------------------------------------------------- 0020 // KDE Includes 0021 0022 #include <KLocalizedString> 0023 0024 // ---------------------------------------------------------------------------- 0025 // Project Includes 0026 0027 #include "dialogenums.h" 0028 #include "icons.h" 0029 #include "mymoneyaccount.h" 0030 #include "mymoneyenums.h" 0031 #include "mymoneyfile.h" 0032 #include "widgetenums.h" 0033 0034 using namespace Icons; 0035 using namespace eMyMoney; 0036 0037 class KMyMoneyAccountSelectorPrivate : public KMyMoneySelectorPrivate 0038 { 0039 Q_DISABLE_COPY(KMyMoneyAccountSelectorPrivate) 0040 0041 public: 0042 KMyMoneyAccountSelectorPrivate(KMyMoneyAccountSelector *qq) : 0043 KMyMoneySelectorPrivate(qq), 0044 m_allAccountsButton(0), 0045 m_noAccountButton(0), 0046 m_incomeCategoriesButton(0), 0047 m_expenseCategoriesButton(0) 0048 { 0049 } 0050 0051 QPushButton* m_allAccountsButton; 0052 QPushButton* m_noAccountButton; 0053 QPushButton* m_incomeCategoriesButton; 0054 QPushButton* m_expenseCategoriesButton; 0055 QList<int> m_typeList; 0056 QStringList m_accountList; 0057 }; 0058 0059 KMyMoneyAccountSelector::KMyMoneyAccountSelector(QWidget *parent, Qt::WindowFlags flags, const bool createButtons) : 0060 KMyMoneySelector(*new KMyMoneyAccountSelectorPrivate(this), parent, flags) 0061 { 0062 Q_D(KMyMoneyAccountSelector); 0063 if (createButtons) { 0064 QVBoxLayout* buttonLayout = new QVBoxLayout(); 0065 buttonLayout->setSpacing(6); 0066 0067 d->m_allAccountsButton = new QPushButton(this); 0068 d->m_allAccountsButton->setObjectName("m_allAccountsButton"); 0069 d->m_allAccountsButton->setText(i18nc("Select all accounts", "All")); 0070 buttonLayout->addWidget(d->m_allAccountsButton); 0071 0072 d->m_incomeCategoriesButton = new QPushButton(this); 0073 d->m_incomeCategoriesButton->setObjectName("m_incomeCategoriesButton"); 0074 d->m_incomeCategoriesButton->setText(i18n("Income")); 0075 buttonLayout->addWidget(d->m_incomeCategoriesButton); 0076 0077 d->m_expenseCategoriesButton = new QPushButton(this); 0078 d->m_expenseCategoriesButton->setObjectName("m_expenseCategoriesButton"); 0079 d->m_expenseCategoriesButton->setText(i18n("Expense")); 0080 buttonLayout->addWidget(d->m_expenseCategoriesButton); 0081 0082 d->m_noAccountButton = new QPushButton(this); 0083 d->m_noAccountButton->setObjectName("m_noAccountButton"); 0084 d->m_noAccountButton->setText(i18nc("No account", "None")); 0085 buttonLayout->addWidget(d->m_noAccountButton); 0086 0087 QSpacerItem* spacer = new QSpacerItem(0, 67, QSizePolicy::Minimum, QSizePolicy::Expanding); 0088 buttonLayout->addItem(spacer); 0089 d->m_layout->addLayout(buttonLayout); 0090 0091 connect(d->m_allAccountsButton, &QAbstractButton::clicked, this, &KMyMoneyAccountSelector::slotSelectAllAccounts); 0092 connect(d->m_noAccountButton, &QAbstractButton::clicked, this, &KMyMoneyAccountSelector::slotDeselectAllAccounts); 0093 connect(d->m_incomeCategoriesButton, &QAbstractButton::clicked, this, &KMyMoneyAccountSelector::slotSelectIncomeCategories); 0094 connect(d->m_expenseCategoriesButton, &QAbstractButton::clicked, this, &KMyMoneyAccountSelector::slotSelectExpenseCategories); 0095 } 0096 } 0097 0098 KMyMoneyAccountSelector::~KMyMoneyAccountSelector() 0099 { 0100 } 0101 0102 void KMyMoneyAccountSelector::removeButtons() 0103 { 0104 Q_D(KMyMoneyAccountSelector); 0105 delete d->m_allAccountsButton; 0106 delete d->m_incomeCategoriesButton; 0107 delete d->m_expenseCategoriesButton; 0108 delete d->m_noAccountButton; 0109 } 0110 0111 void KMyMoneyAccountSelector::slotSelectAllAccounts() 0112 { 0113 selectAllItems(true); 0114 } 0115 0116 void KMyMoneyAccountSelector::slotDeselectAllAccounts() 0117 { 0118 selectAllItems(false); 0119 } 0120 0121 void KMyMoneyAccountSelector::selectCategories(const bool income, const bool expense) 0122 { 0123 Q_D(KMyMoneyAccountSelector); 0124 QTreeWidgetItemIterator it_v(d->m_treeWidget); 0125 0126 for (; *it_v != 0; ++it_v) { 0127 if ((*it_v)->text(0) == i18n("Income categories")) 0128 selectAllSubItems(*it_v, income); 0129 else if ((*it_v)->text(0) == i18n("Expense categories")) 0130 selectAllSubItems(*it_v, expense); 0131 } 0132 Q_EMIT stateChanged(); 0133 } 0134 0135 void KMyMoneyAccountSelector::slotSelectIncomeCategories() 0136 { 0137 selectCategories(true, false); 0138 } 0139 0140 void KMyMoneyAccountSelector::slotSelectExpenseCategories() 0141 { 0142 selectCategories(false, true); 0143 } 0144 0145 void KMyMoneyAccountSelector::setSelectionMode(QTreeWidget::SelectionMode mode) 0146 { 0147 Q_D(KMyMoneyAccountSelector); 0148 d->m_incomeCategoriesButton->setHidden(mode == QTreeWidget::MultiSelection); 0149 d->m_expenseCategoriesButton->setHidden(mode == QTreeWidget::MultiSelection); 0150 KMyMoneySelector::setSelectionMode(mode); 0151 } 0152 0153 QStringList KMyMoneyAccountSelector::accountList(const QList<Account::Type>& filterList) const 0154 { 0155 Q_D(const KMyMoneyAccountSelector); 0156 QStringList list; 0157 QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); 0158 0159 while (*it) { 0160 QVariant id = (*it)->data(0, (int)eWidgets::Selector::Role::Id); 0161 MyMoneyAccount acc = MyMoneyFile::instance()->account(id.toString()); 0162 if (filterList.count() == 0 || filterList.contains(acc.accountType())) 0163 list << id.toString(); 0164 it++; 0165 } 0166 return list; 0167 } 0168 0169 QStringList KMyMoneyAccountSelector::accountList() const 0170 { 0171 return accountList(QList<eMyMoney::Account::Type>()); 0172 } 0173 0174 bool KMyMoneyAccountSelector::match(const QRegularExpression& exp, QTreeWidgetItem* item) const 0175 { 0176 if (!item->flags().testFlag(Qt::ItemIsSelectable)) 0177 return false; 0178 const auto matchingItem(exp.match(item->data(0, static_cast<int>(eWidgets::Selector::Role::Key)).toString().mid(1))); 0179 return matchingItem.hasMatch(); 0180 } 0181 0182 bool KMyMoneyAccountSelector::contains(const QString& txt) const 0183 { 0184 Q_D(const KMyMoneyAccountSelector); 0185 QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); 0186 QTreeWidgetItem* it_v; 0187 0188 QString baseName = i18n("Asset") + '|' + 0189 i18n("Liability") + '|' + 0190 i18n("Income") + '|' + 0191 i18n("Expense") + '|' + 0192 i18n("Equity") + '|' + 0193 i18n("Security"); 0194 0195 while ((it_v = *it) != 0) { 0196 const QRegularExpression exp(QString("^(?:%1):%2$").arg(baseName).arg(QRegularExpression::escape(txt))); 0197 const auto matchingItem(exp.match(it_v->data(0, static_cast<int>(eWidgets::Selector::Role::Key)).toString().mid(1))); 0198 if (matchingItem.hasMatch()) { 0199 return true; 0200 } 0201 it++; 0202 } 0203 return false; 0204 } 0205 0206 QSize KMyMoneyAccountSelector::sizeHint() const 0207 { 0208 auto size = KMyMoneySelector::sizeHint(); 0209 size.setWidth(400); 0210 return size; 0211 } 0212 0213 class AccountSetPrivate 0214 { 0215 Q_DISABLE_COPY(AccountSetPrivate) 0216 0217 public: 0218 AccountSetPrivate() 0219 : m_count(0) 0220 , m_file(MyMoneyFile::instance()) 0221 , m_favorites(0) 0222 , m_hideClosedAccounts(true) 0223 , m_showInvestments(false) 0224 { 0225 } 0226 0227 int m_count; 0228 MyMoneyFile* m_file; 0229 QList<eMyMoney::Account::Type> m_typeList; 0230 QTreeWidgetItem* m_favorites; 0231 bool m_hideClosedAccounts; 0232 bool m_showInvestments; 0233 }; 0234 0235 AccountSet::AccountSet() : 0236 d_ptr(new AccountSetPrivate) 0237 { 0238 } 0239 0240 AccountSet::~AccountSet() 0241 { 0242 Q_D(AccountSet); 0243 delete d; 0244 } 0245 0246 void AccountSet::setShowInvestments(bool show) 0247 { 0248 Q_D(AccountSet); 0249 d->m_showInvestments = show; 0250 } 0251 0252 void AccountSet::addAccountGroup(Account::Type group) 0253 { 0254 Q_D(AccountSet); 0255 if (group == Account::Type::Asset) { 0256 d->m_typeList << Account::Type::Checkings; 0257 d->m_typeList << Account::Type::Savings; 0258 d->m_typeList << Account::Type::Cash; 0259 d->m_typeList << Account::Type::AssetLoan; 0260 d->m_typeList << Account::Type::CertificateDep; 0261 d->m_typeList << Account::Type::Investment; 0262 d->m_typeList << Account::Type::Stock; 0263 d->m_typeList << Account::Type::MoneyMarket; 0264 d->m_typeList << Account::Type::Asset; 0265 d->m_typeList << Account::Type::Currency; 0266 0267 } else if (group == Account::Type::Liability) { 0268 d->m_typeList << Account::Type::CreditCard; 0269 d->m_typeList << Account::Type::Loan; 0270 d->m_typeList << Account::Type::Liability; 0271 0272 } else if (group == Account::Type::Income) { 0273 d->m_typeList << Account::Type::Income; 0274 0275 } else if (group == Account::Type::Expense) { 0276 d->m_typeList << Account::Type::Expense; 0277 0278 } else if (group == Account::Type::Equity) { 0279 d->m_typeList << Account::Type::Equity; 0280 } 0281 } 0282 0283 void AccountSet::addAccountType(Account::Type type) 0284 { 0285 Q_D(AccountSet); 0286 d->m_typeList << type; 0287 } 0288 0289 void AccountSet::removeAccountType(Account::Type type) 0290 { 0291 Q_D(AccountSet); 0292 int index = d->m_typeList.indexOf(type); 0293 if (index != -1) { 0294 d->m_typeList.removeAt(index); 0295 } 0296 } 0297 0298 void AccountSet::clear() 0299 { 0300 Q_D(AccountSet); 0301 d->m_typeList.clear(); 0302 } 0303 0304 int AccountSet::load(KMyMoneyAccountSelector* selector) 0305 { 0306 Q_D(AccountSet); 0307 QStringList list; 0308 QStringList::ConstIterator it_l; 0309 int count = 0; 0310 int typeMask = 0; 0311 QString currentId; 0312 0313 if (selector->selectionMode() == QTreeWidget::SingleSelection) { 0314 selector->selectedItems(list); 0315 if (!list.isEmpty()) 0316 currentId = list.first(); 0317 } 0318 // clang-format off 0319 if (d->m_typeList.contains(Account::Type::Checkings) 0320 || d->m_typeList.contains(Account::Type::Savings) 0321 || d->m_typeList.contains(Account::Type::Cash) 0322 || d->m_typeList.contains(Account::Type::AssetLoan) 0323 || d->m_typeList.contains(Account::Type::CertificateDep) 0324 || d->m_typeList.contains(Account::Type::Investment) 0325 || d->m_typeList.contains(Account::Type::Stock) 0326 || d->m_typeList.contains(Account::Type::MoneyMarket) 0327 || d->m_typeList.contains(Account::Type::Asset) 0328 || d->m_typeList.contains(Account::Type::Currency)) 0329 typeMask |= eDialogs::Category::asset; 0330 0331 if (d->m_typeList.contains(Account::Type::CreditCard) 0332 || d->m_typeList.contains(Account::Type::Loan) 0333 || d->m_typeList.contains(Account::Type::Liability)) 0334 typeMask |= eDialogs::Category::liability; 0335 // clang-format on 0336 0337 if (d->m_typeList.contains(Account::Type::Income)) 0338 typeMask |= eDialogs::Category::income; 0339 0340 if (d->m_typeList.contains(Account::Type::Expense)) 0341 typeMask |= eDialogs::Category::expense; 0342 0343 if (d->m_typeList.contains(Account::Type::Equity)) 0344 typeMask |= eDialogs::Category::equity; 0345 0346 selector->clear(); 0347 QTreeWidget* lv = selector->listView(); 0348 d->m_count = 0; 0349 QString key; 0350 QTreeWidgetItem* after = 0; 0351 0352 // create the favorite section first and sort it to the beginning 0353 key = QString("A%1").arg(i18n("Favorites")); 0354 d->m_favorites = selector->newItem(i18n("Favorites"), key); 0355 0356 //get the account icon from cache or insert it if it is not there 0357 QPixmap accountPixmap; 0358 if (!QPixmapCache::find("account", &accountPixmap)) { 0359 QIcon icon = Icons::get(Icon::BankAccount); 0360 if (!icon.availableSizes().isEmpty()) 0361 accountPixmap = icon.pixmap(icon.availableSizes().first()); 0362 QPixmapCache::insert("account", accountPixmap); 0363 } 0364 d->m_favorites->setIcon(0, QIcon(accountPixmap)); 0365 0366 for (auto mask = 0x01; mask != eDialogs::Category::last; mask <<= 1) { 0367 QTreeWidgetItem* item = 0; 0368 if ((typeMask & mask & eDialogs::Category::asset) != 0) { 0369 ++d->m_count; 0370 key = QString("B%1").arg(i18n("Asset")); 0371 item = selector->newItem(i18n("Asset accounts"), key); 0372 item->setIcon(0, d->m_file->asset().accountIcon()); 0373 list = d->m_file->asset().accountList(); 0374 } 0375 0376 if ((typeMask & mask & eDialogs::Category::liability) != 0) { 0377 ++d->m_count; 0378 key = QString("C%1").arg(i18n("Liability")); 0379 item = selector->newItem(i18n("Liability accounts"), key); 0380 item->setIcon(0, d->m_file->liability().accountIcon()); 0381 list = d->m_file->liability().accountList(); 0382 } 0383 0384 if ((typeMask & mask & eDialogs::Category::income) != 0) { 0385 ++d->m_count; 0386 key = QString("D%1").arg(i18n("Income")); 0387 item = selector->newItem(i18n("Income categories"), key); 0388 item->setIcon(0, d->m_file->income().accountIcon()); 0389 list = d->m_file->income().accountList(); 0390 if (selector->selectionMode() == QTreeWidget::MultiSelection) { 0391 selector->d_func()->m_incomeCategoriesButton->show(); 0392 } 0393 } 0394 0395 if ((typeMask & mask & eDialogs::Category::expense) != 0) { 0396 ++d->m_count; 0397 key = QString("E%1").arg(i18n("Expense")); 0398 item = selector->newItem(i18n("Expense categories"), key); 0399 item->setIcon(0, d->m_file->expense().accountIcon()); 0400 list = d->m_file->expense().accountList(); 0401 if (selector->selectionMode() == QTreeWidget::MultiSelection) { 0402 selector->d_func()->m_expenseCategoriesButton->show(); 0403 } 0404 } 0405 0406 if ((typeMask & mask & eDialogs::Category::equity) != 0) { 0407 ++d->m_count; 0408 key = QString("F%1").arg(i18n("Equity")); 0409 item = selector->newItem(i18n("Equity accounts"), key); 0410 item->setIcon(0, d->m_file->equity().accountIcon()); 0411 list = d->m_file->equity().accountList(); 0412 } 0413 0414 if (!after) 0415 after = item; 0416 0417 if (item != 0) { 0418 // scan all matching accounts found in the engine 0419 for (it_l = list.constBegin(); it_l != list.constEnd(); ++it_l) { 0420 const MyMoneyAccount& acc = d->m_file->account(*it_l); 0421 ++d->m_count; 0422 ++count; 0423 //this will include an account if it matches the account type and 0424 //if it is still open or it has been set to show closed accounts 0425 if (includeAccount(acc) 0426 && (!isHidingClosedAccounts() || !acc.isClosed())) { 0427 QString tmpKey; 0428 tmpKey = key + MyMoneyFile::AccountSeparator + acc.name(); 0429 QTreeWidgetItem* subItem = selector->newItem(item, acc.name(), tmpKey, acc.id()); 0430 subItem->setIcon(0, acc.accountIcon()); 0431 if (acc.value("PreferredAccount", false) && d->m_typeList.contains(acc.accountType())) { 0432 selector->newItem(d->m_favorites, acc.name(), tmpKey, acc.id())->setIcon(0, acc.accountIcon()); 0433 } 0434 if (acc.accountList().count() > 0) { 0435 subItem->setExpanded(true); 0436 count += loadSubAccounts(selector, subItem, tmpKey, acc.accountList()); 0437 } 0438 0439 // the item is not selectable if it has been added only because a subaccount matches the type 0440 if (!d->m_typeList.contains(acc.accountType())) { 0441 selector->setSelectable(subItem, false); 0442 } 0443 subItem->sortChildren(1, Qt::AscendingOrder); 0444 } 0445 } 0446 item->sortChildren(1, Qt::AscendingOrder); 0447 } 0448 } 0449 d->m_favorites->sortChildren(1, Qt::AscendingOrder); 0450 lv->invisibleRootItem()->sortChildren(1, Qt::AscendingOrder); 0451 0452 // if we don't have a favorite account or the selector is for multi-mode 0453 // we get rid of the favorite entry and subentries. 0454 if (d->m_favorites->childCount() == 0 || selector->selectionMode() == QTreeWidget::MultiSelection) { 0455 delete d->m_favorites; 0456 d->m_favorites = 0; 0457 } 0458 0459 if (lv->itemAt(0, 0)) { 0460 if (currentId.isEmpty()) { 0461 lv->setCurrentItem(lv->itemAt(0, 0)); 0462 lv->clearSelection(); 0463 } else { 0464 selector->setSelected(currentId); 0465 } 0466 } 0467 selector->update(); 0468 return count; 0469 } 0470 0471 int AccountSet::load(KMyMoneyAccountSelector* selector, const QString& baseName, const QList<QString>& accountIdList, const bool clear) 0472 { 0473 Q_D(AccountSet); 0474 int count = 0; 0475 QTreeWidgetItem* item = 0; 0476 0477 d->m_typeList.clear(); 0478 if (clear) { 0479 d->m_count = 0; 0480 selector->clear(); 0481 } 0482 0483 item = selector->newItem(baseName); 0484 ++d->m_count; 0485 0486 QList<QString>::ConstIterator it; 0487 for (it = accountIdList.constBegin(); it != accountIdList.constEnd(); ++it) { 0488 const MyMoneyAccount& acc = d->m_file->account(*it); 0489 if (acc.isClosed()) 0490 continue; 0491 QString tmpKey; 0492 // the first character must be preset. Since we don't know any sort order here, we just use A 0493 tmpKey = QString("A%1%2%3").arg(baseName, MyMoneyFile::AccountSeparator, acc.name()); 0494 selector->newItem(item, acc.name(), tmpKey, acc.id())->setIcon(0, acc.accountIcon()); 0495 ++d->m_count; 0496 ++count; 0497 } 0498 0499 QTreeWidget* lv = selector->listView(); 0500 if (lv->itemAt(0, 0)) { 0501 lv->setCurrentItem(lv->itemAt(0, 0)); 0502 lv->clearSelection(); 0503 } 0504 0505 selector->update(); 0506 return count; 0507 } 0508 0509 int AccountSet::count() const 0510 { 0511 Q_D(const AccountSet); 0512 return d->m_count; 0513 } 0514 0515 void AccountSet::setHideClosedAccounts(bool _bool) 0516 { 0517 Q_D(AccountSet); 0518 d->m_hideClosedAccounts = _bool; 0519 } 0520 bool AccountSet::isHidingClosedAccounts() const 0521 { 0522 Q_D(const AccountSet); 0523 return d->m_hideClosedAccounts; 0524 } 0525 0526 int AccountSet::loadSubAccounts(KMyMoneyAccountSelector* selector, QTreeWidgetItem* parent, const QString& key, const QStringList& list) 0527 { 0528 Q_D(AccountSet); 0529 QStringList::ConstIterator it_l; 0530 int count = 0; 0531 0532 for (it_l = list.constBegin(); it_l != list.constEnd(); ++it_l) { 0533 const MyMoneyAccount& acc = d->m_file->account(*it_l); 0534 // don't include stock accounts if not in expert mode 0535 if (acc.isInvest() && !d->m_showInvestments) 0536 continue; 0537 0538 //this will include an account if it matches the account type and 0539 //if it is still open or it has been set to show closed accounts 0540 if (includeAccount(acc) 0541 && (!isHidingClosedAccounts() || !acc.isClosed())) { 0542 QString tmpKey; 0543 tmpKey = key + MyMoneyFile::AccountSeparator + acc.name(); 0544 ++count; 0545 ++d->m_count; 0546 QTreeWidgetItem* item = selector->newItem(parent, acc.name(), tmpKey, acc.id()); 0547 item->setIcon(0, acc.accountIcon()); 0548 if (acc.value("PreferredAccount", false) && d->m_typeList.contains(acc.accountType())) { 0549 selector->newItem(d->m_favorites, acc.name(), tmpKey, acc.id())->setIcon(0, acc.accountIcon()); 0550 } 0551 if (acc.accountList().count() > 0) { 0552 item->setExpanded(true); 0553 count += loadSubAccounts(selector, item, tmpKey, acc.accountList()); 0554 } 0555 0556 // the item is not selectable if it has been added only because a subaccount matches the type 0557 if (!d->m_typeList.contains(acc.accountType())) { 0558 selector->setSelectable(item, false); 0559 } 0560 item->sortChildren(1, Qt::AscendingOrder); 0561 } 0562 } 0563 return count; 0564 } 0565 0566 bool AccountSet::includeAccount(const MyMoneyAccount& acc) 0567 { 0568 Q_D(AccountSet); 0569 if (d->m_typeList.contains(acc.accountType())) 0570 return true; 0571 0572 for (const auto& sAccount : acc.accountList()) 0573 if (includeAccount(d->m_file->account(sAccount))) 0574 return true; 0575 0576 return false; 0577 }