7#include "defaultrenderer.h"
9#include "defaultrenderer_p.h"
11#include "utils/messageviewerutil.h"
13#include "messageviewer_debug.h"
15#include "converthtmltoplaintext.h"
17#include "messagepartrendererbase.h"
18#include "messagepartrendererfactory.h"
19#include "messagepartrenderermanager.h"
20#include "utils/iconnamecache.h"
21#include "viewer/attachmentstrategy.h"
22#include "viewer/csshelperbase.h"
24#include "htmlwriter/bufferedhtmlwriter.h"
25#include <MimeTreeParser/MessagePart>
26#include <MimeTreeParser/ObjectTreeParser>
28#include <QGpgME/Protocol>
30#include <MessageCore/StringUtil>
32#include <KEmailAddress>
34#include <KLocalizedString>
36#include <KTextTemplate/Context>
37#include <KTextTemplate/Engine>
38#include <KTextTemplate/MetaType>
39#include <KTextTemplate/Template>
40#include <KTextTemplate/TemplateLoader>
43using namespace MimeTreeParser;
44using namespace MessageViewer;
45#ifndef COMPILE_WITH_UNITY_CMAKE_SUPPORT
46Q_DECLARE_METATYPE(GpgME::DecryptionResult::Recipient)
47Q_DECLARE_METATYPE(GpgME::Key)
48Q_DECLARE_METATYPE(
const QGpgME::Protocol *)
50static const int SIG_FRAME_COL_UNDEF = 99;
51#define SIG_FRAME_COL_RED -1
52#define SIG_FRAME_COL_YELLOW 0
53#define SIG_FRAME_COL_GREEN 1
54QString sigStatusToString(
const QGpgME::Protocol *cryptProto,
int status_code, GpgME::Signature::Summary summary,
int &frameColor,
bool &showKeyInfos)
62 if (cryptProto == QGpgME::openpgp()) {
65 switch (status_code) {
67 result =
i18n(
"Error: Signature not verified");
68 frameColor = SIG_FRAME_COL_YELLOW;
71 result =
i18n(
"Good signature");
72 frameColor = SIG_FRAME_COL_GREEN;
75 result =
i18n(
"Bad signature");
76 frameColor = SIG_FRAME_COL_RED;
79 result =
i18n(
"No public key to verify the signature");
80 frameColor = SIG_FRAME_COL_RED;
83 result =
i18n(
"No signature found");
84 frameColor = SIG_FRAME_COL_RED;
87 result =
i18n(
"Error verifying the signature");
88 frameColor = SIG_FRAME_COL_RED;
91 result =
i18n(
"Different results for signatures");
92 frameColor = SIG_FRAME_COL_RED;
106 }
else if (cryptProto == QGpgME::smime()) {
110 if (summary == GpgME::Signature::None) {
111 result =
i18n(
"No status information available.");
112 frameColor = SIG_FRAME_COL_YELLOW;
113 showKeyInfos =
false;
117 if (summary & GpgME::Signature::Valid) {
118 result =
i18n(
"Good signature.");
127 frameColor = SIG_FRAME_COL_GREEN;
128 showKeyInfos =
false;
135 frameColor = SIG_FRAME_COL_GREEN;
137 if (summary & GpgME::Signature::KeyExpired) {
139 result2 =
i18n(
"One key has expired.");
141 if (summary & GpgME::Signature::SigExpired) {
143 result2 +=
i18n(
"The signature has expired.");
147 if (summary & GpgME::Signature::KeyMissing) {
148 result2 +=
i18n(
"Unable to verify: key missing.");
151 showKeyInfos =
false;
152 frameColor = SIG_FRAME_COL_YELLOW;
154 if (summary & GpgME::Signature::CrlMissing) {
155 result2 +=
i18n(
"CRL not available.");
156 frameColor = SIG_FRAME_COL_YELLOW;
158 if (summary & GpgME::Signature::CrlTooOld) {
159 result2 +=
i18n(
"Available CRL is too old.");
160 frameColor = SIG_FRAME_COL_YELLOW;
162 if (summary & GpgME::Signature::BadPolicy) {
163 result2 +=
i18n(
"A policy was not met.");
164 frameColor = SIG_FRAME_COL_YELLOW;
166 if (summary & GpgME::Signature::SysError) {
167 result2 +=
i18n(
"A system error occurred.");
171 showKeyInfos =
false;
172 frameColor = SIG_FRAME_COL_YELLOW;
176 if (summary & GpgME::Signature::KeyRevoked) {
178 result2 +=
i18n(
"One key has been revoked.");
179 frameColor = SIG_FRAME_COL_RED;
181 if (summary & GpgME::Signature::Red) {
195 showKeyInfos =
false;
197 frameColor = SIG_FRAME_COL_RED;
202 if (SIG_FRAME_COL_GREEN == frameColor) {
203 result =
i18n(
"Good signature.");
204 }
else if (SIG_FRAME_COL_RED == frameColor) {
205 result =
i18n(
"Bad signature.");
228 : mCSSHelper(cssHelper)
229 , mRendererFactory(rendererFactory)
233DefaultRendererPrivate::~DefaultRendererPrivate() =
default;
242 return mMsgPart->source();
247 for (
const auto &m : msgPart->subParts()) {
248 renderFactory(m, htmlWriter);
261 if (mp->isAttachment()) {
265 renderSubParts(mp, htmlWriter);
272 if (mp->isAttachment()) {
279 renderSubParts(mp, htmlWriter);
284 if (!mp->hasSubParts()) {
287 KTextTemplate::Template t = MessagePartRendererManager::self()->loadByName(QStringLiteral(
"encapsulatedrfc822messagepart.html"));
291 c.
insert(QStringLiteral(
"block"), &block);
292 block.
setProperty(
"link", mp->nodeHelper()->asHREF(mp->message().data(), QStringLiteral(
"body")));
294 c.
insert(QStringLiteral(
"msgHeader"), mCreateMessageHeader(mp->message().data()));
296 renderSubParts(mp, htmlWriter);
299 if (mp->isAttachment()) {
308 KTextTemplate::Template t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral(
"htmlmessagepart.html"));
312 c.
insert(QStringLiteral(
"block"), &block);
314 auto preferredMode = mp->source()->preferredMode();
317 block.
setProperty(
"loadExternal", htmlLoadExternal());
321 const Util::HtmlMessageInfo messageInfo = Util::processHtml(mp->bodyHtml());
323 if (isHtmlPreferred) {
324 mp->nodeHelper()->setNodeDisplayedEmbedded(mp->content(),
true);
325 htmlWriter->setExtraHead(messageInfo.extraHead);
326 htmlWriter->setStyleBody(Util::parseBodyStyle(messageInfo.bodyStyle));
330 c.
insert(QStringLiteral(
"content"), messageInfo.htmlSource);
334 ConvertHtmlToPlainText
convert;
335 convert.setHtmlString(mp->bodyHtml());
338 c.
insert(QStringLiteral(
"plaintext"), plaintext);
344 if (mp->isAttachment()) {
354 const auto metaData = *mp->partMetaData();
355 KTextTemplate::Template t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral(
"encryptedmessagepart.html"));
358 if (node || mp->hasSubParts()) {
361 if (mp->content() && mp->isRoot()) {
362 rBlock = HTMLBlock::Ptr(new RootBlock(htmlWriter));
364 renderSubParts(mp, htmlWriter);
366 }
else if (!metaData.inProgress) {
368 renderWithFactory<MimeTreeParser::MessagePart>(mp, htmlWriter);
372 if (!mp->decryptRecipients().empty()) {
375 c.
insert(QStringLiteral(
"block"), &block);
378 block.
setProperty(
"detailHeader", showEncryptionDetails());
379 block.
setProperty(
"inProgress", metaData.inProgress);
380 block.
setProperty(
"isDecrypted", mp->decryptMessage());
381 block.
setProperty(
"isDecryptable", metaData.isDecryptable);
383 block.
setProperty(
"errorText", metaData.errorText);
385 block.
setProperty(
"isCompliant", metaData.isCompliant);
386 block.
setProperty(
"compliance", metaData.compliance);
394 const auto metaData = *mp->partMetaData();
395 auto cryptoProto = mp->cryptoProto();
397 const bool isSMIME = cryptoProto && (cryptoProto == QGpgME::smime());
398 KTextTemplate::Template t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral(
"signedmessagepart.html"));
406 rBlock = HTMLBlock::Ptr(new RootBlock(htmlWriter));
408 renderSubParts(mp, htmlWriter);
410 }
else if (!metaData.inProgress) {
412 renderWithFactory<MimeTreeParser::MessagePart>(mp, htmlWriter);
416 c.
insert(QStringLiteral(
"block"), &block);
418 block.
setProperty(
"inProgress", metaData.inProgress);
419 block.
setProperty(
"errorText", metaData.errorText);
421 block.
setProperty(
"detailHeader", showSignatureDetails());
424 for (
const auto &addr : metaData.signerMailAddresses) {
428 block.
setProperty(
"technicalProblem", metaData.technicalProblem);
430 if (metaData.creationTime.isValid()) {
433 block.
setProperty(
"isGoodSignature", metaData.isGoodSignature);
434 block.
setProperty(
"isCompliant", metaData.isCompliant);
435 block.
setProperty(
"compliance", metaData.compliance);
438 if (metaData.keyTrust == GpgME::Signature::Unknown) {
439 block.
setProperty(
"keyTrust", QStringLiteral(
"unknown"));
440 }
else if (metaData.keyTrust == GpgME::Signature::Marginal) {
441 block.
setProperty(
"keyTrust", QStringLiteral(
"marginal"));
442 }
else if (metaData.keyTrust == GpgME::Signature::Full) {
443 block.
setProperty(
"keyTrust", QStringLiteral(
"full"));
444 }
else if (metaData.keyTrust == GpgME::Signature::Ultimate) {
445 block.
setProperty(
"keyTrust", QStringLiteral(
"ultimate"));
447 block.
setProperty(
"keyTrust", QStringLiteral(
"untrusted"));
454 startKeyHREF = QStringLiteral(
"<a href=\"kmail:showCertificate#%1 ### %2 ### %3\">")
461 block.
setProperty(
"keyWithWithoutURL", keyWithWithoutURL);
464 bool onlyShowKeyURL =
false;
465 bool showKeyInfos =
false;
466 bool cannotCheckSignature =
true;
467 QString signer = metaData.signer;
472 if (metaData.inProgress) {
473 mClass = QStringLiteral(
"signInProgress");
475 const QStringList &blockAddrs(metaData.signerMailAddresses);
479 int frameColor = SIG_FRAME_COL_UNDEF;
480 statusStr = sigStatusToString(cryptoProto, metaData.status_code, metaData.sigSummary, frameColor, showKeyInfos);
484 statusStr = metaData.status;
486 if (metaData.technicalProblem) {
487 frameColor = SIG_FRAME_COL_YELLOW;
490 switch (frameColor) {
491 case SIG_FRAME_COL_RED:
492 cannotCheckSignature =
false;
494 case SIG_FRAME_COL_YELLOW:
495 cannotCheckSignature =
true;
497 case SIG_FRAME_COL_GREEN:
498 cannotCheckSignature =
false;
505 if (isSMIME && (SIG_FRAME_COL_UNDEF != frameColor)) {
506 switch (frameColor) {
507 case SIG_FRAME_COL_RED:
508 mClass = QStringLiteral(
"signErr");
509 onlyShowKeyURL =
true;
511 case SIG_FRAME_COL_YELLOW:
512 if (metaData.technicalProblem) {
513 mClass = QStringLiteral(
"signWarn");
515 mClass = QStringLiteral(
"signOkKeyBad");
518 case SIG_FRAME_COL_GREEN:
519 mClass = QStringLiteral(
"signOkKeyOk");
524 if (metaData.keyId.isEmpty()) {
525 certificate =
i18n(
"certificate");
527 certificate = startKeyHREF +
i18n(
"certificate") + QStringLiteral(
"</a>");
530 if (!blockAddrs.empty()) {
537 greenCaseWarning = QStringLiteral(
"<u>") +
i18nc(
"Start of warning message.",
"Warning:") + QStringLiteral(
"</u> ")
538 +
i18n(
"Sender's mail address is not stored in the %1 used for signing.", certificate) + QStringLiteral(
"<br />") +
i18n(
"sender: ")
539 + msgFrom + QStringLiteral(
"<br />") +
i18n(
"stored: ");
552 greenCaseWarning.
append(*it);
556 greenCaseWarning = QStringLiteral(
"<u>") +
i18nc(
"Start of warning message.",
"Warning:") + QStringLiteral(
"</u> ")
557 +
i18n(
"No mail address is stored in the %1 used for signing, "
558 "so we cannot compare it to the sender's address %2.",
565 if (showKeyInfos && !cannotCheckSignature) {
566 if (metaData.signer.isEmpty()) {
569 if (!blockAddrs.empty()) {
571 signer = QStringLiteral(
"<a href=\"mailto:%1\">%2</a>")
577 if (metaData.signer.isEmpty() || metaData.technicalProblem || !metaData.isCompliant) {
578 mClass = QStringLiteral(
"signWarn");
582 signer = QStringLiteral(
"<a href=\"mailto:%1\">%1</a>").
arg(signer);
584 if (metaData.isGoodSignature) {
585 if (metaData.keyTrust < GpgME::Signature::Marginal) {
586 mClass = QStringLiteral(
"signOkKeyBad");
588 mClass = QStringLiteral(
"signOkKeyOk");
591 mClass = QStringLiteral(
"signErr");
597 block.
setProperty(
"onlyShowKeyURL", onlyShowKeyURL);
599 block.
setProperty(
"cannotCheckSignature", cannotCheckSignature);
603 block.
setProperty(
"greenCaseWarning", greenCaseWarning);
610 const auto metaData = *mp->partMetaData();
611 if (metaData.isSigned || metaData.inProgress) {
613 if (mp->isAttachment()) {
616 renderSigned(mp, htmlWriter);
621 if (mp->isAttachment()) {
624 if (mp->hasSubParts()) {
625 renderSubParts(mp, htmlWriter);
626 }
else if (!metaData.inProgress) {
627 renderWithFactory<MimeTreeParser::MessagePart>(mp, htmlWriter);
633 const auto metaData = *mp->partMetaData();
635 if (metaData.isEncrypted || metaData.inProgress) {
637 if (mp->isAttachment()) {
640 renderEncrypted(mp, htmlWriter);
645 if (mp->isAttachment()) {
649 if (mp->hasSubParts()) {
650 renderSubParts(mp, htmlWriter);
651 }
else if (!metaData.inProgress) {
652 renderWithFactory<MimeTreeParser::MessagePart>(mp, htmlWriter);
659 if (mp->isAttachment()) {
663 auto mode = mp->preferredMode();
665 const auto availableModes = mp->availableModes();
666 for (
const auto m : availableModes) {
674 if (mp->childParts().contains(mode)) {
675 part = mp->childParts()[mode];
678 render(part, htmlWriter);
683 const GpgME::ImportResult &importResult(mp->importResult());
684 KTextTemplate::Template t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral(
"certmessagepart.html"));
688 c.
insert(QStringLiteral(
"block"), &block);
690 block.
setProperty(
"nImp", importResult.numImported());
691 block.
setProperty(
"nUnc", importResult.numUnchanged());
692 block.
setProperty(
"nSKImp", importResult.numSecretKeysImported());
693 block.
setProperty(
"nSKUnc", importResult.numSecretKeysUnchanged());
695 QVariantList keylist;
696 const auto imports = importResult.imports();
698 auto end(imports.end());
699 for (
auto it = imports.begin(); it !=
end; ++it) {
702 key->setProperty(
"status", (*it).status());
708 if (mp->isAttachment()) {
717 if (!mRendererFactory) {
720 for (
auto r : mRendererFactory->renderersForPart(mo, msgPart)) {
721 if (r->render(msgPart, htmlWriter,
this)) {
732 if (isHiddenHint(msgPart)) {
733 const QByteArray cid = msgPart->content()->contentID()->identifier();
736 const QString fileName = mp->temporaryFilePath();
742 if (renderWithFactory(msgPart, htmlWriter)) {
749 render(mp, htmlWriter);
754 render(mp, htmlWriter);
756 }
else if (className ==
QLatin1StringView(
"MimeTreeParser::EncapsulatedRfc822MessagePart")) {
759 render(mp, htmlWriter);
764 render(mp, htmlWriter);
766 }
else if (className ==
QLatin1StringView(
"MimeTreeParser::SignedMessagePart")) {
769 render(mp, htmlWriter);
771 }
else if (className ==
QLatin1StringView(
"MimeTreeParser::EncryptedMessagePart")) {
774 render(mp, htmlWriter);
776 }
else if (className ==
QLatin1StringView(
"MimeTreeParser::AlternativeMessagePart")) {
779 render(mp, htmlWriter);
784 render(mp, htmlWriter);
787 qCWarning(MESSAGEVIEWER_LOG) <<
"We got a unknown classname, using default behaviour for " << className;
794 auto content = msgPart->content();
796 if (!mp || !content) {
800 if (mShowOnlyOneMimePart && mMsgPart.data() == msgPart->parentPart()) {
801 if (mMsgPart->subParts().at(0) == msgPart.
data()) {
806 if (msgPart->nodeHelper()->isNodeDisplayedHidden(content)) {
811 const bool defaultHidden(as && as->defaultDisplay(content) == AttachmentStrategy::None);
812 auto preferredMode = source()->preferredMode();
816 if (content->contentType(
false) && !content->contentType(
false)->mediaType().isEmpty() && !content->contentType(
false)->subType().isEmpty()) {
817 mediaType = content->contentType(
false)->mediaType();
819 const bool isTextPart = (mediaType == QByteArrayLiteral(
"text"));
821 bool defaultAsIcon =
true;
822 if (!mp->neverDisplayInline()) {
824 defaultAsIcon = as->defaultDisplay(content) == AttachmentStrategy::AsIcon;
829 if (!mp->isImage() && !isTextPart) {
830 defaultAsIcon =
true;
835 hidden = defaultHidden;
837 if (mp->isImage() && isHtmlPreferred && content->
parent() && content->
parent()->contentType(
false)->subType() ==
"related") {
840 hidden = defaultHidden && content->
parent();
841 hidden |= defaultAsIcon && defaultHidden;
844 msgPart->nodeHelper()->setNodeDisplayedHidden(content, hidden);
851 auto content = msgPart->content();
853 if (!content || !mp) {
854 return MimeTreeParser::IconType::NoIcon;
858 const bool defaultDisplayHidden(as && as->defaultDisplay(content) == AttachmentStrategy::None);
859 const bool defaultDisplayInline(as && as->defaultDisplay(content) == AttachmentStrategy::Inline);
860 const bool defaultDisplayAsIcon(as && as->defaultDisplay(content) == AttachmentStrategy::AsIcon);
861 const bool showOnlyOneMimePart(mShowOnlyOneMimePart);
862 auto preferredMode = source()->preferredMode();
866 if (content->contentType(
false) && !content->contentType(
false)->mediaType().isEmpty() && !content->contentType(
false)->subType().isEmpty()) {
867 mediaType = content->contentType(
false)->mediaType();
869 const bool isTextPart = (mediaType == QByteArrayLiteral(
"text"));
871 bool defaultAsIcon =
true;
872 if (!mp->neverDisplayInline()) {
874 defaultAsIcon = defaultDisplayAsIcon;
877 if (mp->isImage() && showOnlyOneMimePart && !mp->neverDisplayInline()) {
878 defaultAsIcon =
false;
882 if (!mp->isImage() && !isTextPart) {
883 defaultAsIcon =
true;
887 if (as && !defaultDisplayInline) {
888 return MimeTreeParser::IconExternal;
890 return MimeTreeParser::NoIcon;
892 if (mp->isImage() && isHtmlPreferred && content->
parent() && content->
parent()->contentType(
false)->subType() ==
"related") {
893 return MimeTreeParser::IconInline;
896 if (defaultDisplayHidden && !showOnlyOneMimePart && content->
parent()) {
897 return MimeTreeParser::IconInline;
901 return MimeTreeParser::IconExternal;
902 }
else if (mp->isImage()) {
903 return MimeTreeParser::IconInline;
907 return MimeTreeParser::NoIcon;
910bool DefaultRendererPrivate::showEmoticons()
const
912 return mShowEmoticons;
915bool DefaultRendererPrivate::isPrinting()
const
920bool DefaultRendererPrivate::htmlLoadExternal()
const
922 return mHtmlLoadExternal;
925bool DefaultRendererPrivate::showExpandQuotesMark()
const
927 return mShowExpandQuotesMark;
930bool DefaultRendererPrivate::showOnlyOneMimePart()
const
932 return mShowOnlyOneMimePart;
935bool DefaultRendererPrivate::showSignatureDetails()
const
937 return mShowSignatureDetails;
940bool DefaultRendererPrivate::showEncryptionDetails()
const
942 return mShowEncryptionDetails;
945int DefaultRendererPrivate::levelQuote()
const
955DefaultRenderer::~DefaultRenderer() =
default;
957void DefaultRenderer::setShowOnlyOneMimePart(
bool onlyOneMimePart)
959 d->mShowOnlyOneMimePart = onlyOneMimePart;
964 d->mAttachmentStrategy = strategy;
967void DefaultRenderer::setShowEmoticons(
bool showEmoticons)
969 d->mShowEmoticons = showEmoticons;
972void DefaultRenderer::setIsPrinting(
bool isPrinting)
974 d->mIsPrinting = isPrinting;
977void DefaultRenderer::setShowExpandQuotesMark(
bool showExpandQuotesMark)
979 d->mShowExpandQuotesMark = showExpandQuotesMark;
982void DefaultRenderer::setShowEncryptionDetails(
bool showEncryptionDetails)
984 d->mShowEncryptionDetails = showEncryptionDetails;
987void DefaultRenderer::setShowSignatureDetails(
bool showSignatureDetails)
989 d->mShowSignatureDetails = showSignatureDetails;
992void DefaultRenderer::setLevelQuote(
int levelQuote)
994 d->mLevelQuote = levelQuote;
997void DefaultRenderer::setHtmlLoadExternal(
bool htmlLoadExternal)
999 d->mHtmlLoadExternal = htmlLoadExternal;
1002void DefaultRenderer::setCreateMessageHeader(
const std::function<
QString(
KMime::Message *)> &createMessageHeader)
1004 d->mCreateMessageHeader = createMessageHeader;
1011 for (
const auto &subPart : messagePart->subParts()) {
1012 ret += renderTreeHelper(subPart, indent);
1019 qCDebug(MESSAGEVIEWER_LOG) <<
"MimeTreeParser structure:";
1020 qCDebug(MESSAGEVIEWER_LOG) << qPrintable(renderTreeHelper(msgPart,
QString()));
1021 d->mMsgPart = msgPart;
1022 d->renderFactory(d->mMsgPart, writer);
void insert(const QString &name, const QVariant &variant)
QString render(Context *c) const
The AttachmentMarkBlock class.
The AttachmentStrategy class.
An interface for HTML sinks.
virtual void embedPart(const QByteArray &contentId, const QString &url)=0
Embed a part with Content-ID contentId, using url url.
QTextStream * stream() const
Returns a QTextStream on device().
The MessagePartRendererFactory class.
The AlternativeMessagePart class.
The CertMessagePart class.
The EncapsulatedRfc822MessagePart class.
The EncryptedMessagePart class.
The HtmlMessagePart class.
Interface for object tree sources.
The MessagePartList class.
The MimeMessagePart class.
The SignedMessagePart class.
The TextMessagePart class.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
KCODECS_EXPORT QUrl encodeMailtoUrl(const QString &mailbox)
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
T convert(const QVariant &value)
PostalAddress address(const QVariant &location)
QString quoteHtmlChars(const QString &str, bool removeLineBreaks)
Quotes the following characters which have a special meaning in HTML: '<' '>' '&' '"'....
MESSAGEVIEWER_EXPORT bool containsExternalReferences(const QString &str, const QString &extraHead)
Checks whether str contains external references.
@ Html
A HTML message, non-multipart.
@ Normal
A normal plaintext message, non-multipart.
@ MultipartPlain
A multipart/alternative message, the plain text part is currently displayed.
@ MultipartHtml
A multipart/alternative message, the HTML part is currently displayed.
bool isEmpty() const const
void append(QList< T > &&value)
const_iterator constBegin() const const
const_iterator constEnd() const const
QObject * parent() const const
bool setProperty(const char *name, QVariant &&value)
QSharedPointer< X > dynamicCast() const const
QString & append(QChar ch)
QString arg(Args &&... args) const const
QString fromLatin1(QByteArrayView str)
QString fromLocal8Bit(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QString join(QChar separator) const const
QUrl fromLocalFile(const QString &localFile)
QString url(FormattingOptions options) const const
QVariant fromValue(T &&value)