KIdentityManagement

signatureconfigurator.cpp
1/* -*- c++ -*-
2 SPDX-FileCopyrightText: 2008 Thomas McGuire <Thomas.McGuire@gmx.net>
3 SPDX-FileCopyrightText: 2008 Edwin Schepers <yez@familieschepers.nl>
4 SPDX-FileCopyrightText: 2004 Marc Mutz <mutz@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.1-or-later
7*/
8
9#include "signatureconfigurator.h"
10#include "identity.h"
11#include "signaturerichtexteditor_p.h"
12
13#include <KActionCollection>
14#include <KIO/JobUiDelegateFactory>
15#include <KIO/OpenUrlJob>
16#include <KLineEdit>
17#include <KLocalizedString>
18#include <KMessageBox>
19#include <KShellCompletion>
20#include <KToolBar>
21#include <KUrlRequester>
22#include <QUrl>
23
24#include <KPIMTextEdit/RichTextComposer>
25#include <KPIMTextEdit/RichTextComposerControler>
26#include <KPIMTextEdit/RichTextComposerImages>
27#include <TextCustomEditor/RichTextEditor>
28
29#include <QCheckBox>
30#include <QComboBox>
31#include <QDir>
32#include <QFileInfo>
33#include <QLabel>
34#include <QStackedWidget>
35
36#include <QHBoxLayout>
37#include <QVBoxLayout>
38
39#include <QStandardPaths>
40#include <cassert>
41
42#include <TextCustomEditor/RichTextEditorWidget>
43
44using namespace KIdentityManagementWidgets;
45
46namespace KIdentityManagementWidgets
47{
48/**
49 Private class that helps to provide binary compatibility between releases.
50 @internal
51 */
52//@cond PRIVATE
53class Q_DECL_HIDDEN SignatureConfiguratorPrivate
54{
55public:
56 explicit SignatureConfiguratorPrivate(SignatureConfigurator *parent);
57 void init();
58 // Returns the current text of the textedit as HTML code, but strips
59 // unnecessary tags Qt inserts
60 [[nodiscard]] QString asCleanedHTML() const;
61
62 QString imageLocation;
63 SignatureConfigurator *const q;
64 QCheckBox *mEnableCheck = nullptr;
65 QCheckBox *mHtmlCheck = nullptr;
66 QComboBox *mSourceCombo = nullptr;
67 KUrlRequester *mFileRequester = nullptr;
68 QPushButton *mEditButton = nullptr;
69 KLineEdit *mCommandEdit = nullptr;
70 KToolBar *mEditToolBar = nullptr;
71 KToolBar *mFormatToolBar = nullptr;
72 KPIMTextEdit::RichTextComposer *mTextEdit = nullptr;
73 bool inlinedHtml = false;
74};
75//@endcond
76
77SignatureConfiguratorPrivate::SignatureConfiguratorPrivate(SignatureConfigurator *parent)
78 : q(parent)
79 , inlinedHtml(true)
80{
81}
82
83QString SignatureConfiguratorPrivate::asCleanedHTML() const
84{
85 QString text = mTextEdit->toHtml();
86
87 // Beautiful little hack to find the html headers produced by Qt.
88 QTextDocument textDocument;
89 const QString html = textDocument.toHtml();
90
91 // Now remove each line from the text, the result is clean html.
92 const QStringList lst = html.split(QLatin1Char('\n'));
93 for (const QString &line : lst) {
94 text.remove(line + QLatin1Char('\n'));
95 }
96 return text;
97}
98
99void SignatureConfiguratorPrivate::init()
100{
101 auto vlay = new QVBoxLayout(q);
102 vlay->setObjectName(QLatin1StringView("main layout"));
103
104 // "enable signature" checkbox:
105 mEnableCheck = new QCheckBox(i18n("&Enable signature"), q);
106 mEnableCheck->setWhatsThis(
107 i18n("Check this box if you want KMail to append a signature to mails "
108 "written with this identity."));
109 vlay->addWidget(mEnableCheck);
110
111 // "obtain signature text from" combo and label:
112 auto hlay = new QHBoxLayout(); // inherits spacing
113 vlay->addLayout(hlay);
114 mSourceCombo = new QComboBox(q);
115 mSourceCombo->setEditable(false);
116 mSourceCombo->setWhatsThis(i18n("Click on the widgets below to obtain help on the input methods."));
117 mSourceCombo->setEnabled(false); // since !mEnableCheck->isChecked()
118 mSourceCombo->addItems(QStringList() << i18nc("continuation of \"obtain signature text from\"", "Input Field Below")
119 << i18nc("continuation of \"obtain signature text from\"", "File")
120 << i18nc("continuation of \"obtain signature text from\"", "Output of Command"));
121 auto label = new QLabel(i18n("Obtain signature &text from:"), q);
122 label->setBuddy(mSourceCombo);
123 label->setEnabled(false); // since !mEnableCheck->isChecked()
124 hlay->addWidget(label);
125 hlay->addWidget(mSourceCombo, 1);
126
127 // widget stack that is controlled by the source combo:
128 auto widgetStack = new QStackedWidget(q);
129 widgetStack->setEnabled(false); // since !mEnableCheck->isChecked()
130 vlay->addWidget(widgetStack, 1);
131 q->connect(mSourceCombo, &QComboBox::currentIndexChanged, widgetStack, &QStackedWidget::setCurrentIndex);
132 q->connect(mSourceCombo, &QComboBox::highlighted, widgetStack, &QStackedWidget::setCurrentIndex);
133 // connects for the enabling of the widgets depending on
134 // signatureEnabled:
135 q->connect(mEnableCheck, &QCheckBox::toggled, mSourceCombo, &QComboBox::setEnabled);
136 q->connect(mEnableCheck, &QCheckBox::toggled, widgetStack, &QStackedWidget::setEnabled);
137 q->connect(mEnableCheck, &QCheckBox::toggled, label, &QLabel::setEnabled);
138 // The focus might be still in the widget that is disabled
139 q->connect(mEnableCheck, &QCheckBox::clicked, mEnableCheck, qOverload<>(&QCheckBox::setFocus));
140
141 int pageno = 0;
142 // page 0: input field for direct entering:
143 auto page = new QWidget(widgetStack);
144 widgetStack->insertWidget(pageno, page);
145 auto page_vlay = new QVBoxLayout(page);
146 page_vlay->setContentsMargins(0, 0, 0, 0);
147
148#ifndef QT_NO_TOOLBAR
149 mEditToolBar = new KToolBar(q);
150 mEditToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
151 page_vlay->addWidget(mEditToolBar, 0);
152
153 mFormatToolBar = new KToolBar(q);
154 mFormatToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
155 page_vlay->addWidget(mFormatToolBar, 1);
156#endif
157
158 mTextEdit = new KPIMTextEdit::RichTextComposer(q);
159
160 auto richTextEditorwidget = new TextCustomEditor::RichTextEditorWidget(mTextEdit, q);
161 page_vlay->addWidget(richTextEditorwidget, 2);
162 mTextEdit->setWhatsThis(i18n("Use this field to enter an arbitrary static signature."));
163
164 // Fill the toolbars.
165 auto actionCollection = new KActionCollection(q);
166 mTextEdit->createActions(actionCollection);
167#ifndef QT_NO_TOOLBAR
168 mEditToolBar->addAction(actionCollection->action(QStringLiteral("format_text_bold")));
169 mEditToolBar->addAction(actionCollection->action(QStringLiteral("format_text_italic")));
170 mEditToolBar->addAction(actionCollection->action(QStringLiteral("format_text_underline")));
171 mEditToolBar->addAction(actionCollection->action(QStringLiteral("format_text_strikeout")));
172 mEditToolBar->addAction(actionCollection->action(QStringLiteral("format_text_foreground_color")));
173 mEditToolBar->addAction(actionCollection->action(QStringLiteral("format_text_background_color")));
174 mEditToolBar->addAction(actionCollection->action(QStringLiteral("format_font_family")));
175 mEditToolBar->addAction(actionCollection->action(QStringLiteral("format_font_size")));
176 mEditToolBar->addAction(actionCollection->action(QStringLiteral("format_reset")));
177
178 mFormatToolBar->addAction(actionCollection->action(QStringLiteral("format_list_style")));
179 mFormatToolBar->addAction(actionCollection->action(QStringLiteral("format_list_indent_more")));
180 mFormatToolBar->addAction(actionCollection->action(QStringLiteral("format_list_indent_less")));
181 mFormatToolBar->addAction(actionCollection->action(QStringLiteral("format_list_indent_less")));
182 mFormatToolBar->addSeparator();
183
184 mFormatToolBar->addAction(actionCollection->action(QStringLiteral("format_align_left")));
185 mFormatToolBar->addAction(actionCollection->action(QStringLiteral("format_align_center")));
186 mFormatToolBar->addAction(actionCollection->action(QStringLiteral("format_align_right")));
187 mFormatToolBar->addAction(actionCollection->action(QStringLiteral("format_align_justify")));
188 mFormatToolBar->addSeparator();
189
190 mFormatToolBar->addAction(actionCollection->action(QStringLiteral("insert_horizontal_rule")));
191 mFormatToolBar->addAction(actionCollection->action(QStringLiteral("manage_link")));
192 mFormatToolBar->addAction(actionCollection->action(QStringLiteral("format_painter")));
193
194 mFormatToolBar->addSeparator();
195 mFormatToolBar->addAction(actionCollection->action(QStringLiteral("add_image")));
196 mFormatToolBar->addSeparator();
197 mFormatToolBar->addAction(actionCollection->action(QStringLiteral("insert_html")));
198 mFormatToolBar->addAction(actionCollection->action(QStringLiteral("insert_table")));
199#endif
200
201 hlay = new QHBoxLayout(); // inherits spacing
202 page_vlay->addLayout(hlay);
203 mHtmlCheck = new QCheckBox(i18n("&Use HTML"), page);
204 q->connect(mHtmlCheck, &QCheckBox::clicked, q, &SignatureConfigurator::slotSetHtml);
205 hlay->addWidget(mHtmlCheck);
206 inlinedHtml = true;
207
208 widgetStack->setCurrentIndex(0); // since mSourceCombo->currentItem() == 0
209
210 // page 1: "signature file" requester, label, "edit file" button:
211 ++pageno;
212 page = new QWidget(widgetStack);
213 widgetStack->insertWidget(pageno, page); // force sequential numbers (play safe)
214 page_vlay = new QVBoxLayout(page);
215 page_vlay->setContentsMargins(0, 0, 0, 0);
216 hlay = new QHBoxLayout(); // inherits spacing
217 page_vlay->addLayout(hlay);
218 mFileRequester = new KUrlRequester(page);
219 mFileRequester->setNameFilter(i18n("Text File (*.txt)"));
220 mFileRequester->setWhatsThis(
221 i18n("Use this requester to specify a text file that contains your "
222 "signature. It will be read every time you create a new mail or "
223 "append a new signature."));
224 label = new QLabel(i18n("S&pecify file:"), page);
225 label->setBuddy(mFileRequester);
226 hlay->addWidget(label);
227 hlay->addWidget(mFileRequester, 1);
228 mFileRequester->button()->setAutoDefault(false);
229 q->connect(mFileRequester, &KUrlRequester::textEdited, q, &SignatureConfigurator::slotUrlChanged);
230 q->connect(mFileRequester, &KUrlRequester::urlSelected, q, &SignatureConfigurator::slotUrlChanged);
231 mEditButton = new QPushButton(i18n("Edit &File"), page);
232 mEditButton->setWhatsThis(i18n("Opens the specified file in a text editor."));
233 q->connect(mEditButton, &QPushButton::clicked, q, &SignatureConfigurator::slotEdit);
234 mEditButton->setAutoDefault(false);
235 mEditButton->setEnabled(false); // initially nothing to edit
236 hlay->addWidget(mEditButton);
237 page_vlay->addStretch(1); // spacer
238
239 // page 2: "signature command" requester and label:
240 ++pageno;
241 page = new QWidget(widgetStack);
242 widgetStack->insertWidget(pageno, page);
243 page_vlay = new QVBoxLayout(page);
244 page_vlay->setContentsMargins(0, 0, 0, 0);
245 hlay = new QHBoxLayout(); // inherits spacing
246 page_vlay->addLayout(hlay);
247 mCommandEdit = new KLineEdit(page);
248 mCommandEdit->setClearButtonEnabled(true);
249 mCommandEdit->setCompletionObject(new KShellCompletion());
250 mCommandEdit->setAutoDeleteCompletionObject(true);
251 mCommandEdit->setWhatsThis(
252 i18n("You can add an arbitrary command here, either with or without path "
253 "depending on whether or not the command is in your Path. For every "
254 "new mail, KMail will execute the command and use what it outputs (to "
255 "standard output) as a signature. Usual commands for use with this "
256 "mechanism are \"fortune\" or \"ksig -random\". "
257 "(Be careful, script needs a shebang line)."));
258 label = new QLabel(i18n("S&pecify command:"), page);
259 label->setBuddy(mCommandEdit);
260 hlay->addWidget(label);
261 hlay->addWidget(mCommandEdit, 1);
262 page_vlay->addStretch(1); // spacer
263}
264
266 : QWidget(parent)
267 , d(new SignatureConfiguratorPrivate(this))
268{
269 d->init();
270}
271
273
275{
276 return d->mEnableCheck->isChecked();
277}
278
280{
281 d->mEnableCheck->setChecked(enable);
282}
283
285{
286 switch (d->mSourceCombo->currentIndex()) {
287 case 0:
288 return Signature::Inlined;
289 case 1:
290 return Signature::FromFile;
291 case 2:
292 return Signature::FromCommand;
293 default:
294 return Signature::Disabled;
295 }
296}
297
299{
300 int idx = 0;
301 switch (type) {
302 case Signature::Inlined:
303 idx = 0;
304 break;
305 case Signature::FromFile:
306 idx = 1;
307 break;
308 case Signature::FromCommand:
309 idx = 2;
310 break;
311 default:
312 idx = 0;
313 break;
314 }
315
316 d->mSourceCombo->setCurrentIndex(idx);
317}
318
320{
321 d->mTextEdit->setTextOrHtml(text);
322}
323
325{
326 QString file = d->mFileRequester->url().path();
327
328 // Force the filename to be relative to ~ instead of $PWD depending
329 // on the rest of the code (KRun::run in Edit and KFileItem on save)
330 if (!file.isEmpty() && QFileInfo(file).isRelative()) {
331 file = QDir::home().absolutePath() + QLatin1Char('/') + file;
332 }
333 return file;
334}
335
337{
338 d->mFileRequester->setUrl(QUrl::fromLocalFile(url));
339 d->mEditButton->setDisabled(url.trimmed().isEmpty());
340}
341
343{
344 return d->mCommandEdit->text();
345}
346
348{
349 d->mCommandEdit->setText(url);
350}
351
353{
354 Signature sig;
355 const Signature::Type sigType = signatureType();
356 switch (sigType) {
357 case Signature::Inlined:
358 sig.setInlinedHtml(d->inlinedHtml);
359 sig.setText(d->inlinedHtml ? d->asCleanedHTML() : d->mTextEdit->textOrHtml());
360 if (d->inlinedHtml) {
361 if (!d->imageLocation.isEmpty()) {
362 sig.setImageLocation(d->imageLocation);
363 }
364 const KPIMTextEdit::ImageWithNameList images = d->mTextEdit->composerControler()->composerImages()->imagesWithName();
365 for (const KPIMTextEdit::ImageWithNamePtr &image : images) {
366 sig.addImage(image->image, image->name);
367 }
368 }
369 break;
370 case Signature::FromCommand:
371 sig.setPath(commandPath(), true);
372 break;
373 case Signature::FromFile:
374 sig.setPath(filePath(), false);
375 break;
376 case Signature::Disabled:
377 /* do nothing */
378 break;
379 }
381 sig.setType(sigType);
382 return sig;
383}
384
386{
387 setSignatureType(sig.type());
388 setSignatureEnabled(sig.isEnabledSignature());
389
390 if (sig.isInlinedHtml()) {
391 d->mHtmlCheck->setCheckState(Qt::Checked);
392 } else {
393 d->mHtmlCheck->setCheckState(Qt::Unchecked);
394 }
395 slotSetHtml();
396
397 // Let insertIntoTextEdit() handle setting the text, as that function also adds the images.
398 d->mTextEdit->clear();
399 SignatureRichTextEditor::insertIntoTextEdit(sig, Signature::Start, Signature::AddNothing, d->mTextEdit, true);
400 if (sig.type() == Signature::FromFile) {
401 setFileURL(sig.path());
402 } else {
404 }
405
406 if (sig.type() == Signature::FromCommand) {
407 setCommandURL(sig.path());
408 } else {
410 }
411}
412
413void SignatureConfigurator::slotUrlChanged()
414{
415 const QString file = filePath();
416 const QFileInfo infoFile(file);
417 if (infoFile.isFile() && (infoFile.size() > 1000)) {
418 KMessageBox::information(this, i18n("This text file size exceeds 1kb."), i18nc("@title:window", "Text File Size"));
419 }
420 d->mEditButton->setDisabled(file.isEmpty());
421}
422
423void SignatureConfigurator::slotEdit()
424{
425 const QString url = filePath();
426 // slotEnableEditButton should prevent this assert from being hit:
427 assert(!url.isEmpty());
428
429 auto job = new KIO::OpenUrlJob(QUrl::fromLocalFile(url), QStringLiteral("text/plain"));
431 job->start();
432}
433
434// "use HTML"-checkbox (un)checked
435void SignatureConfigurator::slotSetHtml()
436{
437 if (d->mHtmlCheck->checkState() == Qt::Unchecked) {
438 d->mHtmlCheck->setText(i18n("&Use HTML"));
439#ifndef QT_NO_TOOLBAR
440 d->mEditToolBar->setVisible(false);
441 d->mEditToolBar->setEnabled(false);
442 d->mFormatToolBar->setVisible(false);
443 d->mFormatToolBar->setEnabled(false);
444#endif
445 d->mTextEdit->switchToPlainText();
446 d->inlinedHtml = false;
447 } else {
448 d->mHtmlCheck->setText(i18n("&Use HTML (disabling removes formatting)"));
449 d->inlinedHtml = true;
450#ifndef QT_NO_TOOLBAR
451 d->mEditToolBar->setVisible(true);
452 d->mEditToolBar->setEnabled(true);
453 d->mFormatToolBar->setVisible(true);
454 d->mFormatToolBar->setEnabled(true);
455#endif
456 d->mTextEdit->activateRichText();
457 }
458}
459
461{
462 d->imageLocation = path;
463}
464
466{
467 const QString dir =
468 QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/emailidentities/%1/").arg(QString::number(identity.uoid()));
469 QDir().mkpath(dir);
470 setImageLocation(dir);
471}
472}
473
474#include "moc_signatureconfigurator.cpp"
User identity information.
Definition identity.h:72
Abstraction of a signature (aka "footer").
Definition signature.h:61
void setPath(const QString &path, bool isExecutable=false)
Set the signature URL and mark this signature as being of "from file" resp.
void setText(const QString &text)
Set the signature text and mark this signature as being of "inline text" type.
void setImageLocation(const QString &path)
Sets the location where the copies of the signature images will be stored.
void setInlinedHtml(bool isHtml)
Sets the inlined signature to text or html.
void setEnabledSignature(bool enabled)
setEnabledSignature
void addImage(const QImage &image, const QString &imageName)
Adds the given image to the signature.
This widget gives an interface so users can edit their signature.
QString commandPath() const
Returns the url of the command which the users wants to use as signature.
void setSignatureEnabled(bool enable)
Use this to activate the signature.
void setInlineText(const QString &text)
Make text the text for the signature.
QString filePath() const
Returns the file url which the user wants to use as a signature.
void setFileURL(const QString &url)
Set url for the file url part of the widget.
void setSignature(const Signature &sig)
Convenience method.
void setSignatureType(Signature::Type type)
Set the signature type to type.
void setImageLocation(const QString &path)
Sets the directory where the images used in the HTML signature will be stored.
Signature::Type signatureType() const
This returns the type of the signature, so that can be Disabled, Inline, fromFile,...
SignatureConfigurator(QWidget *parent=nullptr)
Constructor.
void setCommandURL(const QString &url)
Sets url as the command to execute.
bool isSignatureEnabled() const
Indicated if the user wants a signature.
void urlSelected(const QUrl &)
void textEdited(const QString &)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
KIOCORE_EXPORT KJobUiDelegate * createDefaultJobUiDelegate()
void information(QWidget *parent, const QString &text, const QString &title=QString(), const QString &dontShowAgainName=QString(), Options options=Notify)
QString label(StandardShortcut id)
QCA_EXPORT void init()
void clicked(bool checked)
void toggled(bool checked)
void currentIndexChanged(int index)
void highlighted(int index)
QString absolutePath() const const
QDir home()
bool mkpath(const QString &dirPath) const const
void setCurrentIndex(int index)
QString writableLocation(StandardLocation type)
QString arg(Args &&... args) const const
bool isEmpty() const const
QString number(double n, char format, int precision)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString trimmed() const const
ToolButtonIconOnly
QString toHtml() const const
QUrl fromLocalFile(const QString &localFile)
void setEnabled(bool)
void setFocus()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:20:09 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.