File indexing completed on 2024-05-19 05:08:35

0001 /*
0002     SPDX-FileCopyrightText: 2009-2016 Cristian Oneț <onet.cristian@gmail.com>
0003     SPDX-FileCopyrightText: 2009-2010 Alvaro Soliverez <asoliverez@gmail.com>
0004     SPDX-FileCopyrightText: 2010-2020 Thomas Baumgart <tbaumgart@kde.org>
0005     SPDX-FileCopyrightText: 2017-2018 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "ktagcontainer.h"
0010 
0011 // ----------------------------------------------------------------------------
0012 // QT Includes
0013 
0014 #include <QAbstractItemModel>
0015 #include <QAbstractItemView>
0016 #include <QComboBox>
0017 #include <QEvent>
0018 #include <QHBoxLayout>
0019 #include <QKeyEvent>
0020 #include <QLineEdit>
0021 
0022 // ----------------------------------------------------------------------------
0023 // KDE Includes
0024 
0025 #include <KLocalizedString>
0026 
0027 // ----------------------------------------------------------------------------
0028 // Project Includes
0029 
0030 #include "ktaglabel.h"
0031 #include "idfilter.h"
0032 #include "mymoneyenums.h"
0033 
0034 class KTagContainerPrivate
0035 {
0036     Q_DISABLE_COPY(KTagContainerPrivate)
0037     Q_DECLARE_PUBLIC(KTagContainer)
0038 
0039 public:
0040     KTagContainerPrivate(KTagContainer* parent)
0041         : q_ptr(parent)
0042         , m_tagCombo(nullptr)
0043         , m_idFilter(new IdFilter(parent))
0044         , m_skipSelection(true)
0045         , m_selectAfterFocusOut(false)
0046     {
0047     }
0048 
0049     void addTagWidget(int row)
0050     {
0051         Q_Q(KTagContainer);
0052         const auto idx = m_tagCombo->model()->index(row, 0);
0053         const auto id = idx.data(eMyMoney::Model::IdRole).toString();
0054         q->addTagWidget(id);
0055     }
0056 
0057     QStringList tagIdList() const
0058     {
0059         QStringList tags;
0060         for(const auto tag : qAsConst(m_tagLabelList)) {
0061             tags << tag->id();
0062         }
0063         return tags;
0064     }
0065 
0066     KTagContainer* q_ptr;
0067     QComboBox* m_tagCombo;
0068     QScopedPointer<IdFilter> m_idFilter;
0069     QList<KTagLabel*> m_tagLabelList;
0070     bool m_skipSelection;
0071     bool m_selectAfterFocusOut;
0072 };
0073 
0074 KTagContainer::KTagContainer(QWidget* parent)
0075     : QWidget(parent)
0076     , d_ptr(new KTagContainerPrivate(this))
0077 {
0078     Q_D(KTagContainer);
0079     d->m_tagCombo = new QComboBox(this);
0080     d->m_tagCombo->setEditable(true);
0081     d->m_tagCombo->setInsertPolicy(QComboBox::NoInsert);
0082 
0083     QHBoxLayout *layout = new QHBoxLayout;
0084     layout->setContentsMargins(0, 0, 0, 0);
0085     layout->setSpacing(0);
0086     layout->addWidget(d->m_tagCombo, 100);
0087     setLayout(layout);
0088     setFocusProxy(d->m_tagCombo);
0089     d->m_tagCombo->lineEdit()->setPlaceholderText(i18nc("@info:placeholder tag combo box", "Tag"));
0090 
0091     d->m_idFilter.data()->setSortLocaleAware(true);
0092     d->m_idFilter.data()->sort(0);
0093 
0094     connect(d->m_tagCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [=](int row) {
0095         Q_D(KTagContainer);
0096         if (!d->m_skipSelection) {
0097             d->addTagWidget(row);
0098         }
0099     });
0100 
0101     connect(d->m_tagCombo, QOverload<int>::of(&QComboBox::activated), this, [=](int row) {
0102         Q_D(KTagContainer);
0103         if (d->m_selectAfterFocusOut) {
0104             d->addTagWidget(row);
0105             d->m_selectAfterFocusOut = false;
0106         }
0107     });
0108 
0109     d->m_tagCombo->installEventFilter(this);
0110     d->m_tagCombo->view()->installEventFilter(this);
0111 }
0112 
0113 KTagContainer::~KTagContainer()
0114 {
0115     Q_D(KTagContainer);
0116     delete d;
0117 }
0118 
0119 bool KTagContainer::eventFilter(QObject* o, QEvent* e)
0120 {
0121     Q_D(KTagContainer);
0122     if (o == d->m_tagCombo->view()) {
0123         if (e->type() == QEvent::KeyPress) {
0124             QKeyEvent* kev = static_cast<QKeyEvent*>(e);
0125             switch (kev->key()) {
0126             case Qt::Key_Escape:
0127             case Qt::Key_F4:
0128                 d->m_skipSelection = true;
0129                 break;
0130             default:
0131                 break;
0132             }
0133         } else if (e->type() == QEvent::Show) {
0134             d->m_skipSelection = false;
0135         }
0136 
0137     } else if (o == d->m_tagCombo) {
0138         if (e->type() == QEvent::FocusOut) {
0139             const auto row = d->m_tagCombo->currentIndex();
0140             if (row > 0) {
0141                 d->addTagWidget(row);
0142             } else {
0143                 d->m_selectAfterFocusOut = true;
0144             }
0145         }
0146     }
0147     return false;
0148 }
0149 
0150 void KTagContainer::setModel(QAbstractItemModel* model)
0151 {
0152     Q_D(KTagContainer);
0153     const bool needSetSourceModel = d->m_idFilter->sourceModel() == nullptr;
0154     d->m_idFilter->setSourceModel(model);
0155     if (needSetSourceModel) {
0156         d->m_tagCombo->setModel(d->m_idFilter.data());
0157     }
0158 }
0159 
0160 QComboBox* KTagContainer::tagCombo()
0161 {
0162     Q_D(KTagContainer);
0163     return d->m_tagCombo;
0164 }
0165 
0166 const QList<QString> KTagContainer::selectedTags()
0167 {
0168     Q_D(KTagContainer);
0169     return d->m_idFilter->filterList();
0170 }
0171 
0172 void KTagContainer::loadTags(const QList<QString>& idList)
0173 {
0174     Q_D(KTagContainer);
0175     // reset filter
0176     d->m_idFilter->setFilterList(QStringList());
0177     qDeleteAll(d->m_tagLabelList);
0178     d->m_tagLabelList.clear();
0179     for (const auto& id : idList) {
0180         addTagWidget(id);
0181     }
0182 }
0183 
0184 void KTagContainer::addTagWidget(const QString& id)
0185 {
0186     Q_D(KTagContainer);
0187 
0188     d->m_skipSelection = true;
0189     if (id.isEmpty() || d->m_idFilter->filterList().contains(id))
0190         return;
0191 
0192     // set index to the empty item since we remove
0193     // the selected item as part of this method
0194     d->m_tagCombo->setCurrentIndex(0);
0195 
0196     const auto tagName = d->m_tagCombo->itemText(d->m_tagCombo->findData(QVariant(id), Qt::UserRole, Qt::MatchExactly));
0197     KTagLabel* t = new KTagLabel(id, tagName, this);
0198     connect(t, &KTagLabel::clicked, this, &KTagContainer::slotRemoveTagWidget);
0199     d->m_tagLabelList.append(t);
0200     d->m_idFilter->addFilter(id);
0201     layout()->addWidget(t);
0202 
0203     Q_EMIT tagsChanged(d->tagIdList());
0204 }
0205 
0206 void KTagContainer::slotRemoveTagWidget()
0207 {
0208     Q_D(KTagContainer);
0209     const auto tagLabel = qobject_cast<KTagLabel *>(sender());
0210     d->m_idFilter->removeFilter(tagLabel->id());
0211     int index = d->m_tagLabelList.indexOf(tagLabel);
0212     d->m_tagLabelList.removeAt(index);
0213     delete tagLabel;
0214 
0215     Q_EMIT tagsChanged(d->tagIdList());
0216 
0217     d->m_tagCombo->setCurrentIndex(0);
0218     d->m_tagCombo->setFocus();
0219 }