diff --git a/kmymoney/widgets/kmymoneyselector.cpp b/kmymoney/widgets/kmymoneyselector.cpp index 238e7b45d..2ae41b887 100644 --- a/kmymoney/widgets/kmymoneyselector.cpp +++ b/kmymoney/widgets/kmymoneyselector.cpp @@ -1,570 +1,580 @@ /* * Copyright 2006-2018 Thomas Baumgart * Copyright 2017 Łukasz Wojniłowicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "kmymoneyselector_p.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneysettings.h" #include "widgetenums.h" using namespace eWidgets; KMyMoneySelector::KMyMoneySelector(QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags), d_ptr(new KMyMoneySelectorPrivate(this)) { Q_D(KMyMoneySelector); d->init(); } KMyMoneySelector::KMyMoneySelector(KMyMoneySelectorPrivate &dd, QWidget* parent, Qt::WindowFlags flags) : QWidget(parent, flags), d_ptr(&dd) { Q_D(KMyMoneySelector); d->init(); } KMyMoneySelector::~KMyMoneySelector() { Q_D(KMyMoneySelector); delete d; } void KMyMoneySelector::clear() { Q_D(KMyMoneySelector); d->m_treeWidget->clear(); } void KMyMoneySelector::setSelectable(QTreeWidgetItem *item, bool selectable) { if (selectable) { item->setFlags(item->flags() | Qt::ItemIsSelectable); } else { item->setFlags(item->flags() & ~Qt::ItemIsSelectable); } } void KMyMoneySelector::slotSelectAllItems() { selectAllItems(true); } void KMyMoneySelector::slotDeselectAllItems() { selectAllItems(false); } void KMyMoneySelector::setSelectionMode(const QTreeWidget::SelectionMode mode) { Q_D(KMyMoneySelector); if (d->m_selMode != mode) { d->m_selMode = mode; clear(); // make sure, it's either Multi or Single if (mode != QTreeWidget::MultiSelection) { d->m_selMode = QTreeWidget::SingleSelection; connect(d->m_treeWidget, &QTreeWidget::itemSelectionChanged, this, &KMyMoneySelector::stateChanged); connect(d->m_treeWidget, &QTreeWidget::itemActivated, this, &KMyMoneySelector::slotItemSelected); connect(d->m_treeWidget, &QTreeWidget::itemClicked, this, &KMyMoneySelector::slotItemSelected); } else { disconnect(d->m_treeWidget, &QTreeWidget::itemSelectionChanged, this, &KMyMoneySelector::stateChanged); disconnect(d->m_treeWidget, &QTreeWidget::itemActivated, this, &KMyMoneySelector::slotItemSelected); disconnect(d->m_treeWidget, &QTreeWidget::itemClicked, this, &KMyMoneySelector::slotItemSelected); } } QWidget::update(); } QTreeWidget::SelectionMode KMyMoneySelector::selectionMode() const { Q_D(const KMyMoneySelector); return d->m_selMode; } void KMyMoneySelector::slotItemSelected(QTreeWidgetItem *item) { Q_D(KMyMoneySelector); if (d->m_selMode == QTreeWidget::SingleSelection) { if (item && item->flags().testFlag(Qt::ItemIsSelectable)) { emit itemSelected(item->data(0, (int)Selector::Role::Id).toString()); } } } QTreeWidgetItem* KMyMoneySelector::newItem(const QString& name, QTreeWidgetItem* after, const QString& key, const QString& id) { Q_D(KMyMoneySelector); QTreeWidgetItem* item = new QTreeWidgetItem(d->m_treeWidget, after); item->setText(0, name); item->setData(0, (int)Selector::Role::Key, key); item->setData(0, (int)Selector::Role::Id, id); item->setText(1, key); // hidden, but used for sorting item->setFlags(item->flags() & ~Qt::ItemIsUserCheckable); if (id.isEmpty()) { QFont font = item->font(0); font.setBold(true); item->setFont(0, font); setSelectable(item, false); } item->setExpanded(true); return item; } QTreeWidgetItem* KMyMoneySelector::newItem(const QString& name, QTreeWidgetItem* after, const QString& key) { return newItem(name, after, key, QString()); } QTreeWidgetItem* KMyMoneySelector::newItem(const QString& name, QTreeWidgetItem* after) { return newItem(name, after, QString(), QString()); } QTreeWidgetItem* KMyMoneySelector::newItem(const QString& name, const QString& key, const QString& id) { return newItem(name, 0, key, id); } QTreeWidgetItem* KMyMoneySelector::newItem(const QString& name, const QString& key) { return newItem(name, 0, key, QString()); } QTreeWidgetItem* KMyMoneySelector::newItem(const QString& name) { return newItem(name, 0, QString(), QString()); } QTreeWidgetItem* KMyMoneySelector::newTopItem(const QString& name, const QString& key, const QString& id) { Q_D(KMyMoneySelector); QTreeWidgetItem* item = new QTreeWidgetItem(d->m_treeWidget); item->setText(0, name); item->setData(0, (int)Selector::Role::Key, key); item->setData(0, (int)Selector::Role::Id, id); item->setText(1, key); // hidden, but used for sorting item->setFlags(item->flags() & ~Qt::ItemIsUserCheckable); if (d->m_selMode == QTreeWidget::MultiSelection) { item->setFlags(item->flags() | Qt::ItemIsUserCheckable); item->setCheckState(0, Qt::Checked); } return item; } QTreeWidgetItem* KMyMoneySelector::newItem(QTreeWidgetItem* parent, const QString& name, const QString& key, const QString& id) { Q_D(KMyMoneySelector); QTreeWidgetItem* item = new QTreeWidgetItem(parent); item->setText(0, name); item->setData(0, (int)Selector::Role::Key, key); item->setData(0, (int)Selector::Role::Id, id); item->setText(1, key); // hidden, but used for sorting item->setFlags(item->flags() & ~Qt::ItemIsUserCheckable); if (d->m_selMode == QTreeWidget::MultiSelection) { item->setFlags(item->flags() | Qt::ItemIsUserCheckable); item->setCheckState(0, Qt::Checked); } return item; } void KMyMoneySelector::protectItem(const QString& itemId, const bool protect) { Q_D(KMyMoneySelector); QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); QTreeWidgetItem* it_v; // scan items while ((it_v = *it) != 0) { if (it_v->data(0, (int)Selector::Role::Id).toString() == itemId) { setSelectable(it_v, !protect); break; } ++it; } } QTreeWidgetItem* KMyMoneySelector::item(const QString& id) const { Q_D(const KMyMoneySelector); QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); QTreeWidgetItem* it_v; while ((it_v = *it) != 0) { if (it_v->data(0, (int)Selector::Role::Id).toString() == id) break; ++it; } return it_v; } bool KMyMoneySelector::allItemsSelected() const { Q_D(const KMyMoneySelector); QTreeWidgetItem* rootItem = d->m_treeWidget->invisibleRootItem(); if (d->m_selMode == QTreeWidget::SingleSelection) return false; for (auto i = 0; i < rootItem->childCount(); ++i) { QTreeWidgetItem* item = rootItem->child(i); if (item->flags().testFlag(Qt::ItemIsUserCheckable)) { if (!(item->checkState(0) == Qt::Checked && allItemsSelected(item))) return false; } else { if (!allItemsSelected(item)) return false; } } return true; } bool KMyMoneySelector::allItemsSelected(const QTreeWidgetItem *item) const { for (auto i = 0; i < item->childCount(); ++i) { QTreeWidgetItem* child = item->child(i); if (child->flags().testFlag(Qt::ItemIsUserCheckable)) { if (!(child->checkState(0) == Qt::Checked && allItemsSelected(child))) return false; } } return true; } void KMyMoneySelector::removeItem(const QString& id) { Q_D(KMyMoneySelector); QTreeWidgetItem* it_v; QTreeWidgetItemIterator it(d->m_treeWidget); while ((it_v = *it) != 0) { if (id == it_v->data(0, (int)Selector::Role::Id).toString()) { if (it_v->childCount() > 0) { setSelectable(it_v, false); } else { delete it_v; } } it++; } // get rid of top items that just lost the last children (e.g. Favorites) it = QTreeWidgetItemIterator(d->m_treeWidget, QTreeWidgetItemIterator::NotSelectable); while ((it_v = *it) != 0) { if (it_v->childCount() == 0) delete it_v; it++; } } void KMyMoneySelector::selectAllItems(const bool state) { Q_D(KMyMoneySelector); selectAllSubItems(d->m_treeWidget->invisibleRootItem(), state); emit stateChanged(); } void KMyMoneySelector::selectItems(const QStringList& itemList, const bool state) { Q_D(KMyMoneySelector); selectSubItems(d->m_treeWidget->invisibleRootItem(), itemList, state); emit stateChanged(); } void KMyMoneySelector::selectSubItems(QTreeWidgetItem* item, const QStringList& itemList, const bool state) { for (auto i = 0; i < item->childCount(); ++i) { QTreeWidgetItem* child = item->child(i); if (child->flags().testFlag(Qt::ItemIsUserCheckable) && itemList.contains(child->data(0, (int)Selector::Role::Id).toString())) { child->setCheckState(0, state ? Qt::Checked : Qt::Unchecked); } selectSubItems(child, itemList, state); } emit stateChanged(); } void KMyMoneySelector::selectAllSubItems(QTreeWidgetItem* item, const bool state) { for (auto i = 0; i < item->childCount(); ++i) { QTreeWidgetItem* child = item->child(i); if (child->flags().testFlag(Qt::ItemIsUserCheckable)) { child->setCheckState(0, state ? Qt::Checked : Qt::Unchecked); } selectAllSubItems(child, state); } emit stateChanged(); } void KMyMoneySelector::selectedItems(QStringList& list) const { Q_D(const KMyMoneySelector); list.clear(); if (d->m_selMode == QTreeWidget::SingleSelection) { QTreeWidgetItem* it_c = d->m_treeWidget->currentItem(); if (it_c != 0) list << it_c->data(0, (int)Selector::Role::Id).toString(); } else { QTreeWidgetItem* rootItem = d->m_treeWidget->invisibleRootItem(); for (auto i = 0; i < rootItem->childCount(); ++i) { QTreeWidgetItem* child = rootItem->child(i); if (child->flags().testFlag(Qt::ItemIsUserCheckable)) { if (child->checkState(0) == Qt::Checked) list << child->data(0, (int)Selector::Role::Id).toString(); } selectedItems(list, child); } } } void KMyMoneySelector::selectedItems(QStringList& list, QTreeWidgetItem* item) const { for (auto i = 0; i < item->childCount(); ++i) { QTreeWidgetItem* child = item->child(i); if (child->flags().testFlag(Qt::ItemIsUserCheckable)) { if (child->checkState(0) == Qt::Checked) list << child->data(0, (int)Selector::Role::Id).toString(); } selectedItems(list, child); } } void KMyMoneySelector::itemList(QStringList& list) const { Q_D(const KMyMoneySelector); QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); QTreeWidgetItem* it_v; while ((it_v = *it) != 0) { list << it_v->data(0, (int)Selector::Role::Id).toString(); it++; } } void KMyMoneySelector::setSelected(const QString& id, const bool state) { Q_D(const KMyMoneySelector); QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); QTreeWidgetItem* item; QTreeWidgetItem* it_visible = 0; while ((item = *it) != 0) { if (item->data(0, (int)Selector::Role::Id).toString() == id) { if (item->flags().testFlag(Qt::ItemIsUserCheckable)) { item->setCheckState(0, state ? Qt::Checked : Qt::Unchecked); } d->m_treeWidget->setCurrentItem(item); if (!it_visible) it_visible = item; } it++; } // make sure the first one found is visible if (it_visible) d->m_treeWidget->scrollToItem(it_visible); } QTreeWidget* KMyMoneySelector::listView() const { Q_D(const KMyMoneySelector); return d->m_treeWidget; } int KMyMoneySelector::slotMakeCompletion(const QString& _txt) { QString txt(QRegExp::escape(_txt)); if (KMyMoneySettings::stringMatchFromStart() && QLatin1String(this->metaObject()->className()) == QLatin1String("KMyMoneySelector")) txt.prepend('^'); return slotMakeCompletion(QRegExp(txt, Qt::CaseInsensitive)); } bool KMyMoneySelector::match(const QRegExp& exp, QTreeWidgetItem* item) const { return exp.indexIn(item->text(0)) != -1; } -int KMyMoneySelector::slotMakeCompletion(const QRegExp& exp) +int KMyMoneySelector::slotMakeCompletion(const QRegExp& _exp) { Q_D(KMyMoneySelector); + auto exp(_exp); + QString pattern = exp.pattern(); + if (exp.patternSyntax() == QRegExp::RegExp) { + auto replacement = QStringLiteral(".*:"); + if (!KMyMoneySettings::stringMatchFromStart() || QLatin1String(this->metaObject()->className()) != QLatin1String("KMyMoneySelector")) { + replacement.append(QLatin1String(".*")); + } + pattern.replace(QLatin1String(":"), replacement); + exp.setPattern(pattern); + } QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); QTreeWidgetItem* it_v; // The logic used here seems to be awkward. The problem is, that // QListViewItem::setVisible works recursively on all it's children // and grand-children. // // The way out of this is as follows: Make all items visible. // Then go through the list again and perform the checks. // If an item does not have any children (last leaf in the tree view) // perform the check. Then check recursively on the parent of this // leaf that it has no visible children. If that is the case, make the // parent invisible and continue this check with it's parent. while ((it_v = *it) != 0) { it_v->setHidden(false); ++it; } QTreeWidgetItem* firstMatch = 0; if (!exp.pattern().isEmpty()) { it = QTreeWidgetItemIterator(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); while ((it_v = *it) != 0) { if (it_v->childCount() == 0) { if (!match(exp, it_v)) { // this is a node which does not contain the // text and does not have children. So we can // safely hide it. Then we check, if the parent // has more children which are still visible. If // none are found, the parent node is hidden also. We // continue until the top of the tree or until we // find a node that still has visible children. bool hide = true; while (hide) { it_v->setHidden(true); it_v = it_v->parent(); if (it_v && (it_v->flags() & Qt::ItemIsSelectable)) { hide = !match(exp, it_v); for (auto i = 0; hide && i < it_v->childCount(); ++i) { if (!it_v->child(i)->isHidden()) hide = false; } } else hide = false; } } else if (!firstMatch) { firstMatch = it_v; } ++it; } else if (match(exp, it_v)) { if (!firstMatch) { firstMatch = it_v; } // a node with children contains the text. We want // to display all child nodes in this case, so we need // to advance the iterator to the next sibling of the // current node. This could well be the sibling of a // parent or grandparent node. QTreeWidgetItem* curr = it_v; QTreeWidgetItem* item; while ((item = curr->treeWidget()->itemBelow(curr)) == 0) { curr = curr->parent(); if (curr == 0) break; if (match(exp, curr)) firstMatch = curr; } do { ++it; } while (*it && *it != item); } else { // It's a node with children that does not match. We don't // change it's status here. ++it; } } } // make the first match the one that is selected // if we have no match, make sure none is selected if (d->m_selMode == QTreeWidget::SingleSelection) { if (firstMatch) { d->m_treeWidget->setCurrentItem(firstMatch); d->m_treeWidget->scrollToItem(firstMatch); } else d->m_treeWidget->clearSelection(); } // Get the number of visible nodes for the return code auto cnt = 0; it = QTreeWidgetItemIterator(d->m_treeWidget, QTreeWidgetItemIterator::Selectable | QTreeWidgetItemIterator::NotHidden); while ((it_v = *it) != 0) { cnt++; it++; } return cnt; } bool KMyMoneySelector::contains(const QString& txt) const { Q_D(const KMyMoneySelector); QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); QTreeWidgetItem* it_v; while ((it_v = *it) != 0) { if (it_v->text(0) == txt) { return true; } it++; } return false; } void KMyMoneySelector::slotItemPressed(QTreeWidgetItem* item, int /* col */) { Q_D(KMyMoneySelector); if (QApplication::mouseButtons() != Qt::RightButton) return; if (item->flags().testFlag(Qt::ItemIsUserCheckable)) { QStyleOptionButton opt; opt.rect = d->m_treeWidget->visualItemRect(item); QRect rect = d->m_treeWidget->style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &opt, d->m_treeWidget); if (rect.contains(d->m_treeWidget->mapFromGlobal(QCursor::pos()))) { // we get down here, if we have a right click onto the checkbox item->setCheckState(0, item->checkState(0) == Qt::Checked ? Qt::Unchecked : Qt::Checked); selectAllSubItems(item, item->checkState(0) == Qt::Checked); } } } QStringList KMyMoneySelector::selectedItems() const { QStringList list; selectedItems(list); return list; } QStringList KMyMoneySelector::itemList() const { QStringList list; itemList(list); return list; }