File indexing completed on 2024-05-12 16:44:00
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 <QList> 0014 #include <QVBoxLayout> 0015 #include <QPixmapCache> 0016 #include <QPushButton> 0017 #include <QIcon> 0018 0019 // ---------------------------------------------------------------------------- 0020 // KDE Includes 0021 0022 #include <KLocalizedString> 0023 0024 // ---------------------------------------------------------------------------- 0025 // Project Includes 0026 0027 #include "mymoneyfile.h" 0028 #include "mymoneyaccount.h" 0029 #include "icons/icons.h" 0030 #include "mymoneyenums.h" 0031 #include "dialogenums.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 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 QRegExp& exp, QTreeWidgetItem* item) const 0175 { 0176 if (!item->flags().testFlag(Qt::ItemIsSelectable)) 0177 return false; 0178 return exp.indexIn(item->data(0, (int)eWidgets::Selector::Role::Key).toString().mid(1)) != -1; 0179 } 0180 0181 bool KMyMoneyAccountSelector::contains(const QString& txt) const 0182 { 0183 Q_D(const KMyMoneyAccountSelector); 0184 QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); 0185 QTreeWidgetItem* it_v; 0186 0187 QString baseName = i18n("Asset") + '|' + 0188 i18n("Liability") + '|' + 0189 i18n("Income") + '|' + 0190 i18n("Expense") + '|' + 0191 i18n("Equity") + '|' + 0192 i18n("Security"); 0193 0194 while ((it_v = *it) != 0) { 0195 QRegExp exp(QString("^(?:%1):%2$").arg(baseName).arg(QRegExp::escape(txt))); 0196 if (exp.indexIn(it_v->data(0, (int)eWidgets::Selector::Role::Key).toString().mid(1)) != -1) { 0197 return true; 0198 } 0199 it++; 0200 } 0201 return false; 0202 } 0203 0204 class AccountSetPrivate 0205 { 0206 Q_DISABLE_COPY(AccountSetPrivate) 0207 0208 public: 0209 AccountSetPrivate() 0210 : m_count(0) 0211 , m_file(MyMoneyFile::instance()) 0212 , m_favorites(0) 0213 , m_hideClosedAccounts(true) 0214 , m_showInvestments(false) 0215 { 0216 } 0217 0218 int m_count; 0219 MyMoneyFile* m_file; 0220 QList<eMyMoney::Account::Type> m_typeList; 0221 QTreeWidgetItem* m_favorites; 0222 bool m_hideClosedAccounts; 0223 bool m_showInvestments; 0224 }; 0225 0226 AccountSet::AccountSet() : 0227 d_ptr(new AccountSetPrivate) 0228 { 0229 } 0230 0231 AccountSet::~AccountSet() 0232 { 0233 Q_D(AccountSet); 0234 delete d; 0235 } 0236 0237 void AccountSet::setShowInvestments(bool show) 0238 { 0239 Q_D(AccountSet); 0240 d->m_showInvestments = show; 0241 } 0242 0243 void AccountSet::addAccountGroup(Account::Type group) 0244 { 0245 Q_D(AccountSet); 0246 if (group == Account::Type::Asset) { 0247 d->m_typeList << Account::Type::Checkings; 0248 d->m_typeList << Account::Type::Savings; 0249 d->m_typeList << Account::Type::Cash; 0250 d->m_typeList << Account::Type::AssetLoan; 0251 d->m_typeList << Account::Type::CertificateDep; 0252 d->m_typeList << Account::Type::Investment; 0253 d->m_typeList << Account::Type::Stock; 0254 d->m_typeList << Account::Type::MoneyMarket; 0255 d->m_typeList << Account::Type::Asset; 0256 d->m_typeList << Account::Type::Currency; 0257 0258 } else if (group == Account::Type::Liability) { 0259 d->m_typeList << Account::Type::CreditCard; 0260 d->m_typeList << Account::Type::Loan; 0261 d->m_typeList << Account::Type::Liability; 0262 0263 } else if (group == Account::Type::Income) { 0264 d->m_typeList << Account::Type::Income; 0265 0266 } else if (group == Account::Type::Expense) { 0267 d->m_typeList << Account::Type::Expense; 0268 0269 } else if (group == Account::Type::Equity) { 0270 d->m_typeList << Account::Type::Equity; 0271 } 0272 } 0273 0274 void AccountSet::addAccountType(Account::Type type) 0275 { 0276 Q_D(AccountSet); 0277 d->m_typeList << type; 0278 } 0279 0280 void AccountSet::removeAccountType(Account::Type type) 0281 { 0282 Q_D(AccountSet); 0283 int index = d->m_typeList.indexOf(type); 0284 if (index != -1) { 0285 d->m_typeList.removeAt(index); 0286 } 0287 } 0288 0289 void AccountSet::clear() 0290 { 0291 Q_D(AccountSet); 0292 d->m_typeList.clear(); 0293 } 0294 0295 int AccountSet::load(KMyMoneyAccountSelector* selector) 0296 { 0297 Q_D(AccountSet); 0298 QStringList list; 0299 QStringList::ConstIterator it_l; 0300 int count = 0; 0301 int typeMask = 0; 0302 QString currentId; 0303 0304 if (selector->selectionMode() == QTreeWidget::SingleSelection) { 0305 selector->selectedItems(list); 0306 if (!list.isEmpty()) 0307 currentId = list.first(); 0308 } 0309 // clang-format off 0310 if (d->m_typeList.contains(Account::Type::Checkings) 0311 || d->m_typeList.contains(Account::Type::Savings) 0312 || d->m_typeList.contains(Account::Type::Cash) 0313 || d->m_typeList.contains(Account::Type::AssetLoan) 0314 || d->m_typeList.contains(Account::Type::CertificateDep) 0315 || d->m_typeList.contains(Account::Type::Investment) 0316 || d->m_typeList.contains(Account::Type::Stock) 0317 || d->m_typeList.contains(Account::Type::MoneyMarket) 0318 || d->m_typeList.contains(Account::Type::Asset) 0319 || d->m_typeList.contains(Account::Type::Currency)) 0320 typeMask |= eDialogs::Category::asset; 0321 0322 if (d->m_typeList.contains(Account::Type::CreditCard) 0323 || d->m_typeList.contains(Account::Type::Loan) 0324 || d->m_typeList.contains(Account::Type::Liability)) 0325 typeMask |= eDialogs::Category::liability; 0326 // clang-format on 0327 0328 if (d->m_typeList.contains(Account::Type::Income)) 0329 typeMask |= eDialogs::Category::income; 0330 0331 if (d->m_typeList.contains(Account::Type::Expense)) 0332 typeMask |= eDialogs::Category::expense; 0333 0334 if (d->m_typeList.contains(Account::Type::Equity)) 0335 typeMask |= eDialogs::Category::equity; 0336 0337 selector->clear(); 0338 QTreeWidget* lv = selector->listView(); 0339 d->m_count = 0; 0340 QString key; 0341 QTreeWidgetItem* after = 0; 0342 0343 // create the favorite section first and sort it to the beginning 0344 key = QString("A%1").arg(i18n("Favorites")); 0345 d->m_favorites = selector->newItem(i18n("Favorites"), key); 0346 0347 //get the account icon from cache or insert it if it is not there 0348 QPixmap accountPixmap; 0349 if (!QPixmapCache::find("account", accountPixmap)) { 0350 QIcon icon = Icons::get(Icon::BankAccount); 0351 if (!icon.availableSizes().isEmpty()) 0352 accountPixmap = icon.pixmap(icon.availableSizes().first()); 0353 QPixmapCache::insert("account", accountPixmap); 0354 } 0355 d->m_favorites->setIcon(0, QIcon(accountPixmap)); 0356 0357 for (auto mask = 0x01; mask != eDialogs::Category::last; mask <<= 1) { 0358 QTreeWidgetItem* item = 0; 0359 if ((typeMask & mask & eDialogs::Category::asset) != 0) { 0360 ++d->m_count; 0361 key = QString("B%1").arg(i18n("Asset")); 0362 item = selector->newItem(i18n("Asset accounts"), key); 0363 item->setIcon(0, d->m_file->asset().accountPixmap()); 0364 list = d->m_file->asset().accountList(); 0365 } 0366 0367 if ((typeMask & mask & eDialogs::Category::liability) != 0) { 0368 ++d->m_count; 0369 key = QString("C%1").arg(i18n("Liability")); 0370 item = selector->newItem(i18n("Liability accounts"), key); 0371 item->setIcon(0, d->m_file->liability().accountPixmap()); 0372 list = d->m_file->liability().accountList(); 0373 } 0374 0375 if ((typeMask & mask & eDialogs::Category::income) != 0) { 0376 ++d->m_count; 0377 key = QString("D%1").arg(i18n("Income")); 0378 item = selector->newItem(i18n("Income categories"), key); 0379 item->setIcon(0, d->m_file->income().accountPixmap()); 0380 list = d->m_file->income().accountList(); 0381 if (selector->selectionMode() == QTreeWidget::MultiSelection) { 0382 selector->d_func()->m_incomeCategoriesButton->show(); 0383 } 0384 } 0385 0386 if ((typeMask & mask & eDialogs::Category::expense) != 0) { 0387 ++d->m_count; 0388 key = QString("E%1").arg(i18n("Expense")); 0389 item = selector->newItem(i18n("Expense categories"), key); 0390 item->setIcon(0, d->m_file->expense().accountPixmap()); 0391 list = d->m_file->expense().accountList(); 0392 if (selector->selectionMode() == QTreeWidget::MultiSelection) { 0393 selector->d_func()->m_expenseCategoriesButton->show(); 0394 } 0395 } 0396 0397 if ((typeMask & mask & eDialogs::Category::equity) != 0) { 0398 ++d->m_count; 0399 key = QString("F%1").arg(i18n("Equity")); 0400 item = selector->newItem(i18n("Equity accounts"), key); 0401 item->setIcon(0, d->m_file->equity().accountPixmap()); 0402 list = d->m_file->equity().accountList(); 0403 } 0404 0405 if (!after) 0406 after = item; 0407 0408 if (item != 0) { 0409 // scan all matching accounts found in the engine 0410 for (it_l = list.constBegin(); it_l != list.constEnd(); ++it_l) { 0411 const MyMoneyAccount& acc = d->m_file->account(*it_l); 0412 ++d->m_count; 0413 ++count; 0414 //this will include an account if it matches the account type and 0415 //if it is still open or it has been set to show closed accounts 0416 if (includeAccount(acc) 0417 && (!isHidingClosedAccounts() || !acc.isClosed())) { 0418 QString tmpKey; 0419 tmpKey = key + MyMoneyFile::AccountSeparator + acc.name(); 0420 QTreeWidgetItem* subItem = selector->newItem(item, acc.name(), tmpKey, acc.id()); 0421 subItem->setIcon(0, acc.accountPixmap()); 0422 if (acc.value("PreferredAccount") == "Yes" 0423 && d->m_typeList.contains(acc.accountType())) { 0424 selector->newItem(d->m_favorites, acc.name(), tmpKey, acc.id())->setIcon(0, acc.accountPixmap());; 0425 } 0426 if (acc.accountList().count() > 0) { 0427 subItem->setExpanded(true); 0428 count += loadSubAccounts(selector, subItem, tmpKey, acc.accountList()); 0429 } 0430 0431 // the item is not selectable if it has been added only because a subaccount matches the type 0432 if (!d->m_typeList.contains(acc.accountType())) { 0433 selector->setSelectable(subItem, false); 0434 } 0435 subItem->sortChildren(1, Qt::AscendingOrder); 0436 } 0437 } 0438 item->sortChildren(1, Qt::AscendingOrder); 0439 } 0440 } 0441 d->m_favorites->sortChildren(1, Qt::AscendingOrder); 0442 lv->invisibleRootItem()->sortChildren(1, Qt::AscendingOrder); 0443 0444 // if we don't have a favorite account or the selector is for multi-mode 0445 // we get rid of the favorite entry and subentries. 0446 if (d->m_favorites->childCount() == 0 || selector->selectionMode() == QTreeWidget::MultiSelection) { 0447 delete d->m_favorites; 0448 d->m_favorites = 0; 0449 } 0450 0451 if (lv->itemAt(0, 0)) { 0452 if (currentId.isEmpty()) { 0453 lv->setCurrentItem(lv->itemAt(0, 0)); 0454 lv->clearSelection(); 0455 } else { 0456 selector->setSelected(currentId); 0457 } 0458 } 0459 selector->update(); 0460 return count; 0461 } 0462 0463 int AccountSet::load(KMyMoneyAccountSelector* selector, const QString& baseName, const QList<QString>& accountIdList, const bool clear) 0464 { 0465 Q_D(AccountSet); 0466 int count = 0; 0467 QTreeWidgetItem* item = 0; 0468 0469 d->m_typeList.clear(); 0470 if (clear) { 0471 d->m_count = 0; 0472 selector->clear(); 0473 } 0474 0475 item = selector->newItem(baseName); 0476 ++d->m_count; 0477 0478 QList<QString>::ConstIterator it; 0479 for (it = accountIdList.constBegin(); it != accountIdList.constEnd(); ++it) { 0480 const MyMoneyAccount& acc = d->m_file->account(*it); 0481 if (acc.isClosed()) 0482 continue; 0483 QString tmpKey; 0484 // the first character must be preset. Since we don't know any sort order here, we just use A 0485 tmpKey = QString("A%1%2%3").arg(baseName, MyMoneyFile::AccountSeparator, acc.name()); 0486 selector->newItem(item, acc.name(), tmpKey, acc.id())->setIcon(0, acc.accountPixmap()); 0487 ++d->m_count; 0488 ++count; 0489 } 0490 0491 QTreeWidget* lv = selector->listView(); 0492 if (lv->itemAt(0, 0)) { 0493 lv->setCurrentItem(lv->itemAt(0, 0)); 0494 lv->clearSelection(); 0495 } 0496 0497 selector->update(); 0498 return count; 0499 } 0500 0501 int AccountSet::count() const 0502 { 0503 Q_D(const AccountSet); 0504 return d->m_count; 0505 } 0506 0507 void AccountSet::setHideClosedAccounts(bool _bool) 0508 { 0509 Q_D(AccountSet); 0510 d->m_hideClosedAccounts = _bool; 0511 } 0512 bool AccountSet::isHidingClosedAccounts() const 0513 { 0514 Q_D(const AccountSet); 0515 return d->m_hideClosedAccounts; 0516 } 0517 0518 int AccountSet::loadSubAccounts(KMyMoneyAccountSelector* selector, QTreeWidgetItem* parent, const QString& key, const QStringList& list) 0519 { 0520 Q_D(AccountSet); 0521 QStringList::ConstIterator it_l; 0522 int count = 0; 0523 0524 for (it_l = list.constBegin(); it_l != list.constEnd(); ++it_l) { 0525 const MyMoneyAccount& acc = d->m_file->account(*it_l); 0526 // don't include stock accounts if not in expert mode 0527 if (acc.isInvest() && !d->m_showInvestments) 0528 continue; 0529 0530 //this will include an account if it matches the account type and 0531 //if it is still open or it has been set to show closed accounts 0532 if (includeAccount(acc) 0533 && (!isHidingClosedAccounts() || !acc.isClosed())) { 0534 QString tmpKey; 0535 tmpKey = key + MyMoneyFile::AccountSeparator + acc.name(); 0536 ++count; 0537 ++d->m_count; 0538 QTreeWidgetItem* item = selector->newItem(parent, acc.name(), tmpKey, acc.id()); 0539 item->setIcon(0, acc.accountPixmap()); 0540 if (acc.value("PreferredAccount") == "Yes" 0541 && d->m_typeList.contains(acc.accountType())) { 0542 selector->newItem(d->m_favorites, acc.name(), tmpKey, acc.id())->setIcon(0, acc.accountPixmap()); 0543 } 0544 if (acc.accountList().count() > 0) { 0545 item->setExpanded(true); 0546 count += loadSubAccounts(selector, item, tmpKey, acc.accountList()); 0547 } 0548 0549 // the item is not selectable if it has been added only because a subaccount matches the type 0550 if (!d->m_typeList.contains(acc.accountType())) { 0551 selector->setSelectable(item, false); 0552 } 0553 item->sortChildren(1, Qt::AscendingOrder); 0554 } 0555 } 0556 return count; 0557 } 0558 0559 bool AccountSet::includeAccount(const MyMoneyAccount& acc) 0560 { 0561 Q_D(AccountSet); 0562 if (d->m_typeList.contains(acc.accountType())) 0563 return true; 0564 0565 foreach (const auto sAccount, acc.accountList()) 0566 if (includeAccount(d->m_file->account(sAccount))) 0567 return true; 0568 0569 return false; 0570 }