File indexing completed on 2024-05-12 16:44:04
0001 /* 0002 SPDX-FileCopyrightText: 2006-2018 Thomas Baumgart <tbaumgart@kde.org> 0003 SPDX-FileCopyrightText: 2017 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com> 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kmymoneyselector_p.h" 0008 0009 // ---------------------------------------------------------------------------- 0010 // QT Includes 0011 0012 #include <QStyle> 0013 #include <QRegExp> 0014 #include <QApplication> 0015 0016 // ---------------------------------------------------------------------------- 0017 // KDE Includes 0018 0019 // ---------------------------------------------------------------------------- 0020 // Project Includes 0021 0022 #include "kmymoneysettings.h" 0023 #include "widgetenums.h" 0024 0025 using namespace eWidgets; 0026 0027 KMyMoneySelector::KMyMoneySelector(QWidget *parent, Qt::WindowFlags flags) : 0028 QWidget(parent, flags), 0029 d_ptr(new KMyMoneySelectorPrivate(this)) 0030 { 0031 Q_D(KMyMoneySelector); 0032 d->init(); 0033 } 0034 0035 KMyMoneySelector::KMyMoneySelector(KMyMoneySelectorPrivate &dd, QWidget* parent, Qt::WindowFlags flags) : 0036 QWidget(parent, flags), 0037 d_ptr(&dd) 0038 { 0039 Q_D(KMyMoneySelector); 0040 d->init(); 0041 } 0042 0043 KMyMoneySelector::~KMyMoneySelector() 0044 { 0045 Q_D(KMyMoneySelector); 0046 delete d; 0047 } 0048 0049 void KMyMoneySelector::clear() 0050 { 0051 Q_D(KMyMoneySelector); 0052 d->m_treeWidget->clear(); 0053 } 0054 0055 void KMyMoneySelector::setSelectable(QTreeWidgetItem *item, bool selectable) 0056 { 0057 if (selectable) { 0058 item->setFlags(item->flags() | Qt::ItemIsSelectable); 0059 } else { 0060 item->setFlags(item->flags() & ~Qt::ItemIsSelectable); 0061 } 0062 } 0063 0064 void KMyMoneySelector::slotSelectAllItems() 0065 { 0066 selectAllItems(true); 0067 } 0068 0069 void KMyMoneySelector::slotDeselectAllItems() 0070 { 0071 selectAllItems(false); 0072 } 0073 0074 void KMyMoneySelector::setSelectionMode(const QTreeWidget::SelectionMode mode) 0075 { 0076 Q_D(KMyMoneySelector); 0077 if (d->m_selMode != mode) { 0078 d->m_selMode = mode; 0079 clear(); 0080 0081 // make sure, it's either Multi or Single 0082 if (mode != QTreeWidget::MultiSelection) { 0083 d->m_selMode = QTreeWidget::SingleSelection; 0084 connect(d->m_treeWidget, &QTreeWidget::itemSelectionChanged, this, &KMyMoneySelector::stateChanged); 0085 connect(d->m_treeWidget, &QTreeWidget::itemActivated, this, &KMyMoneySelector::slotItemSelected); 0086 connect(d->m_treeWidget, &QTreeWidget::itemClicked, this, &KMyMoneySelector::slotItemSelected); 0087 } else { 0088 disconnect(d->m_treeWidget, &QTreeWidget::itemSelectionChanged, this, &KMyMoneySelector::stateChanged); 0089 disconnect(d->m_treeWidget, &QTreeWidget::itemActivated, this, &KMyMoneySelector::slotItemSelected); 0090 disconnect(d->m_treeWidget, &QTreeWidget::itemClicked, this, &KMyMoneySelector::slotItemSelected); 0091 } 0092 } 0093 QWidget::update(); 0094 } 0095 0096 QTreeWidget::SelectionMode KMyMoneySelector::selectionMode() const 0097 { 0098 Q_D(const KMyMoneySelector); 0099 return d->m_selMode; 0100 } 0101 0102 void KMyMoneySelector::slotItemSelected(QTreeWidgetItem *item) 0103 { 0104 Q_D(KMyMoneySelector); 0105 if (d->m_selMode == QTreeWidget::SingleSelection) { 0106 if (item && item->flags().testFlag(Qt::ItemIsSelectable)) { 0107 emit itemSelected(item->data(0, (int)Selector::Role::Id).toString()); 0108 } 0109 } 0110 } 0111 0112 QTreeWidgetItem* KMyMoneySelector::newItem(const QString& name, QTreeWidgetItem* after, const QString& key, const QString& id) 0113 { 0114 Q_D(KMyMoneySelector); 0115 QTreeWidgetItem* item = new QTreeWidgetItem(d->m_treeWidget, after); 0116 0117 item->setText(0, name); 0118 item->setData(0, (int)Selector::Role::Key, key); 0119 item->setData(0, (int)Selector::Role::Id, id); 0120 item->setText(1, key); // hidden, but used for sorting 0121 item->setFlags(item->flags() & ~Qt::ItemIsUserCheckable); 0122 0123 if (id.isEmpty()) { 0124 QFont font = item->font(0); 0125 font.setBold(true); 0126 item->setFont(0, font); 0127 setSelectable(item, false); 0128 } 0129 item->setExpanded(true); 0130 return item; 0131 } 0132 0133 QTreeWidgetItem* KMyMoneySelector::newItem(const QString& name, QTreeWidgetItem* after, const QString& key) 0134 { 0135 return newItem(name, after, key, QString()); 0136 } 0137 0138 QTreeWidgetItem* KMyMoneySelector::newItem(const QString& name, QTreeWidgetItem* after) 0139 { 0140 return newItem(name, after, QString(), QString()); 0141 } 0142 0143 QTreeWidgetItem* KMyMoneySelector::newItem(const QString& name, const QString& key, const QString& id) 0144 { 0145 return newItem(name, 0, key, id); 0146 } 0147 0148 QTreeWidgetItem* KMyMoneySelector::newItem(const QString& name, const QString& key) 0149 { 0150 return newItem(name, 0, key, QString()); 0151 } 0152 0153 QTreeWidgetItem* KMyMoneySelector::newItem(const QString& name) 0154 { 0155 return newItem(name, 0, QString(), QString()); 0156 } 0157 0158 QTreeWidgetItem* KMyMoneySelector::newTopItem(const QString& name, const QString& key, const QString& id) 0159 { 0160 Q_D(KMyMoneySelector); 0161 QTreeWidgetItem* item = new QTreeWidgetItem(d->m_treeWidget); 0162 0163 item->setText(0, name); 0164 item->setData(0, (int)Selector::Role::Key, key); 0165 item->setData(0, (int)Selector::Role::Id, id); 0166 item->setText(1, key); // hidden, but used for sorting 0167 item->setFlags(item->flags() & ~Qt::ItemIsUserCheckable); 0168 0169 if (d->m_selMode == QTreeWidget::MultiSelection) { 0170 item->setFlags(item->flags() | Qt::ItemIsUserCheckable); 0171 item->setCheckState(0, Qt::Checked); 0172 } 0173 return item; 0174 } 0175 0176 QTreeWidgetItem* KMyMoneySelector::newItem(QTreeWidgetItem* parent, const QString& name, const QString& key, const QString& id) 0177 { 0178 Q_D(KMyMoneySelector); 0179 QTreeWidgetItem* item = new QTreeWidgetItem(parent); 0180 0181 item->setText(0, name); 0182 item->setData(0, (int)Selector::Role::Key, key); 0183 item->setData(0, (int)Selector::Role::Id, id); 0184 item->setText(1, key); // hidden, but used for sorting 0185 item->setFlags(item->flags() & ~Qt::ItemIsUserCheckable); 0186 0187 if (d->m_selMode == QTreeWidget::MultiSelection) { 0188 item->setFlags(item->flags() | Qt::ItemIsUserCheckable); 0189 item->setCheckState(0, Qt::Checked); 0190 } 0191 return item; 0192 } 0193 0194 void KMyMoneySelector::protectItem(const QString& itemId, const bool protect) 0195 { 0196 Q_D(KMyMoneySelector); 0197 QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); 0198 QTreeWidgetItem* it_v; 0199 0200 // scan items 0201 while ((it_v = *it) != 0) { 0202 if (it_v->data(0, (int)Selector::Role::Id).toString() == itemId) { 0203 setSelectable(it_v, !protect); 0204 break; 0205 } 0206 ++it; 0207 } 0208 } 0209 0210 QTreeWidgetItem* KMyMoneySelector::item(const QString& id) const 0211 { 0212 Q_D(const KMyMoneySelector); 0213 QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); 0214 QTreeWidgetItem* it_v; 0215 0216 while ((it_v = *it) != 0) { 0217 if (it_v->data(0, (int)Selector::Role::Id).toString() == id) 0218 break; 0219 ++it; 0220 } 0221 return it_v; 0222 } 0223 0224 bool KMyMoneySelector::allItemsSelected() const 0225 { 0226 Q_D(const KMyMoneySelector); 0227 QTreeWidgetItem* rootItem = d->m_treeWidget->invisibleRootItem(); 0228 0229 if (d->m_selMode == QTreeWidget::SingleSelection) 0230 return false; 0231 0232 for (auto i = 0; i < rootItem->childCount(); ++i) { 0233 QTreeWidgetItem* item = rootItem->child(i); 0234 if (item->flags().testFlag(Qt::ItemIsUserCheckable)) { 0235 if (!(item->checkState(0) == Qt::Checked && allItemsSelected(item))) 0236 return false; 0237 } else { 0238 if (!allItemsSelected(item)) 0239 return false; 0240 } 0241 } 0242 return true; 0243 } 0244 0245 bool KMyMoneySelector::allItemsSelected(const QTreeWidgetItem *item) const 0246 { 0247 for (auto i = 0; i < item->childCount(); ++i) { 0248 QTreeWidgetItem* child = item->child(i); 0249 if (child->flags().testFlag(Qt::ItemIsUserCheckable)) { 0250 if (!(child->checkState(0) == Qt::Checked && allItemsSelected(child))) 0251 return false; 0252 } 0253 } 0254 return true; 0255 } 0256 0257 void KMyMoneySelector::removeItem(const QString& id) 0258 { 0259 Q_D(KMyMoneySelector); 0260 QTreeWidgetItem* it_v; 0261 QTreeWidgetItemIterator it(d->m_treeWidget); 0262 0263 while ((it_v = *it) != 0) { 0264 if (id == it_v->data(0, (int)Selector::Role::Id).toString()) { 0265 if (it_v->childCount() > 0) { 0266 setSelectable(it_v, false); 0267 } else { 0268 delete it_v; 0269 } 0270 } 0271 it++; 0272 } 0273 0274 // get rid of top items that just lost the last children (e.g. Favorites) 0275 it = QTreeWidgetItemIterator(d->m_treeWidget, QTreeWidgetItemIterator::NotSelectable); 0276 while ((it_v = *it) != 0) { 0277 if (it_v->childCount() == 0) 0278 delete it_v; 0279 it++; 0280 } 0281 } 0282 0283 0284 void KMyMoneySelector::selectAllItems(const bool state) 0285 { 0286 Q_D(KMyMoneySelector); 0287 selectAllSubItems(d->m_treeWidget->invisibleRootItem(), state); 0288 emit stateChanged(); 0289 } 0290 0291 void KMyMoneySelector::selectItems(const QStringList& itemList, const bool state) 0292 { 0293 Q_D(KMyMoneySelector); 0294 selectSubItems(d->m_treeWidget->invisibleRootItem(), itemList, state); 0295 emit stateChanged(); 0296 } 0297 0298 void KMyMoneySelector::selectSubItems(QTreeWidgetItem* item, const QStringList& itemList, const bool state) 0299 { 0300 for (auto i = 0; i < item->childCount(); ++i) { 0301 QTreeWidgetItem* child = item->child(i); 0302 if (child->flags().testFlag(Qt::ItemIsUserCheckable) && itemList.contains(child->data(0, (int)Selector::Role::Id).toString())) { 0303 child->setCheckState(0, state ? Qt::Checked : Qt::Unchecked); 0304 } 0305 selectSubItems(child, itemList, state); 0306 } 0307 emit stateChanged(); 0308 } 0309 0310 void KMyMoneySelector::selectAllSubItems(QTreeWidgetItem* item, const bool state) 0311 { 0312 for (auto i = 0; i < item->childCount(); ++i) { 0313 QTreeWidgetItem* child = item->child(i); 0314 if (child->flags().testFlag(Qt::ItemIsUserCheckable)) { 0315 child->setCheckState(0, state ? Qt::Checked : Qt::Unchecked); 0316 } 0317 selectAllSubItems(child, state); 0318 } 0319 emit stateChanged(); 0320 } 0321 0322 void KMyMoneySelector::selectedItems(QStringList& list) const 0323 { 0324 Q_D(const KMyMoneySelector); 0325 list.clear(); 0326 if (d->m_selMode == QTreeWidget::SingleSelection) { 0327 QTreeWidgetItem* it_c = d->m_treeWidget->currentItem(); 0328 if (it_c != 0 && it_c->isSelected()) 0329 list << it_c->data(0, (int)Selector::Role::Id).toString(); 0330 } else { 0331 QTreeWidgetItem* rootItem = d->m_treeWidget->invisibleRootItem(); 0332 for (auto i = 0; i < rootItem->childCount(); ++i) { 0333 QTreeWidgetItem* child = rootItem->child(i); 0334 if (child->flags().testFlag(Qt::ItemIsUserCheckable)) { 0335 if (child->checkState(0) == Qt::Checked) 0336 list << child->data(0, (int)Selector::Role::Id).toString(); 0337 } 0338 selectedItems(list, child); 0339 } 0340 } 0341 } 0342 0343 void KMyMoneySelector::selectedItems(QStringList& list, QTreeWidgetItem* item) const 0344 { 0345 for (auto i = 0; i < item->childCount(); ++i) { 0346 QTreeWidgetItem* child = item->child(i); 0347 if (child->flags().testFlag(Qt::ItemIsUserCheckable)) { 0348 if (child->checkState(0) == Qt::Checked) 0349 list << child->data(0, (int)Selector::Role::Id).toString(); 0350 } 0351 selectedItems(list, child); 0352 } 0353 } 0354 0355 void KMyMoneySelector::itemList(QStringList& list) const 0356 { 0357 Q_D(const KMyMoneySelector); 0358 QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); 0359 QTreeWidgetItem* it_v; 0360 0361 while ((it_v = *it) != 0) { 0362 list << it_v->data(0, (int)Selector::Role::Id).toString(); 0363 it++; 0364 } 0365 } 0366 0367 void KMyMoneySelector::setSelected(const QString& id, const bool state) 0368 { 0369 Q_D(const KMyMoneySelector); 0370 QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); 0371 QTreeWidgetItem* item; 0372 QTreeWidgetItem* it_visible = 0; 0373 0374 while ((item = *it) != 0) { 0375 if (item->data(0, (int)Selector::Role::Id).toString() == id) { 0376 if (item->flags().testFlag(Qt::ItemIsUserCheckable)) { 0377 item->setCheckState(0, state ? Qt::Checked : Qt::Unchecked); 0378 } 0379 d->m_treeWidget->setCurrentItem(item); 0380 if (!it_visible) 0381 it_visible = item; 0382 } 0383 it++; 0384 } 0385 0386 // make sure the first one found is visible 0387 if (it_visible) 0388 d->m_treeWidget->scrollToItem(it_visible); 0389 } 0390 0391 QTreeWidget* KMyMoneySelector::listView() const 0392 { 0393 Q_D(const KMyMoneySelector); 0394 return d->m_treeWidget; 0395 } 0396 0397 int KMyMoneySelector::slotMakeCompletion(const QString& _txt) 0398 { 0399 QString txt(QRegExp::escape(_txt)); 0400 if (KMyMoneySettings::stringMatchFromStart() && QLatin1String(this->metaObject()->className()) == QLatin1String("KMyMoneySelector")) 0401 txt.prepend('^'); 0402 return slotMakeCompletion(QRegExp(txt, Qt::CaseInsensitive)); 0403 } 0404 0405 bool KMyMoneySelector::match(const QRegExp& exp, QTreeWidgetItem* item) const 0406 { 0407 return exp.indexIn(item->text(0)) != -1; 0408 } 0409 0410 int KMyMoneySelector::slotMakeCompletion(const QRegExp& _exp) 0411 { 0412 Q_D(KMyMoneySelector); 0413 auto exp(_exp); 0414 QString pattern = exp.pattern(); 0415 if (exp.patternSyntax() == QRegExp::RegExp) { 0416 auto replacement = QStringLiteral(".*:"); 0417 if (!KMyMoneySettings::stringMatchFromStart() || QLatin1String(this->metaObject()->className()) != QLatin1String("KMyMoneySelector")) { 0418 replacement.append(QLatin1String(".*")); 0419 } 0420 pattern.replace(QLatin1String(":"), replacement); 0421 exp.setPattern(pattern); 0422 } 0423 QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); 0424 0425 QTreeWidgetItem* it_v; 0426 0427 // The logic used here seems to be awkward. The problem is, that 0428 // QListViewItem::setVisible works recursively on all it's children 0429 // and grand-children. 0430 // 0431 // The way out of this is as follows: Make all items visible. 0432 // Then go through the list again and perform the checks. 0433 // If an item does not have any children (last leaf in the tree view) 0434 // perform the check. Then check recursively on the parent of this 0435 // leaf that it has no visible children. If that is the case, make the 0436 // parent invisible and continue this check with it's parent. 0437 while ((it_v = *it) != 0) { 0438 it_v->setHidden(false); 0439 ++it; 0440 } 0441 0442 QTreeWidgetItem* firstMatch = 0; 0443 0444 if (!exp.pattern().isEmpty()) { 0445 it = QTreeWidgetItemIterator(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); 0446 while ((it_v = *it) != 0) { 0447 if (it_v->childCount() == 0) { 0448 if (!match(exp, it_v)) { 0449 // this is a node which does not contain the 0450 // text and does not have children. So we can 0451 // safely hide it. Then we check, if the parent 0452 // has more children which are still visible. If 0453 // none are found, the parent node is hidden also. We 0454 // continue until the top of the tree or until we 0455 // find a node that still has visible children. 0456 bool hide = true; 0457 while (hide) { 0458 it_v->setHidden(true); 0459 it_v = it_v->parent(); 0460 if (it_v && (it_v->flags() & Qt::ItemIsSelectable)) { 0461 hide = !match(exp, it_v); 0462 for (auto i = 0; hide && i < it_v->childCount(); ++i) { 0463 if (!it_v->child(i)->isHidden()) 0464 hide = false; 0465 } 0466 } else 0467 hide = false; 0468 } 0469 } else if (!firstMatch) { 0470 firstMatch = it_v; 0471 } 0472 ++it; 0473 0474 } else if (match(exp, it_v)) { 0475 if (!firstMatch) { 0476 firstMatch = it_v; 0477 } 0478 // a node with children contains the text. We want 0479 // to display all child nodes in this case, so we need 0480 // to advance the iterator to the next sibling of the 0481 // current node. This could well be the sibling of a 0482 // parent or grandparent node. 0483 QTreeWidgetItem* curr = it_v; 0484 QTreeWidgetItem* item; 0485 while ((item = curr->treeWidget()->itemBelow(curr)) == 0) { 0486 curr = curr->parent(); 0487 if (curr == 0) 0488 break; 0489 if (match(exp, curr)) 0490 firstMatch = curr; 0491 } 0492 do { 0493 ++it; 0494 } while (*it && *it != item); 0495 } else { 0496 // It's a node with children that does not match. We don't 0497 // change it's status here. 0498 ++it; 0499 } 0500 } 0501 } 0502 0503 // make the first match the one that is selected 0504 // if we have no match, make sure none is selected 0505 if (d->m_selMode == QTreeWidget::SingleSelection) { 0506 if (firstMatch) { 0507 d->m_treeWidget->setCurrentItem(firstMatch); 0508 d->m_treeWidget->scrollToItem(firstMatch); 0509 } else 0510 d->m_treeWidget->clearSelection(); 0511 } 0512 0513 // Get the number of visible nodes for the return code 0514 auto cnt = 0; 0515 0516 it = QTreeWidgetItemIterator(d->m_treeWidget, QTreeWidgetItemIterator::Selectable | QTreeWidgetItemIterator::NotHidden); 0517 while ((it_v = *it) != 0) { 0518 cnt++; 0519 it++; 0520 } 0521 return cnt; 0522 } 0523 0524 bool KMyMoneySelector::contains(const QString& txt) const 0525 { 0526 Q_D(const KMyMoneySelector); 0527 QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); 0528 QTreeWidgetItem* it_v; 0529 while ((it_v = *it) != 0) { 0530 if (it_v->text(0) == txt) { 0531 return true; 0532 } 0533 it++; 0534 } 0535 return false; 0536 } 0537 0538 void KMyMoneySelector::slotItemPressed(QTreeWidgetItem* item, int /* col */) 0539 { 0540 Q_D(KMyMoneySelector); 0541 if (QApplication::mouseButtons() != Qt::RightButton) 0542 return; 0543 0544 if (item->flags().testFlag(Qt::ItemIsUserCheckable)) { 0545 QStyleOptionButton opt; 0546 opt.rect = d->m_treeWidget->visualItemRect(item); 0547 QRect rect = d->m_treeWidget->style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &opt, d->m_treeWidget); 0548 if (rect.contains(d->m_treeWidget->mapFromGlobal(QCursor::pos()))) { 0549 // we get down here, if we have a right click onto the checkbox 0550 item->setCheckState(0, item->checkState(0) == Qt::Checked ? Qt::Unchecked : Qt::Checked); 0551 selectAllSubItems(item, item->checkState(0) == Qt::Checked); 0552 } 0553 } 0554 } 0555 0556 QStringList KMyMoneySelector::selectedItems() const 0557 { 0558 QStringList list; 0559 selectedItems(list); 0560 return list; 0561 } 0562 0563 QStringList KMyMoneySelector::itemList() const 0564 { 0565 QStringList list; 0566 itemList(list); 0567 return list; 0568 }