Messagelib

recipientseditor.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Casey Link <unnamedrambler@gmail.com>
3 SPDX-FileCopyrightText: 2009-2010 Klaralvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
4
5 Refactored from earlier code by:
6 SPDX-FileCopyrightText: 2010 Volker Krause <vkrause@kde.org>
7 SPDX-FileCopyrightText: 2004 Cornelius Schumacher <schumacher@kde.org>
8
9 SPDX-License-Identifier: LGPL-2.0-or-later
10*/
11
12#include "recipientseditor.h"
13
14#include "recipient.h"
15#include "recipientseditorsidewidget.h"
16
17#include "distributionlistdialog.h"
18#include "settings/messagecomposersettings.h"
19
20#include "messagecomposer_debug.h"
21
22#include <KEmailAddress>
23#include <KLocalizedString>
24#include <KMessageBox>
25#include <KMime/Headers>
26
27#include <QKeyEvent>
28#include <QLayout>
29
30using namespace MessageComposer;
31using namespace KPIM;
32
33RecipientLineFactory::RecipientLineFactory(QObject *parent)
35{
36}
37
38KPIM::MultiplyingLine *RecipientLineFactory::newLine(QWidget *p)
39{
40 auto line = new RecipientLineNG(p);
42 connect(line,
43 SIGNAL(addRecipient(RecipientLineNG *, QString)),
45 SLOT(addRecipient(RecipientLineNG *, QString)));
46 } else {
47 qCWarning(MESSAGECOMPOSER_LOG) << "RecipientLineFactory::newLine: We can't connect to new line" << parent();
48 }
49 return line;
50}
51
52int RecipientLineFactory::maximumRecipients()
53{
54 return MessageComposer::MessageComposerSettings::self()->maximumRecipients();
55}
56
57class MessageComposer::RecipientsEditorPrivate
58{
59public:
60 RecipientsEditorPrivate() = default;
61
62 KConfig *mRecentAddressConfig = nullptr;
63 RecipientsEditorSideWidget *mSideWidget = nullptr;
64 bool mSkipTotal = false;
65};
66
67RecipientsEditor::RecipientsEditor(QWidget *parent)
68 : RecipientsEditor(new RecipientLineFactory(nullptr), parent)
69{
70}
71
72RecipientsEditor::RecipientsEditor(RecipientLineFactory *lineFactory, QWidget *parent)
73 : MultiplyingLineEditor(lineFactory, parent)
74 , d(new MessageComposer::RecipientsEditorPrivate)
75{
76 factory()->setParent(this); // HACK: can't use 'this' above since it's not yet constructed at that point
77 d->mSideWidget = new RecipientsEditorSideWidget(this, this);
78
79 layout()->addWidget(d->mSideWidget);
80
81 // Install global event filter and listen for keypress events for RecipientLineEdits.
82 // Unfortunately we can't install ourselves directly as event filter for the edits,
83 // because the RecipientLineEdit has its own event filter installed into QApplication
84 // and so it would eat the event before it would reach us.
85 qApp->installEventFilter(this);
86
87 connect(d->mSideWidget, &RecipientsEditorSideWidget::pickedRecipient, this, &RecipientsEditor::slotPickedRecipient);
88 connect(d->mSideWidget, &RecipientsEditorSideWidget::saveDistributionList, this, &RecipientsEditor::saveDistributionList);
89
90 connect(this, &RecipientsEditor::lineAdded, this, &RecipientsEditor::slotLineAdded);
91 connect(this, &RecipientsEditor::lineDeleted, this, &RecipientsEditor::slotLineDeleted);
92
93 addData(); // one default line
94}
95
96RecipientsEditor::~RecipientsEditor() = default;
97
98bool RecipientsEditor::addRecipient(const QString &recipient, Recipient::Type type)
99{
100 return addData(Recipient::Ptr(new Recipient(recipient, type)), false);
101}
102
103void RecipientsEditor::addRecipient(RecipientLineNG *line, const QString &recipient)
104{
105 addRecipient(recipient, line->recipientType());
106}
107
108bool RecipientsEditor::setRecipientString(const QList<KMime::Types::Mailbox> &mailboxes, Recipient::Type type)
109{
110 int count = 1;
111 for (const KMime::Types::Mailbox &mailbox : mailboxes) {
112 if (count++ > MessageComposer::MessageComposerSettings::self()->maximumRecipients()) {
114 i18ncp("@info:status",
115 "Truncating recipients list to %2 of %1 entry.",
116 "Truncating recipients list to %2 of %1 entries.",
117 mailboxes.count(),
118 MessageComposer::MessageComposerSettings::self()->maximumRecipients()));
119 return true;
120 }
121 // Too many
123 return true;
124 }
125 }
126 return false;
127}
128
129Recipient::List RecipientsEditor::recipients() const
130{
133 for (const MultiplyingLineData::Ptr &datum : dataList) {
135 if (rec.isNull()) {
136 continue;
137 }
138 recList << rec;
139 }
140 return recList;
141}
142
143Recipient::Ptr RecipientsEditor::activeRecipient() const
144{
146}
147
148QString RecipientsEditor::recipientString(Recipient::Type type) const
149{
150 return recipientStringList(type).join(QLatin1StringView(", "));
151}
152
153QStringList RecipientsEditor::recipientStringList(Recipient::Type type) const
154{
156 for (const Recipient::Ptr &r : recipients()) {
157 if (r->type() == type) {
158 selectedRecipients << r->email();
159 }
160 }
161 return selectedRecipients;
162}
163
164void RecipientsEditor::removeRecipient(const QString &recipient, Recipient::Type type)
165{
166 // search a line which matches recipient and type
168 MultiplyingLine *line = nullptr;
169 while (it.hasNext()) {
170 line = it.next();
172 if (rec) {
173 if ((rec->recipient()->email() == recipient) && (rec->recipientType() == type)) {
174 break;
175 }
176 }
177 }
178 if (line) {
179 line->slotPropagateDeletion();
180 }
181}
182
183void RecipientsEditor::saveDistributionList()
184{
185 std::unique_ptr<MessageComposer::DistributionListDialog> dlg(new MessageComposer::DistributionListDialog(this));
186 dlg->setRecipients(recipients());
187 dlg->exec();
188}
189
190void RecipientsEditor::selectRecipients()
191{
192 d->mSideWidget->pickRecipient();
193}
194
196{
197 d->mRecentAddressConfig = config;
198 if (config) {
199 const auto linesP{lines()};
200 for (auto line : linesP) {
202 if (rec) {
203 rec->setRecentAddressConfig(config);
204 }
205 }
206 }
207}
208
209void RecipientsEditor::slotPickedRecipient(const Recipient &rec, bool &tooManyAddress)
210{
211 const Recipient::Type t = rec.type();
212 tooManyAddress = addRecipient(rec.email(), t == Recipient::Undefined ? Recipient::To : t);
213 mModified = true;
214}
215
216RecipientsPicker *RecipientsEditor::picker() const
217{
218 return d->mSideWidget->picker();
219}
220
221void RecipientsEditor::slotLineAdded(MultiplyingLine *line)
222{
223 // subtract 1 here, because we want the number of lines
224 // before this line was added.
225 const int count = lines().size() - 1;
227 if (!rec) {
228 return;
229 }
230
231 if (d->mRecentAddressConfig) {
232 rec->setRecentAddressConfig(d->mRecentAddressConfig);
233 }
234
235 if (count > 0) {
236 if (count == 1) {
237 rec->setRecipientType(Recipient::To);
238 } else {
239 auto last_rec = qobject_cast<RecipientLineNG *>(lines().at(lines().count() - 2));
240 if (last_rec) {
241 if (last_rec->recipientType() == Recipient::ReplyTo) {
242 rec->setRecipientType(Recipient::To);
243 } else {
244 rec->setRecipientType(last_rec->recipientType());
245 }
246 }
247 }
248 line->fixTabOrder(lines().constLast()->tabOut());
249 }
250 connect(rec, &RecipientLineNG::countChanged, this, &RecipientsEditor::slotCalculateTotal);
251}
252
253void RecipientsEditor::slotLineDeleted(int pos)
254{
255 bool atLeastOneToLine = false;
256 int firstCC = -1;
257 for (int i = pos, total = lines().count(); i < total; ++i) {
258 MultiplyingLine *line = lines().at(i);
260 if (rec) {
261 if (rec->recipientType() == Recipient::To) {
262 atLeastOneToLine = true;
263 } else if ((rec->recipientType() == Recipient::Cc) && (firstCC < 0)) {
264 firstCC = i;
265 }
266 }
267 }
268
269 if (!atLeastOneToLine && (firstCC >= 0)) {
271 if (firstCCLine) {
272 firstCCLine->setRecipientType(Recipient::To);
273 }
274 }
275
276 slotCalculateTotal();
277}
278
279bool RecipientsEditor::eventFilter(QObject *object, QEvent *event)
280{
281 if (event->type() == QEvent::KeyPress && qobject_cast<RecipientLineEdit *>(object)) {
282 auto ke = static_cast<QKeyEvent *>(event);
283 // Treats comma or semicolon as email separator, will automatically move focus
284 // to a new line, basically preventing user from inputting more than one
285 // email address per line, which breaks our opportunistic crypto in composer
286 if (ke->key() == Qt::Key_Comma || (ke->key() == Qt::Key_Semicolon && MessageComposerSettings::self()->allowSemicolonAsAddressSeparator())) {
287 auto line = qobject_cast<RecipientLineNG *>(object->parent());
288 const auto split = KEmailAddress::splitAddressList(line->rawData() + QLatin1StringView(", "));
289 if (split.size() > 1) {
290 addRecipient(QString(), line->recipientType());
292 return true;
293 }
294 }
295 } else if (event->type() == QEvent::FocusIn && qobject_cast<RecipientLineEdit *>(object)) {
296 Q_EMIT focusInRecipientLineEdit();
297 }
298
299 return false;
300}
301
302void RecipientsEditor::slotCalculateTotal()
303{
304 // Prevent endless recursion when splitting recipient
305 if (d->mSkipTotal) {
306 return;
307 }
308 int empty = 0;
309 const auto currentLines = lines();
310 for (auto line : currentLines) {
312 if (rec) {
313 if (rec->isEmpty()) {
314 ++empty;
315 } else {
316 const int recipientsCount = rec->recipientsCount();
317 if (recipientsCount > 1) {
318 // Ensure we always have only one recipient per line
319 d->mSkipTotal = true;
320 Recipient::Ptr recipient = rec->recipient();
321 const auto split = KEmailAddress::splitAddressList(recipient->email());
322 bool maximumElementFound = false;
323 for (int i = 1 /* sic! */; i < split.count(); ++i) {
324 maximumElementFound = addRecipient(split[i], rec->recipientType());
326 break;
327 }
328 }
329 recipient->setEmail(split[0]);
330 rec->setData(recipient);
331 setFocusBottom(); // focus next empty entry
332 d->mSkipTotal = false;
334 d->mSideWidget->setTotal(lines().count(), lines().count());
335 return;
336 }
337 }
338 }
339 }
340 }
341 // We always want at least one empty line
342 if (empty == 0) {
343 addData({}, false);
344 }
345 int count = 0;
346 const auto linesP{lines()};
347 for (auto line : linesP) {
349 if (rec) {
350 if (!rec->isEmpty()) {
351 count++;
352 }
353 }
354 }
355 // update the side widget
356 d->mSideWidget->setTotal(count, lines().count());
357}
358
359RecipientLineNG *RecipientsEditor::activeLine() const
360{
361 MultiplyingLine *line = MultiplyingLineEditor::activeLine();
363}
364
365#include "moc_recipientseditor.cpp"
QList< MultiplyingLineData::Ptr > allData() const
MultiplyingLineData::Ptr activeData() const
bool addData(const MultiplyingLineData::Ptr &data=MultiplyingLineData::Ptr(), bool showDialogBox=true)
virtual void fixTabOrder(QWidget *previous)=0
The RecipientLineFactory class.
The RecipientLineNG class.
The Recipient class.
Definition recipient.h:28
The RecipientsEditor class.
void setRecentAddressConfig(KConfig *config)
Sets the config file used for storing recent addresses.
void removeRecipient(const QString &recipient, Recipient::Type type)
Removes the recipient provided it can be found and has the given type.
bool addRecipient(const QString &recipient, Recipient::Type type)
Adds a recipient (or multiple recipients) to one line of the editor.
KCODECS_EXPORT QStringList splitAddressList(const QString &aStr)
QString i18ncp(const char *context, const char *singular, const char *plural, const TYPE &arg...)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
Simple interface that both EncryptJob and SignEncryptJob implement so the composer can extract some e...
qsizetype count() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
T qobject_cast(QObject *object)
QString join(QChar separator) const const
Key_Comma
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
virtual bool event(QEvent *event) override
void setParent(QWidget *parent)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:12:43 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.