diff --git a/src/akonadi-contacts/contactviewer.cpp b/src/akonadi-contacts/contactviewer.cpp index fae9d5c9..8841908f 100644 --- a/src/akonadi-contacts/contactviewer.cpp +++ b/src/akonadi-contacts/contactviewer.cpp @@ -1,375 +1,366 @@ /* This file is part of Akonadi Contact. Copyright (c) 2009 Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "contactviewer.h" #include "contactmetadataakonadi_p.h" #include "attributes/contactmetadataattribute_p.h" #include "customfieldmanager_p.h" #include "standardcontactformatter.h" #include "textbrowser_p.h" #include "editor/im/improtocols.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Akonadi; class Q_DECL_HIDDEN ContactViewer::Private { public: Private(ContactViewer *parent) : mParent(parent) { mStandardContactFormatter = new StandardContactFormatter; mContactFormatter = mStandardContactFormatter; KConfig config(QStringLiteral("akonadi_contactrc")); KConfigGroup group(&config, QStringLiteral("View")); mShowQRCode = group.readEntry("QRCodes", true); mQRCode = Prison::createBarcode(Prison::QRCode); - mDataMatrix = Prison::createBarcode(Prison::DataMatrix); } ~Private() { delete mStandardContactFormatter; delete mQRCode; - delete mDataMatrix; } void updateView(const QVariantList &localCustomFieldDescriptions = QVariantList(), const QString &addressBookName = QString()) { static QPixmap defaultPixmap = QIcon::fromTheme(QStringLiteral("user-identity")).pixmap(QSize(100, 100)); static QPixmap defaultMapPixmap = QIcon::fromTheme(QStringLiteral("map-symbolic")).pixmap(QSize(16, 16)); static QPixmap defaultSmsPixmap = QIcon::fromTheme(IMProtocols::self()->icon(QStringLiteral("messaging/sms"))).pixmap(QSize(16, 16)); mParent->setWindowTitle(i18n("Contact %1", mCurrentContact.assembledName())); if (mCurrentContact.photo().isIntern()) { mBrowser->document()->addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("contact_photo")), mCurrentContact.photo().data()); } else if (!mCurrentContact.photo().url().isEmpty()) { QByteArray imageData; QImage image; KIO::TransferJob *job = KIO::get(QUrl(mCurrentContact.photo().url()), KIO::NoReload); QObject::connect(job, &KIO::TransferJob::data, [&imageData](KIO::Job *, const QByteArray &data) { imageData.append(data); }); if (job->exec()) { if (image.loadFromData(imageData)) { mBrowser->document()->addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("contact_photo")), image); } else { mBrowser->document()->addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("contact_photo")), defaultPixmap); } } else { mBrowser->document()->addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("contact_photo")), defaultPixmap); } } else { mBrowser->document()->addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("contact_photo")), defaultPixmap); } if (mCurrentContact.logo().isIntern()) { mBrowser->document()->addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("contact_logo")), mCurrentContact.logo().data()); } else if (!mCurrentContact.logo().url().isEmpty()) { QByteArray imageData; QImage image; KIO::TransferJob *job = KIO::get(QUrl(mCurrentContact.logo().url()), KIO::NoReload); QObject::connect(job, &KIO::TransferJob::data, [&imageData](KIO::Job *, const QByteArray &data) { imageData.append(data); }); if (job->exec()) { if (image.loadFromData(imageData)) { mBrowser->document()->addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("contact_logo")), image); } } } mBrowser->document()->addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("map_icon")), defaultMapPixmap); mBrowser->document()->addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("sms_icon")), defaultSmsPixmap); if (mShowQRCode) { KContacts::VCardConverter converter; KContacts::Addressee addr(mCurrentContact); addr.setPhoto(KContacts::Picture()); addr.setLogo(KContacts::Picture()); const QString data = QString::fromUtf8(converter.createVCard(addr)); if (mQRCode) { mQRCode->setData(data); mBrowser->document()->addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("qrcode")), mQRCode->toImage(QSizeF(50, 50))); } - if (mDataMatrix) { - mDataMatrix->setData(data); - mBrowser->document()->addResource(QTextDocument::ImageResource, - QUrl(QStringLiteral("datamatrix")), - mDataMatrix->toImage(QSizeF(50, 50))); - } } // merge local and global custom field descriptions QVector customFieldDescriptions; const CustomField::List globalCustomFields = CustomFieldManager::globalCustomFieldDescriptions(); customFieldDescriptions.reserve(localCustomFieldDescriptions.count() + globalCustomFields.count()); for (const QVariant &entry : qAsConst(localCustomFieldDescriptions)) { customFieldDescriptions << entry.toMap(); } for (const CustomField &field : qAsConst(globalCustomFields)) { QVariantMap description; description.insert(QStringLiteral("key"), field.key()); description.insert(QStringLiteral("title"), field.title()); customFieldDescriptions << description; } KContacts::Addressee contact(mCurrentContact); if (!addressBookName.isEmpty()) { contact.insertCustom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("AddressBook"), addressBookName); } mContactFormatter->setContact(contact); mContactFormatter->setCustomFieldDescriptions(customFieldDescriptions); mBrowser->setHtml(mContactFormatter->toHtml()); } void slotMailClicked(const QString &, const QString &email) { QString name, address; // remove the 'mailto:' and split into name and email address KContacts::Addressee::parseEmailAddress(email.mid(7), name, address); Q_EMIT mParent->emailClicked(name, address); } void slotUrlClicked(const QUrl &url) { const QUrlQuery query(url); const QString urlScheme(url.scheme()); if (urlScheme == QLatin1String("http") || urlScheme == QLatin1String("https")) { Q_EMIT mParent->urlClicked(url); } else if (urlScheme == QLatin1String("phone")) { const int pos = query.queryItemValue(QStringLiteral("index")).toInt(); const KContacts::PhoneNumber::List numbers = mCurrentContact.phoneNumbers(); if (pos < numbers.count()) { Q_EMIT mParent->phoneNumberClicked(numbers.at(pos)); } } else if (urlScheme == QLatin1String("sms")) { const int pos = query.queryItemValue(QStringLiteral("index")).toInt(); const KContacts::PhoneNumber::List numbers = mCurrentContact.phoneNumbers(); if (pos < numbers.count()) { Q_EMIT mParent->smsClicked(numbers.at(pos)); } } else if (urlScheme == QLatin1String("address")) { const int pos = query.queryItemValue(QStringLiteral("index")).toInt(); const KContacts::Address::List addresses = mCurrentContact.addresses(); if (pos < addresses.count()) { Q_EMIT mParent->addressClicked(addresses.at(pos)); } } else if (urlScheme == QLatin1String("mailto")) { QString name, address; // remove the 'mailto:' and split into name and email address KContacts::Addressee::parseEmailAddress(url.path(), name, address); Q_EMIT mParent->emailClicked(name, address); } } void slotParentCollectionFetched(KJob *job) { mParentCollectionFetchJob = nullptr; QString addressBookName; if (!job->error()) { CollectionFetchJob *fetchJob = qobject_cast(job); if (!fetchJob->collections().isEmpty()) { const Collection collection = fetchJob->collections().at(0); addressBookName = collection.displayName(); } } // load the local meta data of the item ContactMetaDataAkonadi metaData; metaData.load(mCurrentItem); updateView(metaData.customFieldDescriptions(), addressBookName); } QMetaObject::Connection mCollectionFetchJobConnection; KContacts::Addressee mCurrentContact; Item mCurrentItem; ContactViewer *mParent = nullptr; TextBrowser *mBrowser = nullptr; AbstractContactFormatter *mContactFormatter = nullptr; AbstractContactFormatter *mStandardContactFormatter = nullptr; CollectionFetchJob *mParentCollectionFetchJob = nullptr; Prison::AbstractBarcode *mQRCode = nullptr; - Prison::AbstractBarcode *mDataMatrix = nullptr; bool mShowQRCode = true; }; ContactViewer::ContactViewer(QWidget *parent) : QWidget(parent) , d(new Private(this)) { QVBoxLayout *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); d->mBrowser = new TextBrowser; connect(d->mBrowser, &TextBrowser::anchorClicked, this, [this](const QUrl &url) { d->slotUrlClicked(url); }); layout->addWidget(d->mBrowser); // always fetch full payload for contacts fetchScope().fetchFullPayload(); fetchScope().fetchAttribute(); fetchScope().setAncestorRetrieval(ItemFetchScope::Parent); } ContactViewer::~ContactViewer() { delete d; } Akonadi::Item ContactViewer::contact() const { return ItemMonitor::item(); } KContacts::Addressee ContactViewer::rawContact() const { return d->mCurrentContact; } void ContactViewer::setContactFormatter(AbstractContactFormatter *formatter) { if (formatter == nullptr) { d->mContactFormatter = d->mStandardContactFormatter; } else { d->mContactFormatter = formatter; delete d->mStandardContactFormatter; d->mStandardContactFormatter = nullptr; } } void ContactViewer::setContact(const Akonadi::Item &contact) { ItemMonitor::setItem(contact); } void ContactViewer::setRawContact(const KContacts::Addressee &contact) { d->mCurrentContact = contact; d->updateView(); } void ContactViewer::itemChanged(const Item &contactItem) { if (!contactItem.hasPayload()) { return; } d->mCurrentItem = contactItem; d->mCurrentContact = contactItem.payload(); // stop any running fetch job if (d->mParentCollectionFetchJob) { disconnect(d->mCollectionFetchJobConnection); delete d->mParentCollectionFetchJob; d->mParentCollectionFetchJob = nullptr; } d->mParentCollectionFetchJob = new CollectionFetchJob(contactItem.parentCollection(), CollectionFetchJob::Base, this); d->mCollectionFetchJobConnection = connect(d->mParentCollectionFetchJob, &CollectionFetchJob::result, this, [this](KJob *job) {d->slotParentCollectionFetched(job);}); } void ContactViewer::itemRemoved() { d->mBrowser->clear(); } void ContactViewer::updateView() { itemChanged(d->mCurrentItem); } void ContactViewer::setShowQRCode(bool b) { if (d->mShowQRCode != b) { d->mShowQRCode = b; updateView(); } } bool ContactViewer::showQRCode() const { return d->mShowQRCode; } #include "moc_contactviewer.cpp" diff --git a/src/akonadi-contacts/standardcontactformatter.cpp b/src/akonadi-contacts/standardcontactformatter.cpp index 70e22549..40a8764a 100644 --- a/src/akonadi-contacts/standardcontactformatter.cpp +++ b/src/akonadi-contacts/standardcontactformatter.cpp @@ -1,383 +1,381 @@ /* This file is part of Akonadi Contact. Copyright (c) 2010 Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "standardcontactformatter.h" #include #include #include #include #include #include #include #include using namespace Akonadi; class Q_DECL_HIDDEN StandardContactFormatter::Private { public: Private() : displayQRcode(true) { } bool displayQRcode; }; StandardContactFormatter::StandardContactFormatter() : d(new Private()) { } StandardContactFormatter::~StandardContactFormatter() { delete d; } static int contactAge(const QDate &date) { QDate now = QDate::currentDate(); int age = now.year() - date.year(); if (date > now.addYears(-age)) { age--; } return age; } QString StandardContactFormatter::toHtml(HtmlForm form) const { KContacts::Addressee rawContact; const Akonadi::Item localItem = item(); if (localItem.isValid() && localItem.hasPayload()) { rawContact = localItem.payload(); } else { rawContact = contact(); } if (rawContact.isEmpty()) { return QString(); } // We'll be building a table to display the vCard in. // Each row of the table will be built using one of these strings for its HTML. // single data item: // %1 is the item name // %2 is the item value QString rowFmtStr1 = QStringLiteral( "" "%1\n" "%2\n" "\n" ); // data item plus additional icon(s): // %1 is the item name // %2 is the item value // %3 is the icon(s), each as a HTML tag QString rowFmtStr2 = QStringLiteral( "" "%1\n" "%2\n" "%3\n" "\n" ); // Build the table's rows here QString dynamicPart; // Birthday const QDate date = rawContact.birthday().date(); const int years = contactAge(date); if (date.isValid()) { dynamicPart += rowFmtStr1 .arg(KContacts::Addressee::birthdayLabel()) .arg(QLocale().toString(date) +QLatin1String("  ") + i18np("(One year old)", "(%1 years old)", years)); } // Phone Numbers int counter = 0; for (const KContacts::PhoneNumber &number : rawContact.phoneNumbers()) { QString dispLabel = number.typeLabel().replace(QLatin1Char(' '), QStringLiteral(" ")); QString dispValue = QStringLiteral("%2").arg(counter).arg(number.number().toHtmlEscaped()); if (number.type() & KContacts::PhoneNumber::Cell) { QString dispIcon = QStringLiteral("") .arg(counter) .arg(i18nc("@info:tooltip", "Send SMS")); dynamicPart += rowFmtStr2 .arg(dispLabel) .arg(dispValue) .arg(dispIcon); } else { dynamicPart += rowFmtStr1 .arg(dispLabel) .arg(dispValue); } ++counter; } // EMails for (const QString &email : rawContact.emails()) { const QString type = i18nc("a contact's email address", "Email"); const QString fullEmail = QString::fromLatin1(QUrl::toPercentEncoding(rawContact.fullEmail(email))); dynamicPart += rowFmtStr1.arg(type) .arg(QStringLiteral("%2") .arg(fullEmail, email)); } // Homepage if (rawContact.url().isValid()) { QString url = rawContact.url().url().url(); if (!url.startsWith(QLatin1String("http://")) && !url.startsWith(QLatin1String("https://"))) { url = QLatin1String("http://") + url; } url = KStringHandler::tagUrls(url.toHtmlEscaped()); dynamicPart += rowFmtStr1.arg(i18n("Homepage")).arg(url); } // Blog Feed const QString blog = rawContact.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("BlogFeed")); if (!blog.isEmpty()) { dynamicPart += rowFmtStr1.arg(i18n("Blog Feed")).arg(KStringHandler::tagUrls(blog.toHtmlEscaped())); } // Addresses counter = 0; for (const KContacts::Address &address : rawContact.addresses()) { QString formattedAddress; if (address.label().isEmpty()) { formattedAddress = address.formattedAddress().trimmed().toHtmlEscaped(); } else { formattedAddress = address.label().toHtmlEscaped(); } formattedAddress = formattedAddress.replace(QRegExp(QStringLiteral("\n+")), QStringLiteral("
")); const QString url = QStringLiteral("\"%2\"/") .arg(counter) .arg(i18nc("@info:tooltip", "Show address on map")); counter++; dynamicPart += rowFmtStr2 .arg(KContacts::Address::typeLabel(address.type())) .arg(formattedAddress) .arg(url); } // Note QString notes; if (!rawContact.note().isEmpty()) { notes = rowFmtStr1.arg(i18n("Notes")).arg(rawContact.note().toHtmlEscaped().replace(QLatin1Char('\n'), QLatin1String("
"))); } // Custom Data QString customData; static QMap titleMap; if (titleMap.isEmpty()) { titleMap.insert(QStringLiteral("Department"), i18n("Department")); titleMap.insert(QStringLiteral("Profession"), i18n("Profession")); titleMap.insert(QStringLiteral("AssistantsName"), i18n("Assistant's Name")); titleMap.insert(QStringLiteral("ManagersName"), i18n("Manager's Name")); titleMap.insert(QStringLiteral("SpousesName"), i18nc("Wife/Husband/...", "Partner's Name")); titleMap.insert(QStringLiteral("Office"), i18n("Office")); titleMap.insert(QStringLiteral("IMAddress"), i18n("IM Address")); titleMap.insert(QStringLiteral("Anniversary"), i18n("Anniversary")); titleMap.insert(QStringLiteral("AddressBook"), i18n("Address Book")); QMap upperCaseTitleMap; QMap::const_iterator iterator = titleMap.constBegin(); while (iterator != titleMap.constEnd()) { upperCaseTitleMap.insert(iterator.key().toUpper(), iterator.value()); ++iterator; } titleMap.unite(upperCaseTitleMap); } static QSet blacklistedKeys; if (blacklistedKeys.isEmpty()) { blacklistedKeys.insert(QStringLiteral("CRYPTOPROTOPREF")); blacklistedKeys.insert(QStringLiteral("OPENPGPFP")); blacklistedKeys.insert(QStringLiteral("SMIMEFP")); blacklistedKeys.insert(QStringLiteral("CRYPTOSIGNPREF")); blacklistedKeys.insert(QStringLiteral("CRYPTOENCRYPTPREF")); blacklistedKeys.insert(QStringLiteral("MailPreferedFormatting")); blacklistedKeys.insert(QStringLiteral("MailAllowToRemoteContent")); blacklistedKeys.insert(QStringLiteral("MAILPREFEREDFORMATTING")); blacklistedKeys.insert(QStringLiteral("MAILALLOWTOREMOTECONTENT")); } if (!rawContact.customs().empty()) { const QStringList customs = rawContact.customs(); for (QString custom : customs) { if (custom.startsWith(QLatin1String("KADDRESSBOOK-"))) { custom.remove(QStringLiteral("KADDRESSBOOK-X-")); custom.remove(QStringLiteral("KADDRESSBOOK-")); int pos = custom.indexOf(QLatin1Char(':')); QString key = custom.left(pos); QString value = custom.mid(pos + 1); // convert anniversary correctly if (key == QLatin1String("Anniversary") || key == QLatin1String("ANNIVERSARY")) { const QDateTime dateTime = QDateTime::fromString(value, Qt::ISODate); value = QLocale().toString(dateTime.date()); } else if (key == QLatin1String("BlogFeed") || key == QLatin1String("BLOGFEED")) { // blog is handled separated continue; } else if (blacklistedKeys.contains(key)) { continue; } // check whether we have a mapping for the title const QMap::ConstIterator keyIt = titleMap.constFind(key); bool needToEscape = true; if (keyIt != titleMap.constEnd()) { key = keyIt.value(); } else { // check whether it is a custom local field for (const QVariantMap &description : customFieldDescriptions()) { if (description.value(QStringLiteral("key")).toString() == key) { key = description.value(QStringLiteral("title")).toString(); const QString descriptionType = description.value(QStringLiteral("type")).toString(); if (descriptionType == QLatin1String("boolean")) { if (value == QLatin1String("true")) { value = i18nc("Boolean value", "yes"); } else { value = i18nc("Boolean value", "no"); } } else if (descriptionType == QLatin1String("date")) { const QDate date = QDate::fromString(value, Qt::ISODate); value = QLocale().toString(date, QLocale::ShortFormat); } else if (descriptionType == QLatin1String("time")) { const QTime time = QTime::fromString(value, Qt::ISODate); value = QLocale().toString(time); } else if (descriptionType == QLatin1String("datetime")) { const QDateTime dateTime = QDateTime::fromString(value, Qt::ISODate); value = QLocale().toString(dateTime, QLocale::ShortFormat); } else if (descriptionType == QLatin1String("url")) { value = KStringHandler::tagUrls(value.toHtmlEscaped()); needToEscape = false; } break; } } } if (needToEscape) { value = value.toHtmlEscaped(); } customData += rowFmtStr1.arg(key).arg(value); } } } // Assemble all parts QString role = rawContact.title(); if (role.isEmpty()) { role = rawContact.role(); } if (role.isEmpty()) { role = rawContact.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-Profession")); } QString strAddr = QStringLiteral( "
" "" "" "" "" // name "" "" "" // role "" "" "" // organization "") .arg(QStringLiteral("contact_photo")) .arg(rawContact.realName().toHtmlEscaped()) .arg(role.toHtmlEscaped()) .arg(rawContact.organization().toHtmlEscaped()); strAddr.append(dynamicPart); strAddr.append(notes); strAddr.append(customData); strAddr.append(QLatin1String("
" "" // image "%2
%3
%4
")); if (d->displayQRcode) { KConfig config(QStringLiteral("akonadi_contactrc")); KConfigGroup group(&config, QStringLiteral("View")); if (group.readEntry("QRCodes", true)) { strAddr.append(QString::fromLatin1( "

" "" - "" "

" ) - .arg(QStringLiteral("datamatrix")) .arg(QStringLiteral("qrcode"))); } } strAddr.append(QLatin1String("
\n")); if (form == EmbeddableForm) { return strAddr; } const QString document = QStringLiteral( "" "" " " "" "" // text and background color "%3" // contact part "" "") .arg(KColorScheme(QPalette::Active, KColorScheme::View).foreground().color().name()) .arg(KColorScheme(QPalette::Active, KColorScheme::View).background().color().name()) .arg(strAddr); return document; } void StandardContactFormatter::setDisplayQRCode(bool show) { d->displayQRcode = show; } bool StandardContactFormatter::displayQRCode() const { return d->displayQRcode; } diff --git a/src/akonadi-contacts/textbrowser.cpp b/src/akonadi-contacts/textbrowser.cpp index f6db4dc6..9e0c7ceb 100644 --- a/src/akonadi-contacts/textbrowser.cpp +++ b/src/akonadi-contacts/textbrowser.cpp @@ -1,152 +1,151 @@ /* Copyright (c) 2012-2019 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "textbrowser_p.h" #include #include #include #include #include #include #include #include #include #include #include using namespace Akonadi; TextBrowser::TextBrowser(QWidget *parent) : QTextBrowser(parent) { setOpenLinks(false); } void TextBrowser::slotCopyData() { #ifndef QT_NO_CLIPBOARD QClipboard *clip = QApplication::clipboard(); // put the data into the mouse selection and the clipboard if (mDataToCopy.type() == QVariant::Pixmap) { clip->setPixmap(mDataToCopy.value(), QClipboard::Clipboard); clip->setPixmap(mDataToCopy.value(), QClipboard::Selection); } else { clip->setText(mDataToCopy.toString(), QClipboard::Clipboard); clip->setText(mDataToCopy.toString(), QClipboard::Selection); } #endif } #ifndef QT_NO_CONTEXTMENU void TextBrowser::contextMenuEvent(QContextMenuEvent *event) { #ifndef QT_NO_CLIPBOARD QMenu popup; QAction *act = KStandardAction::copy(this, &TextBrowser::copy, this); act->setEnabled(!textCursor().selectedText().isEmpty()); act->setShortcut(QKeySequence()); popup.addAction(act); // Create a new action to correspond with what is under the click act = new QAction(i18nc("@action:inmenu Copy the text of a general item", "Copy Item"), this); mDataToCopy.clear(); // nothing found to copy yet QString link = anchorAt(event->pos()); if (!link.isEmpty()) { if (link.startsWith(QLatin1String("mailto:"))) { mDataToCopy = KCodecs::decodeRFC2047String(QUrl(link).path()); // Action text matches that used in KMail act->setText(i18nc("@action:inmenu Copy a displayed email address", "Copy Email Address")); } else { // A link, but it could be one of our internal ones. There is // no point in copying these. Internal links are always in the // form "protocol:?argument", whereas valid external links should // be in the form starting with "protocol://". if (!link.contains(QRegExp(QStringLiteral("^\\w+:\\?")))) { mDataToCopy = link; // Action text matches that used in Konqueror act->setText(i18nc("@action:inmenu Copy a link URL", "Copy Link URL")); } } } if (!mDataToCopy.isValid()) { // no link was found above QTextCursor curs = cursorForPosition(event->pos()); QString text = curs.block().text(); // try the text under cursor if (!text.isEmpty()) { // curs().block().text() over an image (contact photo or QR code) // returns a string starting with the character 0xFFFC (Unicode // object replacement character). See the documentation for // QTextImageFormat. if (text.startsWith(QChar(0xFFFC))) { QTextCharFormat charFormat = curs.charFormat(); if (charFormat.isImageFormat()) { QTextImageFormat imageFormat = charFormat.toImageFormat(); QString imageName = imageFormat.name(); QVariant imageResource = document()->resource(QTextDocument::ImageResource, QUrl(imageName)); QPixmap pix = imageResource.value(); if (!pix.isNull()) { // There may be other images (e.g. contact type icons) that // there is no point in copying. if (imageName == QLatin1String("contact_photo")) { mDataToCopy = pix; act->setText(i18nc("@action:inmenu Copy a contact photo", "Copy Photo")); - } else if (imageName == QLatin1String("datamatrix") - || imageName == QLatin1String("qrcode")) { + } else if (imageName == QLatin1String("qrcode")) { mDataToCopy = pix; - act->setText(i18nc("@action:inmenu Copy a QR or Datamatrix image", "Copy Code")); + act->setText(i18nc("@action:inmenu Copy a QR code image", "Copy Code")); } } } } else { // Added by our formatter (but not I18N'ed) for a mobile // telephone number. See // kdepim/kaddressbook/grantlee/grantleecontactformatter.cpp and // kdepimlibs/akonadi/contact/standardcontactformatter.cpp text.remove(QRegExp(QStringLiteral("\\s*\\(SMS\\)$"))); // For an item which was formatted with line breaks (as
// in HTML), the returned text contains the character 0x2028 // (Unicode line separator). Convert any of these back to newlines. text.replace(QChar(0x2028), QLatin1Char('\n')); mDataToCopy = text; } } } if (mDataToCopy.isValid()) { connect(act, &QAction::triggered, this, &TextBrowser::slotCopyData); } else { act->setEnabled(false); } popup.addAction(act); popup.exec(event->globalPos()); #endif } #endif