File indexing completed on 2024-11-24 04:39:35
0001 /* 0002 SPDX-FileCopyrightText: 2012-2024 Laurent Montel <montel@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 0006 */ 0007 0008 #include "textbrowser_p.h" 0009 #include <KCodecs> 0010 #include <KLocalizedString> 0011 #include <KStandardAction> 0012 #include <QAction> 0013 #include <QUrl> 0014 0015 #include <QApplication> 0016 #include <QClipboard> 0017 #include <QContextMenuEvent> 0018 #include <QMenu> 0019 #include <QRegularExpression> 0020 #include <QTextBlock> 0021 0022 using namespace Akonadi; 0023 0024 TextBrowser::TextBrowser(QWidget *parent) 0025 : QTextBrowser(parent) 0026 { 0027 setOpenLinks(false); 0028 } 0029 0030 void TextBrowser::slotCopyData() 0031 { 0032 #ifndef QT_NO_CLIPBOARD 0033 QClipboard *clip = QApplication::clipboard(); 0034 // put the data into the mouse selection and the clipboard 0035 if (mDataToCopy.userType() == QMetaType::QPixmap) { 0036 clip->setPixmap(mDataToCopy.value<QPixmap>(), QClipboard::Clipboard); 0037 clip->setPixmap(mDataToCopy.value<QPixmap>(), QClipboard::Selection); 0038 } else { 0039 clip->setText(mDataToCopy.toString(), QClipboard::Clipboard); 0040 clip->setText(mDataToCopy.toString(), QClipboard::Selection); 0041 } 0042 #endif 0043 } 0044 0045 #ifndef QT_NO_CONTEXTMENU 0046 void TextBrowser::contextMenuEvent(QContextMenuEvent *event) 0047 { 0048 #ifndef QT_NO_CLIPBOARD 0049 QMenu popup; 0050 0051 QAction *act = KStandardAction::copy(this, &TextBrowser::copy, this); 0052 act->setEnabled(!textCursor().selectedText().isEmpty()); 0053 act->setShortcut(QKeySequence()); 0054 popup.addAction(act); 0055 0056 // Create a new action to correspond with what is under the click 0057 act = new QAction(i18nc("@action:inmenu Copy the text of a general item", "Copy Item"), this); 0058 0059 mDataToCopy.clear(); // nothing found to copy yet 0060 0061 QString link = anchorAt(event->pos()); 0062 if (!link.isEmpty()) { 0063 if (link.startsWith(QLatin1StringView("mailto:"))) { 0064 mDataToCopy = KCodecs::decodeRFC2047String(QUrl(link).path()); 0065 // Action text matches that used in KMail 0066 act->setText(i18nc("@action:inmenu Copy a displayed email address", "Copy Email Address")); 0067 } else { 0068 // A link, but it could be one of our internal ones. There is 0069 // no point in copying these. Internal links are always in the 0070 // form "protocol:?argument", whereas valid external links should 0071 // be in the form starting with "protocol://". 0072 if (!link.contains(QRegularExpression(QStringLiteral("^\\w+:\\?")))) { 0073 mDataToCopy = link; 0074 // Action text matches that used in Konqueror 0075 act->setText(i18nc("@action:inmenu Copy a link URL", "Copy Link URL")); 0076 } 0077 } 0078 } 0079 0080 if (!mDataToCopy.isValid()) { // no link was found above 0081 QTextCursor curs = cursorForPosition(event->pos()); 0082 QString text = curs.block().text(); // try the text under cursor 0083 0084 if (!text.isEmpty()) { 0085 // curs().block().text() over an image (contact photo or QR code) 0086 // returns a string starting with the character 0xFFFC (Unicode 0087 // object replacement character). See the documentation for 0088 // QTextImageFormat. 0089 if (text.startsWith(QChar(0xFFFC))) { 0090 QTextCharFormat charFormat = curs.charFormat(); 0091 if (charFormat.isImageFormat()) { 0092 const QTextImageFormat imageFormat = charFormat.toImageFormat(); 0093 const QString imageName = imageFormat.name(); 0094 const QVariant imageResource = document()->resource(QTextDocument::ImageResource, QUrl(imageName)); 0095 0096 const auto pix = imageResource.value<QPixmap>(); 0097 if (!pix.isNull()) { 0098 // There may be other images (e.g. contact type icons) that 0099 // there is no point in copying. 0100 if (imageName == QLatin1StringView("contact_photo")) { 0101 mDataToCopy = pix; 0102 act->setText(i18nc("@action:inmenu Copy a contact photo", "Copy Photo")); 0103 } else if (imageName == QLatin1StringView("qrcode")) { 0104 mDataToCopy = pix; 0105 act->setText(i18nc("@action:inmenu Copy a QR code image", "Copy Code")); 0106 } 0107 } 0108 } 0109 } else { 0110 // Added by our formatter (but not I18N'ed) for a mobile 0111 // telephone number. See 0112 // kdepim/kaddressbook/grantlee/grantleecontactformatter.cpp and 0113 // kdepimlibs/akonadi/contact/standardcontactformatter.cpp 0114 text.remove(QRegularExpression(QStringLiteral("\\s*\\(SMS\\)$"))); 0115 0116 // For an item which was formatted with line breaks (as <br> 0117 // in HTML), the returned text contains the character 0x2028 0118 // (Unicode line separator). Convert any of these back to newlines. 0119 text.replace(QChar(0x2028), QLatin1Char('\n')); 0120 0121 mDataToCopy = text; 0122 } 0123 } 0124 } 0125 0126 if (mDataToCopy.isValid()) { 0127 connect(act, &QAction::triggered, this, &TextBrowser::slotCopyData); 0128 } else { 0129 act->setEnabled(false); 0130 } 0131 0132 popup.addAction(act); 0133 popup.exec(event->globalPos()); 0134 #endif 0135 } 0136 0137 #endif 0138 0139 #include "moc_textbrowser_p.cpp"