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"