Pimcommon

blacklistbalooemailcompletionwidget.cpp
1/*
2 SPDX-FileCopyrightText: 2015-2024 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5
6*/
7
8#include "blacklistbalooemailcompletionwidget.h"
9using namespace Qt::Literals::StringLiterals;
10
11#include "blacklistbalooemaillist.h"
12#include "blacklistbalooemailsearchjob.h"
13#include "blacklistbalooemailutil.h"
14#include "blacklistbalooemailwarning.h"
15#include <KLineEditEventHandler>
16
17#include "pimcommonakonadi_debug.h"
18#include <KConfigGroup>
19#include <KListWidgetSearchLine>
20#include <KLocalizedString>
21#include <KSharedConfig>
22#include <QHBoxLayout>
23#include <QLabel>
24#include <QLineEdit>
25#include <QMenu>
26#include <QPushButton>
27#include <QVBoxLayout>
28
29using namespace PimCommon;
30BlackListBalooEmailCompletionWidget::BlackListBalooEmailCompletionWidget(QWidget *parent)
31 : QWidget(parent)
32 , mNumberOfEmailsFound(new QLabel(this))
33 , mSearchLineEdit(new QLineEdit(this))
34 , mExcludeDomainLineEdit(new QLineEdit(this))
35 , mEmailList(new BlackListBalooEmailList(this))
36 , mSearchButton(new QPushButton(QIcon::fromTheme(QStringLiteral("edit-find")), i18n("Search"), this))
37 , mSelectButton(new QPushButton(i18nc("@action:button", "&Select"), this))
38 , mUnselectButton(new QPushButton(i18nc("@action:button", "&Unselect"), this))
39 , mShowAllBlackListedEmails(new QPushButton(i18nc("@action:button", "Show Blacklisted Emails"), this))
40 , mMoreResult(new QLabel(i18nc("@label:textbox", "<qt><a href=\"more_result\">More result...</a></qt>"), this))
41 , mBlackListWarning(new BlackListBalooEmailWarning(this))
42 , mExcludeEmailFromRegularExpressionLineEdit(new QLineEdit(this))
43{
44 auto mainLayout = new QVBoxLayout(this);
45
46 auto searchLayout = new QHBoxLayout;
47 mainLayout->addLayout(searchLayout);
48
49 auto lab = new QLabel(i18nc("@label:textbox", "Search email:"), this);
50 lab->setObjectName("search_label"_L1);
51 searchLayout->addWidget(lab);
52
53 mSearchLineEdit->setPlaceholderText(i18nc("@info:placeholder", "Research is done from 3 characters"));
54 mSearchLineEdit->setFocus();
55 mSearchLineEdit->setClearButtonEnabled(true);
57 mSearchLineEdit->setObjectName("search_lineedit"_L1);
58 connect(mSearchLineEdit, &QLineEdit::returnPressed, this, &BlackListBalooEmailCompletionWidget::slotCheckIfUpdateBlackListIsNeeded);
59 searchLayout->addWidget(mSearchLineEdit);
60
61 mSearchButton->setObjectName("search_button"_L1);
62 connect(mSearchButton, &QAbstractButton::clicked, this, &BlackListBalooEmailCompletionWidget::slotCheckIfUpdateBlackListIsNeeded);
63 mSearchButton->setEnabled(false);
64 searchLayout->addWidget(mSearchButton);
65
66 mShowAllBlackListedEmails->setObjectName("show_blacklisted_email_button"_L1);
67 connect(mShowAllBlackListedEmails, &QAbstractButton::clicked, this, &BlackListBalooEmailCompletionWidget::slotShowAllBlacklistedEmail);
68 searchLayout->addWidget(mShowAllBlackListedEmails);
69
70 mEmailList->setObjectName("email_list"_L1);
71 mainLayout->addWidget(mEmailList);
72
73 mBlackListWarning->setObjectName("backlistwarning"_L1);
74 connect(mBlackListWarning, &BlackListBalooEmailWarning::newSearch, this, &BlackListBalooEmailCompletionWidget::slotSearch);
75 connect(mBlackListWarning, &BlackListBalooEmailWarning::saveChanges, this, &BlackListBalooEmailCompletionWidget::slotSaveChanges);
76 mainLayout->addWidget(mBlackListWarning);
77
78 auto searchLineLayout = new QHBoxLayout;
79 mainLayout->addLayout(searchLineLayout);
80
81 auto selectElementLayout = new QHBoxLayout;
82 searchLineLayout->addLayout(selectElementLayout);
83
84 mSelectButton->setObjectName("select_email"_L1);
85 connect(mSelectButton, &QAbstractButton::clicked, this, &BlackListBalooEmailCompletionWidget::slotSelectEmails);
86 selectElementLayout->addWidget(mSelectButton);
87
88 mUnselectButton->setObjectName("unselect_email"_L1);
89 connect(mUnselectButton, &QAbstractButton::clicked, this, &BlackListBalooEmailCompletionWidget::slotUnselectEmails);
90 selectElementLayout->addWidget(mUnselectButton);
91
92 mMoreResult->setObjectName("moreresultlabel"_L1);
93 selectElementLayout->addWidget(mMoreResult);
94
95 mMoreResult->setContextMenuPolicy(Qt::NoContextMenu);
96 connect(mMoreResult, &QLabel::linkActivated, this, &BlackListBalooEmailCompletionWidget::slotLinkClicked);
97 mMoreResult->setVisible(false);
98 selectElementLayout->addStretch(1);
99
100 connect(mSearchLineEdit, &QLineEdit::textChanged, this, &BlackListBalooEmailCompletionWidget::slotSearchLineEditChanged);
101
102 mSearchInResultLineEdit = new KListWidgetSearchLine(this, mEmailList);
103 mSearchInResultLineEdit->setObjectName("searchinresultlineedit"_L1);
104 mSearchInResultLineEdit->setClearButtonEnabled(true);
105 mSearchInResultLineEdit->setPlaceholderText(i18nc("@info:placeholder", "Search in result..."));
106 KLineEditEventHandler::catchReturnKey(mSearchInResultLineEdit);
107
108 searchLineLayout->addStretch(0);
109 mNumberOfEmailsFound->setObjectName("numberofemailsfound"_L1);
110
111 searchLineLayout->addWidget(mNumberOfEmailsFound, 1);
112 searchLineLayout->addWidget(mSearchInResultLineEdit);
113
114 auto excludeDomainLayout = new QHBoxLayout;
115 excludeDomainLayout->setContentsMargins({});
116 mainLayout->addLayout(excludeDomainLayout);
117
118 auto excludeDomainLabel = new QLabel(i18nc("@label:textbox", "Exclude domain names:"), this);
119 excludeDomainLabel->setObjectName("domain_label"_L1);
120 excludeDomainLayout->addWidget(excludeDomainLabel);
121
122 excludeDomainLayout->addWidget(mExcludeDomainLineEdit);
123 mExcludeDomainLineEdit->setObjectName("domain_lineedit"_L1);
124 mExcludeDomainLineEdit->setClearButtonEnabled(true);
125 KLineEditEventHandler::catchReturnKey(mExcludeDomainLineEdit);
126 mExcludeDomainLineEdit->setPlaceholderText(i18nc("@info:placeholder", "Separate domain with \'%1\'", QLatin1Char(',')));
127
128 auto excludeEmailRegularExpressionLayout = new QHBoxLayout;
129 excludeEmailRegularExpressionLayout->setContentsMargins({});
130 mainLayout->addLayout(excludeEmailRegularExpressionLayout);
131
132 auto excludeEmailRegularExpressionLabel = new QLabel(i18nc("@label:textbox", "Exclude email with Regular Expression:"), this);
133 excludeEmailRegularExpressionLabel->setObjectName("email_regularexpression_label"_L1);
134 excludeEmailRegularExpressionLayout->addWidget(excludeEmailRegularExpressionLabel);
135
136 excludeEmailRegularExpressionLayout->addWidget(mExcludeEmailFromRegularExpressionLineEdit);
137 mExcludeEmailFromRegularExpressionLineEdit->setObjectName("exclude_email_lineedit"_L1);
138 mExcludeEmailFromRegularExpressionLineEdit->setClearButtonEnabled(true);
139 KLineEditEventHandler::catchReturnKey(mExcludeEmailFromRegularExpressionLineEdit);
140 mExcludeEmailFromRegularExpressionLineEdit->setPlaceholderText(i18nc("@info:placeholder", "Separate regular expression with \'%1\'", QLatin1Char(',')));
141
142 connect(mEmailList, &QListWidget::itemSelectionChanged, this, &BlackListBalooEmailCompletionWidget::slotSelectionChanged);
143 slotSelectionChanged();
144 connect(mEmailList, &QListWidget::customContextMenuRequested, this, &BlackListBalooEmailCompletionWidget::slotCustomContextMenuRequested);
145}
146
147BlackListBalooEmailCompletionWidget::~BlackListBalooEmailCompletionWidget() = default;
148
149void BlackListBalooEmailCompletionWidget::slotSelectionChanged()
150{
151 const bool selectionIsNotEmpty = !mEmailList->selectedItems().isEmpty();
152 mSelectButton->setEnabled(selectionIsNotEmpty);
153 mUnselectButton->setEnabled(selectionIsNotEmpty);
154}
155
156void BlackListBalooEmailCompletionWidget::slotCustomContextMenuRequested(const QPoint &pos)
157{
158 const bool selectionIsNotEmpty = !mEmailList->selectedItems().isEmpty();
159 if (selectionIsNotEmpty) {
160 QMenu menu(this);
161 menu.addAction(i18n("Select"), this, &BlackListBalooEmailCompletionWidget::slotSelectEmails);
162 menu.addSeparator();
163 menu.addAction(i18n("Unselect"), this, &BlackListBalooEmailCompletionWidget::slotUnselectEmails);
164 menu.exec(mapToGlobal(pos));
165 }
166}
167
168void BlackListBalooEmailCompletionWidget::load()
169{
170 KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kpimbalooblacklist"));
171 KConfigGroup group(config, QStringLiteral("AddressLineEdit"));
172
173 const QStringList lstExcludeEmailsRegularExpressions = group.readEntry("ExcludeEmailsRegexp", QStringList());
174 mEmailList->setExcludeEmailsRegularExpressions(lstExcludeEmailsRegularExpressions);
175 mExcludeEmailFromRegularExpressionLineEdit->setText(lstExcludeEmailsRegularExpressions.join(QLatin1Char(',')));
176 mOriginalExcludeEmailRegexp = lstExcludeEmailsRegularExpressions;
177
178 const QStringList lstExcludeDomains = group.readEntry("ExcludeDomain", QStringList());
179 mEmailList->setExcludeDomains(lstExcludeDomains);
180 mExcludeDomainLineEdit->setText(lstExcludeDomains.join(QLatin1Char(',')));
181 mOriginalExcludeDomain = lstExcludeDomains;
182 slotSelectionChanged();
183}
184
185void BlackListBalooEmailCompletionWidget::save()
186{
187 const QString excludeEmailsRegexp = mExcludeEmailFromRegularExpressionLineEdit->text().remove(QLatin1Char(' '));
188 const QStringList newExcludeEmailsRegExp = excludeEmailsRegexp.split(QLatin1Char(','), Qt::SkipEmptyParts);
189 bool needToSave = (mOriginalExcludeEmailRegexp != newExcludeEmailsRegExp);
190
191 const QString excludeDomains = mExcludeDomainLineEdit->text().remove(QLatin1Char(' '));
192 const QStringList newExcludeDomain = excludeDomains.split(QLatin1Char(','), Qt::SkipEmptyParts);
193 needToSave |= (mOriginalExcludeDomain != newExcludeDomain);
194
195 KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kpimbalooblacklist"));
196 KConfigGroup group(config, QStringLiteral("AddressLineEdit"));
197 const QHash<QString, bool> result = mEmailList->blackListItemChanged();
198 if (!result.isEmpty()) {
199 needToSave = true;
200 QStringList blackList = group.readEntry("BalooBackList", QStringList());
201 PimCommon::BlackListBalooEmailUtil util;
202 util.initialBlackList(blackList);
203 util.setNewBlackList(result);
204 blackList = util.createNewBlackList();
205 group.writeEntry("BalooBackList", blackList);
206 }
207 if (needToSave) {
208 group.writeEntry("ExcludeEmailsRegexp", newExcludeEmailsRegExp);
209 mEmailList->setExcludeEmailsRegularExpressions(newExcludeEmailsRegExp);
210 mOriginalExcludeEmailRegexp = newExcludeEmailsRegExp;
211
212 group.writeEntry("ExcludeDomain", newExcludeDomain);
213 mEmailList->setExcludeDomains(newExcludeDomain);
214 mOriginalExcludeDomain = newExcludeDomain;
215 group.sync();
216 }
217 config->reparseConfiguration();
218}
219
220void BlackListBalooEmailCompletionWidget::slotSaveChanges()
221{
222 // TODO avoid to save a lot.
223 const QHash<QString, bool> result = mEmailList->blackListItemChanged();
224 KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kpimbalooblacklist"));
225 KConfigGroup group(config, QStringLiteral("AddressLineEdit"));
226 QStringList blackList = group.readEntry("BalooBackList", QStringList());
227 PimCommon::BlackListBalooEmailUtil util;
228 util.initialBlackList(blackList);
229 util.setNewBlackList(result);
230 blackList = util.createNewBlackList();
231 group.writeEntry("BalooBackList", blackList);
232 group.sync();
233 mEmailList->setEmailBlackList(blackList);
234 slotSearch();
235}
236
237void BlackListBalooEmailCompletionWidget::slotUnselectEmails()
238{
239 const QList<QListWidgetItem *> lstSelected = mEmailList->selectedItems();
240 for (QListWidgetItem *item : lstSelected) {
241 item->setCheckState(Qt::Unchecked);
242 }
243}
244
245void BlackListBalooEmailCompletionWidget::slotSelectEmails()
246{
247 const QList<QListWidgetItem *> lstSelected = mEmailList->selectedItems();
248 for (QListWidgetItem *item : lstSelected) {
249 item->setCheckState(Qt::Checked);
250 }
251}
252
253void BlackListBalooEmailCompletionWidget::slotSearchLineEditChanged(const QString &text)
254{
255 mSearchButton->setEnabled(text.trimmed().length() > 2);
256 hideMoreResultAndChangeLimit();
257}
258
259void BlackListBalooEmailCompletionWidget::hideMoreResultAndChangeLimit()
260{
261 mMoreResult->setVisible(false);
262 mLimit = 500;
263}
264
265void BlackListBalooEmailCompletionWidget::slotSearch()
266{
267 const QString searchEmail = mSearchLineEdit->text().trimmed();
268 if (searchEmail.length() > 2) {
269 mSearchInResultLineEdit->clear();
270 auto job = new PimCommon::BlackListBalooEmailSearchJob(this);
271 job->setSearchEmail(searchEmail);
272 job->setLimit(mLimit);
273 connect(job, &BlackListBalooEmailSearchJob::emailsFound, this, &BlackListBalooEmailCompletionWidget::slotEmailFound);
274 if (!job->start()) {
275 qCWarning(PIMCOMMONAKONADI_LOG) << "Impossible to start search job";
276 }
277 }
278}
279
280void BlackListBalooEmailCompletionWidget::slotEmailFound(const QStringList &list)
281{
282 const int numberOfEmails = mEmailList->setEmailFound(list);
283 mMoreResult->setVisible(numberOfEmails == mLimit);
284 mEmailList->scrollToBottom();
285 if (numberOfEmails == 0) {
286 mNumberOfEmailsFound->setText(i18n("No email found."));
287 } else {
288 mNumberOfEmailsFound->setText(i18np("1 email found", "%1 emails found", numberOfEmails));
289 }
290}
291
292void BlackListBalooEmailCompletionWidget::setEmailBlackList(const QStringList &list)
293{
294 mEmailList->setEmailBlackList(list);
295}
296
297void BlackListBalooEmailCompletionWidget::slotCheckIfUpdateBlackListIsNeeded()
298{
299 const QHash<QString, bool> result = mEmailList->blackListItemChanged();
300 if (result.isEmpty()) {
301 slotSearch();
302 } else {
303 mBlackListWarning->animatedShow();
304 }
305}
306
307void BlackListBalooEmailCompletionWidget::slotLinkClicked(const QString &link)
308{
309 if (link == "more_result"_L1) {
310 mLimit += 200;
311 slotSearch();
312 }
313}
314
315void BlackListBalooEmailCompletionWidget::slotShowAllBlacklistedEmail()
316{
317 KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kpimbalooblacklist"));
318 KConfigGroup group(config, QStringLiteral("AddressLineEdit"));
319 const QStringList balooBlackList = group.readEntry("BalooBackList", QStringList());
320 slotEmailFound(balooBlackList);
321}
322
323#include "moc_blacklistbalooemailcompletionwidget.cpp"
void animatedShow()
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
void catchReturnKey(QObject *lineEdit)
folderdialogacltab.h
void clicked(bool checked)
void addLayout(QLayout *layout, int stretch)
bool isEmpty() const const
void linkActivated(const QString &link)
void setText(const QString &)
void setContentsMargins(const QMargins &margins)
void returnPressed()
void setText(const QString &)
void textChanged(const QString &text)
void itemSelectionChanged()
QList< QListWidgetItem * > selectedItems() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
qsizetype length() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString trimmed() const const
QString join(QChar separator) const const
Unchecked
NoContextMenu
SkipEmptyParts
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void customContextMenuRequested(const QPoint &pos)
void setEnabled(bool)
QPoint mapToGlobal(const QPoint &pos) const const
virtual void setVisible(bool visible)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 22 2024 12:12:05 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.