Akonadi Contacts

textbrowser.cpp
1 /*
2  SPDX-FileCopyrightText: 2012-2023 Laurent Montel <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 
6 */
7 
8 #include "textbrowser_p.h"
9 #include <KCodecs>
10 #include <KLocalizedString>
11 #include <KStandardAction>
12 #include <QAction>
13 #include <QUrl>
14 
15 #include <QApplication>
16 #include <QClipboard>
17 #include <QContextMenuEvent>
18 #include <QMenu>
19 #include <QRegularExpression>
20 #include <QTextBlock>
21 
22 using namespace Akonadi;
23 
24 TextBrowser::TextBrowser(QWidget *parent)
25  : QTextBrowser(parent)
26 {
27  setOpenLinks(false);
28 }
29 
30 void TextBrowser::slotCopyData()
31 {
32 #ifndef QT_NO_CLIPBOARD
34  // put the data into the mouse selection and the clipboard
35 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
36  if (mDataToCopy.type() == QVariant::Pixmap) {
37 #else
38  if (mDataToCopy.userType() == QMetaType::QPixmap) {
39 #endif
40  clip->setPixmap(mDataToCopy.value<QPixmap>(), QClipboard::Clipboard);
41  clip->setPixmap(mDataToCopy.value<QPixmap>(), QClipboard::Selection);
42  } else {
43  clip->setText(mDataToCopy.toString(), QClipboard::Clipboard);
44  clip->setText(mDataToCopy.toString(), QClipboard::Selection);
45  }
46 #endif
47 }
48 
49 #ifndef QT_NO_CONTEXTMENU
50 void TextBrowser::contextMenuEvent(QContextMenuEvent *event)
51 {
52 #ifndef QT_NO_CLIPBOARD
53  QMenu popup;
54 
55  QAction *act = KStandardAction::copy(this, &TextBrowser::copy, this);
56  act->setEnabled(!textCursor().selectedText().isEmpty());
57  act->setShortcut(QKeySequence());
58  popup.addAction(act);
59 
60  // Create a new action to correspond with what is under the click
61  act = new QAction(i18nc("@action:inmenu Copy the text of a general item", "Copy Item"), this);
62 
63  mDataToCopy.clear(); // nothing found to copy yet
64 
65  QString link = anchorAt(event->pos());
66  if (!link.isEmpty()) {
67  if (link.startsWith(QLatin1String("mailto:"))) {
68  mDataToCopy = KCodecs::decodeRFC2047String(QUrl(link).path());
69  // Action text matches that used in KMail
70  act->setText(i18nc("@action:inmenu Copy a displayed email address", "Copy Email Address"));
71  } else {
72  // A link, but it could be one of our internal ones. There is
73  // no point in copying these. Internal links are always in the
74  // form "protocol:?argument", whereas valid external links should
75  // be in the form starting with "protocol://".
76  if (!link.contains(QRegularExpression(QStringLiteral("^\\w+:\\?")))) {
77  mDataToCopy = link;
78  // Action text matches that used in Konqueror
79  act->setText(i18nc("@action:inmenu Copy a link URL", "Copy Link URL"));
80  }
81  }
82  }
83 
84  if (!mDataToCopy.isValid()) { // no link was found above
85  QTextCursor curs = cursorForPosition(event->pos());
86  QString text = curs.block().text(); // try the text under cursor
87 
88  if (!text.isEmpty()) {
89  // curs().block().text() over an image (contact photo or QR code)
90  // returns a string starting with the character 0xFFFC (Unicode
91  // object replacement character). See the documentation for
92  // QTextImageFormat.
93  if (text.startsWith(QChar(0xFFFC))) {
94  QTextCharFormat charFormat = curs.charFormat();
95  if (charFormat.isImageFormat()) {
96  const QTextImageFormat imageFormat = charFormat.toImageFormat();
97  const QString imageName = imageFormat.name();
98  const QVariant imageResource = document()->resource(QTextDocument::ImageResource, QUrl(imageName));
99 
100  const auto pix = imageResource.value<QPixmap>();
101  if (!pix.isNull()) {
102  // There may be other images (e.g. contact type icons) that
103  // there is no point in copying.
104  if (imageName == QLatin1String("contact_photo")) {
105  mDataToCopy = pix;
106  act->setText(i18nc("@action:inmenu Copy a contact photo", "Copy Photo"));
107  } else if (imageName == QLatin1String("qrcode")) {
108  mDataToCopy = pix;
109  act->setText(i18nc("@action:inmenu Copy a QR code image", "Copy Code"));
110  }
111  }
112  }
113  } else {
114  // Added by our formatter (but not I18N'ed) for a mobile
115  // telephone number. See
116  // kdepim/kaddressbook/grantlee/grantleecontactformatter.cpp and
117  // kdepimlibs/akonadi/contact/standardcontactformatter.cpp
118  text.remove(QRegularExpression(QStringLiteral("\\s*\\(SMS\\)$")));
119 
120  // For an item which was formatted with line breaks (as <br>
121  // in HTML), the returned text contains the character 0x2028
122  // (Unicode line separator). Convert any of these back to newlines.
123  text.replace(QChar(0x2028), QLatin1Char('\n'));
124 
125  mDataToCopy = text;
126  }
127  }
128  }
129 
130  if (mDataToCopy.isValid()) {
131  connect(act, &QAction::triggered, this, &TextBrowser::slotCopyData);
132  } else {
133  act->setEnabled(false);
134  }
135 
136  popup.addAction(act);
137  popup.exec(event->globalPos());
138 #endif
139 }
140 
141 #endif
T value() const const
QString text() const const
void setShortcut(const QKeySequence &shortcut)
QTextBlock block() const const
QAction * addAction(const QString &text)
QClipboard * clipboard()
KIOCORE_EXPORT CopyJob * link(const QList< QUrl > &src, const QUrl &destDir, JobFlags flags=DefaultFlags)
QAction * copy(const QObject *recvr, const char *slot, QObject *parent)
KCODECS_EXPORT QString decodeRFC2047String(const QByteArray &src, QByteArray *usedCS, const QByteArray &defaultCS=QByteArray(), CharsetOption option=NoOption)
bool isEmpty() const const
bool isImageFormat() const const
QString name() const const
void setText(const QString &text)
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
void setPixmap(const QPixmap &pixmap, QClipboard::Mode mode)
void setText(const QString &text, QClipboard::Mode mode)
QString & replace(int position, int n, QChar after)
QString & remove(int position, int n)
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
void triggered(bool checked)
void setEnabled(bool)
QString path(const QString &relativePath)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QTextImageFormat toImageFormat() const const
QAction * exec()
QTextCharFormat charFormat() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sat Apr 1 2023 04:09:05 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.