File indexing completed on 2025-02-16 04:45:57

0001 /*
0002     This file is part of Akonadi Contact.
0003 
0004     SPDX-FileCopyrightText: 2010 KDAB
0005     SPDX-FileContributor: Tobias Koenig <tokoe@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "emailaddressselectionwidget.h"
0011 
0012 #include "emailaddressselection_p.h"
0013 #include "emailaddressselectionmodel.h"
0014 #include "emailaddressselectionproxymodel_p.h"
0015 
0016 #include "contactsfilterproxymodel.h"
0017 #include "contactstreemodel.h"
0018 #include <Akonadi/ChangeRecorder>
0019 #include <Akonadi/ControlGui>
0020 #include <Akonadi/EntityDisplayAttribute>
0021 #include <Akonadi/EntityTreeView>
0022 #include <Akonadi/ItemFetchScope>
0023 #include <Akonadi/Session>
0024 #include <KLocalizedString>
0025 
0026 #include <QHBoxLayout>
0027 #include <QHeaderView>
0028 #include <QKeyEvent>
0029 #include <QLabel>
0030 #include <QLineEdit>
0031 #include <QTimer>
0032 #include <QVBoxLayout>
0033 using namespace Akonadi;
0034 using namespace Akonadi;
0035 /**
0036  * @internal
0037  */
0038 class SearchLineEdit : public QLineEdit
0039 {
0040     Q_OBJECT
0041 public:
0042     SearchLineEdit(QWidget *receiver, QWidget *parent = nullptr)
0043         : QLineEdit(parent)
0044         , mReceiver(receiver)
0045     {
0046         setClearButtonEnabled(true);
0047         installEventFilter(this);
0048     }
0049 
0050 protected:
0051     void keyPressEvent(QKeyEvent *event) override
0052     {
0053         if (event->key() == Qt::Key_Down) {
0054             QMetaObject::invokeMethod(mReceiver, "setFocus");
0055         }
0056 
0057         QLineEdit::keyPressEvent(event);
0058     }
0059     bool eventFilter(QObject *obj, QEvent *event) override
0060     {
0061         if (obj == this) {
0062             if (event->type() == QEvent::KeyPress) {
0063                 auto e = static_cast<QKeyEvent *>(event);
0064                 if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
0065                     const bool stopEvent = (e->modifiers() == Qt::NoButton || e->modifiers() == Qt::KeypadModifier);
0066                     if (stopEvent) {
0067                         Q_EMIT returnPressed();
0068                     }
0069                     return true;
0070                 }
0071             }
0072         }
0073         return QObject::eventFilter(obj, event);
0074     }
0075 
0076 private:
0077     QWidget *const mReceiver;
0078 };
0079 
0080 /**
0081  * @internal
0082  */
0083 class Akonadi::EmailAddressSelectionWidgetPrivate
0084 {
0085 public:
0086     EmailAddressSelectionWidgetPrivate(bool showOnlyContactWithEmail, EmailAddressSelectionWidget *qq, QAbstractItemModel *model)
0087         : q(qq)
0088         , mModel(model)
0089         , mShowOnlyContactWithEmail(showOnlyContactWithEmail)
0090     {
0091         init();
0092     }
0093 
0094     void init();
0095 
0096     EmailAddressSelectionWidget *const q;
0097     QAbstractItemModel *mModel = nullptr;
0098     QLabel *mDescriptionLabel = nullptr;
0099     SearchLineEdit *mSearchLine = nullptr;
0100     Akonadi::EntityTreeView *mView = nullptr;
0101     EmailAddressSelectionProxyModel *mSelectionModel = nullptr;
0102     bool mShowOnlyContactWithEmail = false;
0103 };
0104 
0105 void EmailAddressSelectionWidgetPrivate::init()
0106 {
0107     // setup internal model if needed
0108     if (!mModel) {
0109         auto model = new Akonadi::EmailAddressSelectionModel(q);
0110         mModel = model->model();
0111     }
0112 
0113     // setup ui
0114     auto layout = new QVBoxLayout(q);
0115     layout->setContentsMargins({});
0116 
0117     mDescriptionLabel = new QLabel;
0118     mDescriptionLabel->hide();
0119     layout->addWidget(mDescriptionLabel);
0120 
0121     auto searchLayout = new QHBoxLayout;
0122     searchLayout->setContentsMargins({});
0123     layout->addLayout(searchLayout);
0124 
0125     mView = new Akonadi::EntityTreeView;
0126     mView->setEditTriggers(Akonadi::EntityTreeView::NoEditTriggers);
0127 
0128     auto label = new QLabel(i18nc("@label Search in a list of contacts", "Search:"));
0129     mSearchLine = new SearchLineEdit(mView);
0130     mSearchLine->setPlaceholderText(i18n("Search Contact..."));
0131     label->setBuddy(mSearchLine);
0132     searchLayout->addWidget(label);
0133     searchLayout->addWidget(mSearchLine);
0134 
0135 #ifndef QT_NO_DRAGANDDROP
0136     mView->setDragDropMode(QAbstractItemView::NoDragDrop);
0137 #endif
0138     layout->addWidget(mView);
0139 
0140     auto filter = new Akonadi::ContactsFilterProxyModel(q);
0141     if (mShowOnlyContactWithEmail) {
0142         filter->setFilterFlags(ContactsFilterProxyModel::HasEmail);
0143     }
0144     filter->setMatchFilterContactFlag(ContactsFilterProxyModel::MatchFilterContactFlag::OnlyNameAndEmailsAddresses);
0145     filter->setExcludeVirtualCollections(true);
0146     filter->setSourceModel(mModel);
0147 
0148     mSelectionModel = new EmailAddressSelectionProxyModel(q);
0149     mSelectionModel->setSourceModel(filter);
0150 
0151     mView->setModel(mSelectionModel);
0152     mView->header()->hide();
0153 
0154     q->connect(mSearchLine, &QLineEdit::textChanged, filter, &ContactsFilterProxyModel::setFilterString);
0155 
0156     q->connect(mView, qOverload<const Akonadi::Item &>(&Akonadi::EntityTreeView::doubleClicked), q, [this]() {
0157         Q_EMIT q->doubleClicked();
0158     });
0159     ControlGui::widgetNeedsAkonadi(q);
0160 
0161     mSearchLine->setFocus();
0162 
0163     if (auto etm = qobject_cast<Akonadi::EntityTreeModel *>(mModel)) {
0164         QObject::connect(etm, &Akonadi::EntityTreeModel::collectionTreeFetched, mView, &QTreeView::expandAll);
0165     } else {
0166         QTimer::singleShot(1000, mView, &QTreeView::expandAll);
0167     }
0168 }
0169 
0170 EmailAddressSelectionWidget::EmailAddressSelectionWidget(QWidget *parent)
0171     : QWidget(parent)
0172     , d(new EmailAddressSelectionWidgetPrivate(true, this, nullptr))
0173 {
0174 }
0175 
0176 EmailAddressSelectionWidget::EmailAddressSelectionWidget(QAbstractItemModel *model, QWidget *parent)
0177     : QWidget(parent)
0178     , d(new EmailAddressSelectionWidgetPrivate(true, this, model))
0179 {
0180 }
0181 
0182 EmailAddressSelectionWidget::EmailAddressSelectionWidget(bool showOnlyContactWithEmail, QAbstractItemModel *model, QWidget *parent)
0183     : QWidget(parent)
0184     , d(new EmailAddressSelectionWidgetPrivate(showOnlyContactWithEmail, this, model))
0185 {
0186 }
0187 
0188 EmailAddressSelectionWidget::~EmailAddressSelectionWidget() = default;
0189 
0190 EmailAddressSelection::List EmailAddressSelectionWidget::selectedAddresses() const
0191 {
0192     EmailAddressSelection::List selections;
0193 
0194     if (!d->mView->selectionModel()) {
0195         return selections;
0196     }
0197 
0198     const QModelIndexList selectedRows = d->mView->selectionModel()->selectedRows(0);
0199     for (const QModelIndex &index : selectedRows) {
0200         EmailAddressSelection selection;
0201         selection.setName(index.data(EmailAddressSelectionProxyModel::NameRole).toString());
0202         selection.setEmail(index.data(EmailAddressSelectionProxyModel::EmailAddressRole).toString());
0203         selection.setItem(index.data(ContactsTreeModel::ItemRole).value<Akonadi::Item>());
0204 
0205         if (d->mShowOnlyContactWithEmail) {
0206             if (!selection.email().isEmpty()) {
0207                 selections << selection;
0208             }
0209         } else {
0210             selections << selection;
0211         }
0212     }
0213 
0214     return selections;
0215 }
0216 
0217 QLineEdit *EmailAddressSelectionWidget::searchLineEdit() const
0218 {
0219     return d->mSearchLine;
0220 }
0221 
0222 QTreeView *EmailAddressSelectionWidget::view() const
0223 {
0224     return d->mView;
0225 }
0226 
0227 #include "emailaddressselectionwidget.moc"
0228 
0229 #include "moc_emailaddressselectionwidget.cpp"